Files
opennurbs/opennurbs_subd.cpp
Bozo The Builder 799431a63b Sync changes from upstream repository
Co-authored-by: Steve Baer <steve@mcneel.com>
Co-authored-by: Nathan Letwory <nathan@mcneel.com>
Co-authored-by: Dale Lear <dalelear@mcneel.com>
2019-11-06 16:24:57 +00:00

15568 lines
432 KiB
C++

#include "opennurbs.h"
#if !defined(ON_COMPILING_OPENNURBS)
// This check is included in all opennurbs source .c and .cpp files to insure
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined
// and the opennurbs .h files alter what is declared and how it is declared.
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
#endif
#include "opennurbs_subd_data.h"
/* $NoKeywords: $ */
/*
//
// Copyright (c) 1993-2015 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>.
//
////////////////////////////////////////////////////////////////
*/
ON_SubDComponentPtr::Type ON_SubDComponentPtr::ComponentPtrTypeFromUnsigned(
unsigned int element_pointer_type_as_unsigned
)
{
switch (element_pointer_type_as_unsigned)
{
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDComponentPtr::Type::Unset);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDComponentPtr::Type::Vertex);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDComponentPtr::Type::Edge);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDComponentPtr::Type::Face);
}
return ON_SUBD_RETURN_ERROR(ON_SubDComponentPtr::Type::Unset);
}
ON_SubD::VertexTag ON_SubD::VertexTagFromUnsigned(
unsigned int vertex_tag_as_unsigned
)
{
switch (vertex_tag_as_unsigned)
{
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Unset);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Smooth);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Crease);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Corner);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Dart);
}
return ON_SUBD_RETURN_ERROR(ON_SubD::VertexTag::Unset);
}
bool ON_SubD::VertexTagIsSet(
ON_SubD::VertexTag vertex_tag
)
{
return (
ON_SubD::VertexTag::Smooth == vertex_tag
|| ON_SubD::VertexTag::Crease == vertex_tag
|| ON_SubD::VertexTag::Corner == vertex_tag
|| ON_SubD::VertexTag::Dart == vertex_tag
);
}
ON_SubD::EdgeTag ON_SubD::EdgeTagFromUnsigned(
unsigned int edge_tag_as_unsigned
)
{
switch (edge_tag_as_unsigned)
{
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Unset);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Smooth);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Crease);
//ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Sharp);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::SmoothX);
}
return ON_SUBD_RETURN_ERROR(ON_SubD::EdgeTag::Unset);
}
bool ON_SubD::EdgeTagIsSet(
ON_SubD::EdgeTag edge_tag
)
{
return (
ON_SubD::EdgeTag::Smooth == edge_tag
|| ON_SubD::EdgeTag::Crease == edge_tag
//|| ON_SubD::EdgeTag::Sharp == edge_tag
|| ON_SubD::EdgeTag::SmoothX == edge_tag
);
}
ON_SubD::VertexFacetType ON_SubD::VertexFacetTypeFromUnsigned(
unsigned int vertex_facet_type_as_unsigned
)
{
switch (vertex_facet_type_as_unsigned)
{
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexFacetType::Unset);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexFacetType::Tri);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexFacetType::Quad);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexFacetType::Ngon);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexFacetType::Mixed);
}
return ON_SUBD_RETURN_ERROR(ON_SubD::VertexFacetType::Unset);
}
unsigned int ON_SubDSectorType::SectorPointRingCountFromEdgeCount(
ON_SubD::VertexTag vertex_tag,
unsigned int sector_edge_count
)
{
if (sector_edge_count >= ON_SubDSectorType::MinimumSectorEdgeCount(vertex_tag) && sector_edge_count <= ON_SubDVertex::MaximumEdgeCount)
{
if (ON_SubD::VertexTag::Smooth == vertex_tag || ON_SubD::VertexTag::Dart == vertex_tag)
{
// interior vertex
return (2 * sector_edge_count + 1);
}
if (ON_SubD::VertexTag::Crease == vertex_tag || ON_SubD::VertexTag::Corner == vertex_tag)
{
// boundary vertex
return (2 * sector_edge_count);
}
}
return ON_SUBD_RETURN_ERROR(0);
}
unsigned int ON_SubDSectorType::SectorPointRingCountFromFaceCount(
ON_SubD::VertexTag vertex_tag,
unsigned int sector_face_count
)
{
unsigned int sector_edge_count = ON_SubDSectorType::SectorEdgeCountFromFaceCount(vertex_tag,sector_face_count);
return (sector_edge_count > 0)
? ON_SubDSectorType::SectorPointRingCountFromEdgeCount(vertex_tag,sector_edge_count)
: ON_SUBD_RETURN_ERROR(0);
}
unsigned int ON_SubDSectorType::SectorFaceCountFromEdgeCount(
ON_SubD::VertexTag vertex_tag,
unsigned int sector_edge_count
)
{
if (sector_edge_count >= 2 && sector_edge_count <= ON_SubDVertex::MaximumEdgeCount)
{
unsigned int sector_face_count
= (ON_SubD::VertexTag::Crease == vertex_tag || ON_SubD::VertexTag::Corner == vertex_tag)
? sector_edge_count-1
: sector_edge_count;
return sector_face_count;
}
return ON_SUBD_RETURN_ERROR(0);
}
unsigned int ON_SubDSectorType::SectorEdgeCountFromFaceCount(
ON_SubD::VertexTag vertex_tag,
unsigned int sector_face_count
)
{
if (sector_face_count > 0 && sector_face_count <= ON_SubDVertex::MaximumFaceCount)
{
unsigned int sector_edge_count
= (ON_SubD::VertexTag::Crease == vertex_tag || ON_SubD::VertexTag::Corner == vertex_tag)
? sector_face_count+1
: sector_face_count;
return sector_edge_count;
}
return ON_SUBD_RETURN_ERROR(0);
}
unsigned int ON_SubDSectorType::MinimumSectorEdgeCount(
ON_SubD::VertexTag vertex_tag
)
{
if (ON_SubD::VertexTag::Smooth == vertex_tag || ON_SubD::VertexTag::Dart == vertex_tag)
return ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag);
if (ON_SubD::VertexTag::Corner == vertex_tag || ON_SubD::VertexTag::Crease == vertex_tag)
return ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag)+1;
return ON_UNSET_UINT_INDEX;
}
unsigned int ON_SubDSectorType::MinimumSectorFaceCount(
ON_SubD::VertexTag vertex_tag
)
{
unsigned int minimum_sector_face_count;
switch (vertex_tag)
{
case ON_SubD::VertexTag::Unset:
ON_SUBD_ERROR("Unset tag.");
minimum_sector_face_count = ON_UNSET_UINT_INDEX;
break;
case ON_SubD::VertexTag::Smooth:
// April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Smooth
// See comments in ON_SubDSectorType::GetSurfaceEvaluationCoefficients()
// for more details on how this case is handled.
minimum_sector_face_count = 2; // 3 without special case handling
break;
case ON_SubD::VertexTag::Crease:
// A "wire" crease can have zero faces - this is the minimum when faces exist
minimum_sector_face_count = 1;
break;
case ON_SubD::VertexTag::Corner:
// A "wire" corner can have zero faces - this is the minimum when faces exist
minimum_sector_face_count = 1;
break;
case ON_SubD::VertexTag::Dart:
// April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Dart
// See comments in ON_SubDSectorType::GetSurfaceEvaluationCoefficients()
// for more details on how this case is handled.
minimum_sector_face_count = 2; // 3 without special case handling
break;
default:
ON_SUBD_ERROR("Invalid tag.");
minimum_sector_face_count = ON_UNSET_UINT_INDEX;
break;
}
return minimum_sector_face_count;
}
bool ON_SubD::IsValidSectorEdgeCount(
ON_SubD::VertexTag vertex_tag,
unsigned int sector_edge_count
)
{
return (sector_edge_count >= ON_SubDSectorType::MinimumSectorEdgeCount(vertex_tag) && sector_edge_count <= ON_SubDVertex::MaximumEdgeCount);
}
bool ON_SubD::IsValidSectorFaceCount(
ON_SubD::VertexTag vertex_tag,
unsigned int sector_face_count
)
{
return (sector_face_count >= ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag) && sector_face_count <= ON_SubDVertex::MaximumFaceCount);
}
//////////////////////////////////////////////////////////////////////////
//
// ON_SubDVertexPtr
//
//
bool ON_SubDVertexPtr::IsNull() const
{
return (nullptr == ON_SUBD_VERTEX_POINTER(m_ptr));
}
bool ON_SubDVertexPtr::IsNotNull() const
{
return (nullptr != ON_SUBD_VERTEX_POINTER(m_ptr));
}
class ON_SubDVertex* ON_SubDVertexPtr::Vertex() const
{
return ON_SUBD_VERTEX_POINTER(m_ptr);
}
ON__UINT_PTR ON_SubDVertexPtr::VertexDirection() const
{
return ON_SUBD_VERTEX_DIRECTION(m_ptr);
}
const ON_ComponentStatus ON_SubDVertexPtr::Status() const
{
const ON_SubDVertex* vertex = ON_SUBD_VERTEX_POINTER(m_ptr);
return (nullptr == vertex) ? ON_ComponentStatus::NoneSet : vertex->m_status;
}
const ON_SubDVertexPtr ON_SubDVertexPtr::Create(
const class ON_SubDVertex* vertex
)
{
return ON_SubDVertexPtr::Create(vertex,0);
}
const ON_SubDVertexPtr ON_SubDVertexPtr::Create(
const class ON_SubDVertex* vertex,
ON__UINT_PTR vertex_mark
)
{
ON_SubDVertexPtr vptr = { (ON__UINT_PTR)vertex | (vertex_mark & ON_SUBD_COMPONENT_DIRECTION_MASK) };
return vptr;
}
const ON_SubDVertexPtr ON_SubDVertexPtr::Create(
const class ON_SubDComponentPtr& vertex_element
)
{
return ON_SubDVertexPtr::Create(vertex_element.Vertex(), vertex_element.ComponentDirection());
}
//////////////////////////////////////////////////////////////////////////
//
// ON_SubDEdgePtr
//
bool ON_SubDEdgePtr::IsNull() const
{
return (nullptr == ON_SUBD_EDGE_POINTER(m_ptr));
}
bool ON_SubDEdgePtr::IsNotNull() const
{
return (nullptr != ON_SUBD_EDGE_POINTER(m_ptr));
}
bool ON_SubDEdgePtr::IsNotNullAndVerticesAreNotNull() const
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr);
return (nullptr != e && nullptr != e->m_vertex[0] && nullptr != e->m_vertex[1]);
}
class ON_SubDEdge* ON_SubDEdgePtr::Edge() const
{
return ON_SUBD_EDGE_POINTER(m_ptr);
}
unsigned int ON_SubDEdgePtr::EdgeId() const
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr);
return (nullptr != e) ? e->m_id : 0U;
}
unsigned int ON_SubDEdgePtr::EdgeFaceCount() const
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr);
return (nullptr != e) ? ((unsigned int)e->m_face_count) : 0U;
}
bool ON_SubDEdgePtr::EdgeIsSmooth() const
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr);
return (nullptr != e) ? e->IsSmooth() : false;
}
bool ON_SubDEdgePtr::EdgeIsCrease() const
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr);
return (nullptr != e) ? e->IsCrease() : false;
}
bool ON_SubDEdgePtr::EdgeIsHardCrease() const
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr);
return (nullptr != e) ? e->IsHardCrease() : false;
}
bool ON_SubDEdgePtr::EdgeIsDartCrease() const
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr);
return (nullptr != e) ? e->IsDartCrease() : false;
}
ON__UINT_PTR ON_SubDEdgePtr::EdgeDirection() const
{
return ON_SUBD_EDGE_DIRECTION(m_ptr);
}
const class ON_SubDVertex* ON_SubDEdgePtr::RelativeVertex(
int relative_vertex_index
) const
{
for (;;)
{
if (relative_vertex_index < 0 || relative_vertex_index>1)
break;
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr);
if (nullptr == edge)
break;
if (0 != ON_SUBD_EDGE_DIRECTION(m_ptr))
relative_vertex_index = 1 - relative_vertex_index;
return edge->m_vertex[relative_vertex_index];
}
return nullptr;
}
const ON_3dVector ON_SubDEdgePtr::RelativeDirection() const
{
for (;;)
{
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr);
if (nullptr == edge)
break;
if (nullptr == edge->m_vertex[0] || nullptr == edge->m_vertex[1])
break;
const int i0 = (0 != ON_SUBD_EDGE_DIRECTION(m_ptr)) ? 1 : 0;
const ON_3dPoint P0(edge->m_vertex[i0]->m_P);
const ON_3dPoint P1(edge->m_vertex[1-i0]->m_P);
return (P1 - P0);
}
return ON_3dVector::NanVector;
}
const ON_ComponentStatus ON_SubDEdgePtr::Status() const
{
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr);
return (nullptr == edge) ? ON_ComponentStatus::NoneSet : edge->m_status;
}
const ON_SubDEdgePtr ON_SubDEdgePtr::Reversed() const
{
return ON_SubDEdgePtr::Create(ON_SUBD_EDGE_POINTER(m_ptr), 1 - (m_ptr & 1));
}
const ON_SubDEdgePtr ON_SubDEdgePtr::Create(
const class ON_SubDEdge* edge
)
{
ON_SubDEdgePtr eptr = { (ON__UINT_PTR)edge };
return eptr;
}
const ON_SubDEdgePtr ON_SubDEdgePtr::Create(
const class ON_SubDEdge* edge,
ON__UINT_PTR direction
)
{
ON_SubDEdgePtr eptr = { (ON__UINT_PTR)edge | (direction & ON_SUBD_COMPONENT_DIRECTION_MASK) };
return eptr;
}
const ON_SubDEdgePtr ON_SubDEdgePtr::Create(
const class ON_SubDComponentPtr& edge_element
)
{
return ON_SubDEdgePtr::Create(edge_element.Edge(), edge_element.ComponentDirection());
}
//////////////////////////////////////////////////////////////////////////
//
// ON_SubDFacePtr
//
bool ON_SubDFacePtr::IsNull() const
{
return (nullptr == ON_SUBD_FACE_POINTER(m_ptr));
}
bool ON_SubDFacePtr::IsNotNull() const
{
return (nullptr != ON_SUBD_FACE_POINTER(m_ptr));
}
ON_SubDFace* ON_SubDFacePtr::Face() const
{
return ON_SUBD_FACE_POINTER(m_ptr);
}
unsigned int ON_SubDFacePtr::FaceId() const
{
const ON_SubDFace* f = ON_SUBD_FACE_POINTER(m_ptr);
return (nullptr != f) ? f->m_id : 0U;
}
unsigned int ON_SubDFacePtr::FaceEdgeCount() const
{
const ON_SubDFace* f = ON_SUBD_FACE_POINTER(m_ptr);
return (nullptr != f) ? ((unsigned int)f->m_edge_count) : 0U;
}
ON__UINT_PTR ON_SubDFacePtr::FaceDirection() const
{
return ON_SUBD_FACE_DIRECTION(m_ptr);
}
const ON_ComponentStatus ON_SubDFacePtr::Status() const
{
const ON_SubDFace* face = ON_SUBD_FACE_POINTER(m_ptr);
return (nullptr == face) ? ON_ComponentStatus::NoneSet : face->m_status;
}
const ON_SubDFacePtr ON_SubDFacePtr::Create(
const class ON_SubDFace* face,
ON__UINT_PTR direction
)
{
ON_SubDFacePtr fptr = { (ON__UINT_PTR)face | (direction & ON_SUBD_COMPONENT_DIRECTION_MASK) };
return fptr;
}
const ON_SubDFacePtr ON_SubDFacePtr::Create(
const class ON_SubDComponentPtr& face_element
)
{
return ON_SubDFacePtr::Create(face_element.Face(), face_element.ComponentDirection());
}
int ON_SubDFacePtr::Compare(
const ON_SubDFacePtr* lhs,
const ON_SubDFacePtr* rhs
)
{
if ( nullptr == lhs )
return 1;
if ( nullptr == rhs )
return -1;
if (lhs->m_ptr < rhs->m_ptr)
return -1;
if (lhs->m_ptr > rhs->m_ptr)
return 1;
return 0;
}
int ON_SubDFacePtr::CompareFacePointer(
const ON_SubDFacePtr* lhs,
const ON_SubDFacePtr* rhs
)
{
if (lhs == rhs)
return 0;
if ( nullptr == lhs )
return 1;
if ( nullptr == rhs )
return -1;
const ON__UINT_PTR lhs_ptr = (lhs->m_ptr & ON_SUBD_COMPONENT_POINTER_MASK);
const ON__UINT_PTR rhs_ptr = (rhs->m_ptr & ON_SUBD_COMPONENT_POINTER_MASK);
if (lhs_ptr < rhs_ptr)
return -1;
if (lhs_ptr > rhs_ptr)
return 1;
return 0;
}
//////////////////////////////////////////////////////////////////////////
//
// ON_SubDComponentPtr
//
bool ON_SubDComponentPtr::IsNull() const
{
return (0 == (ON_SUBD_COMPONENT_POINTER_MASK && m_ptr));
}
bool ON_SubDComponentPtr::IsNotNull() const
{
if (nullptr != ON_SUBD_COMPONENT_POINTER(m_ptr))
{
switch (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)
{
case ON_SUBD_COMPONENT_TYPE_VERTEX:
case ON_SUBD_COMPONENT_TYPE_EDGE:
case ON_SUBD_COMPONENT_TYPE_FACE:
return true;
}
}
return false;
}
unsigned int ON_SubDComponentPtr::ComponentId() const
{
const ON_SubDComponentBase* c = this->ComponentBase();
return (nullptr != c) ? c->m_id : 0U;
}
const ON_3dPoint ON_SubDComponentPtr::ControlNetCenterPoint() const
{
switch (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)
{
case ON_SUBD_COMPONENT_TYPE_VERTEX:
{
const ON_SubDVertex* v = Vertex();
if (nullptr != v)
return v->ControlNetPoint();
}
break;
case ON_SUBD_COMPONENT_TYPE_EDGE:
{
const ON_SubDEdge* e = Edge();
if (nullptr != e)
return e->ControlNetCenterPoint();
}
break;
case ON_SUBD_COMPONENT_TYPE_FACE:
{
const ON_SubDFace* f = Face();
if (nullptr != f)
return f->ControlNetCenterPoint();
}
break;
}
return ON_3dPoint::NanPoint;
}
const ON_BoundingBox ON_SubDComponentPtr::ControlNetBoundingBox() const
{
switch (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)
{
case ON_SUBD_COMPONENT_TYPE_VERTEX:
{
const ON_SubDVertex* v = Vertex();
if (nullptr != v)
return v->ControlNetBoundingBox();
}
break;
case ON_SUBD_COMPONENT_TYPE_EDGE:
{
const ON_SubDEdge* e = Edge();
if (nullptr != e)
return e->ControlNetBoundingBox();
}
break;
case ON_SUBD_COMPONENT_TYPE_FACE:
{
const ON_SubDFace* f = Face();
if (nullptr != f)
return f->ControlNetBoundingBox();
}
break;
}
return ON_BoundingBox::NanBoundingBox;
}
ON__UINT16 ON_SubDComponentPtr::Hash16FromTypeAndId() const
{
const ON_SubDComponentBase* c = ComponentBase();
return (0 != c)
? ON_CRC16((ON__UINT16)(ON_SUBD_COMPONENT_TYPE_MASK & m_ptr), sizeof(c->m_id), &(c->m_id))
: ((ON__UINT16)0U)
;
}
ON__UINT32 ON_SubDComponentPtr::Hash32FromPointer() const
{
const ON__UINT_PTR ptr = (ON__UINT_PTR)ComponentBase();
return ON_CRC32((ON__UINT32)(ON_SUBD_COMPONENT_TYPE_MASK & m_ptr), sizeof(ptr), &ptr);
}
ON_SubDComponentPtr::Type ON_SubDComponentPtr::ComponentType() const
{
switch (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)
{
case ON_SUBD_COMPONENT_TYPE_VERTEX:
return ON_SubDComponentPtr::Type::Vertex;
case ON_SUBD_COMPONENT_TYPE_EDGE:
return ON_SubDComponentPtr::Type::Edge;
case ON_SUBD_COMPONENT_TYPE_FACE:
return ON_SubDComponentPtr::Type::Face;
}
return ON_SubDComponentPtr::Type::Unset;
}
const ON_ComponentStatus ON_SubDComponentPtr::Status() const
{
switch (ComponentType())
{
case ON_SubDComponentPtr::Type::Vertex:
{
const ON_SubDVertex* vertex = Vertex();
if ( nullptr != vertex )
return vertex->m_status;
}
break;
case ON_SubDComponentPtr::Type::Edge:
{
const ON_SubDEdge* edge = Edge();
if ( nullptr != edge )
return edge->m_status;
}
break;
case ON_SubDComponentPtr::Type::Face:
{
const ON_SubDFace* face = Face();
if ( nullptr != face )
return face->m_status;
}
break;
}
return ON_ComponentStatus::NoneSet;
}
unsigned int ON_SubDComponentPtr::SetStatus(
ON_ComponentStatus status
)
{
switch (ComponentType())
{
case ON_SubDComponentPtr::Type::Vertex:
{
const ON_SubDVertex* vertex = Vertex();
if (nullptr != vertex)
return vertex->m_status.SetStatus(status);
}
break;
case ON_SubDComponentPtr::Type::Edge:
{
const ON_SubDEdge* edge = Edge();
if (nullptr != edge)
return edge->m_status.SetStatus(status);
}
break;
case ON_SubDComponentPtr::Type::Face:
{
const ON_SubDFace* face = Face();
if (nullptr != face)
return face->m_status.SetStatus(status);
}
break;
}
return ON_SUBD_RETURN_ERROR(0);
}
unsigned int ON_SubDComponentPtr::SetStates(
ON_ComponentStatus states_to_set
)
{
switch (ComponentType())
{
case ON_SubDComponentPtr::Type::Vertex:
{
const ON_SubDVertex* vertex = Vertex();
if (nullptr != vertex)
return vertex->m_status.SetStates(states_to_set);
}
break;
case ON_SubDComponentPtr::Type::Edge:
{
const ON_SubDEdge* edge = Edge();
if (nullptr != edge)
return edge->m_status.SetStates(states_to_set);
}
break;
case ON_SubDComponentPtr::Type::Face:
{
const ON_SubDFace* face = Face();
if (nullptr != face)
return face->m_status.SetStates(states_to_set);
}
break;
}
return ON_SUBD_RETURN_ERROR(0);
}
unsigned int ON_SubDComponentPtr::ClearStates(
ON_ComponentStatus states_to_clear
)
{
switch (ComponentType())
{
case ON_SubDComponentPtr::Type::Vertex:
{
const ON_SubDVertex* vertex = Vertex();
if (nullptr != vertex)
return vertex->m_status.ClearStates(states_to_clear);
}
break;
case ON_SubDComponentPtr::Type::Edge:
{
const ON_SubDEdge* edge = Edge();
if (nullptr != edge)
return edge->m_status.ClearStates(states_to_clear);
}
break;
case ON_SubDComponentPtr::Type::Face:
{
const ON_SubDFace* face = Face();
if (nullptr != face)
return face->m_status.ClearStates(states_to_clear);
}
break;
}
return ON_SUBD_RETURN_ERROR(0);
}
bool ON_SubDComponentPtr::Mark() const
{
const ON_SubDComponentBase* c = this->ComponentBase();
return (nullptr != c) ? c->m_status.RuntimeMark() : false;
}
bool ON_SubDComponentPtr::ClearMark() const
{
const ON_SubDComponentBase* c = this->ComponentBase();
return (nullptr != c) ? c->m_status.ClearRuntimeMark() : false;
}
bool ON_SubDComponentPtr::SetMark() const
{
const ON_SubDComponentBase* c = this->ComponentBase();
return (nullptr != c) ? c->m_status.SetRuntimeMark() : false;
}
bool ON_SubDComponentPtr::SetMark(
bool bMark
) const
{
const ON_SubDComponentBase* c = this->ComponentBase();
return (nullptr != c) ? c->m_status.SetRuntimeMark(bMark) : false;
}
bool ON_SubDVertexPtr::Mark() const
{
const ON_SubDVertex* c = this->Vertex();
return (nullptr != c) ? c->m_status.RuntimeMark() : false;
}
bool ON_SubDVertexPtr::ClearMark() const
{
const ON_SubDVertex* c = this->Vertex();
return (nullptr != c) ? c->m_status.ClearRuntimeMark() : false;
}
bool ON_SubDVertexPtr::SetMark() const
{
const ON_SubDVertex* c = this->Vertex();
return (nullptr != c) ? c->m_status.SetRuntimeMark() : false;
}
bool ON_SubDVertexPtr::SetMark(
bool bMark
) const
{
const ON_SubDVertex* c = this->Vertex();
return (nullptr != c) ? c->m_status.SetRuntimeMark(bMark) : false;
}
bool ON_SubDEdgePtr::Mark() const
{
const ON_SubDEdge* c = this->Edge();
return (nullptr != c) ? c->m_status.RuntimeMark() : false;
}
bool ON_SubDEdgePtr::ClearMark() const
{
const ON_SubDEdge* c = this->Edge();
return (nullptr != c) ? c->m_status.ClearRuntimeMark() : false;
}
bool ON_SubDEdgePtr::SetMark() const
{
const ON_SubDEdge* c = this->Edge();
return (nullptr != c) ? c->m_status.SetRuntimeMark() : false;
}
bool ON_SubDEdgePtr::SetMark(
bool bMark
) const
{
const ON_SubDEdge* c = this->Edge();
return (nullptr != c) ? c->m_status.SetRuntimeMark(bMark) : false;
}
bool ON_SubDFacePtr::Mark() const
{
const ON_SubDFace* c = this->Face();
return (nullptr != c) ? c->m_status.RuntimeMark() : false;
}
bool ON_SubDFacePtr::ClearMark() const
{
const ON_SubDFace* c = this->Face();
return (nullptr != c) ? c->m_status.ClearRuntimeMark() : false;
}
bool ON_SubDFacePtr::SetMark() const
{
const ON_SubDFace* c = this->Face();
return (nullptr != c) ? c->m_status.SetRuntimeMark() : false;
}
bool ON_SubDFacePtr::SetMark(
bool bMark
) const
{
const ON_SubDFace* c = this->Face();
return (nullptr != c) ? c->m_status.SetRuntimeMark(bMark) : false;
}
ON__UINT_PTR ON_SubDComponentPtr::ComponentDirection() const
{
return ON_SUBD_COMPONENT_DIRECTION(m_ptr);
}
const ON_SubDComponentPtr ON_SubDComponentPtr::ClearComponentDirection() const
{
ON_SubDComponentPtr component_ptr = *this;
component_ptr.m_ptr &= (ON_SUBD_COMPONENT_POINTER_MASK | ON_SUBD_COMPONENT_TYPE_MASK);
return component_ptr;
}
const ON_SubDComponentPtr ON_SubDComponentPtr::SetComponentDirection() const
{
ON_SubDComponentPtr component_ptr = *this;
component_ptr.m_ptr |= ON_SUBD_COMPONENT_DIRECTION_MASK;
return component_ptr;
}
const ON_SubDComponentPtr ON_SubDComponentPtr::SetComponentDirection(ON__UINT_PTR dir) const
{
ON_SubDComponentPtr component_ptr = *this;
if (0 == dir)
component_ptr.m_ptr &= ~((ON__UINT_PTR)ON_SUBD_COMPONENT_DIRECTION_MASK);
else if (1 == dir)
component_ptr.m_ptr |= ON_SUBD_COMPONENT_DIRECTION_MASK;
else
ON_SUBD_ERROR("Invalid dir parameter");
return component_ptr;
}
const ON_SubDComponentPtr ON_SubDComponentPtr::ToggleComponentDirection() const
{
return (0 != (m_ptr & ON_SUBD_COMPONENT_DIRECTION_MASK)) ? ClearComponentDirection() : SetComponentDirection();
}
const ON_SubDComponentPtr ON_SubDComponentPtr::CreateNull(
ON_SubDComponentPtr::Type component_type,
ON__UINT_PTR component_direction
)
{
ON_SubDComponentPtr component_ptr;
switch (component_type)
{
case ON_SubDComponentPtr::Type::Unset:
component_ptr.m_ptr = 0;
break;
case ON_SubDComponentPtr::Type::Vertex:
component_ptr.m_ptr = ON_SUBD_COMPONENT_TYPE_VERTEX;
break;
case ON_SubDComponentPtr::Type::Edge:
component_ptr.m_ptr = ON_SUBD_COMPONENT_TYPE_EDGE;
break;
case ON_SubDComponentPtr::Type::Face:
component_ptr.m_ptr = ON_SUBD_COMPONENT_TYPE_FACE;
break;
default:
component_ptr.m_ptr = 0;
break;
}
if (1 == component_direction)
component_ptr.m_ptr |= ON_SUBD_COMPONENT_DIRECTION_MASK;
return component_ptr;
}
class ON_SubDComponentBase* ON_SubDComponentPtr::ComponentBase() const
{
switch ((ON_SUBD_COMPONENT_TYPE_MASK & m_ptr))
{
case ON_SUBD_COMPONENT_TYPE_VERTEX:
case ON_SUBD_COMPONENT_TYPE_EDGE:
case ON_SUBD_COMPONENT_TYPE_FACE:
return ((class ON_SubDComponentBase*)ON_SUBD_COMPONENT_POINTER(m_ptr));
}
return nullptr;
}
class ON_SubDVertex* ON_SubDComponentPtr::Vertex() const
{
if (ON_SUBD_COMPONENT_TYPE_VERTEX == (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr))
return ON_SUBD_VERTEX_POINTER(m_ptr);
return nullptr;
}
class ON_SubDEdge* ON_SubDComponentPtr::Edge() const
{
if (ON_SUBD_COMPONENT_TYPE_EDGE == (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr))
return ON_SUBD_EDGE_POINTER(m_ptr);
return nullptr;
}
class ON_SubDFace* ON_SubDComponentPtr::Face() const
{
if (ON_SUBD_COMPONENT_TYPE_FACE == (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr))
return ON_SUBD_FACE_POINTER(m_ptr);
return nullptr;
}
const ON_SubDVertexPtr ON_SubDComponentPtr::VertexPtr() const
{
ON__UINT_PTR element_type = ON_SUBD_COMPONENT_TYPE(m_ptr);
if ( ON_SUBD_COMPONENT_TYPE_VERTEX == element_type)
return ON_SubDVertexPtr::Create(Vertex(), ComponentDirection());
if ( 0 == element_type )
return ON_SubDVertexPtr::Null;
return ON_SUBD_RETURN_ERROR(ON_SubDVertexPtr::Null);
}
const ON_SubDEdgePtr ON_SubDComponentPtr::EdgePtr() const
{
ON__UINT_PTR element_type = ON_SUBD_COMPONENT_TYPE(m_ptr);
if ( ON_SUBD_COMPONENT_TYPE_EDGE == element_type)
return ON_SubDEdgePtr::Create(Edge(), ComponentDirection());
if ( 0 == element_type )
return ON_SubDEdgePtr::Null;
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
}
const ON_SubDFacePtr ON_SubDComponentPtr::FacePtr() const
{
ON__UINT_PTR element_type = ON_SUBD_COMPONENT_TYPE(m_ptr);
if ( ON_SUBD_COMPONENT_TYPE_FACE == element_type)
return ON_SubDFacePtr::Create(Face(), ComponentDirection());
if ( 0 == element_type )
return ON_SubDFacePtr::Null;
return ON_SUBD_RETURN_ERROR(ON_SubDFacePtr::Null);
}
const ON_SubDComponentPtr ON_SubDComponentPtr::Create(
const class ON_SubDVertex* vertex
)
{
if (nullptr != vertex)
{
ON_SubDComponentPtr vptr = { (ON__UINT_PTR)vertex | ON_SUBD_COMPONENT_TYPE_VERTEX };
return vptr;
}
return ON_SubDComponentPtr::Null;
}
const ON_SubDComponentPtr ON_SubDComponentPtr::Create(
const class ON_SubDEdge* edge
)
{
if (nullptr != edge)
{
ON_SubDComponentPtr eptr = { (ON__UINT_PTR)edge | ON_SUBD_COMPONENT_TYPE_EDGE };
return eptr;
}
return ON_SubDComponentPtr::Null;
}
const ON_SubDComponentPtr ON_SubDComponentPtr::Create(
const class ON_SubDFace* face
)
{
if (nullptr != face)
{
ON_SubDComponentPtr fptr = { (ON__UINT_PTR)face | ON_SUBD_COMPONENT_TYPE_FACE };
return fptr;
}
return ON_SubDComponentPtr::Null;
}
const ON_SubDComponentPtr ON_SubDComponentPtr::Create(
const class ON_SubDVertex* vertex,
ON__UINT_PTR vertex_direction
)
{
if (nullptr != vertex)
{
ON_SubDComponentPtr vptr = { (ON__UINT_PTR)vertex | ON_SUBD_COMPONENT_TYPE_VERTEX | (vertex_direction & ON_SUBD_COMPONENT_DIRECTION_MASK) };
return vptr;
}
return ON_SubDComponentPtr::Null;
}
const ON_SubDComponentPtr ON_SubDComponentPtr::Create(
const class ON_SubDEdge* edge,
ON__UINT_PTR edge_direction
)
{
if (nullptr != edge)
{
ON_SubDComponentPtr eptr = { (ON__UINT_PTR)edge | (ON_SUBD_COMPONENT_TYPE_EDGE | (edge_direction & ON_SUBD_COMPONENT_DIRECTION_MASK)) };
return eptr;
}
return ON_SubDComponentPtr::Null;
}
const ON_SubDComponentPtr ON_SubDComponentPtr::Create(
const class ON_SubDFace* face,
ON__UINT_PTR face_direction
)
{
if (nullptr != face)
{
ON_SubDComponentPtr fptr = { (ON__UINT_PTR)face | (ON_SUBD_COMPONENT_TYPE_FACE | (face_direction & ON_SUBD_COMPONENT_DIRECTION_MASK)) };
return fptr;
}
return ON_SubDComponentPtr::Null;
}
const ON_SubDComponentPtr ON_SubDComponentPtr::Create(
ON_SubDVertexPtr vertexptr
)
{
return Create(vertexptr.Vertex(), vertexptr.VertexDirection());
}
const ON_SubDComponentPtr ON_SubDComponentPtr::Create(
ON_SubDEdgePtr edgeptr
)
{
return Create(edgeptr.Edge(), edgeptr.EdgeDirection());
}
const ON_SubDComponentPtr ON_SubDComponentPtr::Create(
ON_SubDFacePtr faceptr
)
{
return Create(faceptr.Face(), faceptr.FaceDirection());
}
int ON_SubDComponentPtr::CompareComponentPtrType(
ON_SubDComponentPtr::Type a,
ON_SubDComponentPtr::Type b
)
{
if ( a == b )
return 0;
switch (a)
{
case ON_SubDComponentPtr::Type::Vertex:
return -1;
break;
case ON_SubDComponentPtr::Type::Edge:
return (ON_SubDComponentPtr::Type::Vertex == b) ? 1 : -1;
break;
case ON_SubDComponentPtr::Type::Face:
return (ON_SubDComponentPtr::Type::Vertex == b || ON_SubDComponentPtr::Type::Edge == b) ? 1 : -1;
break;
default:
break;
}
return (((unsigned char)a) < ((unsigned char)b)) ? -1 : 1;
}
int ON_SubDComponentPtr::CompareType(
const ON_SubDComponentPtr* a,
const ON_SubDComponentPtr* b
)
{
if ( a == b )
return 0;
if ( nullptr == a )
return 1;
if ( nullptr == b )
return -1;
return ON_SubDComponentPtr::CompareComponentPtrType(a->ComponentType(), b->ComponentType());
}
int ON_SubDComponentPtr::CompareComponent(
const ON_SubDComponentPtr* a,
const ON_SubDComponentPtr* b
)
{
if (a == b)
return 0;
const int rc = ON_SubDComponentPtr::CompareComponentPtrType(a->ComponentType(), b->ComponentType());
if (0 == rc)
{
const ON__UINT_PTR x = a->m_ptr;
const ON__UINT_PTR y = b->m_ptr;
if (x < y)
return -1;
if (x > y)
return 1;
}
return rc;
}
int ON_SubDComponentPtr::CompareComponentAndDirection(
const ON_SubDComponentPtr* a,
const ON_SubDComponentPtr* b
)
{
if (a == b)
return 0;
const int rc = ON_SubDComponentPtr::CompareComponent(a, b);
if (0 == rc)
{
const ON__UINT_PTR x = (a->m_ptr & ON_SUBD_COMPONENT_POINTER_MASK);
const ON__UINT_PTR y = (b->m_ptr & ON_SUBD_COMPONENT_POINTER_MASK);
if (x < y)
return -1;
if (x > y)
return 1;
}
return rc;
}
int ON_SubDComponentPoint::CompareComponentAndDirection(
const ON_SubDComponentPoint* a,
const ON_SubDComponentPoint* b
)
{
if ( a == b)
return 0;
if ( nullptr == a)
return 1; // nullptr > non-null pointer.
if ( nullptr == b)
return -1; // nullptr > non-null pointer.
// 1st: compare component type
// unset < vertex < edge < face
ON__UINT_PTR x = (ON_SUBD_COMPONENT_TYPE_MASK & a->m_component_ptr.m_ptr);
ON__UINT_PTR y = (ON_SUBD_COMPONENT_TYPE_MASK & b->m_component_ptr.m_ptr);
if ( x < y )
return -1;
if ( x > y )
return 1;
// 2nd: compare component pointer
x = (a->m_component_ptr.m_ptr & ON_SUBD_COMPONENT_POINTER_MASK);
y = (b->m_component_ptr.m_ptr & ON_SUBD_COMPONENT_POINTER_MASK);
if (x < y)
return -1;
if (x > y)
return 1;
// 3rd: compare component direction flag
x = (a->m_component_ptr.m_ptr & ON_SUBD_COMPONENT_DIRECTION_MASK);
y = (b->m_component_ptr.m_ptr & ON_SUBD_COMPONENT_DIRECTION_MASK);
if (x < y)
return -1;
if (x > y)
return 1;
return 0;
}
const ON_SubDComponentPtrPair ON_SubDComponentPtrPair::Create(ON_SubDComponentPtr first_ptr, ON_SubDComponentPtr second_ptr)
{
ON_SubDComponentPtrPair p;
p.m_pair[0] = first_ptr;
p.m_pair[1] = second_ptr;
return p;
}
int ON_SubDComponentPtrPair::CompareComponent(
const ON_SubDComponentPtrPair* lhs,
const ON_SubDComponentPtrPair* rhs
)
{
if (lhs == rhs)
return 0;
// nulls sort to end.
if (nullptr == rhs)
return -1;
if (nullptr == lhs)
return 1;
int rc = ON_SubDComponentPtr::CompareComponent(&lhs->m_pair[0], &rhs->m_pair[0]);
if (0 == rc)
rc = ON_SubDComponentPtr::CompareComponent(&lhs->m_pair[1], &rhs->m_pair[1]);
return rc;
}
int ON_SubDComponentPtrPair::CompareComponentAndDirection(
const ON_SubDComponentPtrPair* lhs,
const ON_SubDComponentPtrPair* rhs
)
{
if (lhs == rhs)
return 0;
// nulls sort to end.
if (nullptr == rhs)
return -1;
if (nullptr == lhs)
return 1;
int rc = ON_SubDComponentPtr::CompareComponentAndDirection(&lhs->m_pair[0], &rhs->m_pair[0]);
if (0 == rc)
rc = ON_SubDComponentPtr::CompareComponentAndDirection(&lhs->m_pair[1], &rhs->m_pair[1]);
return rc;
}
int ON_SubDComponentPtrPair::CompareFirstPointer(
const ON_SubDComponentPtrPair* lhs,
const ON_SubDComponentPtrPair* rhs
)
{
if (lhs == rhs)
return 0;
// nulls sort to end.
if (nullptr == rhs)
return -1;
if (nullptr == lhs)
return 1;
const ON__UINT_PTR lhs_ptr = (ON_SUBD_COMPONENT_POINTER_MASK & lhs->m_pair[0].m_ptr);
const ON__UINT_PTR rhs_ptr = (ON_SUBD_COMPONENT_POINTER_MASK & rhs->m_pair[0].m_ptr);
if (lhs_ptr < rhs_ptr)
return -1;
if (lhs_ptr > rhs_ptr)
return 1;
return 0;
}
ON_SubDComponentPtr::Type ON_SubDComponentPtrPair::ComponentType() const
{
const ON_SubDComponentPtr::Type type = m_pair[0].ComponentType();
return (type == m_pair[1].ComponentType()) ? type : ON_SubDComponentPtr::Type::Unset;
}
const ON_SubDComponentPtrPair ON_SubDComponentPtrPair::SwapPair() const
{
return ON_SubDComponentPtrPair::Create(m_pair[1], m_pair[0]);
}
const ON_SubDComponentPtr ON_SubDComponentPtrPair::First() const
{
return m_pair[0];
}
const ON_SubDComponentPtr ON_SubDComponentPtrPair::Second() const
{
return m_pair[1];
}
//////////////////////////////////////////////////////////////////////////
//
// ON_ToSubDParameters
//
ON_ToSubDParameters::ConvexCornerOption ON_ToSubDParameters::ConvexCornerOptionFromUnsigned(
unsigned int convex_corner_option_as_unsigned
)
{
switch (convex_corner_option_as_unsigned)
{
case (unsigned int)ON_ToSubDParameters::ConvexCornerOption::Unset:
return ON_ToSubDParameters::ConvexCornerOption::Unset;
case (unsigned int)ON_ToSubDParameters::ConvexCornerOption::None:
return ON_ToSubDParameters::ConvexCornerOption::None;
case (unsigned int)ON_ToSubDParameters::ConvexCornerOption::AtMeshCorner:
return ON_ToSubDParameters::ConvexCornerOption::AtMeshCorner;
default:
break;
}
return ON_ToSubDParameters::ConvexCornerOption::Unset;
}
void ON_ToSubDParameters::SetConvexCornerOption(
ON_ToSubDParameters::ConvexCornerOption convex_corner_option
)
{
m_convex_corner_option = ON_ToSubDParameters::ConvexCornerOptionFromUnsigned((unsigned int)convex_corner_option);
}
ON_ToSubDParameters::ConvexCornerOption ON_ToSubDParameters::ConvexCornerTest() const
{
switch (m_convex_corner_option)
{
case ON_ToSubDParameters::ConvexCornerOption::Unset:
case ON_ToSubDParameters::ConvexCornerOption::None:
return m_convex_corner_option;
case ON_ToSubDParameters::ConvexCornerOption::AtMeshCorner:
if ( m_maximum_convex_corner_edge_count >= 2
&& m_maximum_convex_corner_edge_count <= ON_SubDVertex::MaximumEdgeCount
&& m_maximum_convex_corner_angle_radians >= 0.0
&& m_maximum_convex_corner_angle_radians < ON_PI
)
return m_convex_corner_option;
break;
}
return ON_ToSubDParameters::ConvexCornerOption::Unset;
}
void ON_ToSubDParameters::SetMaximumConvexCornerEdgeCount(
unsigned int maximum_convex_corner_edge_count
)
{
if ( maximum_convex_corner_edge_count >= 2 && maximum_convex_corner_edge_count <= ON_SubDVertex::MaximumEdgeCount)
m_maximum_convex_corner_edge_count = (unsigned short)maximum_convex_corner_edge_count;
}
unsigned int ON_ToSubDParameters::MaximumConvexCornerEdgeCount() const
{
return m_maximum_convex_corner_edge_count;
}
void ON_ToSubDParameters::SetMaximumConvexCornerAngleRadians(
double maximum_convex_corner_angle_radians
)
{
if (maximum_convex_corner_angle_radians > 0.0 && maximum_convex_corner_angle_radians < ON_PI)
m_maximum_convex_corner_angle_radians = maximum_convex_corner_angle_radians;
}
double ON_ToSubDParameters::MaximumConvexCornerAngleRadians() const
{
return m_maximum_convex_corner_angle_radians;
}
ON_ToSubDParameters::ConvexCornerOption ON_ToSubDParameters::CopyConvexCornerTest(
ON_ToSubDParameters source_parameters
)
{
SetConvexCornerOption(source_parameters.ConvexCornerTest());
SetMaximumConvexCornerEdgeCount(source_parameters.MaximumConvexCornerEdgeCount());
SetMaximumConvexCornerAngleRadians(source_parameters.MaximumConvexCornerAngleRadians());
return ConvexCornerTest();
}
bool ON_ToSubDParameters::InterpolateMeshVertices() const
{
return m_bInterpolateMeshVertices;
}
void ON_ToSubDParameters::SetInterpolateMeshVertices(
bool bInterpolateMeshVertices
)
{
// Not supported in free opennurbs
m_bInterpolateMeshVertices = false;
}
bool ON_ToSubDParameters::MergeColinearBoundaryEdges() const
{
// clear bit means true, set bit means false
return (0 == (ON_ToSubDParameters::MergeColinearBoundaryEdgesMask & m_merge_edges_bits));
}
void ON_ToSubDParameters::SetMergeColinearBoundaryEdges(
bool bAllowColinearBoundaryEdges
)
{
const unsigned char mask = ON_ToSubDParameters::MergeColinearBoundaryEdgesMask;
if (false == bAllowColinearBoundaryEdges)
m_merge_edges_bits |= mask; // set bit
else
m_merge_edges_bits &= ~mask; // clear bit
}
bool ON_ToSubDParameters::MergeColinearInteriorEdges() const
{
// clear bit means true, set bit means false
return (0 == (ON_ToSubDParameters::MergeColinearInteriorEdgesMask & m_merge_edges_bits));
}
void ON_ToSubDParameters::SetMergeColinearInteriorEdges(
bool bAllowColinearInteriorEdges
)
{
const unsigned char mask = ON_ToSubDParameters::MergeColinearInteriorEdgesMask;
if (false == bAllowColinearInteriorEdges)
m_merge_edges_bits |= mask; // set bit
else
m_merge_edges_bits &= ~mask; // clear bit
}
void ON_ToSubDParameters::SetInteriorCreaseOption(
ON_ToSubDParameters::InteriorCreaseOption interior_crease_option
)
{
m_interior_crease_option = ON_ToSubDParameters::InteriorCreaseOptionFromUnsigned((unsigned int)interior_crease_option);
}
ON_ToSubDParameters::InteriorCreaseOption ON_ToSubDParameters::InteriorCreaseTest() const
{
return m_interior_crease_option;
}
void ON_ToSubDParameters::SetMinimumCreaseAngleRadians(
double minimum_crease_angle_radians
)
{
if (minimum_crease_angle_radians >= 0.0 && minimum_crease_angle_radians < ON_PI)
m_minimum_crease_angle_radians = minimum_crease_angle_radians;
}
double ON_ToSubDParameters::MinimumCreaseAngleRadians() const
{
return m_minimum_crease_angle_radians;
}
ON_ToSubDParameters::InteriorCreaseOption ON_ToSubDParameters::CopyInteriorCreaseTest(
ON_ToSubDParameters source_parameters
)
{
SetInteriorCreaseOption(source_parameters.InteriorCreaseTest());
SetMinimumCreaseAngleRadians(source_parameters.MinimumCreaseAngleRadians());
return InteriorCreaseTest();
}
ON_ToSubDParameters::InteriorCreaseOption ON_ToSubDParameters::InteriorCreaseOptionFromUnsigned(
unsigned int interior_crease_option_as_unsigned
)
{
switch (interior_crease_option_as_unsigned)
{
case (unsigned int)ON_ToSubDParameters::InteriorCreaseOption::Unset:
return ON_ToSubDParameters::InteriorCreaseOption::Unset;
break;
case (unsigned int)ON_ToSubDParameters::InteriorCreaseOption::None:
return ON_ToSubDParameters::InteriorCreaseOption::None;
break;
case (unsigned int)ON_ToSubDParameters::InteriorCreaseOption::AtMeshCrease:
return ON_ToSubDParameters::InteriorCreaseOption::AtMeshCrease;
break;
case (unsigned int)ON_ToSubDParameters::InteriorCreaseOption::AtMeshEdge:
return ON_ToSubDParameters::InteriorCreaseOption::AtMeshEdge;
break;
default:
break;
}
return ON_ToSubDParameters::InteriorCreaseOption::Unset;
}
//////////////////////////////////////////////////////////////////////////
//
// ON_SubDVertex
//
const ON_SubDEdgePtr ON_SubDVertex::EdgePtr(
unsigned int i
) const
{
return (i < m_edge_count) ? m_edges[i] : ON_SubDEdgePtr::Null;
}
const class ON_SubDEdge* ON_SubDVertex::Edge(
unsigned int i
) const
{
return (i < m_edge_count) ? ON_SUBD_EDGE_POINTER(m_edges[i].m_ptr) : nullptr;
}
ON__UINT_PTR ON_SubDVertex::EdgeDirection(
unsigned int i
) const
{
return (i < m_edge_count) ? ON_SUBD_EDGE_DIRECTION(m_edges[i].m_ptr) : 0;
}
unsigned int ON_SubDVertex::EdgeCount() const
{
return m_edge_count;
}
unsigned int ON_SubDVertex::EdgeCount(
ON_SubD::EdgeTag edge_tag
) const
{
if (nullptr != m_edges)
{
unsigned int matching_edge_count = 0;
const unsigned int edge_count = m_edge_count;
for (unsigned int vei = 0; vei < edge_count; vei++)
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr);
if (nullptr != e && edge_tag == e->m_edge_tag)
matching_edge_count++;
}
return matching_edge_count;
}
return 0;
}
unsigned int ON_SubDVertex::EdgeArrayIndex(
const ON_SubDEdge* edge
) const
{
if ( nullptr == edge )
return ON_UNSET_UINT_INDEX;
const unsigned int edge_count = m_edge_count;
if ( 0 == edge_count)
return ON_UNSET_UINT_INDEX;
const ON_SubDEdgePtr* eptr = m_edges;
if ( nullptr == eptr)
return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX);
for ( unsigned int i = 0; i < edge_count; i++, eptr++)
{
if (edge == ON_SUBD_EDGE_POINTER(eptr->m_ptr))
return i;
}
return ON_UNSET_UINT_INDEX;
}
unsigned int ON_SubDVertex::MarkedEdgeCount() const
{
unsigned int mark_count = 0;
for (unsigned short vei = 0; vei < m_edge_count; ++vei)
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr);
if (nullptr != e && e->m_status.RuntimeMark())
++mark_count;
}
return mark_count;
}
unsigned int ON_SubDVertex::MarkedFaceCount() const
{
unsigned int mark_count = 0;
for (unsigned short vfi = 0; vfi < m_face_count; ++vfi)
{
const ON_SubDFace* f = m_faces[vfi];
if (nullptr != f && f->m_status.RuntimeMark())
++mark_count;
}
return mark_count;
}
unsigned int ON_SubDEdge::MarkedVertexCount() const
{
unsigned int mark_count = 0;
for (unsigned evi = 0; evi < 2; ++evi)
{
const ON_SubDVertex* v = m_vertex[evi];
if (nullptr != v && v->m_status.RuntimeMark())
++mark_count;
}
return mark_count;
}
unsigned int ON_SubDEdge::MarkedFaceCount() const
{
unsigned int mark_count = 0;
const ON_SubDFacePtr* fptr = m_face2;
for (unsigned short efi = 0; efi < m_face_count; ++efi, ++fptr)
{
if (2 == efi)
{
fptr = m_facex;
if (nullptr == fptr)
break;
}
const ON_SubDFace* f = ON_SUBD_FACE_POINTER(fptr->m_ptr);
if (nullptr != f && f->m_status.RuntimeMark())
++mark_count;
}
return mark_count;
}
unsigned int ON_SubDVertex::FaceCount() const
{
return m_face_count;
}
const class ON_SubDFace* ON_SubDVertex::Face(
unsigned int i
) const
{
//return (i < m_face_count) ? ON_SUBD_FACE_POINTER(m_faces[i].m_ptr) : nullptr;
return (i < m_face_count) ? m_faces[i] : nullptr;
}
unsigned int ON_SubDVertex::FaceArrayIndex(
const ON_SubDFace* face
) const
{
if ( nullptr == face )
return ON_UNSET_UINT_INDEX;
const unsigned int face_count = m_face_count;
if ( 0 == face_count)
return ON_UNSET_UINT_INDEX;
if ( nullptr == m_faces)
return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX);
for ( unsigned int i = 0; i < face_count; i++)
{
if (face == m_faces[i] )
return i;
}
return ON_UNSET_UINT_INDEX;
}
unsigned int ON_SubDVertex::ReplaceFaceInArray(const ON_SubDFace * old_face, const ON_SubDFace * new_face)
{
unsigned vfi = (nullptr != old_face && old_face != new_face) ? FaceArrayIndex(old_face) : ON_UNSET_UINT_INDEX;
if (ON_UNSET_UINT_INDEX == vfi)
return ON_UNSET_UINT_INDEX;
if (nullptr != new_face)
{
m_faces[vfi] = new_face;
}
else
{
const unsigned c = (unsigned)(m_face_count--);
while (++vfi < c)
m_faces[vfi - 1] = m_faces[vfi];
}
return vfi;
}
const ON_3dPoint ON_SubDVertex::ControlNetPoint() const
{
return ON_3dPoint(m_P);
}
bool ON_SubDVertex::IsSmooth() const
{
return (ON_SubD::VertexTag::Smooth == m_vertex_tag);
}
bool ON_SubDVertex::IsCrease() const
{
return (ON_SubD::VertexTag::Crease == m_vertex_tag);
}
bool ON_SubDVertex::IsCorner() const
{
return (ON_SubD::VertexTag::Corner == m_vertex_tag);
}
bool ON_SubDVertex::IsDart() const
{
return (ON_SubD::VertexTag::Dart == m_vertex_tag);
}
bool ON_SubDVertex::IsCreaseOrCorner() const
{
return (ON_SubD::VertexTag::Crease == m_vertex_tag || ON_SubD::VertexTag::Corner == m_vertex_tag);
}
bool ON_SubDVertex::IsDartOrCreaseOrCorner() const
{
return (
ON_SubD::VertexTag::Dart == m_vertex_tag
|| ON_SubD::VertexTag::Crease == m_vertex_tag
|| ON_SubD::VertexTag::Corner == m_vertex_tag
);
}
bool ON_SubDVertex::IsSmoothOrDart() const
{
return (ON_SubD::VertexTag::Smooth == m_vertex_tag || ON_SubD::VertexTag::Dart == m_vertex_tag);
}
bool ON_SubDVertex::IsSmoothOrCrease() const
{
return (ON_SubD::VertexTag::Smooth == m_vertex_tag || ON_SubD::VertexTag::Crease == m_vertex_tag);
}
const ON_SubDVertexEdgeProperties ON_SubDVertex::EdgeProperties() const
{
ON_SubDVertexEdgeProperties ep;
bool bFirstEdge = true;
const unsigned short edge_count = m_edge_count;
for (unsigned short vei = 0; vei < edge_count; vei++)
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr);
if (nullptr == e)
{
ep.m_null_edge_count++;
continue;
}
if (e->IsCrease())
ep.m_crease_edge_count++;
else if (e->IsSmooth())
ep.m_smooth_edge_count++;
else
ep.m_unset_edge_count++;
const unsigned short edge_face_count = e->m_face_count;
if (bFirstEdge)
{
bFirstEdge = false;
ep.m_min_edge_face_count = edge_face_count;
ep.m_max_edge_face_count = edge_face_count;
}
else if (edge_face_count < ep.m_min_edge_face_count)
ep.m_min_edge_face_count = edge_face_count;
else if (edge_face_count > ep.m_max_edge_face_count)
ep.m_max_edge_face_count = edge_face_count;
if (0 == edge_face_count)
ep.m_wire_edge_count++;
else if (1 == edge_face_count)
ep.m_boundary_edge_count++;
else if (2 == edge_face_count)
ep.m_interior_edge_count++;
else
ep.m_nonmanifold_edge_count++;
}
return ep;
}
bool ON_SubDEdge::IsCrease() const
{
return (ON_SubD::EdgeTag::Crease == m_edge_tag) ? true : false;
}
bool ON_SubDEdge::IsHardCrease() const
{
return
(
ON_SubD::EdgeTag::Crease == m_edge_tag
&& nullptr != m_vertex[0]
&& nullptr != m_vertex[1]
&& m_vertex[0]->IsCreaseOrCorner()
&& m_vertex[1]->IsCreaseOrCorner()
)
? true
: false;
}
bool ON_SubDEdge::IsDartCrease() const
{
return
(ON_SubD::EdgeTag::Crease == m_edge_tag && DartCount() > 0 )
? true
: false;
}
unsigned int ON_SubDEdge::DartCount() const
{
unsigned int dart_count = 0;
if (nullptr != m_vertex[0] && ON_SubD::VertexTag::Dart == m_vertex[0]->m_vertex_tag)
dart_count++;
if (nullptr != m_vertex[1] && ON_SubD::VertexTag::Dart == m_vertex[1]->m_vertex_tag)
dart_count++;
return dart_count;
}
bool ON_SubDEdge::IsSmooth() const
{
return (ON_SubD::EdgeTag::Smooth == m_edge_tag || ON_SubD::EdgeTag::SmoothX == m_edge_tag) ? true : false;
}
bool ON_SubDEdge::IsSmoothNotX() const
{
return (ON_SubD::EdgeTag::Smooth == m_edge_tag) ? true : false;
}
bool ON_SubDEdge::IsSmoothX() const
{
return (ON_SubD::EdgeTag::SmoothX == m_edge_tag) ? true : false;
}
bool ON_SubDVertex::IsSingleSectorVertex() const
{
const bool bIsCreaseOrCorner = IsCreaseOrCorner();
if (bIsCreaseOrCorner)
{
if (m_face_count < 1 || m_face_count + 1 != m_edge_count)
return false;
}
else if (IsSmoothOrDart())
{
if (m_face_count < 2 || m_edge_count!= m_face_count)
return false;
}
else
return false;
unsigned short boundary_crease_count = 0;
unsigned short interior_crease_count = 0;
unsigned short interior_smooth_count = 0;
for (unsigned short vei = 0; vei < m_edge_count; ++vei)
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(this->m_edges[vei].m_ptr);
if (nullptr == e)
return false;
if (e->IsSmooth())
{
if (2 == e->m_face_count)
{
++interior_smooth_count;
continue;
}
}
else if (ON_SubD::EdgeTag::Crease == e->m_edge_tag)
{
if (2 == e->m_face_count)
{
++interior_crease_count;
if (ON_SubD::VertexTag::Dart == m_vertex_tag && 1 == interior_crease_count)
continue;
}
else if (1 == e->m_face_count)
{
++boundary_crease_count;
if (bIsCreaseOrCorner && boundary_crease_count <= 2)
continue;
}
}
return false;
}
if (bIsCreaseOrCorner)
{
if (2 == boundary_crease_count && 2+interior_smooth_count == m_edge_count)
return true;
}
else if (ON_SubD::VertexTag::Dart == m_vertex_tag)
{
if (1 == interior_crease_count && 1+interior_smooth_count == m_edge_count)
return true;
}
else if (ON_SubD::VertexTag::Smooth == m_vertex_tag)
{
if (interior_smooth_count == m_edge_count)
return true;
}
return false;
}
bool ON_SubDVertex::IsManifoldBoundaryVertex() const
{
return IsCreaseOrCorner() && IsSingleSectorVertex();
}
bool ON_SubDVertex::HasInteriorVertexTopology() const
{
if (m_edge_count >= 2 && m_edge_count == m_face_count && nullptr != m_edges && nullptr != m_faces)
{
for (unsigned short vei = 0; vei < m_edge_count; ++vei)
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr);
if (nullptr == e || 2 != e->m_face_count)
return false;
}
return true;
}
return false;
}
bool ON_SubDVertex::HasBoundaryVertexTopology() const
{
if (m_edge_count >= 2 && m_edge_count == m_face_count+1 && nullptr != m_edges && nullptr != m_faces)
{
unsigned boundary_count = 0;
for (unsigned short vei = 0; vei < m_edge_count; ++vei)
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr);
if (nullptr == e || 0 == e->m_face_count || e->m_face_count > 2)
return false;
if (1 == e->m_face_count)
++boundary_count;
}
if (2 == boundary_count)
return true;
}
return false;
}
bool ON_SubDVertex::IsStandard() const
{
if (nullptr == m_edges)
return false;
if (nullptr == m_faces)
return false;
const unsigned int edge_count = m_edge_count;
if (!ON_SubD::IsValidSectorEdgeCount(m_vertex_tag,edge_count))
return false;
const unsigned int face_count = m_face_count;
if (face_count != ON_SubDSectorType::SectorFaceCountFromEdgeCount(m_vertex_tag, edge_count))
return false;
const unsigned short f_edge_count = 4;
unsigned int crease_edge_face_count = ON_UNSET_UINT_INDEX;
bool bTaggedVertex = false;
switch (m_vertex_tag)
{
case ON_SubD::VertexTag::Unset:
return false;
break;
case ON_SubD::VertexTag::Smooth:
if (edge_count != face_count)
return false;
break;
case ON_SubD::VertexTag::Crease:
if (edge_count != face_count+1)
return false;
crease_edge_face_count = 1;
bTaggedVertex = true;
break;
case ON_SubD::VertexTag::Corner:
if (edge_count != face_count+1)
return false;
crease_edge_face_count = 1;
bTaggedVertex = true;
break;
case ON_SubD::VertexTag::Dart:
if (edge_count != face_count)
return false;
crease_edge_face_count = 2;
bTaggedVertex = true;
break;
default:
return false;
break;
}
for (unsigned int vfi = 0; vfi < face_count; vfi++)
{
const ON_SubDFace* f = m_faces[vfi];
if (nullptr == f)
return false;
if (f_edge_count != f->m_edge_count)
return false;
}
unsigned int creased_edge_count = 0;
double sector_weight = 0.0;
for (unsigned int vei = 0; vei < edge_count; vei++)
{
const ON_SubDEdge* e = m_edges[vei].Edge();
if (nullptr == e)
return false;
unsigned int evi;
if (this == e->m_vertex[0])
evi = 0;
else if (this == e->m_vertex[1])
evi = 1;
else
return false;
const ON_SubDVertex* other_vertex = e->m_vertex[1-evi];
if (nullptr == other_vertex)
return false;
if (ON_SubD::EdgeTag::Smooth == e->m_edge_tag)
{
if (2 != e->m_face_count)
return false;
if (bTaggedVertex && 0 == vei)
{
sector_weight = e->m_sector_coefficient[evi];
if (!(0.0 <= sector_weight && sector_weight <= 1.0))
return false;
}
if (!(sector_weight == e->m_sector_coefficient[evi]))
return false;
if (ON_SubD::VertexTag::Smooth == other_vertex->m_vertex_tag)
{
if ( !(0.0 == e->m_sector_coefficient[1-evi]) )
return false;
}
else
{
if ( bTaggedVertex )
return false;
if ( !(0.5 == e->m_sector_coefficient[1-evi]) )
return false;
}
}
else if (ON_SubD::EdgeTag::Crease == e->m_edge_tag)
{
if (crease_edge_face_count != e->m_face_count)
return false;
creased_edge_count++;
if (creased_edge_count > 2)
return false;
if (false == other_vertex->IsDartOrCreaseOrCorner())
return false;
}
else
{
return false;
}
}
switch (creased_edge_count)
{
case 0:
if (false == IsSmooth())
return false;
break;
case 1:
if (false == IsDart())
return false;
break;
case 2:
if (false == IsCreaseOrCorner())
return false;
break;
default:
return false;
}
// The standard subdivison matrix will correctly calculate
// the subdivision location for this vertex.
return true;
}
unsigned int ON_SubDEdge::EdgeAttributes() const
{
unsigned int edge_topology_attributes = 0U;
if (nullptr == m_vertex[0] || nullptr == m_vertex[1])
{
edge_topology_attributes |= ON_ComponentAttributes::Damaged;
}
else
{
const double* P[2] = { m_vertex[0]->m_P, m_vertex[1]->m_P };
if (
fabs(P[0][0]) < ON_UNSET_POSITIVE_VALUE
&& fabs(P[0][1]) < ON_UNSET_POSITIVE_VALUE
&& fabs(P[0][2]) < ON_UNSET_POSITIVE_VALUE
&& fabs(P[1][0]) < ON_UNSET_POSITIVE_VALUE
&& fabs(P[1][1]) < ON_UNSET_POSITIVE_VALUE
&& fabs(P[1][2]) < ON_UNSET_POSITIVE_VALUE
)
{
if (P[0][0] == P[1][0] && P[0][1] == P[1][1] && P[0][2] == P[1][2])
edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::ZeroLength;
else
edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::NonzeroLength;
}
if (m_vertex[0] != m_vertex[1])
edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Open;
else
edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Closed;
}
const ON_SubDFace* f[2] = { ON_SUBD_FACE_POINTER(m_face2[0].m_ptr),ON_SUBD_FACE_POINTER(m_face2[1].m_ptr) };
switch (m_face_count)
{
case 0:
edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Wire;
break;
case 1:
edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Boundary;
if ( nullptr == f[0])
edge_topology_attributes |= ON_ComponentAttributes::Damaged;
break;
case 2:
edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Interior;
if (IsSmooth())
edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorSmooth;
else if (IsCrease())
edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorCrease;
if (nullptr != f[0] && nullptr != f[1])
{
if (ON_SUBD_FACE_DIRECTION(m_face2[0].m_ptr) == ON_SUBD_FACE_DIRECTION(m_face2[1].m_ptr))
edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorNotOriented;
else
edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorOriented;
if (f[0] != f[1])
{
edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorTwoFaced;
}
else
{
const unsigned int fecount = f[0]->EdgeCount();
const unsigned int fei0 = f[0]->EdgeArrayIndex(this);
if (fecount > 2 && fei0 < fecount)
{
for (unsigned int fei1 = fei0 + 1; fei1 < fecount; ++fei1)
{
if (this == f[0]->Edge(fei1))
{
if (f[0]->EdgeDirection(fei0) != f[0]->EdgeDirection(fei1))
{
if ( fei1+1 == fei0 || (0 == fei0 && fei1+1 == fecount))
edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorSlit;
else
edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorSeam;
}
break;
}
}
}
}
}
else
{
edge_topology_attributes |= ON_ComponentAttributes::Damaged;
}
break;
default:
edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Nonmanifold;
if ( nullptr == f[0] || nullptr == f[1] || nullptr == m_facex )
edge_topology_attributes |= ON_ComponentAttributes::Damaged;
break;
}
return edge_topology_attributes;
}
static int Compare_ON__UINT_PTR(const void* a, const void* b)
{
ON__UINT_PTR ai = *((const unsigned int*)a);
ON__UINT_PTR bi = *((const unsigned int*)b);
if (ai < bi)
return -1;
if (ai > bi)
return 1;
return 0;
}
static int ComparePointerArrays(
size_t count,
const ON__UINT_PTR* a,
const ON__UINT_PTR* b
)
{
if (count <= 0)
return 0;
if (nullptr == a)
return ((nullptr == b) ? 0 : -1);
if (nullptr == b)
return 1;
if (1 == count)
return Compare_ON__UINT_PTR(a, b);
unsigned int stack_buffer[128];
unsigned int* adex
= (2 * count <= sizeof(stack_buffer) / sizeof(stack_buffer[0]))
? stack_buffer
: new (std::nothrow) unsigned int[2 * count];
if (nullptr == adex)
return 0;
unsigned int* bdex = adex + count;
ON_Sort(ON::sort_algorithm::quick_sort, adex, a, count, sizeof(a[0]), Compare_ON__UINT_PTR);
ON_Sort(ON::sort_algorithm::quick_sort, bdex, b, count, sizeof(b[0]), Compare_ON__UINT_PTR);
int rc = 0;
for (unsigned int i = 0; 0 == rc && i < count; i++)
{
rc = Compare_ON__UINT_PTR(a + adex[i], b + bdex[i]);
}
if (adex != stack_buffer)
delete[] adex;
return rc;
}
int ON_SubDVertex::CompareUnorderedEdges(
const ON_SubDVertex* a,
const ON_SubDVertex* b
)
{
if (nullptr == a)
return ((nullptr == b) ? 0 : -1);
if (nullptr == b)
return 1;
if (a->m_edge_count < b->m_edge_count)
return -1;
if (a->m_edge_count > b->m_edge_count)
return 1;
return ComparePointerArrays(a->m_edge_count, (const ON__UINT_PTR*)a->m_edges, (const ON__UINT_PTR*)b->m_edges);
}
int ON_SubDVertex::CompareUnorderedFaces(
const ON_SubDVertex* a,
const ON_SubDVertex* b
)
{
if (nullptr == a)
return ((nullptr == b) ? 0 : -1);
if (nullptr == b)
return 1;
if (a->m_face_count < b->m_face_count)
return -1;
if (a->m_face_count > b->m_face_count)
return 1;
return ComparePointerArrays(a->m_face_count, (const ON__UINT_PTR*)a->m_faces, (const ON__UINT_PTR*)b->m_faces);
}
int ON_SubDVertex::CompareUnorderedEdgesAndFaces(
const ON_SubDVertex* a,
const ON_SubDVertex* b
)
{
int rc = CompareUnorderedEdges(a, b);
if (0 == rc)
rc = CompareUnorderedFaces(a, b);
return rc;
}
//////////////////////////////////////////////////////////////////////////
//
// ON_SubDEdge
//
void ON_SubDComponentBase::CopyBaseFrom(
const ON_SubDComponentBase* src
)
{
if ( nullptr == src )
src = &ON_SubDComponentBase::Unset;
*this = *src;
m_subd_point1 = nullptr;
Internal_ClearSurfacePointFlag();
}
void ON_SubDEdge::CopyFrom(
const ON_SubDEdge* src,
bool bReverseEdge,
bool bCopyVertexArray,
bool bCopyFaceArray
)
{
if (nullptr == src)
src = &ON_SubDEdge::Empty;
CopyBaseFrom(src);
m_next_edge = nullptr;
m_edge_tag = src->m_edge_tag;
unsigned int end0 = bReverseEdge ? 1 : 0;
if (bCopyVertexArray)
{
m_vertex[0] = src->m_vertex[end0];
m_vertex[1] = src->m_vertex[1 - end0];
}
m_sector_coefficient[0] = src->m_sector_coefficient[end0];
m_sector_coefficient[1] = src->m_sector_coefficient[1 - end0];
if (bCopyFaceArray)
{
if (src->m_face_count > 0 && (src->m_face_count <= 2 || (nullptr != src->m_facex && nullptr != m_facex)))
{
m_face2[0] = src->m_face2[0];
m_face2[1] = src->m_face2[1];
unsigned int face_count = src->m_face_count;
if (face_count > 2)
{
face_count -= 2;
for (unsigned int efi = 0; efi < face_count; efi++)
m_facex[efi] = src->m_facex[efi];
}
m_face_count = src->m_face_count;
}
else
m_face_count = 0;
}
}
unsigned int ON_SubDEdge::TaggedEndIndex() const
{
unsigned int tagged_end_index = 3;
for (unsigned int evi = 0; evi < 2; evi++)
{
const ON_SubDVertex* v = m_vertex[evi];
if (nullptr == v || false == v->IsDartOrCreaseOrCorner())
continue;
tagged_end_index = (3 == tagged_end_index) ? evi : 2;
}
return tagged_end_index;
}
const ON_SubDFacePtr ON_SubDEdge::FacePtr(
unsigned int i
) const
{
return (i < 2) ? m_face2[i] : ((i < m_face_count) ? m_facex[i - 2] : ON_SubDFacePtr::Null);
//return (i < m_face_count) ? ((i < 2) ? m_face2[i] : m_facex[i - 2]) : ON_SubDFacePtr::Null;
}
unsigned int ON_SubDEdge::FaceCount() const
{
return m_face_count;
}
bool ON_SubDEdge::IsInteriorEdge() const
{
for (;;)
{
if (2 != m_face_count)
break;
if (ON_SUBD_FACE_DIRECTION(m_face2[0].m_ptr) == ON_SUBD_FACE_DIRECTION(m_face2[1].m_ptr))
break;
if (nullptr == ON_SUBD_FACE_POINTER(m_face2[0].m_ptr))
break;
if (nullptr == ON_SUBD_FACE_POINTER(m_face2[1].m_ptr))
break;
}
return false;
}
const class ON_SubDFace* ON_SubDEdge::Face(
unsigned int i
) const
{
return (i < 2) ? ON_SUBD_FACE_POINTER(m_face2[i].m_ptr) : ((i < m_face_count) ? ON_SUBD_FACE_POINTER(m_facex[i - 2].m_ptr) : nullptr);
}
ON__UINT_PTR ON_SubDEdge::FaceDirection(
unsigned int i
) const
{
return (i < 2) ? ON_SUBD_FACE_DIRECTION(m_face2[i].m_ptr) : ((i < m_face_count) ? ON_SUBD_FACE_DIRECTION(m_facex[i - 2].m_ptr) : 0);
}
const ON_SubDFacePtr ON_SubDEdge::FacePtrFromFace(
const class ON_SubDFace* f
) const
{
if (nullptr != f)
{
const ON_SubDFacePtr* fptr = m_face2;
const unsigned int edge_face_count = m_face_count;
for (unsigned int efi = 0; efi < edge_face_count; efi++, fptr++)
{
if (2 == efi)
{
fptr = m_facex;
if (nullptr == fptr)
break;
}
if (fptr->Face() == f)
return *fptr;
}
}
return ON_SubDFacePtr::Null;
}
unsigned int ON_SubDEdge::FaceArrayIndex(
const ON_SubDFace* f
) const
{
if (nullptr == f)
return ON_UNSET_UINT_INDEX;
const unsigned int face_count = m_face_count;
if (face_count > 0)
{
if (f == ON_SUBD_FACE_POINTER(m_face2[0].m_ptr))
return 0;
if (face_count >= 1)
{
if (f == ON_SUBD_FACE_POINTER(m_face2[1].m_ptr))
return 1;
if (face_count > 2 && nullptr != m_facex)
{
const ON_SubDFacePtr* fptr = m_facex - 2;
for (unsigned int efi = 2; efi < face_count; efi++)
{
if (f == ON_SUBD_FACE_POINTER(fptr[efi].m_ptr))
return efi;
}
}
}
}
return ON_UNSET_UINT_INDEX;
}
unsigned int ON_SubDEdge::ReplaceFaceInArray(const ON_SubDFace * old_face, const ON_SubDFace * new_face)
{
unsigned efi = (nullptr != old_face && old_face != new_face) ? FaceArrayIndex(old_face) : ON_UNSET_UINT_INDEX;
if (ON_UNSET_UINT_INDEX == efi)
return ON_UNSET_UINT_INDEX;
ON_SubDFacePtr* fptr = (efi < 2) ? &m_face2[efi] : &m_facex[efi - 2];
if (nullptr != new_face)
{
*fptr = ON_SubDFacePtr::Create(new_face, fptr->FaceDirection());
}
else
{
unsigned efi1 = efi + 1;
ON_SubDFacePtr* fptr1 = (efi1 < 2) ? &m_face2[efi1] : &m_facex[efi1 - 2];
for (const unsigned c = (unsigned)(m_face_count--); efi1 < c; ++efi, ++efi1)
{
if (2 == efi)
fptr = m_facex;
else if (2 == efi1)
fptr1 = m_facex;
*fptr++ = *fptr1++;
}
}
return efi;
}
unsigned int ON_SubDEdge::VertexArrayIndex(const ON_SubDVertex * v) const
{
if (nullptr == v || m_vertex[0] == m_vertex[1])
return ON_UNSET_UINT_INDEX;
if (v == m_vertex[0])
return 0;
if (v == m_vertex[1])
return 1;
return ON_UNSET_UINT_INDEX;
}
const ON_SubDFace* ON_SubDEdge::NeighborFace(
const ON_SubDFace* face,
bool bStopAtCrease
) const
{
if ( nullptr == face || 2 != m_face_count )
return nullptr;
// Do not stop at x tags
if (bStopAtCrease && ON_SubD::EdgeTag::Crease == m_edge_tag)
return nullptr;
const ON_SubDFace* f[2] = { ON_SUBD_FACE_POINTER(m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(m_face2[1].m_ptr) };
if (nullptr == f[0] || nullptr == f[1] )
return ON_SUBD_RETURN_ERROR(nullptr);
if (face == f[0])
{
if (face == f[1] )
return ON_SUBD_RETURN_ERROR(nullptr);
return f[1];
}
if (face == f[1])
{
return f[0];
}
return ON_SUBD_RETURN_ERROR(nullptr);
}
const ON_SubDFacePtr ON_SubDEdge::NeighborFacePtr(
const ON_SubDFace* face,
bool bStopAtCrease
) const
{
if ( nullptr == face || 2 != m_face_count )
return ON_SubDFacePtr::Null;
// Do not stop at x tags
if (bStopAtCrease && ON_SubD::EdgeTag::Crease == m_edge_tag)
return ON_SubDFacePtr::Null;
const ON_SubDFace* f[2] = { ON_SUBD_FACE_POINTER(m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(m_face2[1].m_ptr) };
if (nullptr == f[0] || nullptr == f[1] )
return ON_SUBD_RETURN_ERROR(ON_SubDFacePtr::Null);
if (face == f[0])
{
if (face == f[1] )
return ON_SUBD_RETURN_ERROR(ON_SubDFacePtr::Null);
return m_face2[1];
}
if (face == f[1])
{
return m_face2[0];
}
return ON_SUBD_RETURN_ERROR(ON_SubDFacePtr::Null);
}
const ON_SubDEdgePtr ON_SubDEdge::AdjacentEdgePtr(
unsigned int edge_vertex_index,
unsigned int i
) const
{
if ( edge_vertex_index >= 2 )
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
const ON_SubDFacePtr fptr = FacePtr(i);
const ON_SubDFace* f = ON_SUBD_FACE_POINTER(fptr.m_ptr);
if (nullptr == f)
return ON_SubDEdgePtr::Null;
const unsigned int fe_count = f->m_edge_count;
if ( fe_count < 3 || fe_count > ON_SubDFace::MaximumEdgeCount)
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
const unsigned int fei = f->EdgeArrayIndex(this);
if( fei >= fe_count)
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
const ON_SubDEdgePtr eptr = f->EdgePtr(fei);
if ( this != ON_SUBD_EDGE_POINTER(eptr.m_ptr))
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
const ON__UINT_PTR dir = ON_SUBD_FACE_DIRECTION(fptr.m_ptr);
if (dir != ON_SUBD_EDGE_DIRECTION(eptr.m_ptr))
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
const unsigned int fei1
= (dir == ((ON__UINT_PTR)edge_vertex_index))
? ((fei + fe_count - 1) % fe_count)
: ((fei + 1) % fe_count)
;
return f->EdgePtr(fei1);
}
const ON_SubDEdge* ON_SubDEdge::AdjacentEdge(
unsigned int edge_vertex_index,
unsigned int i
) const
{
return ON_SUBD_EDGE_POINTER(AdjacentEdgePtr(edge_vertex_index, i).m_ptr);
}
const class ON_SubDVertex* ON_SubDEdge::Vertex(
unsigned int i
) const
{
return (i <= 1) ? m_vertex[i] : nullptr;
}
const ON_SubDVertex* ON_SubDEdge::OtherEndVertex(
const ON_SubDVertex* vertex
) const
{
if (nullptr != vertex)
{
if (m_vertex[0] == vertex)
{
if (m_vertex[1] != vertex )
return m_vertex[1];
}
else if (m_vertex[1] == vertex )
return m_vertex[0];
}
return nullptr;
}
const ON_SubDEdgePtr ON_SubDEdge::FromVertices(
const ON_SubDVertex* vertex0,
const ON_SubDVertex* vertex1
)
{
if (nullptr != vertex0 && nullptr != vertex1 && vertex0 != vertex1 && nullptr != vertex0->m_edges)
{
for (unsigned short vei = 0; vei < vertex0->m_edge_count; vei++)
{
const ON_SubDEdgePtr eptr = vertex0->m_edges[vei];
if (vertex1 == eptr.RelativeVertex(1) && vertex0 == eptr.RelativeVertex(0))
return eptr;
}
}
return ON_SubDEdgePtr::Null;
}
const ON_SubDEdge* ON_SubDEdge::FromVertices(
const ON_SubDVertex* vertex0,
const ON_SubDVertex* vertex1,
bool bIgnoreOrientation
)
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(ON_SubDEdge::FromVertices(vertex0, vertex1).m_ptr);
if ( nullptr != e && false == bIgnoreOrientation && vertex0 != e->m_vertex[0] )
e = nullptr;
return e;
}
const ON_3dPoint ON_SubDEdge::ControlNetPoint( unsigned int i) const
{
if (i >= 2 || nullptr == m_vertex[i])
return ON_3dPoint::NanPoint;
return (ON_3dPoint(m_vertex[i]->m_P));
}
const ON_3dVector ON_SubDEdge::ControlNetDirection() const
{
if (nullptr == m_vertex[0] || nullptr == m_vertex[1])
return ON_3dVector::NanVector;
const ON_3dPoint P[2] = { ON_3dPoint(m_vertex[0]->m_P) ,ON_3dPoint(m_vertex[1]->m_P) };
return (P[0].IsValid() && P[1].IsValid()) ? (P[1] - P[0]) : ON_3dVector::NanVector;
}
const ON_3dVector ON_SubDEdge::ControlNetDirectionFrom(
const ON_SubDVertex* v
) const
{
if (nullptr != v)
{
if (v == m_vertex[0] && nullptr != m_vertex[1])
return ControlNetDirection();
if (v == m_vertex[1] && nullptr != m_vertex[0])
return -ControlNetDirection();
}
return ON_3dVector::NanVector;
}
//////////////////////////////////////////////////////////////////////////
//
// ON_SubDFace
//
//bool ON_SubDFace::IsOrdinary(
// ON_SubD::SubDType subdivision_type,
// bool bTestFaces
// ) const
//{
// unsigned int ordinary_face_edge_count = 0;
// if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type)
// ordinary_face_edge_count = 4;
// else if (ON_SubD::SubDType::TriLoopWarren == subdivision_type)
// ordinary_face_edge_count = 3;
// else
// return false;
//
// if (ordinary_face_edge_count != m_edge_count)
// return false;
//
// for (unsigned int fei = 0; fei < ordinary_face_edge_count; fei++)
// {
// ON__UINT_PTR eptr = m_edge4[fei].m_ptr;
// const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr);
// if (nullptr == e || 2 != e->m_face_count || ON_SubD::EdgeTag::Smooth != e->m_edge_tag)
// return false;
// ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(eptr);
// const ON_SubDVertex* v = e->m_vertex[edir];
// if (nullptr == v || false == v->IsOrdinary(subdivision_type,ON_SubD::VertexTag::Unset,bTestFaces))
// return false;
// }
// return true;
//}
void ON_SubDFace::CopyFrom(
const ON_SubDFace* src,
bool bCopyEdgeArray
)
{
if (nullptr == src)
src = &ON_SubDFace::Empty;
CopyBaseFrom(src);
m_next_face = nullptr;
m_zero_face_id = src->m_zero_face_id;
m_parent_face_id = src->m_parent_face_id;
if (bCopyEdgeArray)
{
if (src->m_edge_count > 0 && (src->m_edge_count <= 4 || (nullptr != src->m_edgex && nullptr != m_edgex)))
{
m_edge4[0] = src->m_edge4[0];
m_edge4[1] = src->m_edge4[1];
m_edge4[2] = src->m_edge4[2];
m_edge4[3] = src->m_edge4[3];
unsigned int edge_count = src->m_edge_count;
if (edge_count > 4)
{
edge_count -= 4;
for (unsigned int fei = 0; fei < edge_count; fei++)
m_edgex[fei] = src->m_edgex[fei];
}
m_edge_count = src->m_edge_count;
}
else
m_edge_count = 0;
}
}
const ON_SubDEdgePtr ON_SubDFace::EdgePtr(
unsigned int i
) const
{
return (i < 4) ? m_edge4[i] : ((i < m_edge_count) ? m_edgex[i-4] : ON_SubDEdgePtr::Null);
}
unsigned int ON_SubDFace::EdgeCount() const
{
return m_edge_count;
}
unsigned int ON_SubDFace::MarkedEdgeCount() const
{
unsigned int marked_edge_count = 0;
const ON_SubDEdgePtr* eptr = m_edge4;
for (unsigned short fei = 0; fei < m_edge_count; ++fei, ++ eptr)
{
if (4 == fei)
{
eptr = m_edgex;
if (nullptr == eptr)
break;
}
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr->m_ptr);
if (nullptr != e && e->m_status.RuntimeMark())
++marked_edge_count;
}
return marked_edge_count;
}
unsigned int ON_SubDFace::SetEdgeMarks(bool bMark) const
{
bMark = bMark ? true : false; // so exact compare can be used
unsigned int changed_mark_count = 0;
const ON_SubDEdgePtr* eptr = m_edge4;
for (unsigned short fei = 0; fei < m_edge_count; ++fei, ++ eptr)
{
if (4 == fei)
{
eptr = m_edgex;
if (nullptr == eptr)
break;
}
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr->m_ptr);
if (nullptr != e && bMark != e->m_status.RuntimeMark())
{
e->m_status.SetRuntimeMark(bMark);
++changed_mark_count;
}
}
return changed_mark_count;
}
unsigned int ON_SubDFace::GetEdgeArray(
ON_SimpleArray< ON_SubDEdgePtr >& face_edge_array
) const
{
face_edge_array.SetCount(0);
const unsigned edge_count = m_edge_count;
face_edge_array.Reserve(edge_count);
face_edge_array.Append(edge_count <= 4 ? edge_count : 4U, m_edge4);
if (edge_count > 4)
{
if ( nullptr != m_edgex )
face_edge_array.Append(edge_count-4U, m_edgex);
else
{
for (unsigned fei = 4; fei < edge_count; ++fei)
face_edge_array.Append(ON_SubDEdgePtr::Null);
}
}
return edge_count;
}
unsigned int ON_SubDFace::SetVertexMarks(bool bMark) const
{
bMark = bMark ? true : false; // so exact compare can be used
unsigned int changed_mark_count = 0;
const ON_SubDEdgePtr* eptr = m_edge4;
for (unsigned short fei = 0; fei < m_edge_count; ++fei, ++ eptr)
{
if (4 == fei)
{
eptr = m_edgex;
if (nullptr == eptr)
break;
}
const ON_SubDVertex* v = eptr->RelativeVertex(0);
if (nullptr != v && bMark != v->m_status.RuntimeMark())
{
v->m_status.SetRuntimeMark(bMark);
++changed_mark_count;
}
}
return changed_mark_count;
}
unsigned int ON_SubDFace::MarkedVertexCount() const
{
unsigned int marked_vertex_count = 0;
const ON_SubDEdgePtr* eptr = m_edge4;
for (unsigned short fei = 0; fei < m_edge_count; ++fei, ++ eptr)
{
if (4 == fei)
{
eptr = m_edgex;
if (nullptr == eptr)
break;
}
const ON_SubDVertex* v = eptr->RelativeVertex(0);
if (nullptr != v && v->m_status.RuntimeMark())
++marked_vertex_count;
}
return marked_vertex_count;
}
const class ON_SubDVertex* ON_SubDFace::Vertex(
unsigned int i
) const
{
ON_SubDEdge* e;
const ON__UINT_PTR edge_ptr = (i < 4) ? m_edge4[i].m_ptr : ((i < m_edge_count) ? m_edgex[i - 4].m_ptr : 0);
return (nullptr != (e = ON_SUBD_EDGE_POINTER(edge_ptr))) ? e->m_vertex[ON_SUBD_EDGE_DIRECTION(edge_ptr)] : nullptr;
}
const ON_3dPoint ON_SubDFace::ControlNetPoint(unsigned int i) const
{
const ON_SubDEdge* e;
const ON__UINT_PTR edge_ptr = (i < 4) ? m_edge4[i].m_ptr : ((i < m_edge_count) ? m_edgex[i - 4].m_ptr : 0);
const ON_SubDVertex* v = (nullptr != (e = ON_SUBD_EDGE_POINTER(edge_ptr))) ? e->m_vertex[ON_SUBD_EDGE_DIRECTION(edge_ptr)] : nullptr;
return (nullptr != v) ? ON_3dPoint(v->m_P) : ON_3dPoint::NanPoint;
}
const ON_SubDVertex* ON_SubDFace::QuadOppositeVertex(
const ON_SubDVertex* vertex
) const
{
if ( nullptr == vertex )
return ON_SUBD_RETURN_ERROR(nullptr);
if ( 4 != m_edge_count)
return nullptr; // not an error
ON__UINT_PTR ptr0 = m_edge4[0].m_ptr;
const ON_SubDEdge* e0 = ON_SUBD_EDGE_POINTER(ptr0);
if ( nullptr == e0 )
return ON_SUBD_RETURN_ERROR(nullptr);
ptr0 = ON_SUBD_EDGE_DIRECTION(ptr0);
ON__UINT_PTR ptr2 = m_edge4[2].m_ptr;
const ON_SubDEdge* e2 = ON_SUBD_EDGE_POINTER(ptr2);
if ( nullptr == e2 )
return ON_SUBD_RETURN_ERROR(nullptr);
ptr2 = ON_SUBD_EDGE_DIRECTION(ptr2);
if (vertex == e0->m_vertex[ptr0])
return e2->m_vertex[ptr2];
if (vertex == e0->m_vertex[1-ptr0])
return e2->m_vertex[1-ptr2];
if (vertex == e2->m_vertex[ptr2])
return e0->m_vertex[ptr0];
if (vertex == e2->m_vertex[1-ptr2])
return e0->m_vertex[1-ptr0];
return ON_SUBD_RETURN_ERROR(nullptr);
}
const ON_SubDEdge* ON_SubDFace::QuadOppositeEdge(
const ON_SubDEdge* edge
) const
{
if ( nullptr == edge )
return ON_SUBD_RETURN_ERROR(nullptr);
if ( 4 != m_edge_count)
return nullptr; // not an error
for (unsigned int fei = 0; fei < 4; fei++)
{
const ON_SubDEdge* e0 = ON_SUBD_EDGE_POINTER(m_edge4[fei].m_ptr);
if (nullptr == e0)
return ON_SUBD_RETURN_ERROR(nullptr);
if (e0 == edge)
{
e0 = ON_SUBD_EDGE_POINTER(m_edge4[(fei + 2) % 4].m_ptr);
if (nullptr == e0)
return ON_SUBD_RETURN_ERROR(nullptr);
return e0;
}
}
return ON_SUBD_RETURN_ERROR(nullptr);
}
const class ON_SubDEdge* ON_SubDFace::Edge(
unsigned int i
) const
{
return (i < 4) ? ON_SUBD_EDGE_POINTER(m_edge4[i].m_ptr) : ((i < m_edge_count) ? ON_SUBD_EDGE_POINTER(m_edgex[i - 4].m_ptr) : nullptr);
}
ON__UINT_PTR ON_SubDFace::EdgeDirection(
unsigned int i
) const
{
return (i < 4) ? ON_SUBD_EDGE_DIRECTION(m_edge4[i].m_ptr) : ((i < m_edge_count) ? ON_SUBD_EDGE_DIRECTION(m_edgex[i - 4].m_ptr) : 0);
}
const ON_SubDEdgePtr ON_SubDFace::EdgePtrFromEdge(
const class ON_SubDEdge* e
) const
{
if (nullptr != e)
{
const ON_SubDEdgePtr* eptr = m_edge4;
const unsigned int face_edge_count = m_edge_count;
for (unsigned int fei = 0; fei < face_edge_count; fei++, eptr++)
{
if (4 == fei)
{
eptr = m_edgex;
if (nullptr == eptr)
break;
}
if (e == ON_SUBD_EDGE_POINTER(eptr->m_ptr))
return *eptr;
}
}
return ON_SubDEdgePtr::Null;
}
unsigned int ON_SubDFace::EdgeArrayIndex(
const ON_SubDEdge* e
) const
{
if (nullptr != e)
{
const ON_SubDEdgePtr* eptr = m_edge4;
const unsigned int face_edge_count = m_edge_count;
for (unsigned int fei = 0; fei < face_edge_count; fei++, eptr++)
{
if (4 == fei)
{
eptr = m_edgex;
if (nullptr == eptr)
break;
}
if (e == ON_SUBD_EDGE_POINTER(eptr->m_ptr))
return fei;
}
}
return ON_UNSET_UINT_INDEX;
}
const ON_SubDEdge* ON_SubDFace::PrevEdge(
const ON_SubDEdge* edge
) const
{
unsigned int edge_index = EdgeArrayIndex(edge);
if (ON_UNSET_UINT_INDEX == edge_index)
return nullptr;
const unsigned int edge_count = m_edge_count;
edge_index = (edge_index + (edge_count - 1)) % edge_count;
return Edge(edge_index);
}
const ON_SubDEdge* ON_SubDFace::NextEdge(
const ON_SubDEdge* edge
) const
{
unsigned int edge_index = EdgeArrayIndex(edge);
if (ON_UNSET_UINT_INDEX == edge_index)
return nullptr;
edge_index = (edge_index + 1) % ((unsigned int)m_edge_count);
return Edge(edge_index);
}
unsigned int ON_SubDFace::PrevEdgeArrayIndex(
unsigned int edge_array_index
) const
{
const unsigned int edge_count = m_edge_count;
return (edge_array_index < edge_count) ? ((edge_array_index + edge_count - 1) % edge_count) : ON_UNSET_UINT_INDEX;
}
unsigned int ON_SubDFace::NextEdgeArrayIndex(
unsigned int edge_array_index
) const
{
const unsigned int edge_count = m_edge_count;
return (edge_array_index < edge_count) ? ((edge_array_index + 1) % edge_count) : ON_UNSET_UINT_INDEX;
}
bool ON_SubDVertex::RemoveEdgeFromArray(const ON_SubDEdge * e)
{
if (nullptr == e || 0 == m_edge_count || nullptr == m_edges)
return ON_SUBD_RETURN_ERROR(false);
unsigned short new_count = 0;
for (unsigned short vei = 0; vei < m_edge_count; ++vei)
{
const ON_SubDEdgePtr eptr = m_edges[vei];
if (e == ON_SUBD_EDGE_POINTER(eptr.m_ptr))
continue;
m_edges[new_count++] = eptr;
}
if (new_count == m_edge_count)
return false;
m_edge_count = new_count;
return true;
}
bool ON_SubDVertex::RemoveFaceFromArray(const ON_SubDFace * f)
{
if (nullptr == f || 0 == m_face_count || nullptr == m_faces)
return ON_SUBD_RETURN_ERROR(false);
unsigned short new_count = 0;
for (unsigned short vfi = 0; vfi < m_face_count; ++vfi)
{
const ON_SubDFace* vf = m_faces[vfi];
if (f == vf)
continue;
m_faces[new_count++] = vf;
}
if (new_count == m_face_count)
return false;
m_face_count = new_count;
return true;
}
bool ON_SubDEdge::RemoveFaceFromArray(
const ON_SubDFace* f
)
{
unsigned int i;
if (nullptr == f)
return false;
if (m_face_count <= 2)
{
for (i = 0; i < m_face_count; i++)
{
if (f == ON_SUBD_FACE_POINTER(m_face2[i].m_ptr))
{
for (i++; i < m_face_count; i++)
m_face2[i - 1] = m_face2[i];
m_face_count--;
return true;
}
}
}
else
{
for (i = 0; i < 2; i++)
{
if (f == ON_SUBD_FACE_POINTER(m_face2[i].m_ptr))
{
for (i++; i < 2; i++)
m_face2[i - 1] = m_face2[i];
m_face2[1] = m_facex[0];
for (i = 3; i < m_face_count; i++)
m_facex[i - 3] = m_facex[i - 2];
m_face_count--;
return true;
}
}
for (i = 2; i < m_face_count; i++)
{
if (f == ON_SUBD_FACE_POINTER(m_facex[i - 4].m_ptr))
{
for (i++; i < m_face_count; i++)
m_facex[i - 3] = m_facex[i - 2];
m_face_count--;
return true;
}
}
}
return false;
}
bool ON_SubDEdge::AddFaceToArray(
ON_SubDFacePtr face_ptr
)
{
if (m_face_count < 2)
m_face2[m_face_count] = face_ptr;
else if (nullptr != m_facex && m_face_count < 2 + m_facex_capacity)
m_facex[m_face_count - 2] = face_ptr;
else
{
// not enough room in m_facex.
// If you really are trying to make a non-manifold subd,
// then use ON_SubD::GrowEdgeFaceArray().
return ON_SUBD_RETURN_ERROR(false);
}
m_face_count++;
return true;
}
bool ON_SubDEdge::RemoveFaceFromArray(
unsigned int i,
ON_SubDFacePtr& removed_face
)
{
removed_face = ON_SubDFacePtr::Null;
unsigned int count = m_face_count;
if ( i >= count )
return ON_SUBD_RETURN_ERROR(false);
if (i < 2)
{
removed_face = m_face2[i];
}
if (count > 2)
{
if ( nullptr == m_facex || m_facex_capacity + ((unsigned short)2) < m_face_count )
return ON_SUBD_RETURN_ERROR(false);
if ( i >= 2 )
removed_face = m_facex[i-2];
}
unsigned int j = i+1;
while (j < 2 && j < count )
m_face2[i++] = m_face2[j++];
if (count > 2)
{
m_face2[1] = m_facex[0];
i = 0;
j = 1;
count -= 2;
while (j < count )
m_facex[i++] = m_facex[j++];
}
m_face_count--;
return true;
}
bool ON_SubDFace::ReplaceEdgeInArray(
unsigned int fei0,
ON_SubDEdge* edge_to_remove,
ON_SubDEdge* edge_to_insert
)
{
const unsigned int face_edge_count = m_edge_count;
ON_SubDEdgePtr* eptr = m_edge4;
for (unsigned int fei = 0; fei < face_edge_count; fei++, eptr++)
{
if (4 == fei)
{
eptr = m_edgex;
if (nullptr == eptr)
break;
}
if (fei >= fei0 && edge_to_remove == eptr->Edge() )
{
const ON__UINT_PTR edir = eptr->EdgeDirection();
*eptr = ON_SubDEdgePtr::Create(edge_to_insert,edir);
return true;
}
}
return false;
}
bool ON_SubDFace::RemoveEdgeFromArray(
unsigned int i,
ON_SubDEdgePtr& removed_edge
)
{
removed_edge = ON_SubDEdgePtr::Null;
unsigned int count = m_edge_count;
if ( i >= count )
return ON_SUBD_RETURN_ERROR(false);
if (i < 4)
{
removed_edge = m_edge4[i];
}
if (count > 4)
{
if ( nullptr == m_edgex || m_edgex_capacity + ((unsigned short)4) < m_edge_count )
return ON_SUBD_RETURN_ERROR(false);
if ( i >= 4 )
removed_edge = m_edgex[i-4];
}
unsigned int j = i+1;
while (j < 4 && j < count )
m_edge4[i++] = m_edge4[j++];
if (count > 4)
{
m_edge4[3] = m_edgex[0];
i = 0;
j = 1;
count -= 4;
while (j < count )
m_edgex[i++] = m_edgex[j++];
}
m_edge_count--;
return true;
}
bool ON_SubDFace::RotateEdgeArray(
unsigned int fei0
)
{
if (0 == fei0)
return true;
const unsigned int edge_count = m_edge_count;
if (edge_count < 2 || edge_count > ON_SubDFace::MaximumEdgeCount || fei0 >= edge_count)
return false;
ON_SubDEdgePtr stack_eptr[8];
ON_SubDEdgePtr* eptr
= (edge_count*sizeof(stack_eptr[0]) > sizeof(stack_eptr))
? ((ON_SubDEdgePtr*)onmalloc(edge_count * sizeof(eptr[0])))
: stack_eptr;
if (nullptr == eptr)
return false;
ON_SubDEdgePtr* feptr = m_edge4;
for (unsigned int fei = 0; fei < edge_count; fei++)
{
if (4 == fei)
{
feptr = m_edgex;
if (nullptr == feptr)
{
if ( eptr != stack_eptr )
onfree(eptr);
return false;
}
}
eptr[fei] = *feptr++;
}
feptr = m_edge4;
for (unsigned int fei = 0; fei < edge_count; fei++)
{
if (4 == fei)
feptr = m_edgex;
*feptr++ = eptr[(fei + fei0) % edge_count];
}
if ( eptr != stack_eptr )
onfree(eptr);
return true;
}
bool ON_SubDFace::RemoveEdgeFromArray(
const ON_SubDEdge* e
)
{
unsigned int i;
if (nullptr == e)
return false;
if (m_edge_count <= 4)
{
for (i = 0; i < m_edge_count; i++)
{
if (e == ON_SUBD_EDGE_POINTER(m_edge4[i].m_ptr))
{
for (i++; i < m_edge_count; i++)
m_edge4[i - 1] = m_edge4[i];
m_edge_count--;
m_edge4[m_edge_count] = ON_SubDEdgePtr::Null;
return true;
}
}
}
else
{
for (i = 0; i < 4; i++)
{
if (e == ON_SUBD_EDGE_POINTER(m_edge4[i].m_ptr))
{
for (i++; i < 4; i++)
m_edge4[i - 1] = m_edge4[i];
m_edge4[3] = m_edgex[0];
for (i = 5; i < m_edge_count; i++)
m_edgex[i - 5] = m_edgex[i - 4];
m_edge_count--;
m_edgex[m_edge_count-4] = ON_SubDEdgePtr::Null;
return true;
}
}
for (i = 4; i < m_edge_count; i++)
{
if (e == ON_SUBD_EDGE_POINTER(m_edgex[i - 4].m_ptr))
{
for (i++; i < m_edge_count; i++)
m_edgex[i - 5] = m_edgex[i - 4];
m_edge_count--;
m_edgex[m_edge_count-4] = ON_SubDEdgePtr::Null;
return true;
}
}
}
return false;
}
unsigned int ON_SubDFace::VertexIndex(
const ON_SubDVertex* vertex
) const
{
if (nullptr == vertex)
return ON_UNSET_UINT_INDEX;
const ON_SubDEdgePtr* face_edges = m_edge4;
const unsigned int edge_count = m_edge_count;
for (unsigned int i = 0; i < edge_count; i += 2)
{
if (4 == i)
{
face_edges = m_edgex;
if (nullptr == face_edges)
break;
face_edges -= 4;
}
ON__UINT_PTR e_ptr = face_edges[i].m_ptr;
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(e_ptr);
if (nullptr != edge)
{
if (vertex == edge->m_vertex[0])
return (0 == ON_SUBD_EDGE_DIRECTION(e_ptr)) ? i : ((i + 1) % edge_count);
if (vertex == edge->m_vertex[1])
return (0 == ON_SUBD_EDGE_DIRECTION(e_ptr)) ? ((i + 1) % edge_count) : i;
}
}
return ON_UNSET_UINT_INDEX;
}
//////////////////////////////////////////////////////////////////////////
//
// ON_SubD
//
ON_OBJECT_IMPLEMENT(ON_SubD,ON_Geometry,"F09BA4D9-455B-42C3-BA3B-E6CCACEF853B");
ON_SubD::ON_SubD() ON_NOEXCEPT
: ON_Geometry()
{}
ON_SubD::~ON_SubD()
{
this->Destroy();
}
#if defined(ON_HAS_RVALUEREF)
ON_SubD::ON_SubD( ON_SubD&& src ) ON_NOEXCEPT
: ON_Geometry(std::move(src))
, m_subdimple_sp(std::move(src.m_subdimple_sp))
{}
ON_SubD& ON_SubD::operator=( ON_SubD&& src )
{
if ( this != &src )
{
this->Destroy();
ON_Geometry::operator=(std::move(src));
m_subdimple_sp = std::move(src.m_subdimple_sp);
}
return *this;
}
#endif
ON__UINT64 ON_SubD::RuntimeSerialNumber() const
{
ON_SubDimple* subdimple = m_subdimple_sp.get();
return (nullptr != subdimple) ? subdimple->RuntimeSerialNumber : 0;
}
ON__UINT64 ON_SubD::ContentSerialNumber() const
{
ON_SubDimple* subdimple = m_subdimple_sp.get();
return (nullptr != subdimple) ? subdimple->ContentSerialNumber() : 0;
}
ON__UINT64 ON_SubD::ChangeContentSerialNumberForExperts()
{
if (this == &ON_SubD::Empty)
return 0;
ON_SubDimple* subdimple = m_subdimple_sp.get();
return (nullptr != subdimple) ? subdimple->ChangeContentSerialNumber() : 0;
}
ON_SubDComponentLocation ON_SubD::ToggleSubDAppearanceValue(ON_SubDComponentLocation subd_appearance)
{
if (ON_SubDComponentLocation::Surface == subd_appearance)
return ON_SubDComponentLocation::ControlNet;
if (ON_SubDComponentLocation::ControlNet == subd_appearance)
return ON_SubDComponentLocation::Surface;
return subd_appearance;
}
ON_SubDComponentLocation ON_SubDMeshFragmentIterator::SubDAppearance() const
{
return
(ON_SubDComponentLocation::Surface == m_subd_appearance_override || ON_SubDComponentLocation::ControlNet == m_subd_appearance_override)
? m_subd_appearance_override
: SubD().SubDAppearance();
}
void ON_SubDMeshFragmentIterator::SetSubDAppearanceOverride(ON_SubDComponentLocation subd_appearance_override)
{
if (m_subd_appearance_override != subd_appearance_override )
m_subd_appearance_override = subd_appearance_override;
}
ON_SubDComponentLocation ON_SubD::SubDAppearance() const
{
const ON_SubDimple* subdimple = this->SubDimple();
return (nullptr != subdimple) ? subdimple->SubDAppearance() : ON_SubD::DefaultSubDAppearance;
}
ON_SubDComponentLocation ON_SubDimple::SubDAppearance() const
{
return m_subd_appearance;
}
void ON_SubD::SetSubDAppearance(ON_SubDComponentLocation subd_appearance) const
{
if (
subd_appearance != SubDAppearance()
&& (ON_SubDComponentLocation::Surface == subd_appearance || ON_SubDComponentLocation::ControlNet == subd_appearance)
)
{
const ON_SubDimple* subdimple = const_cast<ON_SubD*>(this)->SubDimple(true);
if (nullptr != subdimple)
subdimple->SetSubDAppearance(subd_appearance);
}
}
void ON_SubDimple::SetSubDAppearance(ON_SubDComponentLocation subd_appearance) const
{
if (
subd_appearance != m_subd_appearance
&& (ON_SubDComponentLocation::Surface == subd_appearance || ON_SubDComponentLocation::ControlNet == subd_appearance)
)
{
m_subd_appearance = subd_appearance;
}
}
//////////////////////////////////////////////////////////////////////////
//
// ON_SubD - ON_Object overrides
//
//virtual
void ON_SubD::MemoryRelocate()
{
}
static bool ON_SubDIsNotValid(bool bSilentError)
{
ON_SubDIncrementErrorCount();
return bSilentError ? false : ON_IsNotValid();
}
static bool EdgeVertexWeightIsSet(
double edge_vertex_weight
)
{
return (0.0 < edge_vertex_weight && edge_vertex_weight < 1.0);
}
static bool EdgeSectorWeightIsValid(
double edge_vertex_weight,
const ON_SubDEdge* edge
)
{
if (0.0 <= edge_vertex_weight && edge_vertex_weight < 1.0)
return true;
if (ON_SubDSectorType::UnsetSectorWeight == edge_vertex_weight && nullptr != edge && 0 == edge->SubdivisionLevel())
return true;
return false;
}
static bool IsValidVertexEdgeLink(
const ON_SubDVertex* vertex,
const ON_SubDEdge* edge,
ON__UINT_PTR end_index,
bool bSilentError
)
{
if (nullptr == vertex || nullptr == edge)
return ON_SubDIsNotValid(bSilentError);
if (end_index > 1)
return ON_SubDIsNotValid(bSilentError);
if (edge->m_vertex[end_index] != vertex)
return ON_SubDIsNotValid(bSilentError);
if (vertex->SubdivisionLevel() != edge->SubdivisionLevel())
return ON_SubDIsNotValid(bSilentError);
if (false == EdgeSectorWeightIsValid(edge->m_sector_coefficient[end_index],edge))
return ON_SubDIsNotValid(bSilentError);
if ( edge->IsSmooth() )
{
// edge->m_edge_tag is ON_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::SmoothX
if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag)
{
if (false == (0.0 == edge->m_sector_coefficient[end_index]))
return ON_SubDIsNotValid(bSilentError);
}
else
{
const unsigned int tagged_end_index = edge->TaggedEndIndex();
if (ON_SubD::EdgeTag::SmoothX == edge->m_edge_tag)
{
if (2 != tagged_end_index)
return ON_SubDIsNotValid(bSilentError);
}
else
{
if (tagged_end_index != (unsigned int)end_index)
return ON_SubDIsNotValid(bSilentError);
}
ON_SubDSectorType st = ON_SubDSectorType::Create(edge,(unsigned int)end_index);
if (!st.IsValid())
return ON_SubDIsNotValid(bSilentError);
const double expected_vertex_weight = st.SectorWeight();
if (false == (expected_vertex_weight == edge->m_sector_coefficient[end_index]))
return ON_SubDIsNotValid(bSilentError);
if (false == EdgeVertexWeightIsSet(expected_vertex_weight))
return ON_SubDIsNotValid(bSilentError);
}
}
else if(ON_SubD::EdgeTag::Crease == edge->m_edge_tag)
{
// crease edge
if (!(0.0 == edge->m_sector_coefficient[end_index]))
return ON_SubDIsNotValid(bSilentError);
if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag)
return ON_SubDIsNotValid(bSilentError);
if (ON_SubD::VertexTag::Unset == vertex->m_vertex_tag)
return ON_SubDIsNotValid(bSilentError);
}
else
{
return ON_SubDIsNotValid(bSilentError);
}
return true;
}
static bool IsValidVertexFaceLink(
const ON_SubDVertex* vertex,
const ON_SubDFace* face,
unsigned int vertex_face_index,
unsigned int face_vertex_index,
bool bSilentError
)
{
if (nullptr == vertex || nullptr == face)
return ON_SubDIsNotValid(bSilentError);
if (vertex->SubdivisionLevel() != face->SubdivisionLevel())
return ON_SubDIsNotValid(bSilentError);
const unsigned int vertex_face_count = vertex->m_face_count;
if (vertex_face_count <= 0)
return ON_SubDIsNotValid(bSilentError);
if (nullptr == vertex->m_faces)
return ON_SubDIsNotValid(bSilentError);
if (vertex_face_index >= vertex_face_count && ON_UNSET_UINT_INDEX != vertex_face_index)
return ON_SubDIsNotValid(bSilentError);
const unsigned int face_vertex_count = face->m_edge_count;
if (face_vertex_count <= 0)
return ON_SubDIsNotValid(bSilentError);
if (face_vertex_count > 4 && nullptr == face->m_edgex)
return ON_SubDIsNotValid(bSilentError);
if (face_vertex_index >= face_vertex_count && ON_UNSET_UINT_INDEX != face_vertex_index)
return ON_SubDIsNotValid(bSilentError);
for (unsigned int i = 0; i < vertex_face_count; i++)
{
if (face == vertex->Face(i))
{
if (ON_UNSET_UINT_INDEX == vertex_face_index)
vertex_face_index = i;
else if (i != vertex_face_index)
return ON_SubDIsNotValid(bSilentError);
}
else if (i == vertex_face_index)
{
return ON_SubDIsNotValid(bSilentError);
}
}
for (unsigned int i = 0; i < face_vertex_count; i++)
{
if (vertex == face->Vertex(i))
{
if (ON_UNSET_UINT_INDEX == face_vertex_index)
face_vertex_index = i;
else if (i != face_vertex_index)
return ON_SubDIsNotValid(bSilentError);
}
else if (i == face_vertex_index)
{
return ON_SubDIsNotValid(bSilentError);
}
}
return true;
}
static bool IsValidEdgeFaceLink(
const ON_SubDEdge* edge,
const ON_SubDFace* face,
unsigned int edge_face_index,
unsigned int face_edge_index,
bool bSilentError
)
{
if (nullptr == edge || nullptr == face)
return ON_SubDIsNotValid(bSilentError);
if (edge->SubdivisionLevel() != face->SubdivisionLevel())
return ON_SubDIsNotValid(bSilentError);
const unsigned int edge_face_count = edge->m_face_count;
if (edge_face_count <= 0)
return ON_SubDIsNotValid(bSilentError);
if (edge_face_count > 2 && nullptr == edge->m_facex)
return ON_SubDIsNotValid(bSilentError);
if (edge_face_index >= edge_face_count && ON_UNSET_UINT_INDEX != edge_face_index)
return ON_SubDIsNotValid(bSilentError);
const unsigned int face_edge_count = face->m_edge_count;
if (face_edge_count <= 0)
return ON_SubDIsNotValid(bSilentError);
if (face_edge_count > 4 && nullptr == face->m_edgex)
return ON_SubDIsNotValid(bSilentError);
if (face_edge_index >= face_edge_count && ON_UNSET_UINT_INDEX != face_edge_index)
return ON_SubDIsNotValid(bSilentError);
for (unsigned int i = 0; i < edge_face_count; i++)
{
if (face == edge->Face(i))
{
if (ON_UNSET_UINT_INDEX == edge_face_index)
edge_face_index = i;
else if (i != edge_face_index)
return ON_SubDIsNotValid(bSilentError);
}
else if (i == edge_face_index)
{
return ON_SubDIsNotValid(bSilentError);
}
}
for (unsigned int i = 0; i < face_edge_count; i++)
{
if (edge == face->Edge(i))
{
if (ON_UNSET_UINT_INDEX == face_edge_index)
face_edge_index = i;
else if (i != face_edge_index)
return ON_SubDIsNotValid(bSilentError);
}
else if (i == face_edge_index)
{
return ON_SubDIsNotValid(bSilentError);
}
}
return true;
}
class ON_Internal_DamagedMarker
{
public:
ON_Internal_DamagedMarker() = default;
~ON_Internal_DamagedMarker()
{
if (nullptr != m_subd_component)
m_subd_component->m_status.SetDamagedState(true);
}
private:
ON_Internal_DamagedMarker(const ON_Internal_DamagedMarker&) = delete;
ON_Internal_DamagedMarker& operator=(const ON_Internal_DamagedMarker&) = delete;
public:
ON_Internal_DamagedMarker(const ON_SubDComponentBase* subd_component)
: m_subd_component(subd_component)
{}
void ClearComponent()
{
m_subd_component = nullptr;
}
private:
const ON_SubDComponentBase* m_subd_component = nullptr;
};
static bool IsValidSubDVertexTag(
const ON_SubDVertex* vertex,
bool bSilentError
)
{
if (nullptr == vertex)
return true; // this error detected elsewhere.
ON_Internal_DamagedMarker dm(vertex);
const unsigned short vertex_edge_count = vertex->m_edge_count;
unsigned short crease_edge_count = 0;
unsigned short smooth_edge_count = 0;
for (unsigned short vei = 0; vei < vertex->m_edge_count; vei++)
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(vertex->m_edges[vei].m_ptr);
if (nullptr == e)
continue;
if (e->IsSmooth())
++smooth_edge_count;
else if (e->IsCrease())
++crease_edge_count;
}
const bool bValidEdgeTags = (vertex_edge_count == crease_edge_count + smooth_edge_count);
switch (vertex->m_vertex_tag)
{
case ON_SubD::VertexTag::Unset:
return ON_SubDIsNotValid(bSilentError);
break;
case ON_SubD::VertexTag::Smooth:
if (false == bValidEdgeTags)
break; // invalid edge tags detected in IsValidSubDEdgeTag();
if (
0 != crease_edge_count
|| vertex_edge_count < 2
|| vertex_edge_count != smooth_edge_count
|| vertex_edge_count != vertex->m_face_count
)
{
return ON_SubDIsNotValid(bSilentError);
}
break;
case ON_SubD::VertexTag::Crease:
if (false == bValidEdgeTags)
break; // invalid edge tags detected in IsValidSubDEdgeTag();
if ( 2 != crease_edge_count )
{
return ON_SubDIsNotValid(bSilentError);
}
break;
case ON_SubD::VertexTag::Corner:
if (false == bValidEdgeTags)
break; // invalid edge tags detected in IsValidSubDEdgeTag();
if ( crease_edge_count < 2 )
{
return ON_SubDIsNotValid(bSilentError);
}
break;
case ON_SubD::VertexTag::Dart:
if (false == bValidEdgeTags)
break; // invalid edge tags detected in IsValidSubDEdgeTag();
if (
1 != crease_edge_count
|| vertex_edge_count < 2
|| vertex_edge_count != smooth_edge_count + crease_edge_count
|| vertex_edge_count != vertex->m_face_count
)
{
return ON_SubDIsNotValid(bSilentError);
}
break;
default:
return ON_SubDIsNotValid(bSilentError);
break;
}
dm.ClearComponent();
return true;
}
static bool IsValidSubDEdgeTag(
const ON_SubDEdge* edge,
bool bSilentError
)
{
if (nullptr == edge)
return true; // this error detected elsewhere.
//ON_SubD::VertexTag vtag[2] = { ON_SubD::VertexTag::Unset,ON_SubD::VertexTag::Unset };
unsigned int smooth_vertex_count = 0;
unsigned int crease_vertex_count = 0;
unsigned int corner_vertex_count = 0;
unsigned int dart_vertex_count = 0;
for ( unsigned int evi = 0; evi < 2; evi++)
{
if (nullptr == edge->m_vertex[evi])
return true; // topology errors detected elsewhere
switch (edge->m_vertex[evi]->m_vertex_tag)
{
case ON_SubD::VertexTag::Smooth:
++smooth_vertex_count;
break;
case ON_SubD::VertexTag::Crease:
++crease_vertex_count;
break;
case ON_SubD::VertexTag::Corner:
++corner_vertex_count;
break;
case ON_SubD::VertexTag::Dart:
++dart_vertex_count;
break;
}
};
if (2 != smooth_vertex_count + crease_vertex_count + corner_vertex_count + dart_vertex_count)
return true; // invalid vertex tags detected in IsValidSubDVertexTag();
ON_Internal_DamagedMarker dm(edge);
//const unsigned short edge_face_count = edge->m_face_count;
switch(edge->m_edge_tag)
{
case ON_SubD::EdgeTag::Unset:
return ON_SubDIsNotValid(bSilentError);
break;
case ON_SubD::EdgeTag::Smooth:
if ( 2 != edge->m_face_count)
return ON_SubDIsNotValid(bSilentError);
if ( smooth_vertex_count < 1)
return ON_SubDIsNotValid(bSilentError);
break;
case ON_SubD::EdgeTag::Crease:
if ( 0 != smooth_vertex_count )
return ON_SubDIsNotValid(bSilentError);
break;
case ON_SubD::EdgeTag::SmoothX:
if ( 2 != edge->m_face_count)
return ON_SubDIsNotValid(bSilentError);
if ( 0 != smooth_vertex_count )
return ON_SubDIsNotValid(bSilentError);
break;
default:
return ON_SubDIsNotValid(bSilentError);
break;
}
dm.ClearComponent();
return true;
}
static bool IsValidSubDVertex(
const ON_SubDVertex* vertex,
unsigned short level,
unsigned int* vertex_id_range,
unsigned short ordinary_valence_count,
bool bSilentError
)
{
if (nullptr == vertex)
return ON_SubDIsNotValid(bSilentError);
if (vertex->SubdivisionLevel() != level)
return ON_SubDIsNotValid(bSilentError);
if (nullptr != vertex_id_range)
{
if (vertex->m_id < vertex_id_range[0])
return ON_SubDIsNotValid(bSilentError);
if (vertex->m_id > vertex_id_range[1])
return ON_SubDIsNotValid(bSilentError);
}
ON_Internal_DamagedMarker dm(vertex);
if (vertex->m_edge_count < vertex->m_face_count)
{
if ( ON_SubD::VertexTag::Corner != vertex->m_vertex_tag || vertex->m_edge_count < 3 )
return ON_SubDIsNotValid(bSilentError);
}
if (vertex->m_edge_count > 0 && nullptr == vertex->m_edges)
return ON_SubDIsNotValid(bSilentError);
if (vertex->m_face_count > 0 && nullptr == vertex->m_faces)
return ON_SubDIsNotValid(bSilentError);
switch (vertex->m_vertex_tag)
{
case ON_SubD::VertexTag::Smooth: // interior vertex
if (vertex->m_edge_count != vertex->m_face_count)
return ON_SubDIsNotValid(bSilentError);
break;
case ON_SubD::VertexTag::Crease:
if ( vertex->m_edge_count < 2 )
return ON_SubDIsNotValid(bSilentError);
break;
case ON_SubD::VertexTag::Corner:
if ( vertex->m_edge_count < 1 )
return ON_SubDIsNotValid(bSilentError);
break;
case ON_SubD::VertexTag::Dart: // interior vertex
if (level > 0 && ordinary_valence_count != vertex->m_edge_count)
return ON_SubDIsNotValid(bSilentError);
if (vertex->m_edge_count != vertex->m_face_count)
return ON_SubDIsNotValid(bSilentError);
break;
default:
// invalid value for this enum
return ON_SubDIsNotValid(bSilentError);
break;
}
unsigned int count = vertex->m_edge_count;
for (unsigned int i = 0; i < count; i++)
{
const ON_SubDEdge* edge = vertex->Edge(i);
if (nullptr == edge)
return ON_SubDIsNotValid(bSilentError);
}
count = vertex->m_face_count;
for (unsigned int i = 0; i < count; i++)
{
const ON_SubDFace* face = vertex->Face(i);
if (nullptr == face)
return ON_SubDIsNotValid(bSilentError);
}
dm.ClearComponent();
return true;
}
static bool IsValidSubDEdge(
const ON_SubDEdge* edge,
unsigned short level,
unsigned int* edge_id_range,
bool bSilentError
)
{
if (nullptr == edge)
return ON_SubDIsNotValid(bSilentError);
if (edge->SubdivisionLevel() != level)
return ON_SubDIsNotValid(bSilentError);
if (nullptr != edge_id_range)
{
if (edge->m_id < edge_id_range[0])
return ON_SubDIsNotValid(bSilentError);
if (edge->m_id > edge_id_range[1])
return ON_SubDIsNotValid(bSilentError);
}
ON_Internal_DamagedMarker dm(edge);
const ON_SubDVertex* edge_vertex[2] = { 0 };
for (unsigned int i = 0; i < 2; i++)
{
const ON_SubDVertex* vertex = edge->Vertex(i);
if (nullptr == vertex)
return ON_SubDIsNotValid(bSilentError);
edge_vertex[i] = vertex;
}
if (edge_vertex[0] == edge_vertex[1])
return ON_SubDIsNotValid(bSilentError);
if (edge->IsSmooth())
{
// m_edge_tag is ON_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::SmoothX
if ( 2 != edge->m_face_count)
return ON_SubDIsNotValid(bSilentError);
}
else if (ON_SubD::EdgeTag::Crease != edge->m_edge_tag)
{
return ON_SubDIsNotValid(bSilentError);
}
if (edge->m_face_count > 2 && nullptr == edge->m_facex)
return ON_SubDIsNotValid(bSilentError);
dm.ClearComponent();
return true;
}
static bool IsValidSubDFace(
const ON_SubDFace* face,
unsigned short level,
unsigned int* face_id_range,
unsigned short ordinary_face_edge_count,
bool bSilentError
)
{
if (nullptr == face)
return ON_SubDIsNotValid(bSilentError);
if (face->SubdivisionLevel() != level)
return ON_SubDIsNotValid(bSilentError);
if (nullptr != face_id_range)
{
if (face->m_id < face_id_range[0])
return ON_SubDIsNotValid(bSilentError);
if (face->m_id > face_id_range[1])
return ON_SubDIsNotValid(bSilentError);
}
ON_Internal_DamagedMarker dm(face);
if (face->m_edge_count < 3)
return ON_SubDIsNotValid(bSilentError);
if (face->m_edge_count > 4 && nullptr == face->m_edgex)
return ON_SubDIsNotValid(bSilentError);
if (level > 0 && ordinary_face_edge_count != face->m_edge_count)
return ON_SubDIsNotValid(bSilentError);
dm.ClearComponent();
return true;
}
bool ON_SubDimple::IsValidLevel(
const ON_SubD& subd,
unsigned int level_index,
bool bSilentError,
ON_TextLog* text_log
) const
{
const unsigned int level_count = m_levels.UnsignedCount();
if (level_index >= level_count || level_index >= 0xFFFF)
return ON_SubDIsNotValid(bSilentError);
const ON_SubDLevel* level = m_levels[level_index];
if ( nullptr == level)
return ON_SubDIsNotValid(bSilentError);
level->ClearComponentDamagedState();
if ( level->m_level_index != level_index)
return ON_SubDIsNotValid(bSilentError);
if (level_index <= 0)
{
if (level->m_vertex_count < 3)
return ON_SubDIsNotValid(bSilentError);
if (level->m_edge_count < 3)
return ON_SubDIsNotValid(bSilentError);
if (level->m_face_count < 1)
return ON_SubDIsNotValid(bSilentError);
}
else
{
const ON_SubDLevel* previous_level = m_levels[level_index - 1];
if (nullptr == previous_level)
return ON_SubDIsNotValid(bSilentError);
if (level->m_vertex_count <= previous_level->m_vertex_count)
return ON_SubDIsNotValid(bSilentError);
if (level->m_edge_count <= previous_level->m_edge_count)
return ON_SubDIsNotValid(bSilentError);
if (level->m_face_count <= previous_level->m_face_count)
return ON_SubDIsNotValid(bSilentError);
}
if (nullptr == level->m_vertex[0])
return ON_SubDIsNotValid(bSilentError);
if (nullptr == level->m_edge[0])
return ON_SubDIsNotValid(bSilentError);
if (nullptr == level->m_face[0])
return ON_SubDIsNotValid(bSilentError);
if (nullptr == level->m_vertex[1])
return ON_SubDIsNotValid(bSilentError);
if (nullptr == level->m_edge[1])
return ON_SubDIsNotValid(bSilentError);
if (nullptr == level->m_face[1])
return ON_SubDIsNotValid(bSilentError);
const unsigned short expected_level = (unsigned short)level_index;
unsigned int i;
const ON_SubDVertex* vertex;
const ON_SubDEdge* edge;
const ON_SubDFace* face;
unsigned int v_id_range[2] = { ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX };
unsigned int e_id_range[2] = { ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX };
unsigned int f_id_range[2] = { ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX };
unsigned int point_vertex_count = 0;
unsigned int wire_edge_count = 0;
// simple vertex validation
if (level_index == subd.ActiveLevelIndex())
{
if (subd.FirstVertex() != level->m_vertex[0])
return ON_SubDIsNotValid(bSilentError);
ON_SubDVertexIterator vit = subd.VertexIterator();
if (vit.FirstVertex() != level->m_vertex[0])
return ON_SubDIsNotValid(bSilentError);
ON_SubDVertexArray va = subd.VertexArray();
if (va.VertexCount() != level->m_vertex_count)
return ON_SubDIsNotValid(bSilentError);
if (va[0] != level->m_vertex[0])
return ON_SubDIsNotValid(bSilentError);
if (va[level->m_vertex_count-1] != level->m_vertex[1])
return ON_SubDIsNotValid(bSilentError);
}
const ON_SubDVertex* last_vertex = nullptr;
for (i = 0, vertex = level->m_vertex[0]; i < level->m_vertex_count && nullptr != vertex; i++, vertex = vertex->m_next_vertex)
{
if (false == IsValidSubDVertex(vertex, expected_level, nullptr, level->m_ordinary_vertex_valence, bSilentError))
return false;
if (0 == i)
{
v_id_range[0] = v_id_range[1] = vertex->m_id;
}
else if (vertex->m_id < v_id_range[0])
v_id_range[0] = vertex->m_id;
else if (vertex->m_id > v_id_range[1])
v_id_range[1] = vertex->m_id;
if (0 == vertex->m_edge_count)
{
point_vertex_count++;
}
last_vertex = vertex;
}
if (level->m_vertex[1] != last_vertex)
return ON_SubDIsNotValid(bSilentError);
if (i != level->m_vertex_count || nullptr != vertex)
return ON_SubDIsNotValid(bSilentError);
if (1 + v_id_range[1] - v_id_range[0] < level->m_vertex_count)
return ON_SubDIsNotValid(bSilentError);
if ( v_id_range[1] > m_max_vertex_id )
return ON_SubDIsNotValid(bSilentError);
// currently, point vertices are not permitted
if (point_vertex_count > 0)
return ON_SubDIsNotValid(bSilentError);
// simple edge validation
if (level_index == subd.ActiveLevelIndex())
{
if (subd.FirstEdge() != level->m_edge[0])
return ON_SubDIsNotValid(bSilentError);
ON_SubDEdgeIterator eit = subd.EdgeIterator();
if (eit.FirstEdge() != level->m_edge[0])
return ON_SubDIsNotValid(bSilentError);
ON_SubDEdgeArray ea = subd.EdgeArray();
if (ea.EdgeCount() != level->m_edge_count)
return ON_SubDIsNotValid(bSilentError);
if (ea[0] != level->m_edge[0])
return ON_SubDIsNotValid(bSilentError);
if (ea[level->m_edge_count-1] != level->m_edge[1])
return ON_SubDIsNotValid(bSilentError);
}
const ON_SubDEdge* last_edge = nullptr;
for (i = 0, edge = level->m_edge[0]; i < level->m_edge_count && nullptr != edge; i++, edge = edge->m_next_edge)
{
if (false == IsValidSubDEdge(edge, expected_level, nullptr, bSilentError))
return false;
if (0 == edge->m_face_count)
{
wire_edge_count++;
}
if (0 == i)
{
e_id_range[0] = e_id_range[1] = edge->m_id;
}
else if (edge->m_id < e_id_range[0])
e_id_range[0] = edge->m_id;
else if (edge->m_id > e_id_range[1])
e_id_range[1] = edge->m_id;
last_edge = edge;
}
if (i != level->m_edge_count || nullptr != edge)
return ON_SubDIsNotValid(bSilentError);
if (1 + e_id_range[1] - e_id_range[0] < level->m_edge_count)
return ON_SubDIsNotValid(bSilentError);
if (level->m_edge[1] != last_edge)
return ON_SubDIsNotValid(bSilentError);
if ( e_id_range[1] > m_max_edge_id )
return ON_SubDIsNotValid(bSilentError);
// currently, wire edges are not permitted
if (wire_edge_count > 0)
return ON_SubDIsNotValid(bSilentError);
// simple face validation
if (level_index == subd.ActiveLevelIndex())
{
if (subd.FirstFace() != level->m_face[0])
return ON_SubDIsNotValid(bSilentError);
ON_SubDFaceIterator fit = subd.FaceIterator();
if (fit.FirstFace() != level->m_face[0])
return ON_SubDIsNotValid(bSilentError);
ON_SubDFaceArray fa = subd.FaceArray();
if (fa.FaceCount() != level->m_face_count)
return ON_SubDIsNotValid(bSilentError);
if (fa[0] != level->m_face[0])
return ON_SubDIsNotValid(bSilentError);
if (fa[0] != level->m_face[0])
return ON_SubDIsNotValid(bSilentError);
}
const ON_SubDFace* last_face = nullptr;
for (i = 0, face = level->m_face[0]; i < level->m_face_count && nullptr != face; i++, face = face->m_next_face)
{
if (false == IsValidSubDFace(face, expected_level, nullptr, level->m_ordinary_face_edge_count, bSilentError))
return false;
if (0 == i)
{
f_id_range[0] = f_id_range[1] = face->m_id;
}
else if (face->m_id < f_id_range[0])
f_id_range[0] = face->m_id;
else if (face->m_id > f_id_range[1])
f_id_range[1] = face->m_id;
last_face = face;
}
if (i != level->m_face_count || nullptr != face)
return ON_SubDIsNotValid(bSilentError);
if (1 + f_id_range[1] - f_id_range[0] < level->m_face_count)
return ON_SubDIsNotValid(bSilentError);
if (level->m_face[1] != last_face)
return ON_SubDIsNotValid(bSilentError);
if ( f_id_range[1] > m_max_face_id )
return ON_SubDIsNotValid(bSilentError);
// vertex topology validation
for (vertex = level->m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex)
{
for (i = 0; i < vertex->m_edge_count; i++)
{
edge = vertex->Edge(i);
if (false == IsValidSubDEdge(edge, expected_level, e_id_range, bSilentError))
return false;
if (false == IsValidVertexEdgeLink(vertex, edge, vertex->EdgeDirection(i), bSilentError))
return false;
}
for (i = 0; i < vertex->m_face_count; i++)
{
face = vertex->Face(i);
if (false == IsValidSubDFace(face, expected_level, f_id_range, level->m_ordinary_face_edge_count, bSilentError))
return false;
if (false == IsValidVertexFaceLink(vertex, face, i, ON_UNSET_UINT_INDEX, bSilentError))
return false;
}
}
// edge topology validation
for (edge = level->m_edge[0]; nullptr != edge; edge = edge->m_next_edge)
{
for (i = 0; i < 2; i++)
{
vertex = edge->m_vertex[i];
if (false == IsValidSubDVertex(vertex, expected_level, v_id_range, level->m_ordinary_vertex_valence, bSilentError))
return false;
if (false == IsValidVertexEdgeLink(vertex, edge, i, bSilentError))
return false;
}
for (i = 0; i < edge->m_face_count; i++)
{
face = edge->Face(i);
if (false == IsValidSubDFace(face, expected_level, f_id_range, level->m_ordinary_face_edge_count, bSilentError))
return false;
if (false == IsValidEdgeFaceLink(edge, face, i, ON_UNSET_UINT_INDEX, bSilentError))
return false;
}
}
// face topology validation
for (face = level->m_face[0]; nullptr != face; face = face->m_next_face)
{
for (i = 0; i < face->m_edge_count; i++)
{
edge = face->Edge(i);
if (false == IsValidSubDEdge(edge, expected_level, e_id_range, bSilentError))
return false;
if (false == IsValidEdgeFaceLink(edge, face, ON_UNSET_UINT_INDEX, i, bSilentError))
return false;
}
for (i = 0; i < face->m_edge_count; i++)
{
vertex = face->Vertex(i);
if (false == IsValidSubDVertex(vertex, expected_level, v_id_range, level->m_ordinary_vertex_valence, bSilentError))
return false;
if (false == IsValidVertexFaceLink(vertex, face, ON_UNSET_UINT_INDEX, i, bSilentError))
return false;
}
}
// edge tag validation
for (edge = level->m_edge[0]; nullptr != edge; edge = edge->m_next_edge)
{
if (false == IsValidSubDEdgeTag(edge, bSilentError))
return false;
}
// vertex tag validation
for (vertex = level->m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex)
{
if (false == IsValidSubDVertexTag(vertex, bSilentError))
return false;
}
// edge length validation
for (edge = level->m_edge[0]; nullptr != edge; edge = edge->m_next_edge)
{
const ON_3dPoint P[2] = { edge->m_vertex[0]->ControlNetPoint(), edge->m_vertex[1]->ControlNetPoint() };
if (false == (P[0] != P[1]))
{
edge->m_status.SetDamagedState(true);
return ON_SubDIsNotValid(bSilentError);
}
}
return true;
}
bool ON_SubDimple::IsValid(
const ON_SubD& subd,
bool bSilentError,
ON_TextLog* text_log
) const
{
const unsigned int level_count = m_levels.UnsignedCount();
if (level_count < 1)
{
return ON_SubDIsNotValid(bSilentError);
}
for (unsigned int level_index = 0; level_index < level_count; level_index++)
{
if (false == IsValidLevel(subd, level_index, bSilentError, text_log))
return false;
}
if (false == m_heap.IsValid())
{
// id values are not increasing in the heap blocks.
if (nullptr != text_log)
text_log->Print("Component ids are not set correctly. m_heap.ResetId() will fix this but may break externally stored component references.\n");
return ON_SubDIsNotValid(bSilentError);
}
if (MaximumVertexId() < m_heap.MaximumVertexId())
{
if (nullptr != text_log)
text_log->Print("MaximumVertexId() = %u < m_heap.MaximumVertexId() = %u\n", MaximumVertexId(), m_heap.MaximumVertexId());
return ON_SubDIsNotValid(bSilentError);
}
if (MaximumEdgeId() < m_heap.MaximumEdgeId())
{
if (nullptr != text_log)
text_log->Print("MaximumEdgeId() = %u < m_heap.MaximumEdgeId() = %u\n", MaximumEdgeId(), m_heap.MaximumEdgeId());
return ON_SubDIsNotValid(bSilentError);
}
if (MaximumFaceId() <m_heap.MaximumFaceId())
{
if (nullptr != text_log)
text_log->Print("MaximumFaceId() = %u < m_heap.MaximumFaceId() = %u\n", MaximumFaceId(), m_heap.MaximumFaceId());
return ON_SubDIsNotValid(bSilentError);
}
return true;
}
//virtual
bool ON_SubD::IsValid(ON_TextLog* text_logx) const
{
// If low bit of text_log pointer is 1, then ON_Error is not called when the
// knot vector is invalid.
const ON__INT_PTR lowbit = 1;
const ON__INT_PTR hightbits = ~lowbit;
const bool bSilentError = (0 != (lowbit & ((ON__INT_PTR)text_logx)));
ON_TextLog* text_log = (ON_TextLog*)(((ON__INT_PTR)text_logx) & hightbits);
const ON_SubDimple* subdimple = SubDimple();
if (nullptr == subdimple)
return ON_SubDIsNotValid(bSilentError);
return subdimple->IsValid(*this, bSilentError, text_log);
}
//virtual
void ON_SubD::Dump(ON_TextLog& text_log) const
{
unsigned int component_sample_count = 16; // dump the first 16 vertices, edges, faces
ON_2udex id_range;
id_range.i = component_sample_count;
id_range.j = 0;
DumpTopology(id_range,id_range,id_range,text_log);
}
unsigned int ON_SubD::DumpTopology(ON_TextLog & text_log) const
{
return DumpTopology(ON_2udex::Zero,ON_2udex::Zero,ON_2udex::Zero,text_log);
}
unsigned int ON_SubD::DumpTopology(
ON_2udex vertex_id_range,
ON_2udex edge_id_range,
ON_2udex face_id_range,
ON_TextLog& text_log
) const
{
const class ON_SubDimple* subdimple = SubDimple();
if (nullptr == subdimple)
{
text_log.Print(L"SubD: Empty\n");
return 0;
}
const unsigned int level_count = LevelCount();
const unsigned int active_level_index = ActiveLevel().m_level_index;
const ON__UINT64 runtime_sn = (text_log.IsTextHash()) ? 0 : RuntimeSerialNumber(); // TextHash ignores settings that don't depend on 3dm file content.
const ON_wString subd_texture_domain = ON_SubD::TextureDomainTypeToString(this->TextureDomainType());
if (level_count > 1)
text_log.Print(L"SubD[%" PRIu64 "]: %u levels. Level %u is active. texture domain type = %ls.\n",
runtime_sn,
level_count,
active_level_index,
static_cast<const wchar_t*>(subd_texture_domain)
);
else
text_log.Print(L"SubD[%" PRIu64 "]: texture domain type = %ls.\n", runtime_sn, static_cast<const wchar_t*>(subd_texture_domain));
ON_SubDLevelIterator lit(subdimple->LevelIterator());
const ON_2udex empty_id_range(ON_UNSET_UINT_INDEX, 0);
unsigned int error_count = 0;
for (const ON_SubDLevel* level = lit.First(); nullptr != level; level = lit.Next())
{
ON_TextLogIndent indent1(text_log);
if (nullptr == level)
continue;
ON_2udex level_vertex_id_range
= (0 != vertex_id_range.j || active_level_index == level->m_level_index)
? vertex_id_range
: empty_id_range;
ON_2udex level_edge_id_range
= (0 != edge_id_range.j || active_level_index == level->m_level_index)
? edge_id_range
: empty_id_range;
ON_2udex level_face_id_range
= (0 != face_id_range.j || active_level_index == level->m_level_index)
? face_id_range
: empty_id_range;
error_count += level->DumpTopology(
subdimple->MaximumVertexId(),
subdimple->MaximumEdgeId(),
subdimple->MaximumFaceId(),
level_vertex_id_range,
level_edge_id_range,
level_face_id_range,
text_log);
}
return error_count;
}
unsigned int ON_SubDLevel::DumpTopology(
const unsigned int validate_max_vertex_id,
const unsigned int validate_max_edge_id,
const unsigned int validate_max_face_id,
ON_2udex vertex_id_range,
ON_2udex edge_id_range,
ON_2udex face_id_range,
ON_TextLog& text_log
) const
{
// NOTE WELL:
// This is a debugging tool.
// The code in this function needs to work when the topology information is corrupt.
const unsigned short level_index = (unsigned short)m_level_index;
const unsigned int vertex_max_dump_count
= (vertex_id_range.i > 0 && vertex_id_range.i != ON_UNSET_UINT_INDEX && 0 == vertex_id_range.j)
? vertex_id_range.i
: 0;
const unsigned int edge_max_dump_count
= (edge_id_range.i > 0 && edge_id_range.i != ON_UNSET_UINT_INDEX && 0 == edge_id_range.j)
? edge_id_range.i
: 0;
const unsigned int face_max_dump_count
= (face_id_range.i > 0 && face_id_range.i != ON_UNSET_UINT_INDEX && 0 == face_id_range.j)
? face_id_range.i
: 0;
const bool bVertexIdTest = (vertex_max_dump_count > 0) || (vertex_id_range.i < vertex_id_range.j) || (ON_UNSET_UINT_INDEX == (vertex_id_range.i));
const bool bEdgeIdTest = (edge_max_dump_count > 0) || (edge_id_range.i < edge_id_range.j) || (ON_UNSET_UINT_INDEX == (edge_id_range.i));
const bool bFaceIdTest = (face_max_dump_count > 0) || (face_id_range.i < face_id_range.j) || (ON_UNSET_UINT_INDEX == (face_id_range.i));
const char error_code_point = '!';
char prefix[16] = {};
unsigned int vertex_error_count = 0;
unsigned int edge_error_count = 0;
unsigned int face_error_count = 0;
text_log.Print(L"SubD level %u topology: %u vertices, %u edges, ", m_level_index, m_vertex_count, m_edge_count);
unsigned int ngon_count[65] = {};
unsigned int maxN = (unsigned int)(sizeof(ngon_count) / sizeof(ngon_count[0])) - 1;
unsigned int face_count = 0;
unsigned int uniformN = 0;
for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face)
{
if (face_count >= m_face_count && f->SubdivisionLevel() != level_index)
break;
face_count++;
unsigned int N = f->EdgeCount();
if (1 == face_count)
uniformN = N;
else if (N != uniformN)
uniformN = 0;
unsigned int j = (N < maxN) ? N : maxN;
if (N < maxN)
ngon_count[j]++;
}
if (face_count != m_face_count)
face_error_count++;
if (uniformN > 0 && face_count == m_face_count)
{
if (3 == uniformN)
text_log.Print(L"%u triangles\n", m_face_count);
else if (4 == uniformN)
text_log.Print(L"%u quads\n", m_face_count);
else
text_log.Print(L"%u %u-gons\n", m_face_count, uniformN);
}
else
{
text_log.Print(L"%u faces\n", m_face_count );
text_log.PushIndent();
for (unsigned int N = 0; N <= maxN; N++)
{
if (0 == ngon_count[N])
continue;
if (3 == N)
text_log.Print(L"%u triangles\n", ngon_count[N]);
else if (4 == N)
text_log.Print(L"%u quads\n", ngon_count[N]);
else if (N < maxN)
text_log.Print(L"%u %u-gons\n", ngon_count[N], N);
else
text_log.Print(L"%u N-gons\n", ngon_count[N]);
}
text_log.PopIndent();
}
if (IsEmpty())
return 0;
///////////////////////////////////////////////////////////////////
//
// Vertex Topology
// vN (x, y, z)
// vEdges[vertex_edge_count] = { +eA, -eB, ... }
// vFaces[vertex_edge_count] = { fP, fQ, fR, ... }
//
unsigned int vertex_count = 0;
unsigned int vertex_dump_count = 0;
ON_2udex skipped_vertex_id = ON_2udex::Zero;
unsigned int max_vertex_id = 0;
for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v; v = v->m_next_vertex)
{
if (vertex_count >= m_vertex_count && v->SubdivisionLevel() != level_index)
break;
if (v->m_id > max_vertex_id)
max_vertex_id = v->m_id;
vertex_count++;
if (bVertexIdTest)
{
bool bSkip = true;
for (;;)
{
if (ON_UNSET_UINT_INDEX == vertex_id_range.i)
break;
if (vertex_max_dump_count > 0)
{
if (vertex_count > vertex_max_dump_count)
break;
}
else
{
if (v->m_id < vertex_id_range.i || v->m_id >= vertex_id_range.j)
break;
}
bSkip = false;
break;
}
if (bSkip)
{
if (0 == skipped_vertex_id.j)
{
skipped_vertex_id.i = v->m_id;
skipped_vertex_id.j = v->m_id;
}
else if (v->m_id < skipped_vertex_id.i)
skipped_vertex_id.i = v->m_id;
else if (v->m_id > skipped_vertex_id.j)
skipped_vertex_id.j = v->m_id;
continue;
}
}
if (0 == vertex_dump_count)
text_log.Print(L"Vertices:\n");
vertex_dump_count++;
ON_TextLogIndent vindent(text_log);
const ON_3dPoint P0(v->ControlNetPoint());
ON_wString vtag;
switch (v->m_vertex_tag)
{
case ON_SubD::VertexTag::Unset:
vtag = L"Unset";
break;
case ON_SubD::VertexTag::Smooth:
vtag = L"Smooth";
break;
case ON_SubD::VertexTag::Crease:
vtag = L"Crease";
break;
case ON_SubD::VertexTag::Corner:
vtag = L"Corner";
break;
case ON_SubD::VertexTag::Dart:
vtag = L"Dart";
break;
default:
vtag = L"INVALID";
break;
}
text_log.Print(
"v%u: %ls (%g, %g, %g)\n",
v->m_id,
static_cast<const wchar_t*>(vtag),
P0.x, P0.y, P0.z
);
text_log.PushIndent();
ON_3dVector D(ON_3dVector::NanVector);
if (v->GetSubdivisionDisplacement(&D.x) && D.IsValid())
text_log.Print( "v.SubdivisionDisplacement: (%g, %g, %g)\n", D.x, D.y, D.z );
ON_3dPoint P1(ON_3dPoint::NanPoint);
if (v->GetSavedSubdivisionPoint(&P1.x) && P1.IsValid())
text_log.Print( "v.SubdivisionPoint: (%g, %g, %g)\n", P1.x, P1.y, P1.z );
ON_3dPoint S(ON_3dPoint::NanPoint);
if (v->GetSavedSurfacePoint(&S.x) && S.IsValid())
text_log.Print( "v.SurfacePoint: (%g, %g, %g)\n", S.x, S.y, S.z );
const unsigned int vertex_edge_count = v->m_edge_count;
text_log.Print("v.Edges[%u] = {", vertex_edge_count);
prefix[0] = ON_String::Space;
prefix[1] = error_code_point;
prefix[2] = 'e';
prefix[3] = 0;
for (unsigned int vei = 0; vei < vertex_edge_count; vei++)
{
prefix[1] = error_code_point;
if (1 == vei)
{
prefix[0] = ',';
}
const ON_SubDEdge* e = v->Edge(vei);
unsigned int eid = 0;
if (nullptr != e)
{
if (v == e->m_vertex[0] && v != e->m_vertex[1])
prefix[1] = '+';
else if (v != e->m_vertex[0] && v == e->m_vertex[1])
prefix[1] = '-';
eid = e->m_id;
}
text_log.Print("%s%u", prefix, eid);
if (error_code_point == prefix[1])
vertex_error_count++;
}
text_log.Print(" }\n");
const unsigned int vertex_face_count = v->m_face_count;
text_log.Print("v.Faces[%u] = {", vertex_face_count);
prefix[0] = ON_String::Space;
prefix[1] = ON_String::Space;
prefix[2] = 'f';
prefix[3] = 0;
for (unsigned int vfi = 0; vfi < vertex_face_count; vfi++)
{
prefix[1] = error_code_point;
if (1 == vfi)
{
prefix[0] = ',';
}
const ON_SubDFace* f = v->Face(vfi);
unsigned int fid = 0;
if (nullptr != f)
{
if (f->VertexIndex(v) < ON_UNSET_UINT_INDEX)
prefix[1] = ON_String::Space;
fid = f->m_id;
}
text_log.Print("%s%u", prefix, fid);
if (error_code_point == prefix[1])
vertex_error_count++;
}
text_log.Print(" }\n");
text_log.PopIndent();
}
text_log.PushIndent();
if (vertex_dump_count > 0 && vertex_dump_count < vertex_count)
{
text_log.Print(
L"... %u additional vertices (v%u to v%u).\n",
vertex_count-vertex_dump_count,
skipped_vertex_id.i,
skipped_vertex_id.j
);
}
text_log.Print("Maximum vertex id = %u. ",max_vertex_id);
if (validate_max_vertex_id >= max_vertex_id)
text_log.Print("Next id = %u.\n", validate_max_vertex_id + 1);
else
text_log.Print("ERROR Next id = %u.\n", validate_max_vertex_id + 1);
text_log.PopIndent();
///////////////////////////////////////////////////////////////////
//
// Edge Topology
// eN (+vA, -vB)
// eFaces[edge_face_count] = { fP, fQ, fR, ... }
//
unsigned int edge_count = 0;
unsigned int edge_dump_count = 0;
ON_2udex skipped_edge_id = ON_2udex::Zero;
unsigned int max_edge_id = 0;
for (const ON_SubDEdge* e = m_edge[0]; nullptr != e; e = e->m_next_edge)
{
if (edge_count >= m_edge_count && e->SubdivisionLevel() != level_index)
break;
if (e->m_id > max_edge_id)
max_edge_id = e->m_id;
edge_count++;
if (bEdgeIdTest)
{
bool bSkip = true;
for (;;)
{
if (ON_UNSET_UINT_INDEX == edge_id_range.i)
break;
if (edge_max_dump_count > 0)
{
if (edge_count > edge_max_dump_count)
break;
}
else
{
if (e->m_id < edge_id_range.i || e->m_id >= edge_id_range.j)
break;
}
bSkip = false;
break;
}
if (bSkip)
{
if (0 == skipped_edge_id.j)
{
skipped_edge_id.i = e->m_id;
skipped_edge_id.j = e->m_id;
}
else if (e->m_id < skipped_edge_id.i)
skipped_edge_id.i = e->m_id;
else if (e->m_id > skipped_edge_id.j)
skipped_edge_id.j = e->m_id;
continue;
}
}
if (0 == edge_dump_count)
text_log.Print(L"Edges:\n");
edge_dump_count++;
ON_TextLogIndent eindent(text_log);
ON_wString etag;
switch (e->m_edge_tag)
{
case ON_SubD::EdgeTag::Unset:
etag = L"Unset";
break;
case ON_SubD::EdgeTag::Smooth:
etag = L"Smooth";
break;
case ON_SubD::EdgeTag::Crease:
etag = L"Crease";
break;
case ON_SubD::EdgeTag::SmoothX:
etag = L"SmmothX";
break;
default:
etag = L"INVALID";
break;
}
text_log.Print(
"e%u: %ls (",
e->m_id,
static_cast<const wchar_t*>(etag)
);
prefix[0] = ON_String::Space;
prefix[1] = error_code_point;
prefix[2] = 'v';
prefix[3] = 0;
for (unsigned int evi = 0; evi < 2; evi++)
{
if (1 == evi)
text_log.Print(" to");
prefix[1] = error_code_point;
const ON_SubDVertex* v = e->m_vertex[evi];
unsigned int vid = 0;
if (nullptr != v)
{
vid = v->m_id;
if (v->EdgeArrayIndex(e) < ON_UNSET_INT_INDEX)
prefix[1] = ON_String::Space;
}
if (error_code_point == prefix[1])
edge_error_count++;
text_log.Print("%s%u", prefix, vid);
}
text_log.Print(")\n");
text_log.PushIndent();
ON_3dVector D(ON_3dVector::NanVector);
if (e->GetSubdivisionDisplacement(&D.x) && D.IsValid())
text_log.Print( "e.SubdivisionDisplacement: (%g, %g, %g)\n", D.x, D.y, D.z );
ON_3dPoint P1(ON_3dPoint::NanPoint);
if (e->GetSavedSubdivisionPoint(&P1.x) && P1.IsValid())
text_log.Print( "e.SubdivisionPoint: (%g, %g, %g)\n", P1.x, P1.y, P1.z );
const unsigned int edge_face_count = e->m_face_count;
text_log.Print("e.Faces[%u] = {", edge_face_count);
prefix[0] = ON_String::Space;
prefix[1] = error_code_point;
prefix[2] = 'f';
prefix[3] = 0;
for (unsigned int efi = 0; efi < edge_face_count; efi++)
{
prefix[1] = error_code_point;
if (1 == efi)
{
prefix[0] = ',';
}
ON_SubDFacePtr fptr = e->FacePtr(efi);
const ON_SubDFace* f = fptr.Face();
const ON__UINT_PTR edge_fdir = fptr.FaceDirection();
unsigned int fid = 0;
if (nullptr != f)
{
fid = f->m_id;
ON_SubDEdgePtr eptr = f->EdgePtrFromEdge(e);
if (eptr.Edge() == e && eptr.EdgeDirection() == edge_fdir)
{
prefix[1] = (0 == edge_fdir) ? '+' : '-';
}
}
if (error_code_point == prefix[1])
edge_error_count++;
text_log.Print("%s%u", prefix, fid);
}
text_log.Print(" }\n");
text_log.PopIndent();
}
text_log.PushIndent();
if (edge_dump_count > 0 && edge_dump_count < edge_count)
{
text_log.Print(L"... %u additional edges (e%u to e%u).\n",
edge_count - edge_dump_count,
skipped_edge_id.i,
skipped_edge_id.j
);
}
text_log.Print("Maximum edge id = %u. ",max_edge_id);
if (validate_max_edge_id >= max_edge_id)
text_log.Print("Next id = %u.\n", validate_max_edge_id + 1);
else
text_log.Print("ERROR Next id = %u.\n", validate_max_edge_id + 1);
text_log.PopIndent();
///////////////////////////////////////////////////////////////////
//
// Face Topology
// fN
// fEdges[face_edge_count] = { +eA, -eB, +eC, ...}
// fVertices[face_edge_count] = { vP, vQ, vR, ... }
//
face_count = 0;
unsigned int face_dump_count = 0;
ON_2udex skipped_face_id = ON_2udex::Zero;
unsigned int max_face_id = 0;
for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face)
{
if (face_count >= m_face_count && f->SubdivisionLevel() != level_index)
break;
if (f->m_id > max_face_id)
max_face_id = f->m_id;
face_count++;
if (bFaceIdTest)
{
bool bSkip = true;
for (;;)
{
if (ON_UNSET_UINT_INDEX == face_id_range.i)
break;
if (face_max_dump_count > 0)
{
if (face_count > face_max_dump_count)
break;
}
else
{
if (f->m_id < face_id_range.i || f->m_id >= face_id_range.j)
break;
}
bSkip = false;
break;
}
if (bSkip)
{
if (0 == skipped_face_id.j)
{
skipped_face_id.i = f->m_id;
skipped_face_id.j = f->m_id;
}
else if (f->m_id < skipped_face_id.i)
skipped_face_id.i = f->m_id;
else if (f->m_id > skipped_face_id.j)
skipped_face_id.j = f->m_id;
continue;
}
}
if (0 == face_dump_count)
text_log.Print(L"Faces:\n");
face_dump_count++;
ON_TextLogIndent eindent(text_log);
text_log.Print(
"f%u:\n",
f->m_id
);
text_log.PushIndent();
ON_3dVector D(ON_3dVector::NanVector);
if (f->GetSubdivisionDisplacement(&D.x) && D.IsValid())
text_log.Print( "f.SubdivisionDisplacement: (%g, %g, %g)\n", D.x, D.y, D.z );
ON_3dPoint P1(ON_3dPoint::NanPoint);
if (f->GetSavedSubdivisionPoint(&P1.x) && P1.IsValid())
text_log.Print( "f.SubdivisionPoint: (%g, %g, %g)\n", P1.x, P1.y, P1.z );
const unsigned int face_edge_count = f->m_edge_count;
text_log.Print("f.Edges[%u] = {", face_edge_count);
prefix[0] = ON_String::Space;
prefix[1] = error_code_point;
prefix[2] = 'e';
prefix[3] = 0;
for (unsigned int fei = 0; fei < face_edge_count; fei++)
{
prefix[1] = error_code_point;
if (1 == fei)
{
prefix[0] = ',';
}
const ON_SubDEdgePtr eptr = f->EdgePtr(fei);
const ON_SubDEdge* e = eptr.Edge();
const ON__UINT_PTR face_edir = eptr.EdgeDirection();
unsigned int eid = 0;
if (nullptr != e)
{
eid = e->m_id;
ON_SubDFacePtr fptr = e->FacePtrFromFace(f);
if (fptr.Face() == f && fptr.FaceDirection() == face_edir)
{
prefix[1] = (0 == face_edir) ? '+' : '-';
}
}
if (error_code_point == prefix[1])
face_error_count++;
text_log.Print("%s%u", prefix, eid);
}
text_log.Print(" }\n");
const ON_SubDEdgePtr last_eptr = f->EdgePtr(face_edge_count - 1);
const ON_SubDEdge* last_edge = last_eptr.Edge();
const ON_SubDVertex* v1
= (nullptr != last_edge)
? last_edge->m_vertex[0 == last_eptr.EdgeDirection() ? 1 : 0]
: nullptr;
text_log.Print("f.Vertices[%u] = {", face_edge_count);
prefix[0] = ON_String::Space;
prefix[1] = error_code_point;
prefix[2] = 'v';
prefix[3] = 0;
for (unsigned int fei = 0; fei < face_edge_count; fei++)
{
prefix[1] = error_code_point;
if (1 == fei)
{
prefix[0] = ',';
}
const ON_SubDEdgePtr eptr = f->EdgePtr(fei);
const ON_SubDEdge* e = eptr.Edge();
const ON__UINT_PTR face_edir = eptr.EdgeDirection();
unsigned int vid = 0;
if (nullptr == e)
{
v1 = nullptr;
}
else
{
const ON_SubDVertex* v0 = e->m_vertex[0 == face_edir ? 0 : 1];
if (nullptr != v0)
{
vid = v0->m_id;
if (v1 == v0)
prefix[1] = ON_String::Space;
}
v1 = e->m_vertex[0 == face_edir ? 1 : 0];
}
if (error_code_point == prefix[1])
face_error_count++;
text_log.Print("%s%u", prefix, vid);
}
text_log.Print(" }\n");
if (f->TextureDomainIsSet())
{
const ON_wString s = ON_SubD::TextureDomainTypeToString(f->TextureDomainType());
const bool bGridOrder = true;
const bool bNormalized = false;
ON_2dPoint corners[4] = {
f->TextureDomainCorner(bGridOrder,bNormalized,0),
f->TextureDomainCorner(bGridOrder,bNormalized,1),
f->TextureDomainCorner(bGridOrder,bNormalized,2),
f->TextureDomainCorner(bGridOrder,bNormalized,3)
};
text_log.Print("%ls texture domain. Grid corners: (%g,%g), (%g,%g), (%g,%g), (%g,%g)\n",
static_cast<const wchar_t*>(s),
corners[0].x, corners[0].y,
corners[1].x, corners[1].y,
corners[2].x, corners[2].y,
corners[3].x, corners[3].y
);
////const ON_SubDMeshFragment *fragments = f->MeshFragments();
////if (nullptr != fragments)
////{
////ON_TextLogIndent indent1(text_log);
//// unsigned short fragment_count = 0;
//// const unsigned short expected_fragment_count = (unsigned short)((4 == face_edge_count) ? 1 : face_edge_count);
//// ON_3dPoint fragmentT[4];
//// bool bDamagedOrIncompleteFragments = false;
//// for (unsigned short fragdex = 0; fragdex <= expected_fragment_count && nullptr != fragments; fragdex++, fragments = fragments->m_next_fragment)
//// {
//// ++fragment_count;
//// if (expected_fragment_count != fragments->m_face_fragment_count)
//// {
//// bDamagedOrIncompleteFragments = true;
//// break;
//// }
//// if (fragdex != fragments->m_face_fragment_index)
//// {
//// bDamagedOrIncompleteFragments = true;
//// break;
//// }
//// }
//// if (expected_fragment_count == fragment_count)
//// {
//// fragments = f->MeshFragments();
//// if (1 == fragment_count)
//// {
//// if (fragments->GetTextureCoordinteCorners(bGridOrder, fragmentT))
//// {
//// text_log.Print("Quad face fragment texture corners = (%g,%g,%g), (%g,%g,%g), (%g,%g,%g), (%g,%g,%g)\n",
//// fragmentT[0].x, fragmentT[0].y, fragmentT[0].z,
//// fragmentT[1].x, fragmentT[1].y, fragmentT[1].z,
//// fragmentT[2].x, fragmentT[2].y, fragmentT[2].z,
//// fragmentT[3].x, fragmentT[3].y, fragmentT[3].z
//// );
//// }
//// else
//// {
//// text_log.Print("Quad face fragment texture corners not available.\n");
//// }
//// }
//// else
//// {
//// text_log.Print("%u face fragments.\n", (unsigned)expected_fragment_count);
//// ON_TextLogIndent indent2(text_log);
//// for (unsigned short fragdex = 0; fragdex <= expected_fragment_count && nullptr != fragments; fragdex++, fragments = fragments->m_next_fragment)
//// {
//// if (fragments->GetTextureCoordinteCorners(bGridOrder, fragmentT))
//// {
//// text_log.Print("fragment[%u] texture corners = (%g,%g,%g), (%g,%g,%g), (%g,%g,%g), (%g,%g,%g)\n",
//// (unsigned)fragdex,
//// fragmentT[0].x, fragmentT[0].y, fragmentT[0].z,
//// fragmentT[1].x, fragmentT[1].y, fragmentT[1].z,
//// fragmentT[2].x, fragmentT[2].y, fragmentT[2].z,
//// fragmentT[3].x, fragmentT[3].y, fragmentT[3].z
//// );
//// }
//// else
//// {
//// text_log.Print("fragment[%u] texture corners not available.\n", (unsigned)fragdex);
//// }
//// }
//// }
//// }
//// else if (nullptr == fragments)
//// {
//// bDamagedOrIncompleteFragments = true;
//// }
//// if (bDamagedOrIncompleteFragments )
//// text_log.Print("Damaged or incomplete mesh fragments.\n");
////}
}
text_log.PopIndent();
}
text_log.PushIndent();
if (face_dump_count > 0 && face_dump_count < face_count)
{
text_log.Print(
L"... %u additional faces (f%u to f%u).\n",
face_count - face_dump_count,
skipped_face_id.i,
skipped_face_id.j
);
}
text_log.Print("Maximum face id = %u. ",max_face_id);
if (validate_max_face_id >= max_face_id)
text_log.Print("Next id = %u.\n", validate_max_face_id + 1);
else
text_log.Print("ERROR Next id = %u.\n", validate_max_face_id + 1);
text_log.PopIndent();
const unsigned int topology_error_count
= vertex_error_count
+ edge_error_count
+ face_error_count;
text_log.PushIndent();
if (0 == topology_error_count)
{
text_log.Print("No topology inconsistencies.\n");
}
else
{
text_log.Print(
"ERRORS: %u vertex, %u edge, %u face topology inconsistencies marked with \"%c\".\n",
vertex_error_count,
edge_error_count,
face_error_count,
error_code_point
);
}
text_log.PopIndent();
return topology_error_count;
}
//virtual
unsigned int ON_SubD::SizeOf() const
{
size_t sz = ON_Geometry::SizeOf();
sz += sizeof(*this) - sizeof(ON_Geometry);
const ON_SubDimple* subdimple = SubDimple();
if ( subdimple )
sz += subdimple->SizeOf();
return (unsigned int)sz;
}
//virtual
ON__UINT32 ON_SubD::DataCRC(ON__UINT32 current_remainder) const
{
return 0;
}
//virtual
ON::object_type ON_SubD::ObjectType() const
{
return ON::subd_object;
}
//virtual
void ON_SubD::DestroyRuntimeCache( bool bDelete )
{
const ON_SubDimple* dimple = SubDimple();
if (nullptr != dimple)
{
unsigned int level_count = dimple->LevelCount();
for (unsigned int level_index = 0; level_index < level_count; level_index++)
{
const ON_SubDLevel* level = dimple->SubDLevel(level_index);
if (level)
{
level->ClearEvaluationCache();
level->AggregateComponentStatus().MarkAsNotCurrent();
}
}
dimple->ChangeContentSerialNumber();
}
return;
}
//virtual
int ON_SubD::Dimension() const
{
return 3;
}
//virtual
bool ON_SubD::GetBBox(
double* boxmin,
double* boxmax,
bool bGrowBox
) const
{
int j;
for ( j = 0; j < 3 && bGrowBox; j++ )
{
if ( !ON_IsValid(boxmin[j]) || !ON_IsValid(boxmax[j]) || boxmin[j] > boxmax[j])
bGrowBox = false;
}
ON_BoundingBox bbox = ON_BoundingBox::EmptyBoundingBox;
bool rc = false;
// GetBBox must always returns the control net box - it contains both the surface and the control net.
bbox = ActiveLevel().ControlNetBoundingBox();
rc = bbox.IsValid();
if (rc)
{
if (bGrowBox)
{
if (bbox.m_min.x < boxmin[0]) boxmin[0] = bbox.m_min.x;
if (bbox.m_max.x > boxmax[0]) boxmax[0] = bbox.m_max.x;
if (bbox.m_min.y < boxmin[1]) boxmin[1] = bbox.m_min.y;
if (bbox.m_max.y > boxmax[1]) boxmax[1] = bbox.m_max.y;
if (bbox.m_min.z < boxmin[2]) boxmin[2] = bbox.m_min.z;
if (bbox.m_max.z > boxmax[2]) boxmax[2] = bbox.m_max.z;
}
else
{
boxmin[0] = bbox.m_min.x; boxmin[1] = bbox.m_min.y; boxmin[2] = bbox.m_min.z;
boxmax[0] = bbox.m_max.x; boxmax[1] = bbox.m_max.y; boxmax[2] = bbox.m_max.z;
}
}
return (rc || bGrowBox);
}
//virtual
void ON_SubD::ClearBoundingBox()
{
// For ON_SubD, ON_SubD::ClearBoundingBox() and ON_SubD::DestroyRuntimeCache(true)
ON_SubD::DestroyRuntimeCache(true);
}
//virtual
bool ON_SubD::Transform(
const ON_Xform& xform
)
{
if ( this == &ON_SubD::Empty)
return true; // transform applied to empty subd is true on purpose
// userdata transformation, etc.
if (!this->ON_Geometry::Transform(xform))
return false;
ON_SubDimple* subdimple = SubDimple(false);
if ( 0 == subdimple )
return true; // transform applied to empty subd is true on purpose
return subdimple->Transform(xform);
}
//virtual
bool ON_SubD::IsDeformable() const
{
return true;
}
//virtual
bool ON_SubD::MakeDeformable()
{
return true;
}
//virtual
bool ON_SubD::SwapCoordinates(
int i,
int j
)
{
return false;
}
//virtual
bool ON_SubD::HasBrepForm() const
{
return false;
}
//virtual
ON_Brep* ON_SubD::BrepForm( ON_Brep* destination_brep) const
{
return nullptr;
}
//virtual
ON_COMPONENT_INDEX ON_SubD::ComponentIndex() const
{
return ON_Geometry::ComponentIndex();
}
//virtual
bool ON_SubD::EvaluatePoint( const class ON_ObjRef& objref, ON_3dPoint& P ) const
{
return false;
}
//////////////////////////////////////////////////////////////
//
//
//
const class ON_SubDLevel& ON_SubD::ActiveLevel() const
{
ON_SubDimple* subdimple = m_subdimple_sp.get();
return (nullptr != subdimple) ? subdimple->ActiveLevel() : ON_SubDLevel::Empty;
}
class ON_SubDLevel const * ON_SubD::ActiveLevelConstPointer() const
{
ON_SubDimple* subdimple = m_subdimple_sp.get();
return (nullptr != subdimple) ? subdimple->ActiveLevelPointer() : nullptr;
}
class ON_SubDLevel* ON_SubD::ActiveLevelPointer()
{
ON_SubDimple* subdimple = m_subdimple_sp.get();
return (nullptr != subdimple) ? subdimple->ActiveLevelPointer() : nullptr;
}
ON_SubDimple* ON_SubD::SubDimple(bool bCreateIfNeeded)
{
ON_SubDimple* subdimple = m_subdimple_sp.get();
if (nullptr == subdimple && bCreateIfNeeded)
{
subdimple = new ON_SubDimple();
m_subdimple_sp = std::shared_ptr<ON_SubDimple>(subdimple);
}
return subdimple;
}
const class ON_SubDimple* ON_SubD::SubDimple() const
{
return m_subdimple_sp.get();
}
unsigned int ON_SubD::SubDimpleUseCount() const
{
return (unsigned int)m_subdimple_sp.use_count();
}
void ON_SubD::ShareDimple(const ON_SubD& other_subd)
{
if (m_subdimple_sp.get() != other_subd.m_subdimple_sp.get())
{
m_subdimple_sp.reset();
m_subdimple_sp = other_subd.m_subdimple_sp;
}
}
void ON_SubD::ShareDimple(const class ON_SubDMeshImpl& subd_limple)
{
std::shared_ptr<ON_SubDimple> limple_sp(subd_limple.m_subdimple_wp.lock());
const ON_SubDimple* subd_imple = m_subdimple_sp.get();
if (nullptr == limple_sp.get())
{
// weak pointer is nullptr, meaning there are no known references to the
// subd used to create this limit mesh.
const_cast<ON_SubDMeshImpl&>(subd_limple).ClearFragmentFacePointers(true);
}
if (subd_imple != limple_sp.get())
{
m_subdimple_sp.reset();
m_subdimple_sp = limple_sp;
}
}
void ON_SubD::SwapDimple(class ON_SubDMeshImpl& subd_limple )
{
std::shared_ptr <ON_SubDimple> limple_sp(subd_limple.m_subdimple_wp.lock());
if (m_subdimple_sp.get() != limple_sp.get())
{
m_subdimple_sp.swap(limple_sp);
subd_limple.m_subdimple_wp = limple_sp;
}
}
void ON_SubD::SwapDimple(ON_SubD& other_subd)
{
if (this != &other_subd)
{
m_subdimple_sp.swap(other_subd.m_subdimple_sp);
}
}
void ON_SubD::Clear()
{
ON_SubDimple* subdimple = SubDimple(false);
if ( subdimple )
subdimple->Clear();
}
void ON_SubD::ClearHigherSubdivisionLevels(
unsigned int max_level_index
)
{
ON_SubDimple* subdimple = SubDimple(false);
if ( subdimple )
subdimple->ClearHigherSubdivisionLevels(max_level_index);
}
void ON_SubD::ClearLowerSubdivisionLevels(
unsigned int min_level_index
)
{
ON_SubDimple* subdimple = SubDimple(false);
if ( subdimple )
subdimple->ClearLowerSubdivisionLevels(min_level_index);
}
void ON_SubD::Destroy()
{
m_subdimple_sp.reset();
}
class ON_SubDVertex* ON_SubD::AddVertex(
const double* P
)
{
return AddVertex(ON_SubD::VertexTag::Unset, P);
}
class ON_SubDVertex* ON_SubD::AddVertex(
ON_SubD::VertexTag vertex_tag,
const double* P
)
{
ON_SubDimple* subdimple = SubDimple(true);
if ( 0 == subdimple )
return 0;
const unsigned int level_index = subdimple->ActiveLevelIndex();
class ON_SubDVertex* v = subdimple->AllocateVertex(vertex_tag, level_index, P); // 0 = level
subdimple->AddVertexToLevel(v);
return v;
}
class ON_SubDEdge* ON_SubDimple::AddEdge(
ON_SubD::EdgeTag edge_tag,
ON_SubDVertex* v0,
double v0_sector_weight,
ON_SubDVertex* v1,
double v1_sector_weight
)
{
if ( false == ON_SubDSectorType::IsValidSectorWeightValue(v0_sector_weight,true) )
return ON_SUBD_RETURN_ERROR(nullptr);
if ( false == ON_SubDSectorType::IsValidSectorWeightValue(v1_sector_weight,true) )
return ON_SUBD_RETURN_ERROR(nullptr);
if ( nullptr != v0 && nullptr != v1 && v0->SubdivisionLevel() != v1->SubdivisionLevel() )
return ON_SUBD_RETURN_ERROR(nullptr);
const bool bEdgeTagSet = ON_SubD::EdgeTagIsSet(edge_tag);
if ( bEdgeTagSet
&& ON_SubDSectorType::IgnoredSectorWeight != v0_sector_weight
&& ON_SubDSectorType::UnsetSectorWeight != v0_sector_weight
&& nullptr != v0
&& ON_SubD::VertexTag::Smooth == v0->m_vertex_tag
)
{
// minimizes checking when building subds because constant crease weights can be passed in
v0_sector_weight = ON_SubDSectorType::IgnoredSectorWeight;
}
if ( bEdgeTagSet
&& ON_SubDSectorType::IgnoredSectorWeight != v1_sector_weight
&& ON_SubDSectorType::UnsetSectorWeight != v1_sector_weight
&& nullptr != v1
&& ON_SubD::VertexTag::Smooth == v1->m_vertex_tag
)
{
// minimizes checking when building subds because constant crease weights can be passed in
v1_sector_weight = ON_SubDSectorType::IgnoredSectorWeight;
}
class ON_SubDEdge* e = AllocateEdge(edge_tag);
if ( nullptr == e)
return ON_SUBD_RETURN_ERROR(nullptr);
if ( nullptr != v0 )
e->SetSubdivisionLevel(v0->SubdivisionLevel());
else if ( nullptr != v1 )
e->SetSubdivisionLevel(v1->SubdivisionLevel());
else if ( ActiveLevelIndex() < ON_UNSET_UINT_INDEX )
e->SetSubdivisionLevel(ActiveLevelIndex());
for (unsigned int i = 0; i < 2; i++)
{
ON_SubDVertex* v = (i ? v1 : v0);
double vertex_weight = (i ? v1_sector_weight : v0_sector_weight);
e->m_vertex[i] = v;
e->m_sector_coefficient[i] = vertex_weight;
if (nullptr != v)
{
if (false == m_heap.GrowVertexEdgeArrayByOne(v))
{
v->m_status.SetDamagedState(true);
ReturnEdge(e);
return ON_SUBD_RETURN_ERROR(nullptr);
}
v->m_edges[v->m_edge_count++] = ON_SubDEdgePtr::Create(e, i);
}
}
if ( nullptr == AddEdgeToLevel(e) )
return ON_SUBD_RETURN_ERROR(nullptr);
return e;
}
ON_SubD::EdgeTag ON_SubD::EdgeTagFromContext(
unsigned int edge_face_count,
const ON_SubDVertex* v0,
const ON_SubDVertex* v1
)
{
return
(nullptr != v0 && nullptr != v1)
? ON_SubD::EdgeTagFromContext(edge_face_count, v0->m_vertex_tag, v1->m_vertex_tag)
: ON_SubD::EdgeTag::Unset;
}
ON_SubD::EdgeTag ON_SubD::EdgeTagFromContext(
unsigned int edge_face_count,
const ON_SubD::VertexTag v0_tag,
const ON_SubD::VertexTag v1_tag
)
{
ON_SubD::EdgeTag edge_tag = ON_SubD::EdgeTag::Unset;
for(;;)
{
if (edge_face_count > 0x7FFFU)
break;
if (1 == edge_face_count || edge_face_count >= 3 )
{
edge_tag = ON_SubD::EdgeTag::Crease;
break;
}
const bool bSmooth0 = ON_SubD::VertexTag::Smooth == v0_tag;
const bool bSmooth1 = ON_SubD::VertexTag::Smooth == v1_tag;
if ( bSmooth0 || bSmooth1 )
{
if ( 2 == edge_face_count && bSmooth0 && bSmooth1)
edge_tag = ON_SubD::EdgeTag::Smooth;
break;
}
if ( ON_SubD::VertexTagIsSet(v0_tag) && ON_SubD::VertexTagIsSet(v1_tag) )
{
if (2 == edge_face_count)
edge_tag = ON_SubD::EdgeTag::SmoothX;
break;
}
break;
}
return edge_tag;
}
const ON_SubDVertex* ON_SubD::FindVertex(
const double* control_net_point,
double distance_tolerance
) const
{
if (nullptr == control_net_point )
return nullptr;
const ON_3dPoint P(control_net_point);
if ( false == P.IsValid() )
return nullptr;
if ( false == (0.0 <= distance_tolerance))
return nullptr;
const ON_SubDVertex* best_v = nullptr;
ON_SubDVertexIterator vit(*this);
for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex())
{
const double d = P.DistanceTo(v->ControlNetPoint());
if (0.0 == d)
return v;
if (d < distance_tolerance && (nullptr == best_v || d <= distance_tolerance) )
{
distance_tolerance = d;
best_v = v;
}
}
return best_v;
}
const ON_SubDVertex* ON_SubD::FindOrAddVertex(
const double* control_net_point,
double distance_tolerance
)
{
if (nullptr == control_net_point)
return nullptr;
const ON_3dPoint P(control_net_point);
if (false == P.IsValid())
return nullptr;
if (false == (0.0 <= distance_tolerance))
return nullptr;
const ON_SubDVertex* v = FindVertex(&P.x, distance_tolerance);
return (nullptr != v) ? v : AddVertex(&P.x);
}
const ON_SubDEdgePtr ON_SubD::FindEdge(
const class ON_SubDVertex* v0,
const class ON_SubDVertex* v1
) const
{
return ON_SubDEdge::FromVertices(v0, v1);
}
const ON_SubDEdgePtr ON_SubD::FindOrAddEdge(
class ON_SubDVertex* v0,
class ON_SubDVertex* v1
)
{
ON_SubDEdgePtr eptr = ON_SubDEdge::FromVertices(v0, v1);
if (nullptr == eptr.Edge())
eptr = ON_SubDEdgePtr::Create(AddEdge(v0, v1), 0);
return eptr;
}
class ON_SubDEdge* ON_SubD::AddEdge(
ON_SubDVertex* v0,
ON_SubDVertex* v1
)
{
return ON_SubD::AddEdge(ON_SubD::EdgeTag::Unset, v0, v1);
}
class ON_SubDEdge* ON_SubD::AddEdge(
ON_SubD::EdgeTag edge_tag,
ON_SubDVertex* v0,
ON_SubDVertex* v1
)
{
// NO automatic edge tag setting - causes more problems than it helps.
// Users can call ON_SubD::EdgeTagFromContext() if they want to
// preset the edge tag based on context.
return AddEdgeWithSectorCoefficients(
edge_tag,
v0,
ON_SubDSectorType::UnsetSectorWeight,
v1,
ON_SubDSectorType::UnsetSectorWeight
);
}
ON_SubDEdge* ON_SubD::AddEdgeWithSectorCoefficients(
ON_SubD::EdgeTag edge_tag,
class ON_SubDVertex* v0,
double v0_sector_coefficient,
class ON_SubDVertex* v1,
double v1_sector_coefficient
)
{
ON_SubDimple* subdimple = SubDimple(true);
if (nullptr != subdimple)
return subdimple->AddEdge(edge_tag, v0, v0_sector_coefficient, v1, v1_sector_coefficient);
return ON_SUBD_RETURN_ERROR(nullptr);
}
class ON_SubDFace* ON_SubDimple::AddFace(
unsigned int edge_count,
const ON_SubDEdgePtr* edge
)
{
return AddFace(nullptr, edge_count, edge);
}
class ON_SubDFace* ON_SubDimple::AddFace(
const ON_SubDFace* candidate_face,
unsigned int edge_count,
const ON_SubDEdgePtr* edge
)
{
if ( edge_count > 0 && nullptr == edge)
return ON_SUBD_RETURN_ERROR(nullptr);
unsigned f_level = 0;
bool bHaveLevel = false;
for (unsigned int i = 0; i < edge_count; i++)
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(edge[i].m_ptr);
if (nullptr == e)
continue;
if (bHaveLevel)
{
if (e->SubdivisionLevel() != f_level)
return ON_SUBD_RETURN_ERROR(nullptr);
}
else
{
f_level = e->SubdivisionLevel();
bHaveLevel = true;
}
}
ON_SubDFace* f = AllocateFace(candidate_face);
if ( nullptr == f)
return ON_SUBD_RETURN_ERROR(nullptr);
f->SetSubdivisionLevel(f_level);
if (edge_count > 0)
{
if (edge_count > 4)
{
if (false == m_heap.GrowFaceEdgeArray(f,edge_count))
{
ReturnFace(f);
return ON_SUBD_RETURN_ERROR(nullptr);
}
}
ON_SubDEdgePtr* f_edge = f->m_edge4;
for (unsigned int i = 0; i < edge_count; i++)
{
if (4 == i)
f_edge = f->m_edgex - 4;
f_edge[i] = edge[i];
ON__UINT_PTR eptr = edge[i].m_ptr;
ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr);
if ( nullptr == e)
continue;
eptr = ON_SUBD_EDGE_DIRECTION(eptr);
ON_SubDVertex* v = const_cast<ON_SubDVertex*>(e->m_vertex[eptr]);
if (false == m_heap.GrowVertexFaceArrayByOne(v))
{
v->m_status.SetDamagedState(true);
ReturnFace(f);
return ON_SUBD_RETURN_ERROR(nullptr);
}
v->m_faces[v->m_face_count++] = f;
//if (1 == v->m_face_count)
//{
// if (4 == f->m_edge_count)
// v->m_vertex_facet_type = ON_SubD::VertexFacetType::Quad;
// else if (3 == f->m_edge_count)
// v->m_vertex_facet_type = ON_SubD::VertexFacetType::Tri;
// else if ( f->m_edge_count > 4)
// v->m_vertex_facet_type = ON_SubD::VertexFacetType::Ngon;
//}
//else
//{
// const ON_SubDFace* f0 = v->m_faces[0];
// if (nullptr == f0 || f0->m_edge_count != f->m_edge_count)
// v->m_vertex_facet_type = ON_SubD::VertexFacetType::Mixed;
//}
//v->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset;
ON_SubDFacePtr* e_faces;
if (e->m_face_count < 2)
{
e_faces = e->m_face2;
}
else
{
// Dale Lear, April 3, 2019 RH-49843 - we support non-manifold SubD objects now.
//if (2 == e->m_face_count)
//{
// // Getting this error in a valid situation means it is time
// // to support non-manifold subdivision objects.
// ON_SubDIncrementErrorCount();
// ON_WARNING("creating non-manifold subdivision object");
//}
if (false == m_heap.GrowEdgeFaceArrayByOne(e))
{
e->m_status.SetDamagedState(true);
continue;
}
e_faces = e->m_facex - 2;
}
e_faces[e->m_face_count++] = ON_SubDFacePtr::Create(f, eptr);
}
f->m_edge_count = (unsigned short)edge_count;
}
if ( nullptr == AddFaceToLevel(f))
return ON_SUBD_RETURN_ERROR(nullptr);
return f;
}
bool ON_SubDEdge::UpdateEdgeSectorCoefficientsForExperts(bool bUnsetEdgeSectorCoefficientsOnly)
{
const double input_sector_coefficient[2] = { m_sector_coefficient[0], m_sector_coefficient[1] };
if (bUnsetEdgeSectorCoefficientsOnly)
{
if (input_sector_coefficient[0] >= 0.0 && input_sector_coefficient[0] <= 1.0
&& input_sector_coefficient[1] >= 0.0 && input_sector_coefficient[1] <= 1.0
)
return false;
}
m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight;
m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight;
if (ON_SubD::EdgeTag::Smooth == m_edge_tag || ON_SubD::EdgeTag::SmoothX == m_edge_tag)
{
const unsigned int tagged_end_index = TaggedEndIndex();
if (tagged_end_index < 2)
{
m_sector_coefficient[tagged_end_index] = ON_SubDSectorType::Create( this, tagged_end_index).SectorWeight();
}
else if (2 == tagged_end_index)
{
if (ON_SubD::EdgeTag::Smooth == m_edge_tag && 2 == m_face_count )
m_edge_tag = ON_SubD::EdgeTag::SmoothX;
if (ON_SubD::EdgeTag::Smooth == m_edge_tag)
m_edge_tag = ON_SubD::EdgeTag::Crease;
else if (ON_SubD::EdgeTag::SmoothX == m_edge_tag)
{
m_sector_coefficient[0] = ON_SubDSectorType::Create( this, 0).SectorWeight();
m_sector_coefficient[1] = ON_SubDSectorType::Create( this, 1).SectorWeight();
}
}
}
const bool bNoChanges
= input_sector_coefficient[0] == m_sector_coefficient[0]
&& input_sector_coefficient[1] == m_sector_coefficient[1];
return (false == bNoChanges);
}
unsigned int ON_SubDLevel::UpdateEdgeSectorCoefficients(
bool bUnsetEdgeSectorCoefficientsOnly
)
{
unsigned int changed_edge_count = 0;
for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge)
{
if (const_cast<ON_SubDEdge*>(edge)->UpdateEdgeSectorCoefficientsForExperts(bUnsetEdgeSectorCoefficientsOnly))
++changed_edge_count;
}
return changed_edge_count;
}
class ON_SubDFace* ON_SubD::AddTriangleFace(
class ON_SubDEdge* edge0,
class ON_SubDEdge* edge1,
class ON_SubDEdge* edge2
)
{
ON_SubDEdge* edges[3] = { edge0,edge1,edge2 };
return AddFace(edges, 3);
}
class ON_SubDFace* ON_SubD::AddQuadFace(
class ON_SubDEdge* edge0,
class ON_SubDEdge* edge1,
class ON_SubDEdge* edge2,
class ON_SubDEdge* edge3
)
{
ON_SubDEdge* edges[4] = { edge0,edge1,edge2,edge3 };
return AddFace(edges, 4);
}
class ON_SubDFace* ON_SubD::AddTriangleFace(
ON_SubDEdgePtr edge0,
ON_SubDEdgePtr edge1,
ON_SubDEdgePtr edge2
)
{
ON_SubDEdgePtr eptr3[3] = { edge0,edge1,edge2 };
return AddFace(eptr3, 3);
}
class ON_SubDFace* ON_SubD::AddQuadFace(
ON_SubDEdgePtr edge0,
ON_SubDEdgePtr edge1,
ON_SubDEdgePtr edge2,
ON_SubDEdgePtr edge3
)
{
ON_SubDEdgePtr eptr4[4] = { edge0,edge1,edge2,edge3 };
return AddFace(eptr4, 4);
}
class ON_SubDFace* ON_SubD::AddFace(
const ON_SimpleArray<ON_SubDEdge*>& edges
)
{
return AddFace(edges.Array(), edges.UnsignedCount());
}
class ON_SubDFace* ON_SubD::AddFace(
const ON_SimpleArray<ON_SubDEdgePtr>& edges
)
{
return AddFace(edges.Array(), edges.UnsignedCount());
}
class ON_SubDFace* ON_SubD::AddFace(
ON_SubDEdge *const* edge,
unsigned int edge_count
)
{
if (edge_count < 3 || nullptr == edge)
return ON_SUBD_RETURN_ERROR(nullptr);
if (nullptr == edge[0] || nullptr == edge[0]->m_vertex[0] || nullptr == edge[0]->m_vertex[1] || edge[0]->m_vertex[0] == edge[0]->m_vertex[1])
return ON_SUBD_RETURN_ERROR(nullptr);
if ( edge[0] == edge[edge_count-1] )
return ON_SUBD_RETURN_ERROR(nullptr);
ON_SubDEdgePtr* eptr = (ON_SubDEdgePtr*)onmalloc(edge_count * sizeof(*eptr));
if (nullptr == eptr)
return ON_SUBD_RETURN_ERROR(nullptr);
eptr[0] = ON_SubDEdgePtr::Create(
edge[0],
(edge[0]->m_vertex[0] == edge[1]->m_vertex[0] || edge[0]->m_vertex[0] == edge[1]->m_vertex[1]) ? 1 : 0
);
eptr[edge_count - 1] = ON_SubDEdgePtr::Null;
for (unsigned int fei = 1; fei < edge_count; ++fei)
{
if (nullptr == edge[fei] || nullptr == edge[fei]->m_vertex[0] || nullptr == edge[fei]->m_vertex[1] || edge[fei]->m_vertex[0] == edge[fei]->m_vertex[1])
break;
if (edge[fei - 1] == edge[fei])
break;
const ON_SubDVertex* v = eptr[fei - 1].RelativeVertex(1);
if (nullptr == v)
break;
eptr[fei] = ON_SubDEdgePtr::Create(edge[fei], v == edge[fei]->m_vertex[0] ? 0 : 1);
if (v != eptr[fei].RelativeVertex(0))
break;
}
ON_SubDFace* face
= (eptr[edge_count - 1].IsNull() && (eptr[0].RelativeVertex(0) != eptr[edge_count - 1].RelativeVertex(1)))
? AddFace(eptr, edge_count)
: nullptr;
onfree(eptr);
if (nullptr == face)
{
ON_SUBD_ERROR("Invalid input edge[] array");
}
return face;
}
class ON_SubDFace* ON_SubD::AddFace(
const ON_SubDEdgePtr* edge,
unsigned int edge_count
)
{
ON_SubDimple* subdimple = SubDimple(true);
return (nullptr != subdimple) ? subdimple->AddFace(edge_count, edge) : nullptr;
}
class ON_SubDFace* ON_SubD::AddFace(
const ON_SubDFace* candidate_face,
const ON_SubDEdgePtr* edge,
unsigned int edge_count
)
{
ON_SubDimple* subdimple = SubDimple(true);
return (nullptr != subdimple) ? subdimple->AddFace(candidate_face, edge_count, edge) : nullptr;
}
bool ON_SubD::AddFaceEdgeConnection(
ON_SubDFace* face,
unsigned int i,
ON_SubDEdge* edge,
ON__UINT_PTR edge_direction
)
{
return AddFaceEdgeConnection(face, i, ON_SubDEdgePtr::Create(edge, edge_direction));
}
bool ON_SubD::AddFaceEdgeConnection(
ON_SubDFace* face,
unsigned int i,
ON_SubDEdgePtr eptr
)
{
if (nullptr == face && i >= ON_SubDFace::MaximumEdgeCount)
{
return ON_SUBD_RETURN_ERROR(false);
}
unsigned int face_edge_count = (unsigned int)face->m_edge_count + 1U;
if ( face_edge_count <= i )
face_edge_count = i+1;
ON_SubDEdge* edge = eptr.Edge();
if (nullptr != edge)
{
if (edge->m_face_count >= edge->m_facex_capacity + (unsigned short)2)
{
if (false == GrowEdgeFaceArray(edge, 0))
return ON_SUBD_RETURN_ERROR(false);
}
ON_SubDFacePtr fptr = ON_SubDFacePtr::Create(face,eptr.EdgeDirection());
unsigned short efi = edge->m_face_count;
if ( efi < 2 )
edge->m_face2[efi] = fptr;
else
{
if ( nullptr == edge->m_facex )
return ON_SUBD_RETURN_ERROR(false);
edge->m_facex[efi - 2] = fptr;
}
edge->m_face_count++;
}
if (face_edge_count > ((unsigned int)face->m_edgex_capacity) + 4U)
{
if (false == GrowFaceEdgeArray(face,face_edge_count))
return ON_SUBD_RETURN_ERROR(false);
}
if (i >= ((unsigned int)face->m_edge_count))
{
unsigned int j = face->m_edge_count;
for (/*empty init*/;j < 4; j++)
face->m_edge4[j] = ON_SubDEdgePtr::Null;
for (/*empty init*/;j < i; j++)
face->m_edgex[j-4] = ON_SubDEdgePtr::Null;
}
else
{
for (unsigned int j = face_edge_count - 1; j > i; j--)
{
if (j > 4)
face->m_edgex[j - 4] = face->m_edgex[j - 5];
else if (4 == j)
face->m_edgex[0] = face->m_edge4[3];
else
face->m_edge4[j] = face->m_edge4[j - 1];
}
}
if ( i < 4 )
face->m_edge4[i] = eptr;
else
face->m_edgex[i-4] = eptr;
face->m_edge_count = (unsigned short)face_edge_count;
return true;
}
bool ON_SubD::RemoveFaceEdgeConnection(
ON_SubDFace* face,
ON_SubDEdge* edge
)
{
ON_SubDEdgePtr removed_edge;
return RemoveFaceEdgeConnection(face, face->EdgeArrayIndex(edge), removed_edge);
}
bool ON_SubD::RemoveFaceEdgeConnection(
ON_SubDFace* face,
unsigned int i
)
{
ON_SubDEdgePtr removed_edge;
return RemoveFaceEdgeConnection(face, i, removed_edge);
}
bool ON_SubD::RemoveFaceEdgeConnection(
ON_SubDFace* face,
unsigned int i,
ON_SubDEdgePtr& removed_edge
)
{
removed_edge = ON_SubDEdgePtr::Null;
if ( nullptr == face && i >= (unsigned int)face->m_edge_count )
{
return ON_SUBD_RETURN_ERROR(false);
}
if ( false == face->RemoveEdgeFromArray(i,removed_edge) )
return ON_SUBD_RETURN_ERROR(false);
ON_SubDEdge* edge = removed_edge.Edge();
if (nullptr != edge)
{
if (false == edge->RemoveFaceFromArray(face))
return ON_SUBD_RETURN_ERROR(false);
}
return true;
}
bool ON_SubD::RemoveFaceConnections(
ON_SubDFace* face
)
{
if ( nullptr == face )
{
return ON_SUBD_RETURN_ERROR(false);
}
if (face->m_edge_count > 0)
{
ON_SubDEdgePtr removed_edge;
for (unsigned short fei = face->m_edge_count; fei > 0; --fei)
{
removed_edge = ON_SubDEdgePtr::Null;
if (false == face->RemoveEdgeFromArray(fei - 1, removed_edge))
return ON_SUBD_RETURN_ERROR(false);
ON_SubDEdge* edge = removed_edge.Edge();
if (nullptr != edge)
{
if (false == edge->RemoveFaceFromArray(face))
return ON_SUBD_RETURN_ERROR(false);
for (int evi = 0; evi < 2; ++evi)
{
ON_SubDVertex* v = const_cast<ON_SubDVertex*>(edge->m_vertex[evi]);
if (nullptr != v)
{
for (unsigned short vfi = 0; vfi < v->m_face_count; ++vfi)
{
if (face == v->m_faces[vfi])
{
for (++vfi; vfi < v->m_face_count; ++vfi)
v->m_faces[vfi - 1] = v->m_faces[vfi];
v->m_face_count--;
break;
}
}
}
}
}
}
face->m_edge_count = 0;
}
return true;
}
static bool ON_SubDFace_GetSubdivisionPointError(
const class ON_SubDFace* face,
double face_point[3],
bool bDamagedState
)
{
if (nullptr == face || nullptr == face_point)
return ON_SUBD_RETURN_ERROR(false); // caller passed a null pointer - edge is not necessarily damaged
face->m_status.SetDamagedState(bDamagedState);
return ON_SUBD_RETURN_ERROR(false);
}
const ON_3dPoint ON_SubDFace::SubdivisionPoint() const
{
ON_3dPoint S;
return (GetSubdivisionPoint(&S.x) && S.IsValid()) ? S : ON_3dPoint::NanPoint;
}
bool ON_SubDFace::GetSubdivisionPoint(
double subdivision_point[3]
) const
{
if (nullptr == subdivision_point)
return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, false);
if (GetSavedSubdivisionPoint(subdivision_point))
return true;
if (EvaluateCatmullClarkSubdivisionPoint(subdivision_point))
{
SetSavedSubdivisionPoint(subdivision_point);
return true;
}
return false;
}
bool ON_SubDFace::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[3]) const
{
if (nullptr == subdivision_point)
return ON_SubDFace_GetSubdivisionPointError(this,subdivision_point,false);
const unsigned int count = m_edge_count;
if (count < 3)
return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true);
double displacementV[3] = { 0 };
const bool bApplyDisplacement = GetSubdivisionDisplacement(displacementV);
const class ON_SubDEdgePtr* edge_ptr = m_edge4;
ON__UINT_PTR e_ptr;
const ON_SubDEdge* e;
ON__UINT_PTR edir;
const double* vertexP[4];
// Use faster code for the case when the face is a quad.
// Since this is a Catmull-Clark subdivision scheme, this
// case is the most common by far and code that gives quads
// special treatment will run noticably faster.
e_ptr = edge_ptr[0].m_ptr;
e = ON_SUBD_EDGE_POINTER(e_ptr);
edir = ON_SUBD_EDGE_DIRECTION(e_ptr);
vertexP[0] = e->m_vertex[edir]->m_P;
vertexP[1] = e->m_vertex[1 - edir]->m_P;
e_ptr = edge_ptr[2].m_ptr;
e = ON_SUBD_EDGE_POINTER(e_ptr);
edir = ON_SUBD_EDGE_DIRECTION(e_ptr);
vertexP[2] = e->m_vertex[edir]->m_P;
vertexP[3] = e->m_vertex[1 - edir]->m_P;
if (4 == count)
{
// most common case in quad subdivision schemes
subdivision_point[0] = (vertexP[0][0] + vertexP[1][0] + vertexP[2][0] + vertexP[3][0])*0.25;
subdivision_point[1] = (vertexP[0][1] + vertexP[1][1] + vertexP[2][1] + vertexP[3][1])*0.25;
subdivision_point[2] = (vertexP[0][2] + vertexP[1][2] + vertexP[2][2] + vertexP[3][2])*0.25;
if (bApplyDisplacement)
{
subdivision_point[0] += displacementV[0];
subdivision_point[1] += displacementV[1];
subdivision_point[2] += displacementV[2];
}
return true;
}
if (3 == count)
{
// 2nd most common case in quad subdivision schemes
subdivision_point[0] = (vertexP[0][0] + vertexP[1][0] + vertexP[2][0]) / 3.0;
subdivision_point[1] = (vertexP[0][1] + vertexP[1][1] + vertexP[2][1]) / 3.0;
subdivision_point[2] = (vertexP[0][2] + vertexP[1][2] + vertexP[2][2]) / 3.0;
if (bApplyDisplacement)
{
subdivision_point[0] += displacementV[0];
subdivision_point[1] += displacementV[1];
subdivision_point[2] += displacementV[2];
}
return true;
}
// count > 4
double faceP[3]
= {
(vertexP[0][0] + vertexP[1][0] + vertexP[2][0] + vertexP[3][0]),
(vertexP[0][1] + vertexP[1][1] + vertexP[2][1] + vertexP[3][1]),
(vertexP[0][2] + vertexP[1][2] + vertexP[2][2] + vertexP[3][2])
};
if (nullptr == m_edgex)
{
return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true);
}
edge_ptr = m_edgex - 4; // -4 because index i begins at 4
unsigned int i;
for (i = 4; i + 1 < count; i += 2)
{
e_ptr = edge_ptr[i].m_ptr;
e = ON_SUBD_EDGE_POINTER(e_ptr);
edir = ON_SUBD_EDGE_DIRECTION(e_ptr);
vertexP[0] = e->m_vertex[edir]->m_P;
vertexP[1] = e->m_vertex[1 - edir]->m_P;
faceP[0] += vertexP[0][0];
faceP[1] += vertexP[0][1];
faceP[2] += vertexP[0][2];
faceP[0] += vertexP[1][0];
faceP[1] += vertexP[1][1];
faceP[2] += vertexP[1][2];
}
if (i < count)
{
// odd number of edges and vertices
e_ptr = edge_ptr[count - 1].m_ptr;
e = ON_SUBD_EDGE_POINTER(e_ptr);
edir = ON_SUBD_EDGE_DIRECTION(e_ptr);
vertexP[0] = e->m_vertex[edir]->m_P;
faceP[0] += vertexP[0][0];
faceP[1] += vertexP[0][1];
faceP[2] += vertexP[0][2];
}
const double n = count;
subdivision_point[0] = faceP[0] / n;
subdivision_point[1] = faceP[1] / n;
subdivision_point[2] = faceP[2] / n;
if (bApplyDisplacement)
{
subdivision_point[0] += displacementV[0];
subdivision_point[1] += displacementV[1];
subdivision_point[2] += displacementV[2];
}
return true;
}
int ON_SubDComponentBase::CompareId(
const ON_SubDComponentBase* lhs,
const ON_SubDComponentBase* rhs
)
{
if (lhs == rhs)
return 0;
// nulls at end of list
if (nullptr == rhs)
return -1;
if (nullptr == lhs)
return 1;
if (lhs->m_id < rhs->m_id)
return -1;
if (lhs->m_id > rhs->m_id)
return 1;
return 0;
}
void ON_SubDComponentBase::ClearSavedSubdivisionPoint() const
{
Internal_ClearSubdivisionPointAndSurfacePointFlags();
}
bool ON_SubDComponentBase::SetSavedSubdivisionPoint(
const double subdivision_point[3]
) const
{
if (nullptr == subdivision_point)
{
Internal_ClearSubdivisionPointAndSurfacePointFlags();
return true;
}
if ( ON_IsValid(subdivision_point[0])
&& ON_IsValid(subdivision_point[1])
&& ON_IsValid(subdivision_point[2])
)
{
m_saved_subd_point1[0] = subdivision_point[0];
m_saved_subd_point1[1] = subdivision_point[1];
m_saved_subd_point1[2] = subdivision_point[2];
m_saved_points_flags |= ON_SUBD_CACHE_POINT_FLAG_BIT;
return true;
}
Internal_ClearSubdivisionPointAndSurfacePointFlags();
return ON_SUBD_RETURN_ERROR(false);
}
bool ON_SubDComponentBase::GetSavedSubdivisionPoint(
double subdivision_point[3]
) const
{
if ( 0 == (ON_SUBD_CACHE_POINT_FLAG_BIT & m_saved_points_flags) )
return false;
if (nullptr != subdivision_point)
{
subdivision_point[0] = m_saved_subd_point1[0];
subdivision_point[1] = m_saved_subd_point1[1];
subdivision_point[2] = m_saved_subd_point1[2];
}
return true;
}
const ON_3dPoint ON_SubDComponentBase::SavedSubdivisionPoint() const
{
ON_3dPoint p(ON_3dPoint::NanPoint);
return GetSavedSubdivisionPoint(&p.x) ? p : ON_3dPoint::NanPoint;
}
unsigned const ON_SubDComponentBase::SubdivisionLevel() const
{
return (unsigned)m_level;
}
void ON_SubDComponentBase::SetSubdivisionLevel(unsigned level)
{
if (level <= 255U)
m_level = ((unsigned char)level);
}
const ON_ComponentStatus ON_SubDComponentBase::Status() const
{
return m_status;
}
bool ON_SubDComponentBase::IsActive() const
{
return (m_id >= 0 && m_archive_id != ON_UNSET_UINT_INDEX);
}
bool ON_SubDComponentBase::Mark() const
{
return m_status.RuntimeMark();
}
bool ON_SubDComponentBase::ClearMark() const
{
return m_status.ClearRuntimeMark();
}
bool ON_SubDComponentBase::SetMark() const
{
return m_status.SetRuntimeMark();
}
bool ON_SubDComponentBase::SetMark(
bool bMark
) const
{
return m_status.SetRuntimeMark(bMark);
}
bool ON_SubDComponentPtr::IsActive() const
{
const ON_SubDComponentBase* c = this->ComponentBase();
return (nullptr != c) ? c->IsActive() : false;
}
bool ON_SubDVertexPtr::IsActive() const
{
const ON_SubDVertex* v = ON_SUBD_VERTEX_POINTER(m_ptr);
return (nullptr != v) ? v->IsActive() : false;
}
bool ON_SubDEdgePtr::IsActive() const
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr);
return (nullptr != e) ? e->IsActive() : false;
}
bool ON_SubDFacePtr::IsActive() const
{
const ON_SubDFace* f = ON_SUBD_FACE_POINTER(m_ptr);
return (nullptr != f) ? f->IsActive() : false;
}
const ON_ComponentStatus ON_SubDVertex::NeighborhoodStatusLogicalOr(
bool bIncludeEdges,
bool bIncludeFaces
) const
{
ON_ComponentStatus s(m_status);
if (bIncludeEdges && nullptr != m_edges)
{
for (unsigned short vei = 0; vei < m_edge_count; vei++)
{
const ON_SubDEdge* e = m_edges[vei].Edge();
if (nullptr != e)
s = ON_ComponentStatus::LogicalOr(s, e->m_status);
}
}
if (bIncludeFaces && nullptr != m_faces)
{
for (unsigned short vfi = 0; vfi < m_face_count; vfi++)
{
const ON_SubDFace* f = m_faces[vfi];
if (nullptr != f)
s = ON_ComponentStatus::LogicalOr(s, f->m_status);
}
}
return s;
}
const ON_ComponentStatus ON_SubDEdge::NeighborhoodStatusLogicalOr(
bool bIncludeVertices,
bool bIncludeFaces
) const
{
ON_ComponentStatus s(m_status);
if (bIncludeVertices)
{
for (unsigned int evi = 0; evi < 2; evi++)
{
const ON_SubDVertex* v = m_vertex[evi];
if (nullptr != v)
s = ON_ComponentStatus::LogicalOr(s, v->m_status);
}
}
if (bIncludeFaces)
{
const ON_SubDFacePtr* fptr = m_face2;
for (unsigned short vfi = 0; vfi < m_face_count; vfi++)
{
const ON_SubDFace* f = fptr->Face();
if (nullptr != f)
s = ON_ComponentStatus::LogicalOr(s, f->m_status);
if (1 == vfi)
{
fptr = m_facex;
if (nullptr == fptr)
break;
}
else
fptr++;
}
}
return s;
}
const ON_ComponentStatus ON_SubDFace::NeighborhoodStatusLogicalOr(bool bIncludeVertices, bool bIncludeEdges) const
{
ON_ComponentStatus s(m_status);
if (bIncludeVertices || bIncludeEdges)
{
const ON_SubDEdgePtr* eptr = m_edge4;
for (unsigned int fei = 0; fei < m_edge_count; fei++)
{
if (4 == fei)
{
eptr = m_edgex;
if (nullptr == eptr)
break;
}
const ON_SubDEdge* e = eptr->Edge();
if (nullptr != e)
{
if (bIncludeEdges)
{
s = ON_ComponentStatus::LogicalOr(s, e->m_status);
}
if (bIncludeVertices)
{
const ON_SubDVertex* v = e->m_vertex[(0!=eptr->EdgeDirection())?1:0];
if (nullptr != v)
s = ON_ComponentStatus::LogicalOr(s, v->m_status);
}
}
eptr++;
}
}
return s;
}
static void Internal_ClearFaceNeighborhoodCache(const ON_SubDFace* face)
{
// Clear cached values for every component associated with this face.
face->ClearSavedSubdivisionPoints();
const ON_SubDEdgePtr* eptr = face->m_edge4;
for (unsigned int efi = 0; efi < face->m_edge_count; efi++)
{
if (4 == efi)
{
eptr = face->m_edgex;
if ( nullptr == eptr)
break;
}
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr->m_ptr);
if (nullptr != edge)
{
edge->ClearSavedSubdivisionPoints();
edge->UnsetSectorCoefficientsForExperts();
for (unsigned int evi = 0; evi < 2; evi++)
{
const ON_SubDVertex* vertex = edge->m_vertex[evi];
if (nullptr != vertex)
vertex->ClearSavedSubdivisionPoints();
}
}
eptr++;
}
}
void ON_SubDVertex::VertexModifiedNofification() const
{
ClearSavedSubdivisionPoints();
if (nullptr != m_edges)
{
for (unsigned short vei = 0; vei < m_edge_count; vei++)
{
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr);
if (nullptr != edge)
{
edge->ClearSavedSubdivisionPoints();
edge->UnsetSectorCoefficientsForExperts();
}
}
// This is needed to clear cached information in the Catmull-Clark
// ring that is not immediately adjacent to this vertex but whose values
// this vertex affects.
for (unsigned short vfi = 0; vfi < m_face_count; vfi++)
{
const ON_SubDFace* face = this->m_faces[vfi];
if (nullptr != face)
Internal_ClearFaceNeighborhoodCache(face);
}
}
}
void ON_SubDEdge::EdgeModifiedNofification() const
{
ClearSavedSubdivisionPoints();
UnsetSectorCoefficientsForExperts();
for (unsigned int evi = 0; evi < 2; evi++)
{
const_cast<ON_SubDEdge*>(this)->m_sector_coefficient[evi] = ON_SubDSectorType::UnsetSectorWeight;
if (nullptr != m_vertex[evi])
m_vertex[evi]->VertexModifiedNofification();
}
// If the topology pointers are complete and accurate, then the following
// is not required. It's here because this SubD may be under construction
// and we cannot assume the topology pointers are complete and accurate.
const ON_SubDFacePtr* fptr = m_face2;
for (unsigned int efi = 0; efi < 2; efi++)
{
if (2 == efi)
{
fptr = m_facex;
if ( nullptr == fptr)
break;
}
const ON_SubDFace* face = ON_SUBD_FACE_POINTER(fptr->m_ptr);
if ( nullptr != face )
Internal_ClearFaceNeighborhoodCache(face);
fptr++;
}
}
void ON_SubDEdge::UnsetSectorCoefficientsForExperts() const
{
const_cast<ON_SubDEdge*>(this)->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight;
const_cast<ON_SubDEdge*>(this)->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight;
}
void ON_SubDVertex::UnsetSectorCoefficientsForExperts(unsigned int relative_edge_end_dex) const
{
for (unsigned short vei = 0; vei < m_edge_count; ++vei)
{
ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr);
if (nullptr == e)
continue;
ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(m_edges[vei].m_ptr);
const unsigned evi
= (relative_edge_end_dex < 2)
? ((0 == edir ? false : true) == (0 == relative_edge_end_dex ? false : true) ? 0U : 1U)
: 2U;
if ( evi < 2)
e->m_sector_coefficient[evi] = ON_SubDSectorType::UnsetSectorWeight;
else
{
e->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight;
e->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight;
}
}
}
void ON_SubDFace::FaceModifiedNofification() const
{
Internal_ClearFaceNeighborhoodCache(this);
// This is needed to clear cached information in the Catmull-Clark
// ring that is not immediately adjacent to this face but whose values
// this face affects.
const ON_SubDEdgePtr* eptr = m_edge4;
for (unsigned int efi = 0; efi < m_edge_count; efi++)
{
if (4 == efi)
{
eptr = m_edgex;
if ( nullptr == eptr)
break;
}
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr->m_ptr);
if (nullptr != edge)
{
const ON_SubDFacePtr* fptr = edge->m_face2;
for (unsigned short fei = 0; fei < edge->m_face_count; fei++)
{
if (2 == fei)
{
fptr = edge->m_facex;
if (nullptr == fptr)
break;
}
const ON_SubDFace* f = ON_SUBD_FACE_POINTER(fptr->m_ptr);
if (nullptr != f && f != this)
Internal_ClearFaceNeighborhoodCache(f);
}
}
eptr++;
}
}
void ON_SubDComponentBase::Internal_ClearSubdivisionPointAndSurfacePointFlags() const
{
ON_SUBD_CACHE_CLEAR_POINT_FLAG(m_saved_points_flags);
ON_SUBD_CACHE_CLEAR_LIMITLOC_FLAG(m_saved_points_flags);
ON_SUBD_CACHE_CLEAR_CTRLNETFRAG_FLAG(m_saved_points_flags);
}
bool ON_SubDComponentBase::Internal_SubdivisionPointFlag() const
{
return (0 != ON_SUBD_CACHE_POINT_FLAG(m_saved_points_flags));
}
void ON_SubDComponentBase::Internal_ClearSubdivisionPointFlag() const
{
ON_SUBD_CACHE_CLEAR_POINT_FLAG(m_saved_points_flags);
}
bool ON_SubDComponentBase::Internal_SurfacePointFlag() const
{
return (0 != ON_SUBD_CACHE_LIMITLOC_FLAG(m_saved_points_flags));
}
bool ON_SubDComponentBase::Internal_ControlNetFragmentFlag() const
{
return (0 != ON_SUBD_CACHE_CTRLNETFRAG_FLAG(m_saved_points_flags));
}
void ON_SubDComponentBase::Internal_ClearSurfacePointFlag() const
{
ON_SUBD_CACHE_CLEAR_LIMITLOC_FLAG(m_saved_points_flags);
ON_SUBD_CACHE_CLEAR_CTRLNETFRAG_FLAG(m_saved_points_flags);
}
void ON_SubDComponentBase::Internal_ClearControlNetFragmentFlag() const
{
ON_SUBD_CACHE_CLEAR_CTRLNETFRAG_FLAG(m_saved_points_flags);
}
bool ON_SubDComponentBase::SavedSubdivisionPointIsSet() const
{
return
(0 != ON_SUBD_CACHE_POINT_FLAG(m_saved_points_flags))
? (ON_IS_VALID(m_saved_subd_point1[0]) && ON_IS_VALID(m_saved_subd_point1[1]) && ON_IS_VALID(m_saved_subd_point1[2]))
: false;
}
bool ON_SubDComponentBase::SubdivisionDisplacementIsNonzero() const
{
return (0 != ON_SUBD_CACHE_DISPLACEMENT_FLAG(m_saved_points_flags))
? (
(0.0 != m_displacement_V[0] || 0.0 != m_displacement_V[1] || 0.0 != m_displacement_V[2])
&& ON_IS_VALID(m_displacement_V[0]) && ON_IS_VALID(m_displacement_V[1]) && ON_IS_VALID(m_displacement_V[2])
)
: false;
}
bool ON_SubDComponentBase::SetSubdivisionDisplacement(
const double displacement[3]
)
{
if (
nullptr == displacement
|| (0.0 == displacement[0] && 0.0 == displacement[1] && 0.0 == displacement[2])
)
{
ClearSubdivisionDisplacement();
return true;
}
if ( ON_IsValid(displacement[0]) && ON_IsValid(displacement[1]) && ON_IsValid(displacement[2]) )
{
m_saved_points_flags |= ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT;
m_displacement_V[0] = displacement[0];
m_displacement_V[1] = displacement[1];
m_displacement_V[2] = displacement[2];
return true;
}
ClearSubdivisionDisplacement();
return ON_SUBD_RETURN_ERROR(false);
}
void ON_SubDComponentBase::ClearSubdivisionDisplacement() const
{
if (0 != (m_saved_points_flags & ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT))
{
ON_SUBD_CACHE_CLEAR_POINT_FLAG(m_saved_points_flags);
ON_SUBD_CACHE_CLEAR_DISPLACEMENT_FLAG(m_saved_points_flags);
}
}
bool ON_SubDComponentBase::GetSubdivisionDisplacement(
double displacement[3]
) const
{
const bool rc = (0 != (ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT & m_saved_points_flags));
if (nullptr != displacement)
{
if (rc)
{
displacement[0] = m_displacement_V[0];
displacement[1] = m_displacement_V[1];
displacement[2] = m_displacement_V[2];
}
else
{
displacement[0] = 0.0;
displacement[1] = 0.0;
displacement[2] = 0.0;
}
}
return rc;
}
const ON_3dVector ON_SubDComponentBase::SubdivisionDisplacement() const
{
ON_3dVector v(ON_3dVector::ZeroVector);
return GetSubdivisionDisplacement(&v.x) ? v : ON_3dVector::ZeroVector;
}
void ON_SubDComponentBase::Internal_SetSavedSurfacePointFlag(bool bSavedSurfacePointFlag) const
{
if (bSavedSurfacePointFlag)
m_saved_points_flags |= ON_SUBD_CACHE_LIMITLOC_FLAG_BIT;
else
Internal_ClearSurfacePointFlag();
}
void ON_SubDComponentBase::Internal_SetSavedControlNetFragmentFlag(bool bSavedControlNetFragmentFlag) const
{
if (bSavedControlNetFragmentFlag)
m_saved_points_flags |= ON_SUBD_CACHE_CTRLNETFRAG_FLAG_BIT;
else
Internal_ClearSurfacePointFlag();
}
bool ON_SubDFace::ReverseEdgeList()
{
const unsigned int edge_count = m_edge_count;
if ( 0 == edge_count)
return true;
if (edge_count > 4 && nullptr == m_edgex)
{
return ON_SUBD_RETURN_ERROR(false);
}
ON_SubDEdgePtr buffer[16];
ON_SubDEdgePtr* reversed_eptrs;
if ( edge_count <= sizeof(buffer)/sizeof(buffer[0]) )
reversed_eptrs = buffer;
else
{
reversed_eptrs = new(std::nothrow) ON_SubDEdgePtr[edge_count];
if ( nullptr == reversed_eptrs)
return ON_SUBD_RETURN_ERROR(false);
}
ON_SubDEdgePtr* face_eptrs = m_edge4;
for (unsigned int fei = 0; fei < edge_count; fei++, face_eptrs++)
{
if (4 == fei)
face_eptrs = m_edgex;
ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(face_eptrs->m_ptr);
if ( nullptr == e)
continue;
ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(face_eptrs->m_ptr);
reversed_eptrs[edge_count-1-fei] = ON_SubDEdgePtr::Create(e,1-edir);
ON_SubDFacePtr* edges_fptrs = e->m_face2;
const unsigned int face_count = e->m_face_count;
for (unsigned int efi = 0; efi < face_count; efi++, edges_fptrs++)
{
if (2 == efi)
{
edges_fptrs = e->m_facex;
if ( nullptr == edges_fptrs)
break;
}
if ( this != ON_SUBD_FACE_POINTER(edges_fptrs->m_ptr) )
continue;
*edges_fptrs = ON_SubDFacePtr::Create(this,1-ON_SUBD_FACE_DIRECTION(edges_fptrs->m_ptr));
break;
}
}
face_eptrs = m_edge4;
for (unsigned int fei = 0; fei < edge_count; fei++)
{
if (4 == fei)
face_eptrs = m_edgex;
*face_eptrs++ = reversed_eptrs[fei];
}
if ( reversed_eptrs != buffer )
delete[] reversed_eptrs;
return true;
}
static bool ON_SubDEdge_GetSubdivisionPointError(
const class ON_SubDEdge* edge,
double edge_point[3],
const double* edgeP[2],
bool bDamagedState
)
{
if (nullptr == edge || nullptr == edge_point)
return false; // caller passed a null pointer - edge is not necessarily damaged
ON_SubDIncrementErrorCount();
edge->m_status.SetDamagedState(bDamagedState);
if (nullptr != edgeP && nullptr != edgeP[0] && nullptr != edgeP[1])
{
const double edgePsum[3] = { edgeP[0][0] + edgeP[1][0], edgeP[0][1] + edgeP[1][1], edgeP[0][2] + edgeP[1][2] };
edge_point[0] = 0.5*edgePsum[0];
edge_point[1] = 0.5*edgePsum[1];
edge_point[2] = 0.5*edgePsum[2];
}
return true;
}
unsigned int ON_SubDEdge::GetFacePointSum(
const ON_SubDFace* face,
const ON_SubDEdge* edge,
double* facePsum
)
{
const ON_SubDEdge* e;
ON__UINT_PTR e_ptr, edir;
const double* vertexP[2];
if (nullptr == face)
return 0;
const unsigned int n = face->m_edge_count;
if (3 == n)
{
if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[0].m_ptr))
e_ptr = face->m_edge4[1].m_ptr;
else if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[1].m_ptr))
e_ptr = face->m_edge4[2].m_ptr;
else if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[2].m_ptr))
e_ptr = face->m_edge4[0].m_ptr;
else
return 0;
e = ON_SUBD_EDGE_POINTER(e_ptr);
if (nullptr == e)
return 0;
if (nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1])
return 0;
edir = ON_SUBD_EDGE_DIRECTION(e_ptr);
if (edge->m_vertex[0] != e->m_vertex[edir] && edge->m_vertex[1] != e->m_vertex[edir])
return 0;
vertexP[0] = e->m_vertex[1 - edir]->m_P;
facePsum[0] = vertexP[0][0];
facePsum[1] = vertexP[0][1];
facePsum[2] = vertexP[0][2];
return n;
}
if (4 == n)
{
if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[0].m_ptr))
e_ptr = face->m_edge4[2].m_ptr;
else if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[1].m_ptr))
e_ptr = face->m_edge4[3].m_ptr;
else if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[2].m_ptr))
e_ptr = face->m_edge4[0].m_ptr;
else if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[3].m_ptr))
e_ptr = face->m_edge4[1].m_ptr;
else
return 0;
e = ON_SUBD_EDGE_POINTER(e_ptr);
if (nullptr == e)
return 0;
if (nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1])
return 0;
edir = ON_SUBD_EDGE_DIRECTION(e_ptr);
vertexP[0] = e->m_vertex[edir]->m_P;
vertexP[1] = e->m_vertex[1 - edir]->m_P;
facePsum[0] = vertexP[0][0] + vertexP[1][0];
facePsum[1] = vertexP[0][1] + vertexP[1][1];
facePsum[2] = vertexP[0][2] + vertexP[1][2];
return n;
}
if (n < 3)
return 0;
const ON_SubDEdgePtr* edgeptr = face->m_edge4;
const ON_SubDVertex* edge_vertex[2] = { edge->m_vertex[0], edge->m_vertex[1] };
facePsum[0] = 0.0;
facePsum[1] = 0.0;
facePsum[2] = 0.0;
for (unsigned i = 0; i < n; i++)
{
if (4 == i)
edgeptr = face->m_edgex - 4;
e = ON_SUBD_EDGE_POINTER(edgeptr[i].m_ptr);
if (nullptr == e)
return 0;
if (edge == e)
continue;
edir = ON_SUBD_EDGE_DIRECTION(edgeptr[i].m_ptr);
const ON_SubDVertex* e_vertex[2] = { e->m_vertex[edir], e->m_vertex[1 - edir] };
if (nullptr == e_vertex[0] || nullptr == e_vertex[1])
return 0;
if (edge_vertex[0] != e_vertex[0] && edge_vertex[1] != e_vertex[0])
{
vertexP[0] = e_vertex[0]->m_P;
facePsum[0] += vertexP[0][0];
facePsum[1] += vertexP[0][1];
facePsum[2] += vertexP[0][2];
}
if (i + 1 < n)
{
// start of next edge = end of this edge
if (edge_vertex[0] != e_vertex[1] && edge_vertex[1] != e_vertex[1])
{
vertexP[0] = e_vertex[1]->m_P;
facePsum[0] += vertexP[0][0];
facePsum[1] += vertexP[0][1];
facePsum[2] += vertexP[0][2];
}
i++;
if (4 == i && n > 4)
edgeptr = face->m_edgex - 4;
}
}
return n;
}
const ON_3dPoint ON_SubDEdge::SubdivisionPoint() const
{
ON_3dPoint S;
return (GetSubdivisionPoint(&S.x) && S.IsValid()) ? S : ON_3dPoint::NanPoint;
}
bool ON_SubDEdge::GetSubdivisionPoint(
double subdivision_point[3]
) const
{
if (nullptr == subdivision_point)
return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, nullptr, false);
if (GetSavedSubdivisionPoint(subdivision_point))
return true;
if (EvaluateCatmullClarkSubdivisionPoint(subdivision_point))
{
SetSavedSubdivisionPoint(subdivision_point);
return true;
}
return false;
}
const ON_3dPoint ON_SubDEdge::ControlNetCenterPoint() const
{
return 0.5*(ControlNetPoint(0) + ControlNetPoint(1));
}
const ON_3dVector ON_SubDEdge::ControlNetCenterNormal(
unsigned int edge_face_index
) const
{
const ON_SubDFace* face = Face(edge_face_index);
return (nullptr != face) ? face->ControlNetCenterNormal() : ON_3dVector::NanVector;
}
bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[3]) const
{
if (nullptr == subdivision_point)
return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, nullptr, false);
const ON_SubDVertex* edge_vertex[2] = { m_vertex[0], m_vertex[1] };
if (nullptr == edge_vertex[0] || nullptr == edge_vertex[1])
return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, nullptr, true);
double displacementV[3] = { 0 };
const bool bApplyDisplacement = GetSubdivisionDisplacement(displacementV);
const double* edgeP[2] = { edge_vertex[0]->m_P, edge_vertex[1]->m_P };
if ( IsSmooth() )
{
// A smooth edge must have exactly two neighboring faces and
// at most one end vertex can be tagged.
if (2 != m_face_count)
return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, edgeP, true);
const ON_SubDFace* faces[2] = { ON_SUBD_FACE_POINTER(m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(m_face2[1].m_ptr) };
if (nullptr == faces[0] || nullptr == faces[1])
return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, edgeP, true);
// for each neighbor face, sum the vertex locations that are not on this edge
double facePsum[2][3];
const unsigned int face_edge_count[2]
= { ON_SubDEdge::GetFacePointSum(faces[0], this, facePsum[0]),
ON_SubDEdge::GetFacePointSum(faces[1], this, facePsum[1])
};
if (0 == face_edge_count[0] || 0 == face_edge_count[1])
return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, edgeP, true);
const unsigned int tagged_end
= (ON_SubD::VertexTag::Smooth != edge_vertex[0]->m_vertex_tag)
? 0
: ((ON_SubD::VertexTag::Smooth != edge_vertex[1]->m_vertex_tag) ? 1 : ON_UNSET_UINT_INDEX);
double edgePsum[3];
if (
ON_UNSET_UINT_INDEX == tagged_end
|| 0.5 == m_sector_coefficient[tagged_end]
|| (ON_SubD::EdgeTag::SmoothX == m_edge_tag)
)
{
// ignore edge weights
edgePsum[0] = 0.375*(edgeP[0][0] + edgeP[1][0]);
edgePsum[1] = 0.375*(edgeP[0][1] + edgeP[1][1]);
edgePsum[2] = 0.375*(edgeP[0][2] + edgeP[1][2]);
}
else if (ON_SubD::VertexTag::Smooth == edge_vertex[1 - tagged_end]->m_vertex_tag
&& m_sector_coefficient[tagged_end] > 0.0
&& m_sector_coefficient[tagged_end] < 1.0
)
{
double w[2];
w[tagged_end] = m_sector_coefficient[tagged_end];
w[1 - tagged_end] = 1.0 - w[tagged_end];
edgePsum[0] = 0.75*(w[0] * edgeP[0][0] + w[1] * edgeP[1][0]);
edgePsum[1] = 0.75*(w[0] * edgeP[0][1] + w[1] * edgeP[1][1]);
edgePsum[2] = 0.75*(w[0] * edgeP[0][2] + w[1] * edgeP[1][2]);
}
else
{
// error:
// Both ends of a smooth vertex are tagged
// or weights are incorrectly set
// or ...
return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, edgeP, true);
}
if (4 == face_edge_count[0] && 4 == face_edge_count[1])
{
// common case when both neighboring faces are quads
subdivision_point[0] = edgePsum[0] + 0.0625*(facePsum[0][0] + facePsum[1][0]);
subdivision_point[1] = edgePsum[1] + 0.0625*(facePsum[0][1] + facePsum[1][1]);
subdivision_point[2] = edgePsum[2] + 0.0625*(facePsum[0][2] + facePsum[1][2]);
if (bApplyDisplacement)
{
subdivision_point[0] += displacementV[0];
subdivision_point[1] += displacementV[1];
subdivision_point[2] += displacementV[2];
}
return true;
}
if (3 == face_edge_count[0] && 3 == face_edge_count[1])
{
// common case when both neighboring faces are triangles
subdivision_point[0] = edgePsum[0] + 0.125*(facePsum[0][0] + facePsum[1][0]);
subdivision_point[1] = edgePsum[1] + 0.125*(facePsum[0][1] + facePsum[1][1]);
subdivision_point[2] = edgePsum[2] + 0.125*(facePsum[0][2] + facePsum[1][2]);
if (bApplyDisplacement)
{
subdivision_point[0] += displacementV[0];
subdivision_point[1] += displacementV[1];
subdivision_point[2] += displacementV[2];
}
return true;
}
// general formula works for all cases including face_edge_count[0] != face_count[2]
const double f0 = 0.125 / ((double)(face_edge_count[0]-2));
const double f1 = 0.125 / ((double)(face_edge_count[1]-2));
subdivision_point[0] = edgePsum[0] + f0 * facePsum[0][0] + f1 * facePsum[1][0];
subdivision_point[1] = edgePsum[1] + f0 * facePsum[0][1] + f1 * facePsum[1][1];
subdivision_point[2] = edgePsum[2] + f0 * facePsum[0][2] + f1 * facePsum[1][2];
if (bApplyDisplacement)
{
subdivision_point[0] += displacementV[0];
subdivision_point[1] += displacementV[1];
subdivision_point[2] += displacementV[2];
}
return true;
}
if ( IsCrease() )
{
subdivision_point[0] = 0.5*(edgeP[0][0] + edgeP[1][0]);
subdivision_point[1] = 0.5*(edgeP[0][1] + edgeP[1][1]);
subdivision_point[2] = 0.5*(edgeP[0][2] + edgeP[1][2]);
if (bApplyDisplacement)
{
subdivision_point[0] += displacementV[0];
subdivision_point[1] += displacementV[1];
subdivision_point[2] += displacementV[2];
}
return true;
}
// invalid edge->m_edge_tag
return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, edgeP, true);
}
static unsigned int GetSectorBoundaryEdgesError()
{
return ON_SUBD_RETURN_ERROR(0);
}
unsigned int ON_SubDEdge::GetSectorBoundaryEdges(
unsigned int edge_vertex_index,
ON_SubDEdgePtr* edge_ptr0,
ON_SubDEdgePtr* edge_ptr1
) const
{
if (nullptr != edge_ptr0)
*edge_ptr0 = ON_SubDEdgePtr::Null;
if (nullptr != edge_ptr1)
*edge_ptr1 = ON_SubDEdgePtr::Null;
const unsigned int edge_face_count = m_face_count;
if (edge_face_count <= 0 || edge_face_count > 2)
return GetSectorBoundaryEdgesError();
if (2 == edge_face_count && ON_SubD::EdgeTag::Crease == m_edge_tag)
return GetSectorBoundaryEdgesError();
if (0 != edge_vertex_index && 1 != edge_vertex_index)
return GetSectorBoundaryEdgesError();
const ON_SubDVertex* vertex = m_vertex[edge_vertex_index];
if (nullptr == vertex || vertex->m_face_count <= 0)
return GetSectorBoundaryEdgesError();
const unsigned int vertex_face_count = vertex->m_face_count;
unsigned int sector_face_count = 0;
ON_SubDEdgePtr sector_boundary[2] = {};
for (unsigned int edge_face_index = 0; edge_face_index < edge_face_count; edge_face_index++)
{
const ON_SubDEdge* edge0 = this;
unsigned int edge0_end_index = edge_vertex_index;
unsigned int edge0_face_index = edge_face_index;
ON_SubDFacePtr face_ptr = edge0->m_face2[edge0_face_index];
while (sector_face_count < vertex_face_count)
{
const ON_SubDFace* face = ON_SUBD_FACE_POINTER(face_ptr.m_ptr);
if (0 == face)
return GetSectorBoundaryEdgesError();
ON__UINT_PTR face_dir = ON_SUBD_FACE_DIRECTION(face_ptr.m_ptr);
sector_face_count++;
unsigned int face_edge0_index = face->EdgeArrayIndex(edge0);
if (ON_UNSET_UINT_INDEX == face_edge0_index)
return 0;
unsigned int face_edge1_index
= face_edge0_index;
face_edge1_index +=
(1 == (edge0_end_index + face_dir))
? 1
: (face->m_edge_count - 1);
face_edge1_index %= face->m_edge_count;
ON_SubDEdgePtr edge1_ptr = face->EdgePtr(face_edge1_index);
const ON_SubDEdge* edge1 = ON_SUBD_EDGE_POINTER(edge1_ptr.m_ptr);
if (nullptr == edge1)
return GetSectorBoundaryEdgesError();
unsigned int edge1_end_index = (0 == face_dir) ? (1 - edge0_end_index) : edge0_end_index;
if (1 == ON_SUBD_EDGE_DIRECTION(edge1_ptr.m_ptr))
edge1_end_index = 1 - edge1_end_index;
if (vertex != edge1->m_vertex[edge1_end_index])
return GetSectorBoundaryEdgesError();
if ( edge1->IsSmooth() && 2 == edge1->m_face_count )
{
const ON_SubDFace* edge1_faces[2] = { ON_SUBD_FACE_POINTER(edge1->m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(edge1->m_face2[1].m_ptr) };
unsigned int edge1_face_index = (face == edge1_faces[0] ? 1 : 0);
if (nullptr == edge1_faces[edge1_face_index] || face == edge1_faces[edge1_face_index])
return GetSectorBoundaryEdgesError();
face_ptr = edge1->m_face2[edge1_face_index];
edge0 = edge1;
edge0_face_index = edge1_face_index;
edge0_end_index = edge1_end_index;
continue;
}
sector_boundary[edge_face_index] = ON_SubDEdgePtr::Create(edge1, edge1_end_index);
break;
}
}
if (sector_face_count <= 0 || sector_boundary[0].IsNull())
return GetSectorBoundaryEdgesError();
if (1 == edge_face_count)
sector_boundary[1] = ON_SubDEdgePtr::Create(this, edge_vertex_index);
else if (sector_boundary[1].IsNull())
return GetSectorBoundaryEdgesError();
if (nullptr != edge_ptr0)
*edge_ptr0 = sector_boundary[0];
if (nullptr != edge_ptr1)
*edge_ptr1 = sector_boundary[1];
return sector_face_count;
}
class ON_ScratchBuffer
{
public:
ON_ScratchBuffer(
size_t sizeof_buffer,
void* stack_buffer,
size_t sizeof_stack_buffer
)
: m_buffer(nullptr)
, m_heap_buffer(nullptr)
{
m_buffer
= (sizeof_buffer > sizeof_stack_buffer || nullptr == stack_buffer)
? stack_buffer
: (m_heap_buffer = new (std::nothrow) double[1 + sizeof_buffer / sizeof(double)]);
}
void* Buffer()
{
return m_buffer;
}
~ON_ScratchBuffer()
{
if (nullptr != m_heap_buffer)
{
double* p = m_heap_buffer;
m_heap_buffer = nullptr;
delete[] p;
}
}
private:
void* m_buffer;
double* m_heap_buffer;
private:
// prohibit use - no implementation
ON_ScratchBuffer(const ON_ScratchBuffer&);
ON_ScratchBuffer& operator-(const ON_ScratchBuffer&);
};
class FACE_AND_FACE_POINT
{
public:
const ON_SubDFace* m_face;
double m_faceP[3];
static int CompareFacePointer(const void* a, const void* b);
};
int FACE_AND_FACE_POINT::CompareFacePointer(const void* a, const void* b)
{
ON__UINT_PTR fa = (ON__UINT_PTR)(((const FACE_AND_FACE_POINT*)a)->m_face);
ON__UINT_PTR fb = (ON__UINT_PTR)(((const FACE_AND_FACE_POINT*)b)->m_face);
if (fa < fb)
return -1;
if (fa > fb)
return 1;
return 0;
}
bool ON_SubDSectorSurfacePoint::IsUnset() const
{
return (m_limitP[0] == ON_UNSET_VALUE);
}
bool ON_SubDSectorSurfacePoint::IsNan() const
{
return !(m_limitP[0] == m_limitP[0]);
}
bool ON_SubDSectorSurfacePoint::IsZero() const
{
const double* p = m_limitP;
const double* p1 = p+12;
while (p < p1)
{
if (!(0.0 == *p++))
return false;
}
return true;
}
bool ON_SubDSectorSurfacePoint::IsSet(
bool bUndefinedNormalIsPossible
) const
{
double x, y;
const double* p = m_limitP;
const double* p1 = p+3;
while (p < p1)
{
x = *p++;
if (ON_UNSET_VALUE == x || !(x == x))
return false;
}
if (false == bUndefinedNormalIsPossible)
{
p = m_limitT1;
p1 = p + 6;
while (p < p1)
{
const double* p2 = p + 3;
y = 0.0;
while (p < p2)
{
x = *p++;
if (ON_UNSET_VALUE == x || !(x == x))
return false;
if (0.0 != x)
y = x;
}
if (!(y != 0.0))
return false;
}
p = m_limitN;
p1 = p + 3;
y = 0.0;
while (p < p1)
{
x = *p++;
if (ON_UNSET_VALUE == x || !(x == x))
return false;
y += x * x;
}
if (!(fabs(y - 1.0) <= 1e-4))
return false;
}
return true;
}
void ON_SubDVertex::CopyFrom(
const ON_SubDVertex* src,
bool bCopyEdgeArray,
bool bCopyFaceArray,
bool bCopyLimitPointList
)
{
if (nullptr == src)
src = &ON_SubDVertex::Empty;
ClearSavedSubdivisionPoints();
CopyBaseFrom(src);
m_vertex_tag = src->m_vertex_tag;
m_P[0] = src->m_P[0];
m_P[1] = src->m_P[1];
m_P[2] = src->m_P[2];
if (bCopyLimitPointList)
{
if ( src->SurfacePointIsSet() )
{
for (const ON_SubDSectorSurfacePoint* p = &src->m_limit_point; nullptr != p; p = p->m_next_sector_limit_point)
{
ON_SubDSectorSurfacePoint limit_point = *p;
limit_point.m_next_sector_limit_point = (ON_SubDSectorSurfacePoint*)1; // disable checks
SetSavedSurfacePoint( true, limit_point);
}
}
}
if (bCopyEdgeArray)
{
if (src->m_edge_count > 0 && nullptr != src->m_edges && nullptr != m_edges && src->m_edge_count <= m_edge_capacity)
{
m_edge_count = src->m_edge_count;
const unsigned int edge_count = src->m_edge_count;
for (unsigned int vei = 0; vei < edge_count; vei++)
m_edges[vei] = src->m_edges[vei];
}
else
m_edge_count = 0;
}
if (bCopyFaceArray)
{
if (src->m_face_count > 0 && nullptr != src->m_faces && nullptr != m_faces && src->m_face_count <= m_face_capacity)
{
m_face_count = src->m_face_count;
const unsigned int face_count = src->m_face_count;
for (unsigned int vfi = 0; vfi < face_count; vfi++)
m_faces[vfi] = src->m_faces[vfi];
}
else
m_face_count = 0;
}
}
static bool ON_SubDVertex_GetSubdivisionPointError(
const class ON_SubDVertex* vertex,
double vertex_point[3],
const double* vertexP,
bool bDamagedState
)
{
if (nullptr == vertex || nullptr == vertex_point)
return false; // caller passed a null pointer - vertex is not necessarily damaged
ON_SubDIncrementErrorCount();
vertex->m_status.SetDamagedState(bDamagedState);
vertex->ClearSavedSubdivisionPoints();
if (nullptr != vertexP)
{
vertex_point[0] = vertexP[0];
vertex_point[1] = vertexP[1];
vertex_point[2] = vertexP[2];
}
return true;
}
bool ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint(
const class ON_SubDVertex* vertex,
double vertex_point[3]
)
{
if (nullptr != vertex_point)
{
vertex_point[0] = ON_DBL_QNAN;
vertex_point[1] = ON_DBL_QNAN;
vertex_point[2] = ON_DBL_QNAN;
}
if (nullptr == vertex)
{
ON_SUBD_ERROR("input vertex is nullptr.");
return false;
}
const unsigned int n = vertex->m_face_count;
if (nullptr == vertex
|| nullptr == vertex->m_faces
|| nullptr == vertex->m_edges
|| vertex->m_face_count != vertex->m_edge_count
|| n < ON_SubDSectorType::MinimumSectorFaceCount(ON_SubD::VertexTag::Smooth)
)
{
ON_SUBD_ERROR("input vertex is not valid.");
return false;
}
const double* vertexP = vertex->m_P;
// It is critical to use the centroids of the neighboring faces
// in this step because the number of edges in each face's
// boundary may not be constant.
double facePsum[3] = { 0 };
const ON_SubDFace*const* vertex_faces = vertex->m_faces;
for (unsigned int i = 0; i < n; i++)
{
const ON_SubDFace* face = vertex_faces[i];
if (nullptr != face)
{
double faceC[3];
if (face->GetSubdivisionPoint( faceC))
{
facePsum[0] += faceC[0];
facePsum[1] += faceC[1];
facePsum[2] += faceC[2];
continue;
}
}
// treat missing or damaged face as infinitesimally small
facePsum[0] += vertexP[0];
facePsum[1] += vertexP[1];
facePsum[2] += vertexP[2];
}
double edgePsum[3] = { 0 };
class ON_SubDEdgePtr* edges = vertex->m_edges;
for (unsigned int i = 0; i < n; i++)
{
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(edges[i].m_ptr);
if (nullptr != edge)
{
const ON_SubDVertex* edge_vertex = edge->OtherEndVertex(vertex);
if (nullptr != edge_vertex)
{
const double* edgeP = edge_vertex->m_P;
edgePsum[0] += edgeP[0];
edgePsum[1] += edgeP[1];
edgePsum[2] += edgeP[2];
continue;
}
}
// treat missing or damaged edge as infinitesimally small
edgePsum[0] += vertexP[0];
edgePsum[1] += vertexP[1];
edgePsum[2] += vertexP[2];
}
const double v_weight = 1.0 - 2.0 / ((double)n);
const double ef_weight = 1.0 / ((double)(n*n));
vertex_point[0] = v_weight*vertexP[0] + ef_weight*(edgePsum[0] + facePsum[0]);
vertex_point[1] = v_weight*vertexP[1] + ef_weight*(edgePsum[1] + facePsum[1]);
vertex_point[2] = v_weight*vertexP[2] + ef_weight*(edgePsum[2] + facePsum[2]);
return true;
}
bool ON_SubDVertex::Internal_GetCatmullClarkSubdivisionPoint(
const class ON_SubDVertex* vertex,
double vertex_point[3]
)
{
// This function is used to convert an arbitrary control polygon into the
// "level 1" quad subD. It cannot use the faster sub-D formulas because
// a face can have an arbitrary number of edges.
if (nullptr == vertex || nullptr == vertex_point)
return ON_SubDVertex_GetSubdivisionPointError(vertex,vertex_point,nullptr,false);
const double* vertexP = vertex->m_P;
const unsigned int n = (nullptr != vertex->m_edges ? vertex->m_edge_count : 0);
if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag || ON_SubD::VertexTag::Dart == vertex->m_vertex_tag)
{
const unsigned int minimum_n = ON_SubDSectorType::MinimumSectorEdgeCount(vertex->m_vertex_tag);
if (n < minimum_n || n != vertex->m_face_count || nullptr == vertex->m_faces)
return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, vertexP, true);
double facePsum[3] = { 0 };
const ON_SubDFace*const* vertex_faces = vertex->m_faces;
const ON_SubDFace* face = vertex_faces[0];
if (nullptr == face)
return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, vertexP, true);
////// for debugging code below, uncomment this line
////// and look for differences in results.
////return GetGeneralQuadSubdivisionPoint(vertex, vertex_point);
const unsigned int k = (nullptr == face) ? 0U : face->m_edge_count;
if (4 == k)
{
// possibly (probably?) every face is a quad
double sum[3];
for (unsigned int i = 0; i < n; i++)
{
const ON_SubDFace* vface = vertex_faces[i];
const unsigned int face_n = ON_SubDVertex::Internal_GetFacePointSum(vface, vertex, sum);
if (4 != face_n)
{
// The first face is a quadrangle and this face is not a quadrangle.
//
// It is critical to use the centroids of the neighboring faces
// for this vertex subdivision point because the number of edges
// in each face's boundary is not constant.
return ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint(vertex, vertex_point);
}
facePsum[0] += sum[0];
facePsum[1] += sum[1];
facePsum[2] += sum[2];
}
}
else if (3 == k)
{
// possibly (probably?) every face is a triangle
for (unsigned int i = 0; i < n; i++)
{
const ON_SubDFace* vface = vertex_faces[i];
if (k != ((nullptr == vface) ? 0U : vface->m_edge_count))
{
// The first face is a triangle and this face is not a triangle.
//
// It is critical to use the centroids of the neighboring faces
// for this vertex subdivision point because the number of edges
// in each face's boundary is not constant.
return ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint(vertex, vertex_point);
}
}
}
else
{
// The first face has 5 or more edges.
// It is likely this is the initial subdivision being applied
// to the level zero SubD control polygon.
//
// It may be critical to use the centroids of the neighboring faces
// for this vertex subdivision point because the number of edges
// in each face's boundary may not constant. In any case, this
// situation is not common and typically happens only on the
// first subdivision step.
return ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint(vertex, vertex_point);
}
double edgePsum[3] = { 0 };
class ON_SubDEdgePtr* edges = vertex->m_edges;
for (unsigned int i = 0; i < n; i++)
{
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(edges[i].m_ptr);
if (nullptr != edge)
{
const ON_SubDVertex* edge_vertex = edge->OtherEndVertex(vertex);
if (nullptr != edge_vertex)
{
const double* edgeP = edge_vertex->m_P;
edgePsum[0] += edgeP[0];
edgePsum[1] += edgeP[1];
edgePsum[2] += edgeP[2];
continue;
}
}
// treat missing or damaged edge as infinitesimally small
edgePsum[0] += vertexP[0];
edgePsum[1] += vertexP[1];
edgePsum[2] += vertexP[2];
}
if (4 == k)
{
// all faces were quads
const double v_weight = 1.0 - 1.75 / ((double)n);
const double e_weight = 1.5 / ((double)(n*n));
const double f_weight = 0.25 / ((double)(n*n));
vertex_point[0] = v_weight*vertexP[0] + e_weight*edgePsum[0] + f_weight*facePsum[0];
vertex_point[1] = v_weight*vertexP[1] + e_weight*edgePsum[1] + f_weight*facePsum[1];
vertex_point[2] = v_weight*vertexP[2] + e_weight*edgePsum[2] + f_weight*facePsum[2];
}
else
{
// all faces were triangles
const double v_weight = 1.0 - 5.0 / ((double)(3 * n));
const double e_weight = 5.0 / ((double)(3*n*n));
vertex_point[0] = v_weight*vertexP[0] + e_weight*edgePsum[0];
vertex_point[1] = v_weight*vertexP[1] + e_weight*edgePsum[1];
vertex_point[2] = v_weight*vertexP[2] + e_weight*edgePsum[2];
}
return true;
}
// vertex->m_vertex_tag is damaged
return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, vertexP, true);
}
const ON_3dPoint ON_SubDVertex::SubdivisionPoint() const
{
ON_3dPoint S;
return (GetSubdivisionPoint(&S.x) && S.IsValid()) ? S : ON_3dPoint::NanPoint;
}
bool ON_SubDVertex::GetSubdivisionPoint(
double subdivision_point[3]
) const
{
if (nullptr == subdivision_point)
return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, nullptr, false);
if (GetSavedSubdivisionPoint(subdivision_point))
return true;
if (EvaluateCatmullClarkSubdivisionPoint(subdivision_point))
{
SetSavedSubdivisionPoint(subdivision_point);
return true;
}
return false;
}
bool ON_SubDVertex::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[3]) const
{
// This function is used to convert an arbitrary control polygon into the
// "level 1" subD. It cannot use the faster sub-D formulas because
// a face can have an arbitrary number of edges.
if (nullptr == subdivision_point )
return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, nullptr, false);
double displacementV[3] = { 0 };
const bool bApplyDisplacement = GetSubdivisionDisplacement(displacementV);
const double* vertexP = m_P;
const unsigned int n = (nullptr != m_edges ? m_edge_count : 0);
if (n < 2)
return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true);
if (ON_SubD::VertexTag::Smooth == m_vertex_tag || ON_SubD::VertexTag::Dart == m_vertex_tag)
{
return ON_SubDVertex::Internal_GetCatmullClarkSubdivisionPoint(this, subdivision_point);
}
if (ON_SubD::VertexTag::Crease == m_vertex_tag)
{
class ON_SubDEdgePtr* edges = m_edges;
const ON_SubDVertex* edge0_vertex = nullptr;
for (unsigned int i = 0; i < n; i++)
{
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(edges[i].m_ptr);
if (nullptr == edge)
{
ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true);
continue;
}
if (ON_SubD::EdgeTag::Crease != edge->m_edge_tag)
continue;
const ON_SubDVertex* edge_vertex = edge->OtherEndVertex(this);
if (nullptr == edge_vertex)
{
ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true);
continue;
}
if (nullptr == edge0_vertex)
{
edge0_vertex = edge_vertex;
continue;
}
if (edge0_vertex == edge_vertex)
{
ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true);
continue;
}
// We found the two crease edges that share this crease vertex.
// (The parenthesis around the edgeP sum is to insure this result
// is independent of the order of the edges.)
vertexP = m_P;
const double* edgeP[2] = { edge0_vertex->m_P, edge_vertex->m_P };
subdivision_point[0] = (vertexP[0] * 6.0 + (edgeP[0][0] + edgeP[1][0]))*0.125;
subdivision_point[1] = (vertexP[1] * 6.0 + (edgeP[0][1] + edgeP[1][1]))*0.125;
subdivision_point[2] = (vertexP[2] * 6.0 + (edgeP[0][2] + edgeP[1][2]))*0.125;
if (bApplyDisplacement)
{
subdivision_point[0] += displacementV[0];
subdivision_point[1] += displacementV[1];
subdivision_point[2] += displacementV[2];
}
return true;
}
return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true);
}
if (ON_SubD::VertexTag::Corner == m_vertex_tag)
{
vertexP = m_P;
subdivision_point[0] = vertexP[0];
subdivision_point[1] = vertexP[1];
subdivision_point[2] = vertexP[2];
if (bApplyDisplacement)
{
subdivision_point[0] += displacementV[0];
subdivision_point[1] += displacementV[1];
subdivision_point[2] += displacementV[2];
}
return true;
}
// vertex is damaged
return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true);
}
unsigned int ON_SubDVertex::Internal_GetFacePointSum(
const ON_SubDFace* face,
const ON_SubDVertex* vertex,
double* facePsum
)
{
const ON_SubDEdge* e;
ON__UINT_PTR e_ptr, edir;
const double* faceP;
if (nullptr == face)
return 0;
const unsigned int n = face->m_edge_count;
facePsum[0] = 0.0;
facePsum[1] = 0.0;
facePsum[2] = 0.0;
if (3 == n)
{
return n;
}
if (4 == n)
{
for (unsigned int i = 0; i < 4; i++)
{
e_ptr = face->m_edge4[i].m_ptr;
e = ON_SUBD_EDGE_POINTER(e_ptr);
if (nullptr != e && (vertex == e->m_vertex[0] || vertex == e->m_vertex[1]))
{
edir = ON_SUBD_EDGE_DIRECTION(e_ptr);
e_ptr = face->m_edge4[(i + ((vertex == e->m_vertex[edir]) ? 2 : 3)) % 4].m_ptr;
e = ON_SUBD_EDGE_POINTER(e_ptr);
edir = ON_SUBD_EDGE_DIRECTION(e_ptr);
if (nullptr == e || nullptr == e->m_vertex[edir])
return 0;
faceP = e->m_vertex[edir]->m_P;
facePsum[0] = faceP[0];
facePsum[1] = faceP[1];
facePsum[2] = faceP[2];
return n;
}
}
return 0;
}
if (n <= 4 || nullptr == face->m_edgex)
return 0;
e_ptr = face->m_edgex[n-5].m_ptr;
e = ON_SUBD_EDGE_POINTER(e_ptr);
if (nullptr == e)
return 0;
edir = ON_SUBD_EDGE_DIRECTION(e_ptr);
unsigned int skipped_edge_count = (vertex == e->m_vertex[edir]) ? 1 : 0;
unsigned int facePcount = 0;
const ON_SubDEdgePtr* edge_ptrs = face->m_edge4;
for (unsigned int i = skipped_edge_count; i < n; i++)
{
if (4 == i)
edge_ptrs = face->m_edgex - 4;
e_ptr = edge_ptrs[i].m_ptr;
e = ON_SUBD_EDGE_POINTER(e_ptr);
if (nullptr == e)
return 0;
edir = ON_SUBD_EDGE_DIRECTION(e_ptr);
if (vertex == e->m_vertex[0] || vertex == e->m_vertex[1])
{
skipped_edge_count++;
if (skipped_edge_count > 2)
{
facePsum[0] = 0.0;
facePsum[1] = 0.0;
facePsum[2] = 0.0;
return 0;
}
if (vertex == e->m_vertex[edir])
{
i++;
if (4 == i)
edge_ptrs = face->m_edgex - 4;
}
continue;
}
faceP = e->m_vertex[edir]->m_P;
facePsum[0] += faceP[0];
facePsum[1] += faceP[1];
facePsum[2] += faceP[2];
facePcount++;
}
if (n == facePcount + 3)
return n;
facePsum[0] = 0.0;
facePsum[1] = 0.0;
facePsum[2] = 0.0;
return 0;
}
bool ON_SubDimple::LocalSubdivide(
ON_SubDFace const*const* face_list,
size_t face_list_count
)
{
if (nullptr == face_list || face_list_count < 1 || m_levels.UnsignedCount() < 1)
return false;
unsigned int level0_index = ON_UNSET_UINT_INDEX;
for (size_t i = 0; i < face_list_count; ++i)
{
const ON_SubDFace* f = face_list[i];
if (nullptr == f || f->m_edge_count < 3 || f->SubdivisionLevel() >= m_levels.UnsignedCount())
continue;
level0_index = f->SubdivisionLevel();
break;
}
if (level0_index >= m_levels.UnsignedCount() || nullptr == m_levels[level0_index])
return ON_SUBD_RETURN_ERROR(false);
ClearHigherSubdivisionLevels(level0_index + 1);
if (level0_index + 1 != m_levels.UnsignedCount())
return ON_SUBD_RETURN_ERROR(false);
m_active_level = m_levels[level0_index];
if ( nullptr == m_active_level || 0 == m_active_level->m_face_count)
return ON_SUBD_RETURN_ERROR(false);
ON_SubDLevel& level0 = *m_levels[level0_index];
m_active_level = &level0;
level0.ClearRuntimeMarks(true, true, true);
unsigned face_count = 0;
unsigned edge_count = 0;
unsigned vertex_count = 0;
ON_3dPoint P;
for (size_t i = 0; i < face_list_count; ++i)
{
const ON_SubDFace* f = face_list[i];
if (nullptr == f || f->m_edge_count < 3 || level0_index != f->SubdivisionLevel() )
continue;
if (f->m_status.RuntimeMark())
continue;
f->m_status.SetRuntimeMark();
if ( false == f->GetSubdivisionPoint(P))
return ON_SUBD_RETURN_ERROR(false);
const ON_SubDEdgePtr* eptr = f->m_edge4;
for (unsigned short fei = 0; fei < f->m_edge_count; ++fei, ++eptr)
{
if (4 == fei)
{
eptr = f->m_edgex;
if (nullptr == eptr)
return ON_SUBD_RETURN_ERROR(false);
}
ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr->m_ptr);
if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1])
return ON_SUBD_RETURN_ERROR(false);
if (e->m_status.RuntimeMark())
continue;
e->UpdateEdgeSectorCoefficientsForExperts(true);
e->m_status.SetRuntimeMark();
if (false == e->GetSubdivisionPoint(P))
return ON_SUBD_RETURN_ERROR(false);
for (unsigned evi = 0; evi < 2; ++evi)
{
if (e->m_vertex[evi]->m_status.RuntimeMark())
continue;
e->m_vertex[evi]->m_status.SetRuntimeMark();
if ( false == e->m_vertex[evi]->GetSubdivisionPoint(P) )
return ON_SUBD_RETURN_ERROR(false);
++vertex_count;
}
++edge_count;
}
++face_count;
}
if (face_count < 1 || edge_count < 3 || vertex_count < 3)
return false;
if (face_count >= level0.m_face_count)
return GlobalSubdivide();
// Get face subdivision points
ON_SimpleArray<ON_SubDFace*> faces(face_count);
ON_SimpleArray<ON_3dPoint> face_points(face_count);
for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face)
{
if (false == f0->m_status.RuntimeMark())
continue;
faces.Append(const_cast<ON_SubDFace*>(f0));
P = f0->SubdivisionPoint();
if ( false == P.IsValid())
return ON_SUBD_RETURN_ERROR(false);
face_points.Append(P);
}
if (face_count != face_points.UnsignedCount())
return ON_SUBD_RETURN_ERROR(false);
// Get edge subdivision points
ON_SimpleArray<ON_3dPoint> edge_points(edge_count);
ON_SimpleArray<ON_SubDEdge*> edges(edge_count);
for (const ON_SubDEdge* e0 = level0.m_edge[0]; nullptr != e0; e0 = e0->m_next_edge)
{
if (false == e0->m_status.RuntimeMark())
continue;
P = (e0->IsSmooth() && 2 == e0->m_face_count && 2 == e0->MarkedFaceCount()) ? e0->SubdivisionPoint() : e0->ControlNetCenterPoint();
edge_points.Append(P);
edges.Append(const_cast<ON_SubDEdge*>(e0));
}
if ( edge_count != edge_points.UnsignedCount())
return ON_SUBD_RETURN_ERROR(false);
// Set vertex points
for (const ON_SubDVertex* v0 = level0.m_vertex[0]; nullptr != v0; v0 = v0->m_next_vertex)
{
if (false == v0->m_status.RuntimeMark() || v0->m_edge_count < 2 || ((unsigned)v0->m_edge_count) != v0->MarkedEdgeCount())
continue;
P = v0->SubdivisionPoint();
ON_SubDVertex* v = const_cast<ON_SubDVertex*>(v0);
v->m_P[0] = P.x;
v->m_P[1] = P.y;
v->m_P[2] = P.z;
}
// split edges
for (unsigned edex = 0; edex < edge_count; ++edex)
{
ON_SubDEdge* e = edges[edex];
e->EdgeModifiedNofification();
const ON_SubDEdge* new_edge = SplitEdge(e, edge_points[edex]);
if (nullptr == new_edge)
return ON_SUBD_RETURN_ERROR(false);
new_edge->m_status.ClearRuntimeMark();
e->m_status.SetRuntimeMark();
}
ON_SimpleArray<ON_SubDEdgePtr> fbdry(32);
ON_SimpleArray<ON_SubDEdge*> radial_edges(32);
for (unsigned fdex = 0; fdex < face_count; ++fdex)
{
ON_SubDFace* f = faces[fdex];
f->FaceModifiedNofification();
P = face_points[fdex];
fbdry.SetCount(0);
const unsigned e_count = f->GetEdgeArray(fbdry);
if (e_count < 6 || 0 != e_count %2)
return ON_SUBD_RETURN_ERROR(false);
// Get edges[] with edge[0] = marked edges.
const ON_SubDEdgePtr* eptrs = fbdry.Array();
if (nullptr != eptrs[0].RelativeVertex(0) && false == eptrs[0].RelativeVertex(0)->Mark() )
{
fbdry.Append(eptrs[0]);
eptrs = fbdry.Array() + 1;
}
// save face status and candidate_face
const ON_ComponentStatus fstatus = f->m_status;
const ON_SubDFace* candidate_face = f;
for (unsigned fei = 0; fei < e_count; ++fei)
{
ON_SubDEdge* e = eptrs[fei].Edge();
if ( nullptr == e)
return ON_SUBD_RETURN_ERROR(false);
const ON_SubDVertex* ev[2] = { eptrs[fei].RelativeVertex(0), eptrs[fei].RelativeVertex(1) };
if ( nullptr == ev[0] || nullptr == ev[1] || ev[0] == ev[1])
return ON_SUBD_RETURN_ERROR(false);
if (0 == fei % 2)
{
if (false == ev[0]->Mark())
return ON_SUBD_RETURN_ERROR(false);
if (ev[1]->Mark())
return ON_SUBD_RETURN_ERROR(false);
}
else
{
if (ev[0]->Mark())
return ON_SUBD_RETURN_ERROR(false);
if (false == ev[1]->Mark())
return ON_SUBD_RETURN_ERROR(false);
}
}
// remove face that will be subdivided;
for (unsigned short fei = 0; fei < f->m_edge_count; ++fei)
{
eptrs[fei].Edge()->RemoveFaceFromArray(f);
const_cast<ON_SubDVertex*>(eptrs[fei].RelativeVertex(0))->RemoveFaceFromArray(f);
}
ReturnFace(f);
ON_SubDVertex* center = AllocateVertex(ON_SubD::VertexTag::Smooth, level0_index, &P.x);
AddVertexToLevel(center);
radial_edges.SetCount(0);
radial_edges.Reserve(e_count /2);
for (unsigned fei = 0; fei < e_count; fei += 2)
{
ON_SubDEdge* r = AddEdge(ON_SubD::EdgeTag::Smooth, center, ON_SubDSectorType::UnsetSectorWeight, const_cast<ON_SubDVertex*>(eptrs[fei].RelativeVertex(1)), ON_SubDSectorType::UnsetSectorWeight);
radial_edges.Append(r);
}
ON_SubDEdge* r[2] = { nullptr,radial_edges[e_count / 2 - 1] };
for (unsigned fei = 0; fei < e_count; fei += 2)
{
r[0] = r[1];
r[1] = radial_edges[fei / 2];
const ON_SubDEdgePtr qbdry[4] = {
ON_SubDEdgePtr::Create(r[0],0),
eptrs[(fei + e_count - 1) % e_count],
eptrs[fei],
ON_SubDEdgePtr::Create(r[1],1)
};
ON_SubDFace* q = AddFace(candidate_face, 4, qbdry);
candidate_face = nullptr;
q->m_status = fstatus;
}
}
level0.ClearRuntimeMarks(true, true, false);
level0.ClearBoundingBox();
level0.ClearEvaluationCache();
level0.ClearEdgeFlags();
return true;
}
unsigned int ON_SubDimple::GlobalSubdivide()
{
if (m_levels.UnsignedCount() <= 0)
return ON_SUBD_RETURN_ERROR(0U);
const unsigned int level0_index = m_levels.UnsignedCount()-1;
if ( nullptr == m_levels[level0_index])
return ON_SUBD_RETURN_ERROR(0U);
ON_SubDLevel& level0 = *m_levels[level0_index];
if (level0.IsEmpty())
return ON_SUBD_RETURN_ERROR(0U);
if ( level0.m_edge_count <= 0U )
return ON_SUBD_RETURN_ERROR(0U);
level0.UpdateEdgeSectorCoefficients(true);
const unsigned int level1_index = level0_index+1;
ON_SubDLevel* level1 = SubDLevel(level1_index,true);
if ( nullptr == level1 )
return ON_SUBD_RETURN_ERROR(0);
double P[3];
ON_SubDVertex* v;
// Add face points
for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face)
{
if (false == f0->GetSubdivisionPoint(P))
continue;
if (nullptr == f0->m_subd_point1)
{
const_cast<ON_SubDFace*>(f0)->m_subd_point1 = v = AllocateVertex(ON_SubD::VertexTag::Smooth, level1_index, P);
AddVertexToLevel(v);
}
else
{
v = const_cast<ON_SubDVertex*>(f0->m_subd_point1);
v->m_P[0] = P[0];
v->m_P[1] = P[1];
v->m_P[2] = P[2];
}
}
// Add edge points
for (const ON_SubDEdge* e0 = level0.m_edge[0]; nullptr != e0; e0 = e0->m_next_edge)
{
if (false == e0->GetSubdivisionPoint(P))
continue;
// (the subdivision point of an edge tagged as ON_SubD::EdgeTag::SmoothX is a smooth vertex.)
const ON_SubD::VertexTag vertex_tag
= ON_SubD::EdgeTag::Crease == e0->m_edge_tag
? ON_SubD::VertexTag::Crease
: ON_SubD::VertexTag::Smooth;
if (nullptr == e0->m_subd_point1)
{
const_cast<ON_SubDEdge*>(e0)->m_subd_point1 = v = AllocateVertex(vertex_tag, level1_index, P );
AddVertexToLevel(v);
}
else
{
v = const_cast<ON_SubDVertex*>(e0->m_subd_point1);
v->m_vertex_tag = vertex_tag;
v->m_P[0] = P[0];
v->m_P[1] = P[1];
v->m_P[2] = P[2];
}
}
// Add vertex points
for (const ON_SubDVertex* v0 = level0.m_vertex[0]; nullptr != v0; v0 = v0->m_next_vertex)
{
if (false == v0->GetSubdivisionPoint(P))
continue;
if (nullptr == v0->m_subd_point1)
{
const_cast<ON_SubDVertex*>(v0)->m_subd_point1 = v = AllocateVertex(v0->m_vertex_tag, level1_index, P);
AddVertexToLevel(v);
}
else
{
v = const_cast<ON_SubDVertex*>(v0->m_subd_point1);
v->m_vertex_tag = v0->m_vertex_tag;
v->m_P[0] = P[0];
v->m_P[1] = P[1];
v->m_P[2] = P[2];
}
}
// subdivide edges
for (const ON_SubDEdge* e0 = level0.m_edge[0]; nullptr != e0; e0 = e0->m_next_edge)
{
if (nullptr == e0->m_subd_point1)
continue;
ON_SubDVertex* end_vertex[2] = { const_cast<ON_SubDVertex*>(e0->m_vertex[0]->m_subd_point1), const_cast<ON_SubDVertex*>(e0->m_vertex[1]->m_subd_point1) };
ON_SubDVertex* mid_vertex = const_cast<ON_SubDVertex*>(e0->m_subd_point1);
double w[2] = { e0->m_sector_coefficient[0], e0->m_sector_coefficient[1] };
ON_SubD::EdgeTag edge_tag = e0->m_edge_tag;
if (ON_SubD::EdgeTag::SmoothX == edge_tag && 2 == e0->m_face_count)
{
if ( nullptr != mid_vertex && ON_SubD::VertexTag::Smooth == mid_vertex->m_vertex_tag )
edge_tag = ON_SubD::EdgeTag::Smooth;
}
AddEdge(edge_tag, end_vertex[0], w[0], mid_vertex, 0.0);
AddEdge(edge_tag, mid_vertex, 0.0, end_vertex[1], w[1]);
}
for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face)
{
Internal_GlobalQuadSubdivideFace(f0);
}
return level1_index;
}
unsigned int ON_SubDimple::Internal_GlobalQuadSubdivideFace(
const ON_SubDFace* f0
)
{
// This is a private member function.
// The caller insures f0 != nullptr.
const unsigned int f0_edge_count = f0->m_edge_count;
if (f0_edge_count < 3)
return 0;
const unsigned int parent_face_id = f0->m_id;
const unsigned int zero_face_id = (0 == f0->SubdivisionLevel()) ? parent_face_id : f0->m_zero_face_id;
if (nullptr == f0->m_subd_point1)
{
// add face centroid
double faceC[3];
if (false == f0->GetSubdivisionPoint( faceC))
return 0;
f0->SetSavedSubdivisionPoint(faceC);
unsigned int level1_index = f0->SubdivisionLevel() + 1;
ON_SubDVertex* v = AllocateVertex(ON_SubD::VertexTag::Smooth, level1_index, faceC );
AddVertexToLevel(v);
const_cast<ON_SubDFace*>(f0)->m_subd_point1 = v;
}
ON_SubDEdge* E0[2];
ON__UINT_PTR E0dir[2];
ON_SubDEdge* E1[4];
ON__UINT_PTR E1dir[4];
ON_SubDEdgePtr f1edges[4];
ON__UINT_PTR e_ptr;
ON_SubDEdge* FirstE1(nullptr);
double w;
e_ptr = f0->EdgePtr(f0_edge_count - 1).m_ptr;
E0[1] = ON_SUBD_EDGE_POINTER(e_ptr);
E0dir[1] = ON_SUBD_EDGE_DIRECTION(e_ptr);
E1[2] = nullptr;
unsigned int f1_count = 0;
const double w_2facesector = ON_SubDSectorType::CreaseSectorWeight(2);
for (unsigned int i = 0; i < f0_edge_count; i++)
{
E0[0] = E0[1];
E0dir[0] = E0dir[1];
e_ptr = f0->EdgePtr(i).m_ptr;
E0[1] = ON_SUBD_EDGE_POINTER(e_ptr);
E0dir[1] = ON_SUBD_EDGE_DIRECTION(e_ptr);
if (nullptr == E0[0] || nullptr == E0[1])
continue;
if (nullptr == E0[0]->m_subd_point1 || nullptr == E0[1]->m_subd_point1)
continue;
e_ptr = E0[0]->m_subd_point1->m_edges[0 == E0dir[0] ? 1 : 0].m_ptr;
E1[0] = ON_SUBD_EDGE_POINTER(e_ptr);
E1dir[0] = E0dir[0];
e_ptr = E0[1]->m_subd_point1->m_edges[0 == E0dir[1] ? 0 : 1].m_ptr;
E1[1] = ON_SUBD_EDGE_POINTER(e_ptr);
E1dir[1] = E0dir[1];
E1[3] = E1[2];
if (nullptr == E1[3])
{
// The value of E0[0]->m_subd_point1->m_vertex_tag should be either
// ON_SubD::VertexTag::Smooth or ON_SubD::VertexTag::Crease. In the
// case when it's value is "crease", the resulting edge end weight
// will be 0.5 because the edge has two adjacent faces and "theta"
// will be pi/2.
// The resulting quad edge weight is 0.5 = 1/2 + 1/3*cos(pi/2).
w = (ON_SubD::VertexTag::Crease == E0[0]->m_subd_point1->m_vertex_tag) ? w_2facesector : 0.0;
E1[3] = AddEdge(ON_SubD::EdgeTag::Smooth, const_cast<ON_SubDVertex*>(f0->m_subd_point1), 0.0, const_cast<ON_SubDVertex*>(E0[0]->m_subd_point1), w);
if (nullptr == FirstE1)
FirstE1 = E1[3];
}
E1dir[3] = 0;
if (i + 1 < f0_edge_count || nullptr == FirstE1)
{
// The value of E0[0]->m_subd_point1->m_vertex_tag should be either
// ON_SubD::VertexTag::Smooth or ON_SubD::VertexTag::Crease. In the
// case when it's value is "crease", the resulting edge end weight
// will be zero because the edge has two adjacent faces and "theta"
// will be pi/2. The resulting edge weight is 0.5.
w = (ON_SubD::VertexTag::Crease == E0[1]->m_subd_point1->m_vertex_tag) ? w_2facesector : 0.0;
E1[2] = AddEdge(ON_SubD::EdgeTag::Smooth, const_cast<ON_SubDVertex*>(f0->m_subd_point1), 0.0, const_cast<ON_SubDVertex*>(E0[1]->m_subd_point1), w);
}
else
{
E1[2] = FirstE1;
}
E1dir[2] = 1;
f1edges[0] = ON_SubDEdgePtr::Create(E1[0], E1dir[0]);
f1edges[1] = ON_SubDEdgePtr::Create(E1[1], E1dir[1]);
f1edges[2] = ON_SubDEdgePtr::Create(E1[2], E1dir[2]);
f1edges[3] = ON_SubDEdgePtr::Create(E1[3], E1dir[3]);
ON_SubDFace* f1 = AddFace(4, f1edges);
if (nullptr != f1)
{
f1->m_zero_face_id = zero_face_id;
f1->m_parent_face_id = parent_face_id;
f1_count++;
}
}
// return number of new faces
return f1_count;
}
bool ON_SubD::GlobalSubdivide()
{
return GlobalSubdivide(1U);
}
bool ON_SubD::GlobalSubdivide(
unsigned int count
)
{
ON_SubDimple* subdimple = SubDimple(false);
if (nullptr == subdimple)
return ON_SUBD_RETURN_ERROR(false);
return subdimple->GlobalSubdivide(count);
}
bool ON_SubD::LocalSubdivide(
const ON_SimpleArray<ON_COMPONENT_INDEX>& face_list
)
{
const int count = face_list.Count();
ON_SimpleArray< const ON_SubDFace* > ptr_list(count);
for (int i = 0; i < count; ++i)
{
const ON_COMPONENT_INDEX ci = face_list[i];
if (ON_COMPONENT_INDEX::TYPE::subd_face != ci.m_type)
continue;
if (ci.m_index <= 0)
continue;
const ON_SubDFace* f = this->FaceFromId(ci.m_index);
if (nullptr == f)
continue;
ptr_list.Append(f);
}
const bool rc = LocalSubdivide(ptr_list);
UpdateAllTagsAndSectorCoefficients(true);
return rc;
}
bool ON_SubD::LocalSubdivide(const ON_SimpleArray< const ON_SubDFace* >& face_list)
{
return LocalSubdivide(face_list.Array(), face_list.UnsignedCount());
}
bool ON_SubD::LocalSubdivide( ON_SubDFace const*const* face_list, size_t face_count)
{
ON_SubDimple* subdimple = SubDimple(false);
if (nullptr == subdimple)
return ON_SUBD_RETURN_ERROR(false);
return subdimple->LocalSubdivide(face_list, face_count);
}
bool ON_SubDimple::GlobalSubdivide(
unsigned int count
)
{
if (m_levels.UnsignedCount() < 1)
return ON_SUBD_RETURN_ERROR(false);
if (nullptr == m_active_level)
{
m_active_level = m_levels[m_levels.UnsignedCount() - 1];
if (nullptr == m_active_level)
return ON_SUBD_RETURN_ERROR(false);
}
const unsigned level0_index = m_active_level->m_level_index;
if (level0_index >= m_levels.UnsignedCount() || nullptr == m_levels[level0_index])
return ON_SUBD_RETURN_ERROR(false);
if (count <= 0)
return ON_SUBD_RETURN_ERROR(false);
if (level0_index + count > ON_SubD::maximum_subd_level)
return ON_SUBD_RETURN_ERROR(false);
ClearHigherSubdivisionLevels(level0_index + 1);
if (level0_index + 1 != m_levels.UnsignedCount() )
return ON_SUBD_RETURN_ERROR(false);
m_active_level = m_levels[level0_index];
for (unsigned int i = level0_index +1; i <= level0_index +count; i++)
{
unsigned int rc = GlobalSubdivide();
if (i != rc)
return ON_SUBD_RETURN_ERROR(false);
m_active_level = m_levels[i];
}
return true;
}
ON_SubDEdgePtr ON_SubDimple::MergeEdges(
ON_SubDEdgePtr eptr0,
ON_SubDEdgePtr eptr1
)
{
if ( false == ON_SubD::EdgesCanBeMerged(eptr0,eptr1) )
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
ON_SubDEdge* e[2] = { ON_SUBD_EDGE_POINTER(eptr0.m_ptr), ON_SUBD_EDGE_POINTER(eptr1.m_ptr) };
ON__UINT_PTR edir[2] = { ON_SUBD_EDGE_DIRECTION(eptr0.m_ptr), ON_SUBD_EDGE_DIRECTION(eptr1.m_ptr) };
const ON_SubDVertex* end_v[2] = {e[0]->m_vertex[edir[0]], e[1]->m_vertex[1 - edir[1]]};
if (nullptr == end_v[0] || nullptr == end_v[1] || end_v[0] == end_v[1] )
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
ON_SubD::EdgeTag merged_edge_tag
= (e[0]->IsSmooth() || e[1]->IsSmooth())
? ON_SubD::EdgeTag::Smooth
: ON_SubD::EdgeTag::Crease;
for (unsigned int j = 0; j < e[1]->m_face_count; j++)
{
ON_SubDFace* f = const_cast<ON_SubDFace*>(e[1]->Face(j));
if (nullptr == f)
continue;
f->RemoveEdgeFromArray(e[1]);
}
// remove middle vertex
ON_SubDVertex* middle_v = const_cast< ON_SubDVertex* >(e[1]->m_vertex[edir[1]]);
if (nullptr != middle_v && middle_v != end_v[0] && middle_v != end_v[1] )
{
if (middle_v->m_edge_count > 0 && nullptr != middle_v->m_edges)
{
unsigned int vei0 = middle_v->EdgeArrayIndex(e[0]);
unsigned int vei1 = middle_v->EdgeArrayIndex(e[1]);
unsigned int vei_count = middle_v->m_edge_count;
middle_v->m_edge_count = 0;
for (unsigned int vei = 0; vei < vei_count; vei++)
{
if ( vei == vei0 || vei == vei1 )
continue;
// happens when middle_v is a multiple sector corner, non-manifold, or other rare cases
if (vei > middle_v->m_edge_count)
middle_v->m_edges[middle_v->m_edge_count] = middle_v->m_edges[vei];
middle_v->m_edge_count++;
}
}
if (0 == middle_v->m_edge_count || nullptr == middle_v->m_edges)
{
ReturnVertex(middle_v);
middle_v = nullptr;
}
}
e[0]->m_vertex[1-edir[0]] = nullptr;
e[1]->m_vertex[edir[1]] = nullptr;
e[1]->m_vertex[1-edir[1]] = nullptr;
for (unsigned int i = 0; i < end_v[1]->m_edge_count; i++)
{
if (e[1] == ON_SUBD_EDGE_POINTER(end_v[1]->m_edges[i].m_ptr))
{
const_cast< ON_SubDVertex* >(end_v[1])->m_edges[i] = ON_SubDEdgePtr::Create(e[0], 1 - edir[0]);
e[0]->m_vertex[1 - edir[0]] = end_v[1];
break;
}
}
e[0]->m_sector_coefficient[1 - edir[0]] = e[1]->m_sector_coefficient[1 - edir[1]];
const bool bTagged[2] = { end_v[0]->IsCreaseOrCorner(), end_v[1]->IsCreaseOrCorner() };
if (ON_SubD::EdgeTag::Smooth == merged_edge_tag || false == bTagged[0] || false == bTagged[1])
{
e[0]->m_edge_tag
= (bTagged[0] && bTagged[1])
? ON_SubD::EdgeTag::SmoothX
: ON_SubD::EdgeTag::Smooth;
if ( false == bTagged[0])
e[0]->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight;
else if (!(e[0]->m_sector_coefficient[0] > 0.0 && e[0]->m_sector_coefficient[0] < 1.0))
e[0]->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight;
if ( false == bTagged[1])
e[0]->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight;
else if (!(e[0]->m_sector_coefficient[1] > 0.0 && e[0]->m_sector_coefficient[1] < 1.0))
e[0]->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight;
}
else
{
e[0]->m_edge_tag = ON_SubD::EdgeTag::Crease;
e[0]->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight;
e[0]->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight;
}
ReturnEdge(e[1]);
return eptr0;
}
ON_SubDEdgePtr ON_SubD::MergeEdges(
ON_SubDEdgePtr eptr0,
ON_SubDEdgePtr eptr1
)
{
ON_SubDimple* subdimple = SubDimple(false);
return (nullptr != subdimple) ? subdimple->MergeEdges(eptr0, eptr1) : ON_SubDEdgePtr::Null;
}
static bool EdgesAreMergableTest(
const ON_SubDEdgePtr eptr[2],
bool bTestColinearity,
double distance_tolerance,
double maximum_aspect,
double sin_angle_tolerance
)
{
const ON_SubDEdge* e[2] = { ON_SUBD_EDGE_POINTER(eptr[0].m_ptr), ON_SUBD_EDGE_POINTER(eptr[1].m_ptr) };
ON__UINT_PTR edir[2] = { ON_SUBD_EDGE_DIRECTION(eptr[0].m_ptr), ON_SUBD_EDGE_DIRECTION(eptr[1].m_ptr) };
if (
nullptr == e[0] || nullptr == e[1]
|| e[0] == e[1]
|| edir[0] > 1 || edir[1] > 1
|| e[0]->m_face_count != e[1]->m_face_count
//|| e[0]->m_edge_tag != e[1]->m_edge_tag
)
{
return false;
}
if ( nullptr == e[1]->m_vertex[0] || nullptr == e[1]->m_vertex[1] )
{
return false;
}
// v[0] = start
// v[1] = end
// v[2] = middle (will be removed)
const ON_SubDVertex* v[4] = { e[0]->m_vertex[edir[0]], e[1]->m_vertex[1 - edir[1]], e[0]->m_vertex[1 - edir[0]], e[1]->m_vertex[edir[1]] };
if (nullptr == v[0] || nullptr == v[1] || nullptr == v[2] || v[0] == v[1] || v[2] != v[3])
{
return false;
}
if (bTestColinearity)
{
if (ON_UNSET_UINT_INDEX == v[2]->EdgeArrayIndex(e[0]) || ON_UNSET_UINT_INDEX == v[2]->EdgeArrayIndex(e[1]))
bTestColinearity = false;
}
// edges must have the same faces
switch (e[0]->m_face_count)
{
case 0:
break;
case 1:
if (ON_SUBD_FACE_POINTER(e[0]->m_face2[0].m_ptr) == ON_SUBD_FACE_POINTER(e[1]->m_face2[0].m_ptr))
break;
return false;
case 2:
if (ON_SUBD_FACE_POINTER(e[0]->m_face2[0].m_ptr) == ON_SUBD_FACE_POINTER(e[1]->m_face2[0].m_ptr)
&& ON_SUBD_FACE_POINTER(e[0]->m_face2[1].m_ptr) == ON_SUBD_FACE_POINTER(e[1]->m_face2[1].m_ptr))
break;
if (ON_SUBD_FACE_POINTER(e[0]->m_face2[0].m_ptr) == ON_SUBD_FACE_POINTER(e[1]->m_face2[1].m_ptr)
&& ON_SUBD_FACE_POINTER(e[0]->m_face2[1].m_ptr) == ON_SUBD_FACE_POINTER(e[1]->m_face2[0].m_ptr))
break;
return false;
default:
// non-manifold edge
{
unsigned int j, k;
for (j = 0; j < e[0]->m_face_count; j++)
{
const ON_SubDFace* f = e[0]->Face(j);
for (k = 0; k < e[1]->m_face_count; k++)
{
if (f == e[1]->Face(k))
break;
}
if (k < e[1]->m_face_count)
continue;
break;
}
if (j != e[0]->m_face_count)
return false;
}
break;
}
if (bTestColinearity)
{
const ON_3dPoint* P[3] = { (const ON_3dPoint*)v[0]->m_P, (const ON_3dPoint*)v[2]->m_P, (const ON_3dPoint*)v[1]->m_P };
ON_3dVector D(*P[2] - *P[0]);
const double d = D.Length();
if (!(d > 0.0))
return false;
const ON_3dVector V(*P[1] - *P[0]);
const double t = (V*D) / (d*d);
if (!(t > ON_EPSILON && t < 1.0 - ON_EPSILON))
return false;
const ON_3dPoint M = (1.0 - t)*(*P[0]) + t*(*P[2]);
const double h = P[1]->DistanceTo(M);
if (0.0 == h)
return true;
if (!(h > 0.0))
return false;
const double miniscule_distance_tolerance = ON_ZERO_TOLERANCE;
if (h <= miniscule_distance_tolerance && !(distance_tolerance >= 0.0 && distance_tolerance < miniscule_distance_tolerance))
{
// skip parameter tests for miniscule h.
return true;
}
const double miniscule_maximum_aspect = 1e-4;
if (h <= miniscule_maximum_aspect * d && !(maximum_aspect >= 0.0 && maximum_aspect < miniscule_maximum_aspect))
{
// skip parameter tests for miniscule h/d.
return true;
}
if (distance_tolerance >= 0.0 && !(h <= distance_tolerance))
return false; // failed distance to chord test
if (maximum_aspect >= 0.0 && !(h <= maximum_aspect * d))
return false; // failed maximum aspect test
if (sin_angle_tolerance >= 0.0 && sin_angle_tolerance < 1.0 && !(ON_CrossProduct(V, (*P[1] - *P[2])).Length() <= sin_angle_tolerance))
return false; // failed minimum angle test
}
return true;
}
bool ON_SubD::EdgesCanBeMerged(
ON_SubDEdgePtr eptr0,
ON_SubDEdgePtr eptr1
)
{
ON_SubDEdgePtr eptr[2] = { eptr0,eptr1 };
return EdgesAreMergableTest(eptr,false,ON_DBL_QNAN,ON_DBL_QNAN,ON_DBL_QNAN);
}
static bool Internal_EdgesPassTypeFilter(
const ON_SubDEdgePtr eptr[2],
bool bMergeBoundaryEdges,
bool bMergeInteriorCreaseEdges,
bool bMergeInteriorSmoothEdges
)
{
for (unsigned i = 0; i < 2; ++i)
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr[i].m_ptr);
if (nullptr == e || e->m_face_count < 1)
return false;
if (1 == e->m_face_count)
{
if (false == bMergeBoundaryEdges)
return false;
}
else if (e->m_face_count >= 2)
{
if (false == (e->IsSmooth() ? bMergeInteriorSmoothEdges : bMergeInteriorCreaseEdges))
return false;
}
else
return false;
};
return true;
}
unsigned int ON_SubDimple::MergeColinearEdges(
bool bMergeBoundaryEdges,
bool bMergeInteriorCreaseEdges,
bool bMergeInteriorSmoothEdges,
double distance_tolerance,
double maximum_aspect,
double sin_angle_tolerance
)
{
if (1 != m_levels.UnsignedCount())
return false;
unsigned int removed_edge_count = 0;
for (const ON_SubDFace* f = ActiveLevel().m_face[0]; nullptr != f; f = f->m_next_face)
{
unsigned int edge_count = f->m_edge_count;
if (edge_count < 3)
continue;
// First - find a pair of edges that should not be merged.
ON_SubDEdgePtr eptr[2] = { ON_SubDEdgePtr::Null, f->EdgePtr(edge_count - 1) };
unsigned int fei0 = 0;
while (fei0 < edge_count)
{
eptr[0] = eptr[1];
eptr[1] = f->EdgePtr(fei0);
if (false == EdgesAreMergableTest(eptr, true, distance_tolerance, maximum_aspect, sin_angle_tolerance))
break;
if (false == Internal_EdgesPassTypeFilter(eptr, bMergeBoundaryEdges, bMergeInteriorCreaseEdges, bMergeInteriorSmoothEdges))
break;
++fei0;
}
if (fei0 >= edge_count)
{
// face is degenerate or corrupt.
continue;
}
if (0 != fei0)
{
if (false == (const_cast<ON_SubDFace*>(f)->RotateEdgeArray(fei0)))
{
// face is degenerate or corrupt.
continue;
}
}
// At this point, we know the last edge and the first edge should not me merged.
eptr[1] = f->EdgePtr(0U);
unsigned int fei = 1;
while ( fei < edge_count)
{
eptr[0] = eptr[1];
eptr[1] = f->EdgePtr(fei);
if (
EdgesAreMergableTest(eptr, true, distance_tolerance, maximum_aspect, sin_angle_tolerance)
&& Internal_EdgesPassTypeFilter(eptr, bMergeBoundaryEdges, bMergeInteriorCreaseEdges, bMergeInteriorSmoothEdges)
)
{
// merge edges f->Edge(fei-1) and f->Edge(fei) into f->Edge(fei-1).
if (eptr[0].m_ptr != MergeEdges(eptr[0], eptr[1]).m_ptr)
{
ON_SUBD_ERROR("Bug in edge merging.");
break;
}
++removed_edge_count;
--edge_count;
eptr[1] = eptr[0];
}
else
{
++fei;
}
}
}
return removed_edge_count;
}
unsigned int ON_SubD::MergeColinearEdges(
bool bMergeBoundaryEdges,
bool bMergeInteriorCreaseEdges,
bool bMergeInteriorSmoothEdges,
double distance_tolerance,
double maximum_aspect,
double sin_angle_tolerance
)
{
ON_SubDimple* subdimple = SubDimple(false);
return
(nullptr != subdimple)
? subdimple->MergeColinearEdges( bMergeBoundaryEdges, bMergeInteriorCreaseEdges, bMergeInteriorSmoothEdges, distance_tolerance, maximum_aspect, sin_angle_tolerance)
: 0;
}
unsigned int ON_SubD::LevelCount() const
{
const ON_SubDimple* subdimple = SubDimple();
return (0 != subdimple ? subdimple->LevelCount() : 0);
}
unsigned int ON_SubD::ActiveLevelIndex() const
{
return ActiveLevel().m_level_index;
}
bool ON_SubD::IsEmpty() const
{
return (nullptr == SubDimple());
}
bool ON_SubD::IsNotEmpty() const
{
return (nullptr != SubDimple());
}
/////////////////////////////////////////////////////////
//
// Element (Vertex, Edge, Face) access
//
const ON_COMPONENT_INDEX ON_SubDComponentPtr::ComponentIndex() const
{
switch (ComponentType())
{
case ON_SubDComponentPtr::Type::Vertex:
{
const ON_SubDVertex* vertex = ON_SUBD_VERTEX_POINTER(m_ptr);
if ( nullptr != vertex)
return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_vertex,vertex->m_id);
}
break;
case ON_SubDComponentPtr::Type::Edge:
{
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr);
if ( nullptr != edge)
return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_edge,edge->m_id);
}
break;
case ON_SubDComponentPtr::Type::Face:
{
const ON_SubDFace* face = ON_SUBD_FACE_POINTER(m_ptr);
if ( nullptr != face)
return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_face,face->m_id);
}
break;
default:
if (IsNull() )
return ON_COMPONENT_INDEX::UnsetComponentIndex;
break;
}
return ON_SUBD_RETURN_ERROR(ON_COMPONENT_INDEX::UnsetComponentIndex);
}
const ON_SubDComponentPtr ON_SubD::ComponentPtrFromComponentIndex(
ON_COMPONENT_INDEX component_index
) const
{
if (0 != component_index.m_index && -1 != component_index.m_index)
{
switch (component_index.m_type)
{
case ON_COMPONENT_INDEX::TYPE::subd_vertex:
return ON_SubDComponentPtr::Create(VertexFromId(component_index.m_index));
case ON_COMPONENT_INDEX::TYPE::subd_edge:
return ON_SubDComponentPtr::Create(EdgeFromId(component_index.m_index));
case ON_COMPONENT_INDEX::TYPE::subd_face:
return ON_SubDComponentPtr::Create(FaceFromId(component_index.m_index));
default:
break;
}
}
else if (ON_COMPONENT_INDEX::TYPE::invalid_type == component_index.m_type)
{
return ON_SubDComponentPtr::Null;
}
return ON_SUBD_RETURN_ERROR(ON_SubDComponentPtr::Null);
}
/////////////////////////////////////////////////////////
//
// Vertex access
//
unsigned int ON_SubD::VertexCount() const
{
return ActiveLevel().m_vertex_count;
}
const ON_SubDVertex* ON_SubD::FirstVertex() const
{
return ActiveLevel().m_vertex[0];
}
const ON_SubDVertex* ON_SubD::LastVertex() const
{
return ActiveLevel().m_vertex[1];
}
ON_SubDVertexIterator ON_SubD::VertexIterator() const
{
return ON_SubDVertexIterator(*this);
}
ON_SubDVertexIterator ON_SubDRef::VertexIterator() const
{
return ON_SubDVertexIterator(*this);
}
ON_SubDVertexArray ON_SubD::VertexArray() const
{
return ON_SubDVertexArray(*this);
}
const class ON_SubDVertex* ON_SubD::VertexFromId(
unsigned int vertex_id
) const
{
for (;;)
{
if (0 == vertex_id || ON_UNSET_UINT_INDEX == vertex_id )
break;
const ON_SubDimple* subdimple = SubDimple();
if (nullptr == subdimple)
break;
return subdimple->VertexFromId(vertex_id);
}
return nullptr;
}
const ON_COMPONENT_INDEX ON_SubDVertex::ComponentIndex() const
{
return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_vertex,m_id);
}
const ON_SubDComponentPtr ON_SubDVertex::ComponentPtr() const
{
return ON_SubDComponentPtr::Create(this);
}
/////////////////////////////////////////////////////////
//
// Edge access
//
unsigned int ON_SubD::EdgeCount() const
{
return ActiveLevel().m_edge_count;
}
const ON_SubDEdge* ON_SubD::FirstEdge() const
{
return ActiveLevel().m_edge[0];
}
const ON_SubDEdge* ON_SubD::LastEdge() const
{
return ActiveLevel().m_edge[1];
}
ON_SubDEdgeIterator ON_SubD::EdgeIterator() const
{
return ON_SubDEdgeIterator(*this);
}
ON_SubDEdgeIterator ON_SubDRef::EdgeIterator() const
{
return ON_SubDEdgeIterator(*this);
}
ON_SubDEdgeArray ON_SubD::EdgeArray() const
{
return ON_SubDEdgeArray(*this);
}
const class ON_SubDEdge* ON_SubD::EdgeFromId(
unsigned int edge_id
) const
{
for (;;)
{
if (0 == edge_id || ON_UNSET_UINT_INDEX == edge_id )
break;
const ON_SubDimple* subdimple = SubDimple();
if (nullptr == subdimple)
break;
return subdimple->EdgeFromId(edge_id);
}
return nullptr;
}
const ON_COMPONENT_INDEX ON_SubDEdge::ComponentIndex() const
{
return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_edge,m_id);
}
const ON_SubDComponentPtr ON_SubDEdge::ComponentPtr() const
{
return ON_SubDComponentPtr::Create(this);
}
/////////////////////////////////////////////////////////
//
// Face access
//
unsigned int ON_SubD::FaceCount() const
{
return ActiveLevel().m_face_count;
}
const ON_SubDFace* ON_SubD::FirstFace() const
{
return ActiveLevel().m_face[0];
}
const ON_SubDFace* ON_SubD::LastFace() const
{
return ActiveLevel().m_face[1];
}
ON_SubDFaceIterator ON_SubD::FaceIterator() const
{
return ON_SubDFaceIterator(*this);
}
ON_SubDFaceIterator ON_SubDRef::FaceIterator() const
{
return ON_SubDFaceIterator(*this);
}
ON_SubDFaceArray ON_SubD::FaceArray() const
{
return ON_SubDFaceArray(*this);
}
const class ON_SubDFace* ON_SubD::FaceFromId(
unsigned int face_id
) const
{
for (;;)
{
if (0 == face_id || ON_UNSET_UINT_INDEX == face_id )
break;
const ON_SubDimple* subdimple = SubDimple();
if (nullptr == subdimple)
break;
return subdimple->FaceFromId(face_id);
}
return nullptr;
}
const ON_COMPONENT_INDEX ON_SubDFace::ComponentIndex() const
{
return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_face,m_id);
}
const ON_SubDComponentPtr ON_SubDFace::ComponentPtr() const
{
return ON_SubDComponentPtr::Create(this);
}
/////////////////////////////////////////////////////////
//
// ON_SubD properties
//
bool ON_SubD::IsOriented() const
{
for (const ON_SubDEdge* edge = FirstEdge(); nullptr != edge; edge = edge->m_next_edge)
{
if ( 2 != edge->m_face_count )
continue;
if (nullptr == ON_SUBD_FACE_POINTER(edge->m_face2[0].m_ptr) || nullptr == ON_SUBD_FACE_POINTER(edge->m_face2[1].m_ptr) )
continue;
if ( ON_SUBD_FACE_DIRECTION(edge->m_face2[0].m_ptr) == ON_SUBD_FACE_DIRECTION(edge->m_face2[1].m_ptr) )
return false;
}
return true;
}
// reverses the orientation of all facets
bool ON_SubD::ReverseOrientation() const
{
// Limit point normals and limit surface mesh fragments will need to be recalculated.
// DestroyRuntimeCache() will clear all this information.
const_cast<ON_SubD*>(this)->DestroyRuntimeCache(true);
for (const ON_SubDFace* face = FirstFace(); nullptr != face; face = face->m_next_face)
{
const_cast<ON_SubDFace*>(face)->ReverseEdgeList();
}
return true;
}
// Attempts to orient all facet to match the first facet.
static unsigned int OrientFaceNeighbors(
unsigned int recursion_level,
const ON_SubDFace** face_list,
unsigned int id0,
const ON_SubDFace* face
)
{
ON_SubDFace* next_set[4];
const unsigned int next_set_capacity = (unsigned int)(sizeof(next_set) / sizeof(next_set[0]));
unsigned int next_set_count = 0;
const unsigned int edge_count = face->m_edge_count;
const ON_SubDEdgePtr* face_eptr = face->m_edge4;
const ON_SubDEdge* e;
ON_SubDFace* neighbor_face;
if (nullptr != face_list[face->m_id - id0])
{
// search for an oriented neighbor
neighbor_face = nullptr;
for (unsigned int fei = 0; fei < edge_count; fei++, face_eptr++)
{
if (4 == fei)
{
face_eptr = face->m_edgex;
if (nullptr == face_eptr)
break;
}
e = ON_SUBD_EDGE_POINTER(face_eptr->m_ptr);
if (nullptr == e || 2 != e->m_face_count)
continue;
neighbor_face = ON_SUBD_FACE_POINTER(e->m_face2[0].m_ptr);
if (face == neighbor_face)
neighbor_face = ON_SUBD_FACE_POINTER(e->m_face2[1].m_ptr);
else if (face != ON_SUBD_FACE_POINTER(e->m_face2[1].m_ptr))
continue;
if (nullptr == neighbor_face)
continue;
if (nullptr == face_list[neighbor_face->m_id - id0])
return OrientFaceNeighbors(recursion_level,face_list,id0,neighbor_face);
}
// nothing near face is oriented.
return 0;
}
unsigned int orient_count = 0;
for (unsigned int fei = 0; fei < edge_count; fei++, face_eptr++)
{
if (4 == fei)
{
face_eptr = face->m_edgex;
if ( nullptr == face_eptr)
break;
}
e = ON_SUBD_EDGE_POINTER(face_eptr->m_ptr);
if (nullptr == e || 2 != e->m_face_count)
continue;
neighbor_face = ON_SUBD_FACE_POINTER(e->m_face2[0].m_ptr);
if (face == neighbor_face)
neighbor_face = ON_SUBD_FACE_POINTER(e->m_face2[1].m_ptr);
else if (face != ON_SUBD_FACE_POINTER(e->m_face2[1].m_ptr))
continue;
if (nullptr == neighbor_face)
continue;
if (nullptr == face_list[neighbor_face->m_id - id0])
continue;
if (ON_SUBD_FACE_DIRECTION(e->m_face2[0].m_ptr) == ON_SUBD_FACE_DIRECTION(e->m_face2[1].m_ptr))
neighbor_face->ReverseEdgeList();
face_list[neighbor_face->m_id - id0] = nullptr;
orient_count++;
if (recursion_level < 12)
{
if (next_set_count >= next_set_capacity)
{
for (unsigned i = 0; i < next_set_capacity; i++)
orient_count += OrientFaceNeighbors(recursion_level + 1, face_list, id0, next_set[i]);
next_set_count = 0;
}
next_set[next_set_count++] = neighbor_face;
}
}
for ( unsigned i = 0; i < next_set_count; i++)
orient_count += OrientFaceNeighbors(recursion_level+1,face_list,id0,next_set[i]);
return orient_count;
}
bool ON_SubD::Orient() const
{
const ON_SubDFace* first_face = FirstFace();
if ( nullptr == first_face || nullptr == first_face->m_next_face)
return true;
unsigned int nonzero_face_count = 0;
ON_SimpleArray< const ON_SubDFace* > faces_array(FaceCount());
unsigned int face_id0 = first_face->m_id;
unsigned int face_id1 = first_face->m_id;
for (const ON_SubDFace* face = FirstFace(); nullptr != face; face = face->m_next_face)
{
faces_array.Append(face);
if (face->m_id > face_id1)
face_id1 = face->m_id;
else if (face->m_id < face_id1)
face_id0 = face->m_id;
nonzero_face_count++;
}
const unsigned int face_count = faces_array.UnsignedCount();
if (face_count <= 1)
return true;
const ON_SubDFace** faces = faces_array.Array();
if (face_id1 - face_id0 > faces_array.UnsignedCount())
{
faces_array.Reserve(face_id1 - face_id0);
faces_array.SetCount(face_id1 - face_id0);
faces_array.Zero();
for (const ON_SubDFace* face = FirstFace(); nullptr != face; face = face->m_next_face)
{
faces[face->m_id-face_id0] = face;
}
}
unsigned int orient_count = 0;
unsigned int connected_region_count = 0;
bool bSearchForNewComponent = true;
unsigned int first_face_index = 0;
for (;;)
{
unsigned int orient_count0 = orient_count;
while (first_face_index < face_count && nullptr == faces[first_face_index])
first_face_index++;
if ( first_face_index >= face_count)
break;
for (unsigned int i = first_face_index; i < face_count && orient_count < nonzero_face_count; i++)
{
const ON_SubDFace* face = faces[i];
if (nullptr == face)
continue;
if (bSearchForNewComponent)
{
// first face in new connected component
orient_count++;
connected_region_count++;
faces[i] = nullptr;
bSearchForNewComponent = false;
first_face_index = i+1;
}
orient_count += OrientFaceNeighbors(0, faces, face_id0, face);
}
if ( orient_count >= nonzero_face_count)
break;
if (orient_count0 >= orient_count)
{
if (bSearchForNewComponent)
break;
bSearchForNewComponent = true;
}
}
return (connected_region_count > 0 && orient_count > 0);
}
const ON_SubDVertex * ON_SubD::ReplaceFaceWithTriangleFan(ON_SubDFace * face, ON_3dPoint fan_center_point, bool bMarkFaces)
{
const unsigned edge_count = face->m_edge_count;
if (edge_count < 3)
return ON_SUBD_RETURN_ERROR(nullptr);
ON_SubDimple* subdimple = SubDimple(false);
if (nullptr == subdimple)
return ON_SUBD_RETURN_ERROR(nullptr);
// validate and get centroid (which may not be needed).
ON_3dPoint P = ON_3dPoint::Origin;
ON_SimpleArray<ON_SubDEdgePtr> edges(edge_count);
const ON_SubDEdgePtr* eptr = face->m_edge4;
for (unsigned i = 0; i < edge_count; ++i, ++eptr)
{
if (4U == i)
{
eptr = face->m_edgex;
if (nullptr == eptr)
return ON_SUBD_RETURN_ERROR(nullptr);
}
const ON_SubDVertex* v = eptr->RelativeVertex(0);
if (nullptr == v)
return ON_SUBD_RETURN_ERROR(nullptr);
P += v->ControlNetPoint();
edges.Append(*eptr);
}
if (fan_center_point.IsValid())
P = fan_center_point;
else
P /= ((double)edge_count);
ON_SubDVertex* v0 = AddVertex(ON_SubD::VertexTag::Smooth, P);
if (nullptr == v0)
return ON_SUBD_RETURN_ERROR(nullptr);
for (unsigned i = 0; i < edge_count; ++i, ++eptr)
{
if (nullptr == AddEdge(ON_SubD::EdgeTag::Smooth, v0, const_cast<ON_SubDVertex*>(edges[i].RelativeVertex(0))))
{
ON_SubDComponentPtr cptr = ON_SubDComponentPtr::Create(v0);
DeleteComponents(&cptr, 1, false);
return ON_SUBD_RETURN_ERROR(nullptr);
}
}
for (unsigned i = 0; i < edge_count; ++i, ++eptr)
{
if (i < 4)
face->m_edge4[i] = ON_SubDEdgePtr::Null;
else
face->m_edgex[i - 4] = ON_SubDEdgePtr::Null;;
ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(edges[i].m_ptr);
e->RemoveFaceFromArray(face);
const_cast<ON_SubDVertex*>(edges[i].RelativeVertex(0))->RemoveFaceFromArray(face);
}
subdimple->ReturnFace(face);
for (unsigned i = 0; i < edge_count; ++i, ++eptr)
{
AddTriangleFace(v0->m_edges[i], edges[i], v0->m_edges[(i + 1) % edge_count].Reversed());
}
for (unsigned i = 0; i < edge_count; ++i, ++eptr)
{
ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(edges[i].m_ptr);
e->EdgeModifiedNofification();
e->UpdateEdgeSectorCoefficientsForExperts(false);
}
for (unsigned i = 0; i < edge_count; ++i, ++eptr)
ON_SUBD_EDGE_POINTER(edges[i].m_ptr)->UpdateEdgeSectorCoefficientsForExperts(false);
for (unsigned i = 0; i < edge_count; ++i, ++eptr)
ON_SUBD_EDGE_POINTER(v0->m_edges[i].m_ptr)->UpdateEdgeSectorCoefficientsForExperts(false);
return v0;
}
const ON_SubDEdge* ON_SubDimple::SplitEdge(
ON_SubDEdge* edge,
ON_3dPoint vertex_location
)
{
if ( nullptr == edge )
return ON_SUBD_RETURN_ERROR(nullptr);
if (nullptr == edge->m_vertex[0] || nullptr == edge->m_vertex[1] )
return ON_SUBD_RETURN_ERROR(nullptr);
if (nullptr == edge->m_vertex[0]->m_edges || edge->m_vertex[0]->m_edge_count <= 0 || edge->m_vertex[0]->m_edge_capacity < edge->m_vertex[0]->m_edge_count )
return ON_SUBD_RETURN_ERROR(nullptr);
if (nullptr == edge->m_vertex[1]->m_edges || edge->m_vertex[1]->m_edge_count <= 0 || edge->m_vertex[1]->m_edge_capacity < edge->m_vertex[1]->m_edge_count )
return ON_SUBD_RETURN_ERROR(nullptr);
if ( vertex_location.IsUnsetOrNan() )
{
ON_Line L;
L.from = ON_3dPoint(edge->m_vertex[0]->m_P);
L.to = ON_3dPoint(edge->m_vertex[1]->m_P);
vertex_location = L.PointAt(0.5);
}
if ( false == vertex_location.IsValid() )
return ON_SUBD_RETURN_ERROR(nullptr);
if ( vertex_location == ON_3dPoint(edge->m_vertex[0]->m_P) || vertex_location == ON_3dPoint(edge->m_vertex[1]->m_P) )
return ON_SUBD_RETURN_ERROR(nullptr);
ON_SubD::EdgeTag edge_tag = edge->m_edge_tag;
ON_SubD::VertexTag vertex_tag;
switch (edge->m_edge_tag)
{
case ON_SubD::EdgeTag::Smooth:
vertex_tag = ON_SubD::VertexTag::Smooth;
break;
case ON_SubD::EdgeTag::Crease:
vertex_tag = ON_SubD::VertexTag::Crease;
break;
case ON_SubD::EdgeTag::SmoothX:
vertex_tag = ON_SubD::VertexTag::Smooth;
edge_tag = ON_SubD::EdgeTag::Smooth;
break;
default:
return ON_SUBD_RETURN_ERROR(nullptr);
break;
}
ON_SubDVertex* end_vertex[2] = { const_cast<ON_SubDVertex*>(edge->m_vertex[0]), const_cast<ON_SubDVertex*>(edge->m_vertex[1]) };
ON_SubDVertex* new_vertex = nullptr;
ON_SubDEdge* new_edge = nullptr;
for (;;)
{
new_vertex = AllocateVertex(vertex_tag, edge->SubdivisionLevel(), static_cast<const double*>(vertex_location), 2, edge->m_face_count);
if (nullptr == new_vertex)
break;
new_edge = AllocateEdge(edge_tag, edge->SubdivisionLevel(), edge->m_face_count);
if (nullptr == new_edge)
break;
// change end_vertex[1] edge reference from edge to new_edge
bool bConnectedNewEdgeToEndVertex = false;
const ON_SubDEdgePtr old_edge_reference = ON_SubDEdgePtr::Create(edge,1);
for (unsigned short vei = 0; vei < end_vertex[1]->m_edge_count; vei++)
{
if (old_edge_reference.m_ptr == end_vertex[1]->m_edges[vei].m_ptr)
{
// change end_vertex[1]->m_edges[vei] reference
// from "edge" to "new_edge".
bConnectedNewEdgeToEndVertex = true;
end_vertex[1]->m_edges[vei] = ON_SubDEdgePtr::Create(new_edge,1);
break;
}
}
if (false == bConnectedNewEdgeToEndVertex)
{
// end_vertex[1]->m_edges[] does not reference edge
break;
}
// finish setting new_vertex and end_vertex[] <-> new_edge connections
new_edge->m_vertex[0] = new_vertex;
new_edge->m_vertex[1] = end_vertex[1];
new_vertex->m_edges[new_vertex->m_edge_count++] = ON_SubDEdgePtr::Create(new_edge,0);
// finish setting new_vertex <-> input edge and connections
edge->m_edge_tag = edge_tag; // changes "X" to "Smooth" if required
edge->m_vertex[1] = new_vertex;
new_vertex->m_edges[new_vertex->m_edge_count++] = ON_SubDEdgePtr::Create(edge,1);
// add new_vertex and new_edge <-> edge->m_face[] connections.
const ON_SubDFacePtr* fptr0 = edge->m_face2;
ON_SubDFacePtr* fptr1 = new_edge->m_face2;
for (unsigned short efi = 0; efi < edge->m_face_count; efi++)
{
if (2 == efi)
{
fptr0 = edge->m_facex;
fptr1 = new_edge->m_facex;
}
ON_SubDFace* face = ON_SUBD_FACE_POINTER(fptr0->m_ptr);
if (nullptr != face)
{
face->FaceModifiedNofification();
// add new_vertex reference to face
new_vertex->m_faces[new_vertex->m_face_count++] = face;
// insert new_edge into face->m_edge[] list after "edge"
if (GrowFaceEdgeArray(face, face->m_edge_count + 1))
{
if ( face->m_edge_count < 4 )
face->m_edge4[face->m_edge_count] = ON_SubDEdgePtr::Null;
else
face->m_edgex[face->m_edge_count-4] = ON_SubDEdgePtr::Null;
face->m_edge_count++;
ON_SubDEdgePtr* face_edge = face->m_edge4;
for (unsigned short fei = 0; fei < face->m_edge_count; fei++)
{
if (4 == fei)
face_edge = face->m_edgex;
if (edge == ON_SUBD_EDGE_POINTER(face_edge->m_ptr))
{
ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(face_edge->m_ptr);
ON_SubDEdgePtr eptr = ON_SubDEdgePtr::Create(new_edge,edir);
if (0 == edir)
{
fei++;
face_edge++;
}
for (/*empty init*/; fei < face->m_edge_count; fei++)
{
if (4 == fei)
face_edge = face->m_edgex;
const ON_SubDEdgePtr tmp = *face_edge;
*face_edge = eptr;
eptr = tmp;
face_edge++;
}
break;
}
face_edge++;
}
}
}
*fptr1++ = *fptr0++;
new_edge->m_face_count++;
}
// original ending vertex
new_edge->m_sector_coefficient[1] = edge->m_sector_coefficient[1];
// Either edge was a crease, new_edge is a crease, and sector weights do not applie
// or edge was X or Smooth, edge is smooth, new_edge is smooth, new_vertex is smooth,
// and the sector weights at this vertex do not apply.
edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight;
new_edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight;
AddVertexToLevel(new_vertex);
AddEdgeToLevel(new_edge);
end_vertex[0]->VertexModifiedNofification();
end_vertex[1]->VertexModifiedNofification();
// TODO
// Delete any levels greater than this level.
return new_edge;
}
if ( nullptr != new_vertex)
ReturnVertex(new_vertex);
if ( nullptr != new_edge)
ReturnEdge(new_edge);
return ON_SUBD_RETURN_ERROR(nullptr);
}
const ON_SubDEdge* ON_SubDimple::SplitFace(
ON_SubDFace* face,
unsigned int fvi0,
unsigned int fvi1
)
{
if ( nullptr == face || fvi0 == fvi1)
return ON_SUBD_RETURN_ERROR(nullptr);
const unsigned int edge_count = face->m_edge_count;
if (edge_count < 4 || (edge_count > 4 && nullptr == face->m_edgex))
return ON_SUBD_RETURN_ERROR(nullptr);
if (fvi0 >= edge_count || fvi1 >= edge_count)
return ON_SUBD_RETURN_ERROR(nullptr);
if ((fvi0 + 1) % edge_count == fvi1 || (fvi1 + 1) % edge_count == fvi0)
return ON_SUBD_RETURN_ERROR(nullptr);
if (face->SubdivisionLevel() >= m_levels.UnsignedCount())
return ON_SUBD_RETURN_ERROR(nullptr);
ON_SubDLevel* level = m_levels[face->SubdivisionLevel()];
if ( nullptr == level)
return ON_SUBD_RETURN_ERROR(nullptr);
ON_SubDVertex* v[2] = { const_cast<ON_SubDVertex*>(face->Vertex(fvi0)), const_cast<ON_SubDVertex*>(face->Vertex(fvi1)) };
if (nullptr == v[0] || v[0]->m_face_count <= 0 || nullptr == v[0]->m_faces || v[0]->m_edge_count < 2 || nullptr == v[0]->m_edges )
return ON_SUBD_RETURN_ERROR(nullptr);
if (nullptr == v[1] || v[1]->m_face_count <= 0 || nullptr == v[1]->m_faces || v[1]->m_edge_count < 2 || nullptr == v[1]->m_edges)
return ON_SUBD_RETURN_ERROR(nullptr);
if (v[0] == v[1])
return ON_SUBD_RETURN_ERROR(nullptr);
unsigned new_edge_count[2];
if (fvi0 < fvi1)
{
new_edge_count[1] = fvi1 - fvi0 + 1;
new_edge_count[0] = (edge_count + 2) - new_edge_count[1];
}
else
{
new_edge_count[0] = fvi0 - fvi1 + 1;
new_edge_count[1] = (edge_count + 2) - new_edge_count[0];
}
// make sure each side is at least a triangle and no overflows occured
if (new_edge_count[0] < 3 || new_edge_count[0] >= edge_count)
return ON_SUBD_RETURN_ERROR(nullptr);
if (new_edge_count[1] < 3 || new_edge_count[1] >= edge_count)
return ON_SUBD_RETURN_ERROR(nullptr);
if (new_edge_count[0] + new_edge_count[1] != edge_count+2 )
return ON_SUBD_RETURN_ERROR(nullptr);
// make sure face topology is valid
ON_SimpleArray< ON_SubDEdgePtr > edges(edge_count);
ON_SubDEdgePtr* eptr = face->m_edge4;
edges.SetCount(edge_count);
for (unsigned int fei = 0; fei < edge_count; ++fei, ++eptr)
{
if (4 == fei)
eptr = face->m_edgex;
const ON_SubDEdge* face_e = ON_SUBD_EDGE_POINTER(eptr->m_ptr);
if (nullptr == face_e)
return ON_SUBD_RETURN_ERROR(nullptr);
if (ON_UNSET_UINT_INDEX == face_e->FaceArrayIndex(face) )
return ON_SUBD_RETURN_ERROR(nullptr);
const ON_SubDVertex* face_v = face_e->m_vertex[ON_SUBD_EDGE_DIRECTION(eptr->m_ptr)];
if ( nullptr == face_v)
return ON_SUBD_RETURN_ERROR(nullptr);
if (ON_UNSET_UINT_INDEX == face_v->FaceArrayIndex(face) )
return ON_SUBD_RETURN_ERROR(nullptr);
edges[fei] = *eptr;
}
// create diagonal edge
ON_SubDEdge* new_e = nullptr;
ON_SubDFace* new_f = nullptr;
for (;;)
{
if (false == m_heap.GrowVertexFaceArrayByOne(v[0]))
break;
if (false == m_heap.GrowVertexFaceArrayByOne(v[1]))
break;
new_f = AllocateFace();
if (nullptr == new_f)
break;
new_f->SetSubdivisionLevel( face->SubdivisionLevel() );
AddFaceToLevel(new_f);
if (new_edge_count[1] > 4)
{
if (false == m_heap.GrowFaceEdgeArray(new_f, new_edge_count[1]))
break;
}
new_e = AddEdge(
((v[0]->IsSmooth() || v[1]->IsSmooth()) ? ON_SubD::EdgeTag::Smooth : ON_SubD::EdgeTag::SmoothX),
v[0], ON_SubDSectorType::UnsetSectorWeight,
v[1], ON_SubDSectorType::UnsetSectorWeight);
if (nullptr == new_e)
break;
face->FaceModifiedNofification();
v[0]->m_faces[v[0]->m_face_count++] = new_f;
v[1]->m_faces[v[1]->m_face_count++] = new_f;
new_e->m_face2[0] = ON_SubDFacePtr::Create(face, 0);
new_e->m_face2[1] = ON_SubDFacePtr::Create(new_f, 1);
new_e->m_face_count = 2;
const ON_SubDEdgePtr new_eptr = ON_SubDEdgePtr::Create(new_e);
eptr = face->m_edge4;
for (unsigned int fei = 0; fei < edge_count; ++fei, ++eptr)
{
if (4 == fei)
eptr = face->m_edgex;
*eptr = ON_SubDEdgePtr::Null;
}
face->m_edge_count = 0;
if (new_edge_count[0] <= 4 && nullptr != face->m_edgex)
m_heap.ReturnFaceExtraArray(face);
// update old face
face->m_edge4[0] = new_eptr;
eptr = &(face->m_edge4[1]);
for (unsigned fei = 1; fei < new_edge_count[0]; ++fei, ++eptr)
{
if (4 == fei)
eptr = face->m_edgex;
*eptr = edges[(fvi1 + fei - 1) % edge_count];
}
face->m_edge_count = (unsigned short)new_edge_count[0];
// initialize new_f
new_f->m_edge4[0] = new_eptr.Reversed();
eptr = &(new_f->m_edge4[1]);
for (unsigned fei = 1; fei < new_edge_count[1]; ++fei, ++eptr)
{
if (4 == fei)
eptr = new_f->m_edgex;
*eptr = edges[(fvi0 + fei - 1) % edge_count];
// change edge's face reference from old face to new_f.
ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr->m_ptr);
e->ReplaceFaceInArray(face, new_f);
ON_SubDVertex* vtx = const_cast<ON_SubDVertex*>(eptr->RelativeVertex(0));
if (nullptr != vtx && v[0] != vtx && v[1] != vtx)
vtx->ReplaceFaceInArray(face, new_f);
}
new_f->m_edge_count = (unsigned short)new_edge_count[1];
// update sector weights because they depend on the number of edges
for (unsigned int vi = 0; vi < 2; vi++)
{
for (unsigned short evi = 0; evi < v[vi]->m_edge_count; ++evi)
{
ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(v[vi]->m_edges[evi].m_ptr);
if (nullptr != edge)
edge->UpdateEdgeSectorCoefficientsForExperts(true);
}
}
// Debugging code
const ON_SubDVertex* v0[6] = {
face->m_edge4[0].RelativeVertex(0),face->m_edge4[0].RelativeVertex(1),
face->m_edge4[1].RelativeVertex(0),face->m_edge4[1].RelativeVertex(1),
face->EdgePtr(face->m_edge_count-1).RelativeVertex(0),face->EdgePtr(face->m_edge_count - 1).RelativeVertex(1),
};
const ON_SubDEdge* e0[3] = {
face->m_edge4[0].Edge(),
face->m_edge4[1].Edge(),
face->EdgePtr(face->m_edge_count - 1).Edge()
};
const ON_SubDVertex* v1[6] = {
new_f->m_edge4[0].RelativeVertex(0),new_f->m_edge4[0].RelativeVertex(1),
new_f->m_edge4[1].RelativeVertex(0),new_f->m_edge4[1].RelativeVertex(1),
new_f->EdgePtr(new_f->m_edge_count - 1).RelativeVertex(0),new_f->EdgePtr(new_f->m_edge_count - 1).RelativeVertex(1),
};
const ON_SubDEdge* e1[3] = {
new_f->m_edge4[0].Edge(),
new_f->m_edge4[1].Edge(),
new_f->EdgePtr(new_f->m_edge_count - 1).Edge()
};
if (nullptr == v0[5] && nullptr == v1[5] && nullptr == e0[2] && nullptr == e1[2])
return nullptr;
return new_e;
}
if ( nullptr != new_f )
ReturnFace(new_f);
if (nullptr != new_e)
{
v[0]->m_edge_count--;
v[0]->m_edges[v[0]->m_edge_count] = ON_SubDEdgePtr::Null;
v[1]->m_edge_count--;
v[1]->m_edges[v[1]->m_edge_count] = ON_SubDEdgePtr::Null;
new_e->m_vertex[0] = nullptr;
new_e->m_vertex[1] = nullptr;
new_e->m_face_count = 0;
ReturnEdge(new_e);
}
return ON_SUBD_RETURN_ERROR(nullptr);
}
const ON_SubDEdge* ON_SubD::SplitEdge(
ON_SubDEdge* edge,
ON_3dPoint vertex_location
)
{
ON_SubDimple* subdimple = SubDimple(false);
if ( nullptr == subdimple )
return ON_SUBD_RETURN_ERROR(nullptr);
return subdimple->SplitEdge(edge,vertex_location);
}
const ON_SubDEdge * ON_SubD::SplitFace(
ON_SubDFace * face,
const ON_SubDVertex * v0,
const ON_SubDVertex * v1
)
{
if (nullptr == face || nullptr == v0 || nullptr == v1 || v0 == v1)
return ON_SUBD_RETURN_ERROR(nullptr);
unsigned int fvi0 = ON_UNSET_UINT_INDEX;
unsigned int fvi1 = ON_UNSET_UINT_INDEX;
ON_SubDEdgePtr* eptr = face->m_edge4;
for (unsigned short fei = 0; fei < face->m_edge_count; ++fei, ++eptr)
{
if (4 == fei)
{
eptr = face->m_edgex;
if (nullptr == eptr)
return ON_SUBD_RETURN_ERROR(nullptr);
}
const ON_SubDVertex* v = eptr->RelativeVertex(0);
if (v == v0)
{
if (ON_UNSET_UINT_INDEX != fvi0)
return ON_SUBD_RETURN_ERROR(nullptr);
fvi0 = fei;
}
else if (v == v1)
{
if (ON_UNSET_UINT_INDEX != fvi1)
return ON_SUBD_RETURN_ERROR(nullptr);
fvi1 = fei;
}
}
return (ON_UNSET_UINT_INDEX != fvi0 && ON_UNSET_UINT_INDEX != fvi1)
? SplitFace(face, fvi0, fvi1)
: ON_SUBD_RETURN_ERROR(nullptr);
}
const ON_SubDEdge* ON_SubD::SplitFace(
ON_SubDFace* face,
unsigned int fvi0,
unsigned int fvi1
)
{
ON_SubDimple* subdimple = SubDimple(false);
if ( 0 == subdimple )
return ON_SUBD_RETURN_ERROR(nullptr);
return subdimple->SplitFace(face,fvi0,fvi1);
}
void ON_SubD::MarkAggregateComponentStatusAsNotCurrent() const
{
const ON_SubDLevel* level = ActiveLevelConstPointer();
if ( level )
level->MarkAggregateComponentStatusAsNotCurrent();
}
unsigned int ON_SubDLevel::ClearStates(
ON_ComponentStatus states_to_clear
) const
{
unsigned int rc = 0;
m_aggregates.m_aggregate_status.ClearAggregateStatus(states_to_clear);
for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex)
rc += vertex->m_status.ClearStates(states_to_clear);
for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge)
rc += edge->m_status.ClearStates(states_to_clear);
for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face)
rc += face->m_status.ClearStates(states_to_clear);
return rc;
}
unsigned int ON_SubDLevel::ClearRuntimeMarks(
bool bClearVertexMarks,
bool bClearEdgeMarks,
bool bClearFaceMarks
) const
{
unsigned int rc = 0;
if (bClearVertexMarks)
{
for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex)
{
if (vertex->m_status.ClearRuntimeMark())
++rc;
}
}
if (bClearEdgeMarks)
{
for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge)
{
if (edge->m_status.ClearRuntimeMark())
++rc;
}
}
if (bClearFaceMarks)
{
for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face)
{
if (face->m_status.ClearRuntimeMark())
++rc;
}
}
return rc;
}
unsigned int ON_SubD::ClearComponentStates(
ON_ComponentStatus states_to_clear
) const
{
const ON_SubDLevel* level = ActiveLevelConstPointer();
if ( level )
return level->ClearStates(states_to_clear);
return ON_SUBD_RETURN_ERROR(0);
}
ON_AggregateComponentStatus ON_SubD::AggregateComponentStatus() const
{
return ActiveLevel().AggregateComponentStatus();
}
unsigned int ON_SubDLevel::GetComponentsWithSetStates(
ON_ComponentStatus states_filter,
bool bAllEqualStates,
ON_SimpleArray< ON_SubDComponentPtr >& components_with_set_states
) const
{
components_with_set_states.SetCount(0);
if (states_filter.IsClear())
return 0;
ON_AggregateComponentStatus acs = AggregateComponentStatus();
ON_ComponentStatus as = acs.AggregateStatus();
if (bAllEqualStates)
{
if ( false == as.AllEqualStates(states_filter, states_filter) )
return 0;
}
else
{
if ( false == as.SomeEqualStates(states_filter, states_filter) )
return 0;
}
unsigned int c = 0;
if ( states_filter.IsSelected() && c < m_aggregates.m_aggregate_status.SelectedCount() )
c = m_aggregates.m_aggregate_status.SelectedCount();
if ( states_filter.IsHighlighted() && c < m_aggregates.m_aggregate_status.HighlightedCount() )
c = m_aggregates.m_aggregate_status.HighlightedCount();
if ( states_filter.IsHidden() && c < m_aggregates.m_aggregate_status.HiddenCount() )
c = m_aggregates.m_aggregate_status.HiddenCount();
if ( states_filter.IsLocked() && c < m_aggregates.m_aggregate_status.LockedCount() )
c = m_aggregates.m_aggregate_status.LockedCount();
if ( states_filter.IsDamaged() && c < m_aggregates.m_aggregate_status.DamagedCount() )
c = m_aggregates.m_aggregate_status.DamagedCount();
if ( states_filter.IsSelected() && c < m_aggregates.m_aggregate_status.SelectedCount() )
c = m_aggregates.m_aggregate_status.SelectedCount();
components_with_set_states.Reserve(c);
if (bAllEqualStates)
{
for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex)
{
if (vertex->m_status.AllEqualStates(states_filter, states_filter))
components_with_set_states.Append(ON_SubDComponentPtr::Create(vertex));
}
for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge)
{
if (edge->m_status.AllEqualStates(states_filter, states_filter))
components_with_set_states.Append(ON_SubDComponentPtr::Create(edge));
}
for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face)
{
if (face->m_status.AllEqualStates(states_filter, states_filter))
components_with_set_states.Append(ON_SubDComponentPtr::Create(face));
}
}
else
{
for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex)
{
if (vertex->m_status.SomeEqualStates(states_filter, states_filter))
components_with_set_states.Append(ON_SubDComponentPtr::Create(vertex));
}
for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge)
{
if (edge->m_status.SomeEqualStates(states_filter, states_filter))
components_with_set_states.Append(ON_SubDComponentPtr::Create(edge));
}
for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face)
{
if (face->m_status.SomeEqualStates(states_filter, states_filter))
components_with_set_states.Append(ON_SubDComponentPtr::Create(face));
}
}
return components_with_set_states.UnsignedCount();
}
unsigned int ON_SubD::GetComponentsWithSetStates(
ON_ComponentStatus states_filter,
bool bAllEqualStates,
ON_SimpleArray< ON_SubDComponentPtr >& components_with_set_states
) const
{
return ActiveLevel().GetComponentsWithSetStates(
states_filter,
bAllEqualStates,
components_with_set_states
);
}
unsigned int ON_SubD::GetComponentsWithSetStates(
ON_ComponentStatus states_filter,
bool bAllEqualStates,
ON_SimpleArray< ON_COMPONENT_INDEX >& components_with_set_states
) const
{
components_with_set_states.SetCount(0);
ON_SimpleArray< ON_SubDComponentPtr > cptr;
GetComponentsWithSetStates(
states_filter,
bAllEqualStates,
cptr
);
unsigned int count = cptr.UnsignedCount();
if (count > 0)
{
components_with_set_states.Reserve(count);
components_with_set_states.SetCount(count);
const ON_SubDComponentPtr* cp = cptr.Array();
ON_COMPONENT_INDEX* ci = components_with_set_states.Array();
for ( const ON_SubDComponentPtr* cp1 = cp+count; cp < cp1; cp++ )
*ci++ = cp->ComponentIndex();
}
return count;
}
unsigned int ON_SubDLevel::SetStates(
ON_SubDComponentPtr component_ptr,
ON_ComponentStatus states_to_set
) const
{
if (0 != component_ptr.SetStates(states_to_set))
{
m_aggregates.m_aggregate_status.MarkAsNotCurrent();
return 1;
}
return 0;
}
unsigned int ON_SubDLevel::ClearStates(
ON_SubDComponentPtr component_ptr,
ON_ComponentStatus states_to_clear
) const
{
if (0 != component_ptr.ClearStates(states_to_clear))
{
m_aggregates.m_aggregate_status.MarkAsNotCurrent();
return 1;
}
return 0;
}
unsigned int ON_SubDLevel::SetStatus(
ON_SubDComponentPtr component_ptr,
ON_ComponentStatus status_to_copy
) const
{
if (0 != component_ptr.SetStatus(status_to_copy))
{
m_aggregates.m_aggregate_status.MarkAsNotCurrent();
return 1;
}
return 0;
}
unsigned int ON_SubD::SetComponentStates(
ON_COMPONENT_INDEX component_index,
ON_ComponentStatus states_to_set
) const
{
return SetComponentStates(ComponentPtrFromComponentIndex(component_index),states_to_set);
}
unsigned int ON_SubD::SetComponentStates(
ON_SubDComponentPtr component_ptr,
ON_ComponentStatus states_to_set
) const
{
const ON_SubDLevel* level = ActiveLevelConstPointer();
if ( nullptr != level )
return level->SetStates(component_ptr,states_to_set);
return ON_SUBD_RETURN_ERROR(0);
}
unsigned int ON_SubD::ClearComponentStates(
ON_COMPONENT_INDEX component_index,
ON_ComponentStatus states_to_clear
) const
{
return ClearComponentStates(ComponentPtrFromComponentIndex(component_index),states_to_clear);
}
unsigned int ON_SubD::ClearComponentStates(
ON_SubDComponentPtr component_ptr,
ON_ComponentStatus states_to_clear
) const
{
const ON_SubDLevel* level = ActiveLevelConstPointer();
if ( nullptr != level )
return level->ClearStates(component_ptr,states_to_clear);
return ON_SUBD_RETURN_ERROR(0);
}
unsigned int ON_SubD::SetComponentStatus(
ON_COMPONENT_INDEX component_index,
ON_ComponentStatus status_to_copy
) const
{
return ClearComponentStates(ComponentPtrFromComponentIndex(component_index),status_to_copy);
}
unsigned int ON_SubD::SetComponentStatus(
ON_SubDComponentPtr component_ptr,
ON_ComponentStatus status_to_copy
) const
{
const ON_SubDLevel* level = ActiveLevelConstPointer();
if ( nullptr != level )
return level->SetStatus(component_ptr,status_to_copy);
return ON_SUBD_RETURN_ERROR(0);
}
void ON_SubDLevel::ClearEvaluationCache() const
{
ClearEdgeFlags();
ClearBoundingBox();
m_surface_mesh = ON_SubDMesh::Empty;
m_control_net_mesh = ON_SubDMesh::Empty;
for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex)
{
vertex->ClearSavedSubdivisionPoints();
}
for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge)
{
edge->ClearSavedSubdivisionPoints();
// NO // edge->UnsetSectorCoefficients();
// Leave these set - they are not "cached" values and except for corner case below
// the are independent vertex locations.
if ( edge->IsSmooth() )
{
for (unsigned evi = 0; evi < 2; evi++)
{
if ( false == (edge->m_sector_coefficient[evi] > 0.0 && edge->m_sector_coefficient[evi] < 1.0) )
continue;
const ON_SubDVertex* v = edge->m_vertex[evi];
if (nullptr == v)
continue;
if (ON_SubD::VertexTag::Corner != v->m_vertex_tag)
continue;
// corner sector coefficients depend on the subtended angle of the sector's crease boundary.
// All other sector coefficients are independent of vertex location.
const_cast<ON_SubDEdge* >(edge)->m_sector_coefficient[evi] = ON_SubDSectorType::Create(edge, evi).SectorWeight();
}
}
}
for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face)
{
face->ClearSavedSubdivisionPoints();
}
}
unsigned int ON_SubD::ComponentPtrFromComponentIndex(
const ON_COMPONENT_INDEX* ci_list,
size_t ci_count,
bool bIncludeVertices,
bool bIncludeEdges,
bool bIncludeFaces,
ON_SimpleArray<ON_SubDComponentPtr>& cptr_list
) const
{
if ( ci_count <= 0 )
return 0;
if (
false == bIncludeVertices
&& false == bIncludeEdges
&& false == bIncludeFaces
)
return 0;
if ( nullptr == ci_list )
return ON_SUBD_RETURN_ERROR(0);
const unsigned int count0 = cptr_list.UnsignedCount();
cptr_list.Reserve(count0 + ci_count);
const bool bFilter
= false == bIncludeVertices
|| false == bIncludeEdges
|| false == bIncludeFaces;
for (size_t i = 0; i < ci_count; i++)
{
const ON_COMPONENT_INDEX ci = ci_list[i];
if (bFilter)
{
if (false == bIncludeVertices || ON_COMPONENT_INDEX::TYPE::subd_vertex == ci.m_type)
continue;
if (false == bIncludeEdges || ON_COMPONENT_INDEX::TYPE::subd_edge == ci.m_type)
continue;
if (false == bIncludeFaces || ON_COMPONENT_INDEX::TYPE::subd_face == ci.m_type)
continue;
}
ON_SubDComponentPtr cptr = ComponentPtrFromComponentIndex(ci_list[i]);
if (cptr.IsNull())
continue;
cptr_list.Append(cptr);
}
return (cptr_list.UnsignedCount() - count0);
}
unsigned int ON_SubD::ComponentPtrFromComponentIndex(
const ON_COMPONENT_INDEX* ci_list,
size_t ci_count,
ON_SimpleArray<ON_SubDComponentPtr>& cptr_list
) const
{
return ComponentPtrFromComponentIndex(ci_list, ci_count, true, true, true, cptr_list);
}
bool ON_SubD::DeleteComponents(
const ON_COMPONENT_INDEX* ci_list,
size_t ci_count
)
{
ON_SimpleArray<ON_SubDComponentPtr> cptr_list;
if (ComponentPtrFromComponentIndex(ci_list,ci_count,cptr_list) <= 0)
return true; // nothing to delete
return DeleteComponents(cptr_list.Array(),cptr_list.UnsignedCount(),false);
}
bool ON_SubD::DeleteComponents(
const ON_SubDComponentPtr* cptr_list,
size_t cptr_count,
bool bMarkDeletedFaceEdges
)
{
const bool bDeleteIsolatedEdges = true;
const bool bUpdateTagsAndCoefficients = true;
return DeleteComponentsForExperts(cptr_list, cptr_count, bDeleteIsolatedEdges, bUpdateTagsAndCoefficients, bMarkDeletedFaceEdges);
}
bool ON_SubD::DeleteComponentsForExperts(
const ON_SubDComponentPtr* cptr_list,
size_t cptr_count,
bool bDeleteIsolatedEdges,
bool bUpdateTagsAndCoefficients,
bool bMarkDeletedFaceEdges
)
{
if (bMarkDeletedFaceEdges)
ClearComponentMarks(false,true,false,nullptr);
if ( cptr_count <= 0 )
return true;
if ( nullptr == cptr_list )
return ON_SUBD_RETURN_ERROR(false);
ON_SubDimple* subdimple = SubDimple(false);
if ( nullptr == subdimple )
return ON_SUBD_RETURN_ERROR(false);
const unsigned int level_count = subdimple->LevelCount();
if (level_count <= 0)
return ON_SUBD_RETURN_ERROR(false);
unsigned level_index = level_count;
for (size_t i = 0; i < cptr_count; i++)
{
const ON_SubDComponentBase* c = cptr_list[i].ComponentBase();
if ( nullptr == c)
continue;
if ( c->SubdivisionLevel() < level_index )
level_index = c->SubdivisionLevel();
}
if ( level_index == level_count )
return ON_SUBD_RETURN_ERROR(false);
if ( false == subdimple->SetActiveLevel(level_index) )
return ON_SUBD_RETURN_ERROR(false);
subdimple->ClearHigherSubdivisionLevels(level_index);
const ON_SubDLevel* level = subdimple->ActiveLevelPointer();
if ( nullptr == level || level->m_level_index != level_index)
return ON_SUBD_RETURN_ERROR(false);
// Make sure no components have a status = ON_ComponentStatus::AllSet
// because this uncommon status value is used to mark components that will be be deleted.
ON_SubDComponentIterator cit(*this);
for (ON_SubDComponentPtr cptr = cit.FirstComponent(); cptr.IsNotNull(); cptr = cit.NextComponent())
{
if ( ON_ComponentStatus::AllSet == cptr.Status() )
cptr.ClearStates(ON_ComponentStatus::Damaged);
}
// Set the status of every compoent in cptr_list[] to ON_ComponentStatus::AllSet.
// If that component is a vertex, set the status of every edge and face that
// touch the vertex to ON_ComponentStatus::AllSet.
// If that component is an edge, set the status of every face that
// touches the edge to ON_ComponentStatus::AllSet.
for (size_t i = 0; i < cptr_count; i++)
{
ON_SubDComponentPtr cptr = cptr_list[i];
const ON_SubDComponentBase* c = cptr.ComponentBase();
if (nullptr == c)
continue;
if (c->SubdivisionLevel() != level_index)
continue;
c->m_status = ON_ComponentStatus::AllSet;
switch (cptr.ComponentType())
{
case ON_SubDComponentPtr::Type::Vertex:
{
const ON_SubDVertex* vertex = cptr.Vertex();
if (nullptr == vertex)
continue;
for (unsigned short vei = 0; vei < vertex->m_edge_count; vei++)
{
const ON_SubDEdge* edge = vertex->Edge(vei);
if (nullptr == edge)
continue;
edge->m_status = ON_ComponentStatus::AllSet;
}
for (unsigned short vfi = 0; vfi < vertex->m_face_count; vfi++)
{
const ON_SubDFace* face = vertex->Face(vfi);
if (nullptr == face)
continue;
face->m_status = ON_ComponentStatus::AllSet;
}
}
break;
case ON_SubDComponentPtr::Type::Edge:
{
const ON_SubDEdge* edge = cptr.Edge();
if (nullptr == edge)
continue;
edge->m_status = ON_ComponentStatus::AllSet;
for (unsigned short efi = 0; efi < edge->m_face_count; efi++)
{
const ON_SubDFace* face = edge->Face(efi);
if (nullptr == face)
continue;
face->m_status = ON_ComponentStatus::AllSet;
}
}
break;
case ON_SubDComponentPtr::Type::Face:
{
const ON_SubDFace* face = cptr.Face();
if (nullptr == face)
continue;
face->m_status = ON_ComponentStatus::AllSet;
}
break;
}
}
// Minimum count of what will be deleted. (
unsigned int deleted_vertex_count = 0;
unsigned int deleted_edge_count = 0;
unsigned int deleted_face_count = 0;
for (ON_SubDComponentPtr cptr = cit.FirstComponent(); cptr.IsNotNull(); cptr = cit.NextComponent())
{
if (ON_ComponentStatus::AllSet == cptr.Status())
{
switch (cptr.ComponentType())
{
case ON_SubDComponentPtr::Type::Vertex:
deleted_vertex_count++;
break;
case ON_SubDComponentPtr::Type::Edge:
deleted_edge_count++;
break;
case ON_SubDComponentPtr::Type::Face:
deleted_face_count++;
break;
}
continue;
}
}
if ( 0 == deleted_vertex_count && 0 == deleted_edge_count && 0 == deleted_face_count )
return false;
const bool bDestroy
= deleted_vertex_count >= level->m_vertex_count
|| deleted_edge_count >= level->m_edge_count
|| (deleted_face_count >= level->m_face_count && bDeleteIsolatedEdges)
;
if (bDestroy)
{
Destroy();
return true;
}
unsigned int deleted_component_count = subdimple->DeleteComponents(level_index,bDeleteIsolatedEdges,bUpdateTagsAndCoefficients,bMarkDeletedFaceEdges);
if (0 == subdimple->LevelCount())
{
Destroy();
return true;
}
if (deleted_component_count > 0 || level_index > 0)
{
// remove lower levels
subdimple->ClearLowerSubdivisionLevels(level_index);
}
return (deleted_component_count > 0);
}
unsigned int ON_SubDLevel::UpdateEdgeTags(
bool bUnsetEdgeTagsOnly
)
{
// Update edge flags and sector weights.
unsigned int edge_change_count = 0;
ON_SubDEdge* next_edge = m_edge[0];
for (ON_SubDEdge* edge = next_edge; nullptr != edge; edge = next_edge)
{
next_edge = const_cast<ON_SubDEdge*>(edge->m_next_edge);
const ON_SubD::EdgeTag edge_tag0 = edge->m_edge_tag;
if (bUnsetEdgeTagsOnly && ON_SubD::EdgeTag::Unset != edge_tag0 )
{
continue;
}
if (nullptr == edge->m_vertex[0] || nullptr == edge->m_vertex[1])
{
ON_SUBD_ERROR("nullptr edge->m_vertex[] values");
continue;
}
const double edge_sector_coefficient0[2] = { edge->m_sector_coefficient[0], edge->m_sector_coefficient[1] };
if (2 != edge->m_face_count)
{
edge->m_edge_tag = ON_SubD::EdgeTag::Crease;
edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight;
edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight;
}
else
{
edge->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight;
edge->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight;
const bool bBothVertexTagsAreSet
= ON_SubD::VertexTag::Unset != edge->m_vertex[0]->m_vertex_tag
&& ON_SubD::VertexTag::Unset != edge->m_vertex[1]->m_vertex_tag
;
const unsigned int tagged_end_index = edge->TaggedEndIndex();
if (0 == tagged_end_index || 1 == tagged_end_index)
edge->m_sector_coefficient[tagged_end_index] = ON_SubDSectorType::IgnoredSectorWeight;
switch (edge_tag0)
{
case ON_SubD::EdgeTag::Unset:
if (2 == tagged_end_index)
{
edge->m_edge_tag = ON_SubD::EdgeTag::SmoothX;
}
else if ( bBothVertexTagsAreSet )
{
edge->m_edge_tag = ON_SubD::EdgeTag::Smooth;
if (3 == tagged_end_index)
{
edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight;
edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight;
}
}
break;
case ON_SubD::EdgeTag::Smooth:
if (2 == tagged_end_index)
{
edge->m_edge_tag = ON_SubD::EdgeTag::SmoothX;
}
else if (3 == tagged_end_index && bBothVertexTagsAreSet)
{
edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight;
edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight;
}
break;
case ON_SubD::EdgeTag::Crease:
edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight;
edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight;
break;
//case ON_SubD::EdgeTag::Sharp:
// ON_SUBD_ERROR("ON_SubD::EdgeTag::Sharp is not valid in this version of opennurbs.");
// break;
case ON_SubD::EdgeTag::SmoothX:
if ( 2 != tagged_end_index && bBothVertexTagsAreSet)
edge->m_edge_tag = ON_SubD::EdgeTag::Smooth;
break;
default:
break;
}
}
if (!(edge_tag0 == edge->m_edge_tag
&& edge_sector_coefficient0[0] == edge->m_sector_coefficient[0]
&& edge_sector_coefficient0[1] == edge->m_sector_coefficient[1]))
edge_change_count++;
}
return edge_change_count;
}
unsigned int ON_SubDLevel::UpdateVertexTags(
bool bUnsetVertexTagsOnly
)
{
// Update edge flags and sector weights.
unsigned int vertex_change_count = 0;
ON_SubDVertex* next_vertex = m_vertex[0];
for (ON_SubDVertex* vertex = next_vertex; nullptr != vertex; vertex = next_vertex)
{
next_vertex = const_cast<ON_SubDVertex*>(vertex->m_next_vertex);
const ON_SubD::VertexTag vertex_tag0 = vertex->m_vertex_tag;
if (bUnsetVertexTagsOnly && ON_SubD::VertexTag::Unset != vertex_tag0 )
{
continue;
}
const unsigned int edge_count = vertex->m_edge_count;
unsigned int creased_edge_count = 0;
unsigned int sharp_edge_count = 0;
for (unsigned int vei = 0; vei < edge_count; vei++)
{
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(vertex->m_edges[vei].m_ptr);
if (nullptr == edge)
{
ON_SUBD_ERROR("nullptr vertex->m_edges[] values");
continue;
}
if ( ON_SubD::EdgeTag::Crease == edge->m_edge_tag || 2 != edge->m_face_count )
creased_edge_count++;
else if (((ON_SubD::EdgeTag)3) == edge->m_edge_tag) // ON_SubD::EdgeTag::Sharp
{
ON_SUBD_ERROR("ON_SubD::EdgeTag::Sharp is not valid in this version of opennurbs.");
sharp_edge_count++;
}
// NOTE:
// edges tagged as ON_SubD::EdgeTag::Unset with two faces
// ending at a vertex with 3 or more edges
// will be tagged as smooth in subsequent passes
// once this vertex is tagged as smooth.
}
ON_SubD::VertexTag vertex_tag1 = vertex_tag0;
if ( (creased_edge_count+sharp_edge_count) >= 2 )
{
if ( ON_SubD::VertexTag::Corner != vertex_tag0 )
vertex_tag1 = ON_SubD::VertexTag::Crease;
}
else if ( edge_count >= 2 )
{
if ( 1 == creased_edge_count && 0 == sharp_edge_count )
vertex_tag1 = ON_SubD::VertexTag::Dart;
else
vertex_tag1 = ON_SubD::VertexTag::Smooth;
}
if (vertex_tag0 != vertex_tag1)
{
vertex->m_vertex_tag = vertex_tag1;
vertex_change_count++;
}
}
return vertex_change_count;
}
unsigned int ON_SubDLevel::UpdateAllTagsAndSectorCoefficients(
bool bUnsetValuesOnly
)
{
unsigned int change_count = 0;
bool bUpdateEdges = true;
bool bUpdateVertices = true;
for ( unsigned int it_count = 0; it_count < 8; it_count++)
{
const unsigned int edge_change_count
= bUpdateEdges
? UpdateEdgeTags(bUnsetValuesOnly)
: 0;
bUpdateVertices = (edge_change_count > 0 || 0 == it_count);
change_count += edge_change_count;
const unsigned int vertex_change_count
= bUpdateVertices
? UpdateVertexTags(bUnsetValuesOnly)
: 0;
bUpdateEdges = (vertex_change_count > 0);
change_count += vertex_change_count;
bUpdateVertices = false;
if ( false == bUpdateEdges)
break;
}
if (bUpdateVertices && bUpdateEdges)
{
ON_SUBD_ERROR("Recursion limit exceeded.");
}
// Adjust edge tag smooth/X settings
// This must be done before UpdateEdgeSectorCoefficients().
// It is done between the heavy handed setting above so as not to disturb that delicate code.
ON_SubDEdge* next_edge = m_edge[0];
for (ON_SubDEdge* edge = next_edge; nullptr != edge; edge = next_edge)
{
next_edge = const_cast<ON_SubDEdge*>(edge->m_next_edge);
if (edge->IsSmooth())
{
const ON_SubD::EdgeTag etag = (2 == edge->TaggedEndIndex()) ? ON_SubD::EdgeTag::SmoothX : ON_SubD::EdgeTag::Smooth;
if (etag != edge->m_edge_tag)
{
edge->m_edge_tag = etag;
++change_count;
}
}
}
change_count += UpdateEdgeSectorCoefficients(false);
if (change_count > 0)
{
m_surface_mesh = ON_SubDMesh::Empty;
m_control_net_mesh = ON_SubDMesh::Empty;
}
return change_count;
}
unsigned int ON_SubDLevel::ClearComponentDamagedState() const
{
return ClearComponentDamagedState(true, true, true);
}
unsigned int ON_SubDLevel::ClearComponentDamagedState(
bool bClearVerticesDamagedState,
bool bClearEdgesDamagedState,
bool bClearFacesDamagedState
) const
{
unsigned int change_count = 0;
unsigned int i;
if (bClearVerticesDamagedState)
{
i = 0;
for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v && i++ < m_vertex_count; v = v->m_next_vertex)
if (0 != v->m_status.SetDamagedState(false))
++change_count;
}
if (bClearEdgesDamagedState)
{
i = 0;
for (const ON_SubDEdge* e = m_edge[0]; nullptr != e && i++ < m_edge_count; e = e->m_next_edge)
if (0 != e->m_status.SetDamagedState(false))
++change_count;
}
if (bClearFacesDamagedState)
{
i = 0;
for (const ON_SubDFace* f = m_face[0]; nullptr != f && i++ < m_face_count; f = f->m_next_face)
if (0 != f->m_status.SetDamagedState(false))
++change_count;
}
return change_count;
}
unsigned int ON_SubD::UpdateVertexTags(
bool bUnsetVertexTagsOnly
)
{
ON_SubDLevel* level = ActiveLevelPointer();
if ( nullptr == level )
return ON_SUBD_RETURN_ERROR(0);
return level->UpdateVertexTags(bUnsetVertexTagsOnly);
}
unsigned int ON_SubD::UpdateEdgeTags(
bool bUnsetEdgeTagsOnly
)
{
ON_SubDLevel* level = ActiveLevelPointer();
if ( nullptr == level )
return ON_SUBD_RETURN_ERROR(0);
return level->UpdateEdgeTags(bUnsetEdgeTagsOnly);
}
unsigned int ON_SubD::UpdateEdgeSectorCoefficients(
bool bUnsetSectorCoefficientsOnly
)
{
ON_SubDLevel* level = ActiveLevelPointer();
if ( nullptr == level )
return ON_SUBD_RETURN_ERROR(0);
return level->UpdateEdgeSectorCoefficients(bUnsetSectorCoefficientsOnly);
}
void ON_SubD::SubDModifiedNofification()
{
// DestroyRuntimeCache()
// Clears
// Saved subdivision points.
// Saved limit surface information.
// Bounding boxes.
//
DestroyRuntimeCache();
// This is a heavy handed tag update.
UpdateAllTagsAndSectorCoefficients(false);
}
unsigned int ON_SubD::UpdateAllTagsAndSectorCoefficients(
bool bUnsetValuesOnly
)
{
ON_SubDLevel* level = ActiveLevelPointer();
if ( nullptr == level )
return ON_SUBD_RETURN_ERROR(0);
return level->UpdateAllTagsAndSectorCoefficients(bUnsetValuesOnly);
}
unsigned int ON_SubDimple::DeleteComponents(
unsigned int level_index,
bool bDeleteIsolatedEdges,
bool bUpdateTagsAndCoefficients,
bool bMarkDeletedFaceEdges
)
{
unsigned int deleted_component_count = 0;
if (level_index >= m_levels.UnsignedCount())
return ON_SUBD_RETURN_ERROR(0);
ON_SubDLevel* level = m_levels[level_index];
if (nullptr == level)
return ON_SUBD_RETURN_ERROR(0);
if (bMarkDeletedFaceEdges)
level->ClearRuntimeMarks(false,true,false);
ON_SubDFace* next_face = level->m_face[0];
for (ON_SubDFace* face = next_face; nullptr != face; face = next_face)
{
next_face = const_cast< ON_SubDFace* >(face->m_next_face);
bool bDelete = (ON_ComponentStatus::AllSet == face->m_status || 0 == face->m_edge_count);
if (false == bDelete)
{
const ON_SubDEdgePtr* eptr = face->m_edge4;
for (unsigned short fei = 0; fei < face->m_edge_count && false == bDelete; ++fei, ++eptr)
{
if (4 == fei)
{
eptr = face->m_edgex;
if (nullptr == eptr)
break;
}
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr->m_ptr);
if (nullptr == edge
|| nullptr == edge->m_vertex[0]
|| nullptr == edge->m_vertex[1]
|| ON_ComponentStatus::AllSet == edge->m_status
|| ON_ComponentStatus::AllSet == edge->m_vertex[0]->m_status
|| ON_ComponentStatus::AllSet == edge->m_vertex[1]->m_status
)
{
bDelete = true;
if (nullptr != edge && ON_ComponentStatus::AllSet != edge->m_status)
edge->m_status = ON_ComponentStatus::AllSet;
}
}
if (false == bDelete)
continue;
}
if (bMarkDeletedFaceEdges)
{
// Set runtime mark on face's boundary edges.
const ON_SubDEdgePtr* eptr = face->m_edge4;
for (unsigned short fei = 0; fei < face->m_edge_count ; ++fei, ++eptr)
{
if (4 == fei)
{
eptr = face->m_edgex;
if (nullptr == eptr)
break;
}
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr->m_ptr);
if (nullptr != edge)
edge->m_status.SetRuntimeMark();
}
}
level->RemoveFace(face);
m_heap.ReturnFace(face);
deleted_component_count++;
}
ON_SubDEdge* next_edge = level->m_edge[0];
for (ON_SubDEdge* edge = next_edge; nullptr != edge; edge = next_edge)
{
next_edge = const_cast< ON_SubDEdge* >(edge->m_next_edge);
bool bDelete = (ON_ComponentStatus::AllSet == edge->m_status || (bDeleteIsolatedEdges && 0 == edge->m_face_count) );
if (false == bDelete)
{
for (unsigned short evi = 0; evi < 2 && false == bDelete; evi++)
{
if (nullptr == edge->m_vertex[0]
|| nullptr == edge->m_vertex[1]
|| ON_ComponentStatus::AllSet == edge->m_vertex[0]->m_status
|| ON_ComponentStatus::AllSet == edge->m_vertex[1]->m_status
)
bDelete = true;
}
if (false == bDelete)
continue;
}
level->RemoveEdge(edge);
m_heap.ReturnEdge(edge);
deleted_component_count++;
}
ON_SubDVertex* next_vertex = level->m_vertex[0];
for (ON_SubDVertex* vertex = next_vertex; nullptr != vertex; vertex = next_vertex)
{
next_vertex = const_cast<ON_SubDVertex*>(vertex->m_next_vertex);
bool bDelete = (ON_ComponentStatus::AllSet == vertex->m_status || (bDeleteIsolatedEdges && 0 == vertex->m_face_count) || 0 == vertex->m_edge_count );
if ( false == bDelete )
continue;
level->RemoveVertex(vertex);
m_heap.ReturnVertex(vertex);
deleted_component_count++;
}
if ( 0 == deleted_component_count )
return 0;
// Remove edge references to deleted faces
next_edge = level->m_edge[0];
for (ON_SubDEdge* edge = next_edge; nullptr != edge; edge = next_edge)
{
next_edge = const_cast<ON_SubDEdge*>(edge->m_next_edge);
ON_SubDFacePtr* fptr0 = edge->m_face2;
ON_SubDFacePtr* fptr1 = edge->m_face2;
const unsigned short edge_face_count = edge->m_face_count;
edge->m_face_count = 0;
for (unsigned short efi = 0; efi < edge_face_count; efi++, fptr0++)
{
if (2 == efi)
fptr0 = edge->m_face2;
const ON_SubDFace* face = fptr0->Face();
if (nullptr == face || ON_UNSET_UINT_INDEX == face->ArchiveId())
continue;
*fptr1++ = *fptr0;
edge->m_face_count++;
if (2 == edge->m_face_count)
fptr1 = edge->m_facex;
}
if (0 == edge->m_face_count && bDeleteIsolatedEdges)
{
level->RemoveEdge(edge);
m_heap.ReturnEdge(edge);
deleted_component_count++;
continue;
}
if (edge->m_face_count <= 2 && nullptr != edge->m_facex)
m_heap.ReturnEdgeExtraArray(edge);
if (bUpdateTagsAndCoefficients)
{
if (edge->m_face_count != 2)
edge->m_edge_tag = ON_SubD::EdgeTag::Crease;
edge->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight;
edge->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight;
}
}
// Remove vertex references to deleted edges and faces
next_vertex = level->m_vertex[0];
for (ON_SubDVertex* vertex = next_vertex; nullptr != vertex; vertex = next_vertex)
{
next_vertex = const_cast<ON_SubDVertex*>(vertex->m_next_vertex);
unsigned int count = vertex->m_edge_count;
vertex->m_edge_count = 0;
bool bInteriorVertex = true;
unsigned int crease_count = 0;
for (unsigned short vei = 0; vei < count; vei++)
{
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(vertex->m_edges[vei].m_ptr);
if (nullptr == edge || ON_UNSET_UINT_INDEX == edge->ArchiveId())
{
bInteriorVertex = false;
continue;
}
if (edge->IsCrease())
crease_count++;
if (2 != edge->m_face_count)
bInteriorVertex = false;
vertex->m_edges[vertex->m_edge_count++] = vertex->m_edges[vei];
}
if ( crease_count > 2 )
vertex->m_vertex_tag = ON_SubD::VertexTag::Corner;
else if (false == bInteriorVertex || crease_count > 1)
{
if (false == vertex->IsCreaseOrCorner())
vertex->m_vertex_tag = ON_SubD::VertexTag::Crease;
}
count = vertex->m_face_count;
vertex->m_face_count = 0;
for (unsigned short vfi = 0; vfi < count; vfi++)
{
const ON_SubDFace* face = vertex->m_faces[vfi];
if (nullptr == face || ON_UNSET_UINT_INDEX == face->ArchiveId())
continue;
vertex->m_faces[vertex->m_face_count++] = vertex->m_faces[vfi];
}
if (0 == vertex->m_face_count && 0 == vertex->m_edge_count)
{
level->RemoveVertex(vertex);
m_heap.ReturnVertex(vertex);
deleted_component_count++;
}
}
if (0 == level->m_vertex_count || 0 == level->m_edge_count || (bDeleteIsolatedEdges && 0 == level->m_face_count))
{
Destroy();
}
else
{
// remove all information that is no longer valid
level->MarkAggregateComponentStatusAsNotCurrent();
level->ClearEvaluationCache();
ClearHigherSubdivisionLevels(level_index);
if (bUpdateTagsAndCoefficients)
{
// Update vertex tags, edge tags, and sector weights.
level->UpdateAllTagsAndSectorCoefficients(false);
}
}
ChangeContentSerialNumber();
return deleted_component_count;
}
unsigned int ON_SubD::ClearComponentMarks() const
{
return ClearComponentMarks(true, true, true, nullptr);
}
unsigned int ON_SubD::ClearVertexMarks() const
{
return ClearComponentMarks(true, false, false, nullptr);
}
unsigned int ON_SubD::ClearEdgeMarks() const
{
return ClearComponentMarks(false, true, false, nullptr);
}
unsigned int ON_SubD::ClearFaceMarks() const
{
return ClearComponentMarks(false, false, true, nullptr);
}
unsigned int ON_SubD::ClearComponentMarks(
bool bClearVertexMarks,
bool bClearEdgeMarks,
bool bClearFaceMarks,
ON_SimpleArray< const ON_SubDComponentBase* >* marked_component_list
) const
{
unsigned int clear_count = 0;
if (bClearVertexMarks)
{
ON_SubDVertexIterator vit(*this);
for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex())
{
if (v->m_status.ClearRuntimeMark())
{
if (nullptr != marked_component_list)
marked_component_list->Append(v);
clear_count++;
}
}
}
if (bClearEdgeMarks)
{
ON_SubDEdgeIterator eit(*this);
for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge())
{
if (e->m_status.ClearRuntimeMark())
{
if (nullptr != marked_component_list)
marked_component_list->Append(e);
clear_count++;
}
}
}
if (bClearFaceMarks)
{
ON_SubDFaceIterator fit(*this);
for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace())
{
if (f->m_status.ClearRuntimeMark())
{
if (nullptr != marked_component_list)
marked_component_list->Append(f);
clear_count++;
}
}
}
return clear_count;
}
unsigned int ON_SubD::UnselectComponents(
bool bUnselectAllVertices,
bool bUnselectAllEdges,
bool bUnselectAllFaces
) const
{
unsigned int unselected_count = 0;
if (bUnselectAllVertices)
{
ON_SubDVertexIterator vit(*this);
for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex())
{
if (v->m_status.SetSelectedState(false, false, false))
++unselected_count;
}
}
if (bUnselectAllEdges)
{
ON_SubDEdgeIterator eit(*this);
for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge())
{
if (e->m_status.SetSelectedState(false, false, false))
++unselected_count;
}
}
if (bUnselectAllFaces)
{
ON_SubDFaceIterator fit(*this);
for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace())
{
if (f->m_status.SetSelectedState(false, false, false))
++unselected_count;
}
}
return unselected_count;
}
unsigned int ON_SubD::SetComponentMarks(
bool bClearBeforeSet,
const ON_SimpleArray< const ON_SubDComponentBase* >& marked_component_list
) const
{
unsigned int set_count = 0;
if (bClearBeforeSet)
ClearComponentMarks(true, true, true, nullptr);
const unsigned count = marked_component_list.Count();
if (count <= 0)
return 0;
const ON_SubDComponentBase*const* a = marked_component_list.Array();
if (nullptr == a)
return 0;
for (const ON_SubDComponentBase*const* a1 = a; a < a1; a++)
{
const ON_SubDComponentBase* c = *a;
if (nullptr == c)
continue;
if (c->m_status.SetRuntimeMark())
set_count++;
}
return set_count;
}
unsigned int ON_SubD::GetMarkedComponents(
bool bIncludeVertices,
bool bIncludeEdges,
bool bIncludeFaces,
ON_SimpleArray< const ON_SubDComponentBase* >& marked_component_list
) const
{
unsigned int mark_count = 0;
if (bIncludeVertices)
{
ON_SubDVertexIterator vit(*this);
for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex())
{
if (v->m_status.RuntimeMark())
{
marked_component_list.Append(v);
mark_count++;
}
}
}
if (bIncludeEdges)
{
ON_SubDEdgeIterator eit(*this);
for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge())
{
if (e->m_status.RuntimeMark())
{
marked_component_list.Append(e);
mark_count++;
}
}
}
if (bIncludeFaces)
{
ON_SubDFaceIterator fit(*this);
for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace())
{
if (f->m_status.RuntimeMark())
{
marked_component_list.Append(f);
mark_count++;
}
}
}
return mark_count;
}
unsigned int ON_SubD::GetComponentStatus(
bool bGetVertexStatus,
bool bGetEdgeStatus,
bool bGetFaceStatus,
bool bClearStatus,
ON_ComponentStatus status_mask,
ON_SimpleArray< const class ON_SubDComponentBase* >& component_list,
ON_SimpleArray< ON_ComponentStatus >& status_list
) const
{
component_list.SetCount(0);
status_list.SetCount(0);
if ( ON_ComponentStatus::NoneSet == status_mask )
return 0;
ON_ComponentStatus s;
if (bGetVertexStatus)
{
ON_SubDVertexIterator vit(*this);
for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex())
{
s = ON_ComponentStatus::LogicalAnd(status_mask, v->m_status);
if (ON_ComponentStatus::NoneSet == s)
continue;
component_list.Append(v);
status_list.Append(s);
}
}
if (bGetEdgeStatus)
{
ON_SubDEdgeIterator eit(*this);
for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge())
{
s = ON_ComponentStatus::LogicalAnd(status_mask, e->m_status);
if (ON_ComponentStatus::NoneSet == s)
continue;
component_list.Append(e);
status_list.Append(s);
}
}
if (bGetFaceStatus)
{
ON_SubDFaceIterator fit(*this);
for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace())
{
s = ON_ComponentStatus::LogicalAnd(status_mask, f->m_status);
if (ON_ComponentStatus::NoneSet == s)
continue;
component_list.Append(f);
status_list.Append(s);
}
}
const unsigned int count = component_list.UnsignedCount();
if (bClearStatus && count > 0)
{
const bool bRuntimeMark = status_mask.RuntimeMark();
for (unsigned int i = 0; i < count; ++i)
{
const ON_SubDComponentBase* c = component_list[i];
if (nullptr == c)
continue;
c->m_status.ClearStates(status_mask);
if (bRuntimeMark)
c->m_status.ClearRuntimeMark();
}
}
return count;
}
unsigned int ON_SubD::SetComponentStatus(
ON_ComponentStatus status_mask,
const ON_SimpleArray< const class ON_SubDComponentBase* >& component_list,
const ON_SimpleArray< ON_ComponentStatus >& status_list
) const
{
const unsigned int count = component_list.UnsignedCount();
if (count < 1 || count != status_list.UnsignedCount())
return 0;
const bool bRuntimeMark = status_mask.RuntimeMark();
for (unsigned int i = 0; i < count; ++i)
{
const ON_SubDComponentBase* c = component_list[i];
if (nullptr == c)
continue;
const ON_ComponentStatus s = status_list[i];
c->m_status.ClearStates(status_mask);
c->m_status.SetStates(s);
if (bRuntimeMark)
{
if ( s.RuntimeMark())
c->m_status.SetRuntimeMark();
else
c->m_status.ClearRuntimeMark();
}
}
return count;
}
ON_SubDComponentMarksClearAndRestore::ON_SubDComponentMarksClearAndRestore(
ON_SubD& subd
)
{
m_subd.ShareContentsFrom(subd);
m_subd.ClearComponentMarks(true, true, true, &m_component_list);
}
ON_SubDComponentMarksClearAndRestore::~ON_SubDComponentMarksClearAndRestore()
{
Restore(true);
}
const ON_SimpleArray<const class ON_SubDComponentBase*>& ON_SubDComponentMarksClearAndRestore::ComponentList() const
{
return m_component_list;
}
bool ON_SubDComponentMarksClearAndRestore::Restore(
bool bDisableFutureRestore )
{
const bool rc = m_bRestore;
if (rc)
{
if ( bDisableFutureRestore)
m_bRestore = false;
m_subd.ClearComponentMarks(true, true, true, nullptr);
// if ( ON_ComponentStatus::Marked == m_status_mask )
{
// RuntimeMark is the only bit being managed
if (m_component_list.UnsignedCount() > 0)
m_subd.SetComponentMarks(false, m_component_list);
}
//else if ( m_status_mask.IsNotClear() )
//{
// // something fancier is going on
// // clear current settings
// ON_SubDVertexIterator vit(m_subd);
// for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex())
// v->m_status.ClearStates(m_status_mask);
// ON_SubDEdgeIterator eit(m_subd);
// for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge())
// e->m_status.ClearStates(m_status_mask);
// ON_SubDFaceIterator fit(m_subd);
// for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace())
// f->m_status.ClearStates(m_status_mask);
// // restore settings
// m_subd.SetComponentStatus(m_status_mask, m_component_list, m_status_list);
//}
if (bDisableFutureRestore)
{
m_component_list.Destroy();
//m_status_list.Destroy();
}
}
return rc;
}
void ON_SubDComponentMarksClearAndRestore::DisableRestore()
{
m_bRestore = false;
}
unsigned int ON_SubD::TransformComponents(
const ON_Xform& xform,
const ON_COMPONENT_INDEX* ci_list,
size_t ci_count,
ON_SubDComponentLocation component_location
)
{
if (
false == xform.IsValidAndNotZeroAndNotIdentity()
|| ci_count <= 0
|| nullptr == ci_list
)
return 0;
ON_SimpleArray<ON_SubDComponentPtr> cptr_list;
if (ComponentPtrFromComponentIndex(ci_list,ci_count,cptr_list) <= 0)
return true; // nothing to delete
return TransformComponents(xform,cptr_list.Array(),cptr_list.UnsignedCount(),component_location);
}
static unsigned int Internal_MarkStuffAndMaybeMoveVertices(
const ON_SubD& subd,
const ON_SubDComponentPtr* cptr_list,
size_t cptr_count,
const ON_Xform* xform,
ON_SubDComponentLocation component_location,
bool bExtrusionMarking,
bool bExtrudeBoundaries,
unsigned int& list_vertex_count,
unsigned int& list_edge_count,
unsigned int& list_face_count
)
{
list_vertex_count = 0;
list_edge_count = 0;
list_face_count = 0;
if (false == bExtrusionMarking)
bExtrudeBoundaries = false;
const bool bTransform
= false == bExtrusionMarking
&& nullptr != xform
&& xform->IsValidAndNotZeroAndNotIdentity()
;
if ((bTransform ? 1 : 0) == (bExtrusionMarking ? 1 : 0))
{
ON_SUBD_ERROR("Invalid input.");
return 0;
}
unsigned int marked_vertex_count = 0;
//unsigned int potential_isolated_vertex_count = 0;
unsigned int potential_isolated_edge_count = 0;
if (bExtrusionMarking && 0 == cptr_count && nullptr == cptr_list)
{
// entire subd is being extruded
ON_SubDFaceIterator fit(subd);
for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace())
{
++list_face_count;
f->m_status.SetRuntimeMark();
const unsigned int face_vertex_count = f->m_edge_count;
for (unsigned int fvi = 0; fvi < face_vertex_count; ++fvi)
{
// used when extruding selected components
const ON_SubDEdge* e = f->Edge(fvi);
if (nullptr != e && false == e->m_status.RuntimeMark())
e->m_status.SetRuntimeMark();
const ON_SubDVertex* v = f->Vertex(fvi);
if (nullptr != v && false == v->m_status.RuntimeMark())
{
v->m_status.SetRuntimeMark();
++marked_vertex_count;
}
}
}
}
else
{
for (size_t i = 0; i < cptr_count; i++)
{
switch (cptr_list[i].ComponentType())
{
case ON_SubDComponentPtr::Type::Vertex:
{
const ON_SubDVertex* v = cptr_list[i].Vertex();
if (nullptr == v)
continue;
++list_vertex_count;
if (v->m_status.RuntimeMark())
continue;
if (bTransform)
{
v->m_status.SetRuntimeMark();
if (bTransform)
const_cast<ON_SubDVertex*>(v)->Transform(false, *xform);
++marked_vertex_count;
}
}
break;
case ON_SubDComponentPtr::Type::Edge:
{
const ON_SubDEdge* e = cptr_list[i].Edge();
if (nullptr == e)
continue;
++list_edge_count;
if (e->m_status.RuntimeMark())
continue;
if (bTransform)
{
e->m_status.SetRuntimeMark();
for (unsigned int evi = 0; evi < 2; ++evi)
{
const ON_SubDVertex* v = e->m_vertex[evi];
if (nullptr != v && false == v->m_status.RuntimeMark())
{
v->m_status.SetRuntimeMark();
const_cast<ON_SubDVertex*>(v)->Transform(false, *xform);
++marked_vertex_count;
}
}
}
else if (bExtrudeBoundaries && 1 == e->m_face_count && nullptr != e->m_face2[0].Face())
++potential_isolated_edge_count;
}
break;
case ON_SubDComponentPtr::Type::Face:
{
const ON_SubDFace* f = cptr_list[i].Face();
if (nullptr != f)
{
++list_face_count;
f->m_status.SetRuntimeMark();
const unsigned int face_vertex_count = f->m_edge_count;
for (unsigned int fvi = 0; fvi < face_vertex_count; ++fvi)
{
if (bExtrusionMarking)
{
// used when extruding selected components
const ON_SubDEdge* e = f->Edge(fvi);
if (nullptr != e && false == e->m_status.RuntimeMark())
e->m_status.SetRuntimeMark();
}
const ON_SubDVertex* v = f->Vertex(fvi);
if (nullptr != v && false == v->m_status.RuntimeMark())
{
v->m_status.SetRuntimeMark();
if (bTransform)
const_cast<ON_SubDVertex*>(v)->Transform(false, *xform);
++marked_vertex_count;
}
}
}
}
break;
}
}
if (bExtrusionMarking && potential_isolated_edge_count > 0)
{
for (size_t i = 0; i < cptr_count; i++)
{
if (ON_SubDComponentPtr::Type::Edge != cptr_list[i].ComponentType())
continue;
const ON_SubDEdge* e = cptr_list[i].Edge();
if (nullptr == e)
continue;
if (e->m_status.RuntimeMark())
continue; // this edge us part of a boundary belonging to a face in cptr_list[]
if (1 == e->m_face_count && nullptr != e->m_face2[0].Face())
{
// this boundary edge was explicitly picked its attached face was not picked.
// It will be extruded to a face.
e->m_status.SetRuntimeMark();
for (unsigned int evi = 0; evi < 2; ++evi)
{
const ON_SubDVertex* v = e->m_vertex[evi];
if (nullptr != v && false == v->m_status.RuntimeMark())
{
v->m_status.SetRuntimeMark();
++marked_vertex_count;
}
}
if (0 == --potential_isolated_edge_count)
break;
}
}
}
}
const_cast<ON_SubD&>(subd).ChangeContentSerialNumberForExperts();
return marked_vertex_count;
}
static unsigned int Internal_MarkExtrudeComponents(
const ON_SubD& subd,
const ON_SubDComponentPtr* cptr_list,
size_t cptr_count,
bool bExtrudeBoundaries,
unsigned int& list_vertex_count,
unsigned int& list_edge_count,
unsigned int& list_face_count
)
{
const bool bExtrusionMarking = true;
const int marked_vertex_count = Internal_MarkStuffAndMaybeMoveVertices(
subd,
cptr_list,
cptr_count,
nullptr,
ON_SubDComponentLocation::Unset,
bExtrusionMarking,
bExtrudeBoundaries,
list_vertex_count,
list_edge_count,
list_face_count
);
// It appears the best "hurestic" is to require the user to pick edges and faces.
// isolated vertices will be ignored and no attempts to guess if a user wants to
// extrude the boundary of a face (all its edges) or the face itself.
// lots of delete "hurestic" code here :)
return marked_vertex_count;
}
static unsigned int Internal_TransformComponents(
const ON_SubD& subd,
const ON_SubDComponentPtr* cptr_list,
size_t cptr_count,
const ON_Xform& xform,
ON_SubDComponentLocation component_location
)
{
// This version is used by SDK tools that simply transform components in cptr_list
if (false == xform.IsValidAndNotZeroAndNotIdentity())
return 0;
const bool bExtrusionMarking = false;
unsigned int list_vertex_count = 0;
unsigned int list_edge_count = 0;
unsigned int list_face_count = 0;
return Internal_MarkStuffAndMaybeMoveVertices(
subd,
cptr_list,
cptr_count,
&xform,
component_location,
bExtrusionMarking, false,
list_vertex_count,
list_edge_count,
list_face_count
);
}
unsigned int ON_SubD::TransformComponents(
const ON_Xform& xform,
const ON_SubDComponentPtr* cptr_list,
size_t cptr_count,
ON_SubDComponentLocation component_location
)
{
if (
false == xform.IsValidAndNotZeroAndNotIdentity()
|| cptr_count <= 0
|| nullptr == cptr_list
)
return 0;
ON_SimpleArray<const ON_SubDComponentBase*> marked_components;
const bool bRestoreMarks = ClearComponentMarks(true, true, true, &marked_components) > 0;
const unsigned int v_count = Internal_TransformComponents(*this, cptr_list, cptr_count, xform, component_location);
if (v_count > 0)
{
this->ClearEvaluationCache();
}
if (bRestoreMarks)
SetComponentMarks(true, marked_components);
return (v_count > 0);
}
unsigned int ON_SubD::ExtrudeComponents(
const ON_Xform& xform,
const ON_COMPONENT_INDEX* ci_list,
size_t ci_count
)
{
const bool bExtrudeBoundaries = true;
const bool bPermitNonManifoldEdgeCreation = false;
const ON_SubD::EdgeTag original_edge_tag = ON_SubD::EdgeTag::Unset;
const ON_SubD::EdgeTag moved_edge_tag = ON_SubD::EdgeTag::Unset;
return ExtrudeComponents(xform, ci_list, ci_count, bExtrudeBoundaries, bPermitNonManifoldEdgeCreation, original_edge_tag, moved_edge_tag);
}
unsigned int ON_SubD::ExtrudeComponents(
const ON_Xform& xform,
const ON_COMPONENT_INDEX* ci_list,
size_t ci_count,
bool bExtrudeBoundaries,
bool bPermitNonManifoldEdgeCreation,
ON_SubD::EdgeTag original_edge_tag,
ON_SubD::EdgeTag moved_edge_tag
)
{
if (
false == xform.IsValidAndNotZeroAndNotIdentity()
|| xform.IsIdentity()
|| ci_count <= 0
|| nullptr == ci_list
)
return 0;
ON_SimpleArray<ON_SubDComponentPtr> cptr_list;
if (ComponentPtrFromComponentIndex(ci_list,ci_count,cptr_list) <= 0)
return true; // nothing to extrude
return ExtrudeComponents(
xform,
cptr_list.Array(),
cptr_list.UnsignedCount(),
bExtrudeBoundaries,
bPermitNonManifoldEdgeCreation,
original_edge_tag,
moved_edge_tag
);
}
class ON_Internal_ExtrudedVertexPair
{
public:
ON_Internal_ExtrudedVertexPair() = default;
~ON_Internal_ExtrudedVertexPair() = default;
ON_Internal_ExtrudedVertexPair(const ON_Internal_ExtrudedVertexPair&) = default;
ON_Internal_ExtrudedVertexPair& operator=(const ON_Internal_ExtrudedVertexPair&) = default;
static const ON_Internal_ExtrudedVertexPair Unset;
// the marked vertex was in the original subd and will be moved.
ON_SubDVertex* m_marked_vertex = nullptr;
// the unmarked vertex replaces the marked vertex at the original location.
ON_SubDVertex* m_unmarked_vertex = nullptr;
// from new vertex to original vertex
ON_SubDEdge* m_new_side = nullptr;
static int CompareMarkedVertexId(
const ON_Internal_ExtrudedVertexPair* lhs,
const ON_Internal_ExtrudedVertexPair* rhs
);
};
const ON_Internal_ExtrudedVertexPair ON_Internal_ExtrudedVertexPair::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Internal_ExtrudedVertexPair);
class ON_Internal_ExtrudedSide
{
public:
ON_Internal_ExtrudedSide() = default;
~ON_Internal_ExtrudedSide() = default;
ON_Internal_ExtrudedSide(const ON_Internal_ExtrudedSide&) = default;
ON_Internal_ExtrudedSide& operator=(const ON_Internal_ExtrudedSide&) = default;
static const ON_Internal_ExtrudedSide Unset;
bool m_bExtrudedBoundaryEdge = false;
bool m_bHasMovedFace = false; // true if the edge touches a fae that will be moved.
bool m_bHasStationaryFace = false; // true if the edge touches a face that will remain stationary.
ON_SubD::EdgeTag m_original_marked_edge_tag = ON_SubD::EdgeTag::Unset;
ON_SubD::EdgeTag MarkedEdgeTag() const
{
// this tag is calculated just before the side face is made
if ( m_bExtrudedBoundaryEdge )
{
const ON_SubDVertex* v0 = nullptr != m_marked_edge ? m_marked_edge->m_vertex[0] : nullptr;
const ON_SubDVertex* v1 = nullptr != m_marked_edge ? m_marked_edge->m_vertex[1] : nullptr;
if (
m_bHasMovedFace
&& false == m_bHasStationaryFace
&& nullptr != v0
&& nullptr != v1
)
{
return
(v0->IsDartOrCreaseOrCorner() && v1->IsDartOrCreaseOrCorner())
? ON_SubD::EdgeTag::SmoothX
: ON_SubD::EdgeTag::Smooth;
}
}
return m_original_marked_edge_tag;
}
ON_SubD::EdgeTag UnmarkedEdgeTag(
const ON_SubDVertex* v0,
const ON_SubDVertex* v1
) const
{
// This tag is calculate before m_unmarked_edge is created.
if (
m_bExtrudedBoundaryEdge
&& m_bHasStationaryFace
&& false == m_bHasMovedFace
&& nullptr != v0
&& nullptr != v1
)
{
return
(v0->IsDartOrCreaseOrCorner() && v1->IsDartOrCreaseOrCorner())
? ON_SubD::EdgeTag::SmoothX
: ON_SubD::EdgeTag::Smooth;
}
return
(ON_SubD::EdgeTag::Crease == m_original_marked_edge_tag)
? ON_SubD::EdgeTag::Crease
: ON_SubD::EdgeTag::Unset
;
}
// the marked edge was in the original object and will be moved.
ON_SubDEdge* m_marked_edge = nullptr;
// the unmarked edge replaces the marked edge at the original location.
ON_SubDEdge* m_unmarked_edge = nullptr;
// start at new vertex and end at original vertex;
ON_SubDEdge* m_new_side0 = nullptr;
ON_SubDEdge* m_new_side1 = nullptr;
ON_SubDFace* m_new_face = nullptr;
};
const ON_Internal_ExtrudedSide ON_Internal_ExtrudedSide::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Internal_ExtrudedSide);
int ON_Internal_ExtrudedVertexPair::CompareMarkedVertexId(
const ON_Internal_ExtrudedVertexPair* lhs,
const ON_Internal_ExtrudedVertexPair* rhs
)
{
const unsigned int lhs_id = lhs->m_marked_vertex->m_id;
const unsigned int rhs_id = rhs->m_marked_vertex->m_id;
if (lhs_id < rhs_id)
return -1;
if (lhs_id > rhs_id)
return 1;
return 0;
}
static ON_SubD::EdgeTag Internal_AdjustedEdgeTag(const ON_SubDEdge* edge)
{
if (nullptr == edge || nullptr == edge->m_vertex[0] || nullptr == edge->m_vertex[1])
return ON_SubD::EdgeTag::Unset;
// adjust moved edge tag because vertex tags can change when they get moved.
const ON_SubD::VertexTag evtag[2] = { edge->m_vertex[0]->m_vertex_tag ,edge->m_vertex[1]->m_vertex_tag };
if (ON_SubD::VertexTag::Unset == evtag[0] || ON_SubD::VertexTag::Unset == evtag[1])
return ON_SubD::EdgeTag::Unset;
if (ON_SubD::VertexTag::Smooth == evtag[0] || ON_SubD::VertexTag::Smooth == evtag[1])
return ON_SubD::EdgeTag::Smooth;
const ON_SubD::EdgeTag etag = edge->m_edge_tag;
if (ON_SubD::EdgeTag::Smooth == etag || ON_SubD::EdgeTag::SmoothX == etag )
return ON_SubD::EdgeTag::SmoothX;
return etag;
}
static void Internal_SetEdgeVertices(
ON_SubD& subd,
ON_Internal_ExtrudedVertexPair& vertex_pair
)
{
// marked edges use the marked vertex.
ON_SubDVertex* marked_vertex = vertex_pair.m_marked_vertex;
ON_SubDVertex* unmarked_vertex = vertex_pair.m_unmarked_vertex;
const unsigned int vertex_edge_count = marked_vertex->EdgeCount();
unsigned int marked_edge_count = 0;
unsigned int unmarked_edge_count = 0;
unsigned int new_edge_count = 0;
for (unsigned int vei = 0; vei < vertex_edge_count; vei++)
{
ON_SubDEdgePtr eptr = marked_vertex->m_edges[vei];
const ON_SubDEdge* e = eptr.Edge();
if (nullptr == e)
continue;
if (vertex_pair.m_new_side == e)
new_edge_count++;
else if (e->m_status.RuntimeMark())
marked_edge_count++;
else
unmarked_edge_count++;
}
if (unmarked_edge_count <= 0)
return;
unmarked_edge_count += unmarked_vertex->m_edge_count;
if ( unmarked_vertex->m_edge_capacity < (unmarked_edge_count+new_edge_count) )
{
subd.GrowVertexEdgeArray(unmarked_vertex, unmarked_edge_count);
}
marked_vertex->m_edge_count = 0;
for (unsigned int vei = 0; vei < vertex_edge_count; vei++)
{
ON_SubDEdgePtr eptr = marked_vertex->m_edges[vei];
ON_SubDEdge* e = eptr.Edge();
if (nullptr == e)
continue;
if (vertex_pair.m_new_side == e || e->m_status.RuntimeMark())
{
marked_vertex->m_edges[marked_vertex->m_edge_count] = eptr;
marked_vertex->m_edge_count++;
}
else
{
if (e->m_vertex[0] == marked_vertex)
e->m_vertex[0] = unmarked_vertex;
else if (e->m_vertex[1] == marked_vertex)
e->m_vertex[1] = unmarked_vertex;
unmarked_vertex->m_edges[unmarked_vertex->m_edge_count] = eptr;
unmarked_vertex->m_edge_count++;
}
}
}
static ON_SubDFace* Internal_AddNewFace(
ON_SubD& subd,
ON_Internal_ExtrudedSide& side
)
{
// All components that will be moved have the runtime mark set.
// All other components have a clear runtime mark.
// The original_edge will be moved.
// The new_edge will not be moved.
// new edge and original edge go the same direction.
// new_side_edges[2] run from new to original edges.
// change edges of unmarked faces to use the new edge
ON__UINT_PTR edir = 0;
ON_SubDEdge* marked_edge = side.m_marked_edge; // will be moved
ON_SubDEdge* unmarked_edge = side.m_unmarked_edge; // fixed
unsigned int marked_edge_face_count0 = marked_edge->m_face_count;
ON_SubDFacePtr* marked_edge_fptr1 = marked_edge->m_face2;
const ON_SubDFacePtr* marked_edge_fptr0 = marked_edge_fptr1;
unsigned int marked_edge_face_count1 = 0;
subd.GrowEdgeFaceArray(unmarked_edge, marked_edge_face_count0);
for (unsigned int efi = 0; efi < marked_edge_face_count0; efi++, marked_edge_fptr0++)
{
if (2 == efi)
marked_edge_fptr0 = marked_edge->m_facex;
if (2 == marked_edge_face_count1)
marked_edge_fptr1 = marked_edge->m_facex;
ON_SubDFace* f = marked_edge_fptr0->Face();
if (nullptr == f)
{
ON_SUBD_ERROR("null face pointer");
continue;
}
if (f->m_status.RuntimeMark())
{
edir = marked_edge_fptr0->FaceDirection();
marked_edge_face_count1++;
*marked_edge_fptr1 = *marked_edge_fptr0;
marked_edge_fptr1++;
continue; // this face will be moved and keeps edge e1
}
// f is unmarked.
// change referenced edge from marked_edge to unmarked_edge.
f->ReplaceEdgeInArray(0, marked_edge, unmarked_edge);
unmarked_edge->AddFaceToArray(*marked_edge_fptr0);
}
// When marked_edge is a manifold edge, face_count goes from 2 to 1.
marked_edge->m_face_count = static_cast<unsigned short>(marked_edge_face_count1);
ON_SubDEdge* side0 = (0 == edir) ? side.m_new_side0 : side.m_new_side1;
ON_SubDEdge* side1 = (0 == edir) ? side.m_new_side1 : side.m_new_side0;
ON_SubDEdgePtr new_face_eptr[4];
new_face_eptr[0] = ON_SubDEdgePtr::Create(side.m_marked_edge, 1-edir);
new_face_eptr[1] = ON_SubDEdgePtr::Create(side0, 1);
new_face_eptr[2] = ON_SubDEdgePtr::Create(side.m_unmarked_edge, edir);
new_face_eptr[3] = ON_SubDEdgePtr::Create(side1, 0);
const ON_SubD::EdgeTag marked_edge_tag = side.MarkedEdgeTag();
if (marked_edge_tag != marked_edge->m_edge_tag)
marked_edge->m_edge_tag = marked_edge_tag;
side.m_new_face = subd.AddFace(new_face_eptr, 4);
if (nullptr != side.m_new_face)
{
// When isolated edges are extruded, we need to flip the face.
// In all other cases, we don't.
bool bFlip = false;
bool bFlipSet = false;
for (unsigned int fei = 0; fei < 4; fei++)
{
const ON_SubDEdgePtr eptr = side.m_new_face->m_edge4[fei];
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr.m_ptr);
if (nullptr == e || e->m_face_count > 2)
{
bFlipSet = false;
break;
}
if (2 != e->m_face_count)
continue;
const ON__UINT_PTR fedir = ON_SUBD_EDGE_DIRECTION(eptr.m_ptr);
const ON_SubDFacePtr fptr[2] = { e->m_face2[0], e->m_face2[1] };
const ON_SubDFace* f[2] = { ON_SUBD_FACE_POINTER(fptr[0].m_ptr), ON_SUBD_FACE_POINTER(fptr[1].m_ptr) };
if (nullptr == f[0] || nullptr == f[1] || f[0] == f[1])
{
bFlipSet = false;
break;
}
if (side.m_new_face != f[0] && side.m_new_face != f[1] )
{
bFlipSet = false;
break;
}
const ON__UINT_PTR fdir[2] = { ON_SUBD_FACE_DIRECTION(fptr[0].m_ptr), ON_SUBD_FACE_DIRECTION(fptr[1].m_ptr) };
if (fedir != fdir[(f[0] == side.m_new_face) ? 0 : 1])
{
bFlipSet = false;
break;
}
const bool bSameDir = (fdir[0] == fdir[1]) ? true : false;
if (false == bFlipSet)
{
bFlipSet = true;
bFlip = bSameDir;
}
else if (bSameDir != bFlip)
{
bFlipSet = false;
break;
}
}
if (bFlip)
side.m_new_face->ReverseEdgeList();
}
return side.m_new_face;
}
static ON_SubD::EdgeTag Internal_ConnectingEdgeTagAtVertex(
bool bExtrudeBoundaries,
const ON_SubDVertex* v,
ON_SubD::VertexTag& moved_vertex_tag,
ON_SubD::VertexTag& stationary_vertex_tag
)
{
moved_vertex_tag = ON_SubD::VertexTag::Unset;
stationary_vertex_tag = ON_SubD::VertexTag::Unset;
if (ON_SubD::VertexTag::Crease != v->m_vertex_tag && ON_SubD::VertexTag::Dart != v->m_vertex_tag)
{
ON_SUBD_ERROR("This function requires a crease or dart vertex as input.");
return ON_SubD::EdgeTag::Unset;
}
const unsigned int vertex_edge_count = v->m_edge_count;
// total_count = number of edges currently attached to v that are creases
unsigned int total_count = 0;
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// stationary, moved_to_crease, moved_to_smooth, and split are mutually exclusive sitations.
// stationary_count = number of edges currently attached to v that are creases and remain in current locations
unsigned int stationary_count = 0;
// moved_to_crease_count = number of edges currently attached to v that are creases and move and remain creases
unsigned int moved_to_crease_count = 0;
// moved_to_crease_count = number of edges currently attached to v that are creases and move and become smooth
unsigned int moved_to_smooth_count = 0;
// split_count = number of edges currently attached to v that are creases and are between a moved and stationary face
unsigned int split_count = 0;
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// bdry, interior, wire, nonman are mutually exclusive sitations.
// bdry_count = number of edges currently attached to v that are creases and have 1 face.
unsigned int bdry_count = 0;
// interior_count = number of edges currently attached to v that are creases and have 2 faces
unsigned int interior_count = 0;
// wire_count = number of edges currently attached to v that are creases and have 0 faces
unsigned int wire_count = 0;
// nonman_count = number of edges currently attached to v that are creases and have 3 or more faces
unsigned int nonman_count = 0;
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// tally up what is happening at each crease edge attached to vertex v
//
for (unsigned int vei = 0; vei < vertex_edge_count; vei++)
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(v->m_edges[vei].m_ptr);
if (nullptr == e)
continue;
if (ON_SubD::EdgeTag::Crease != e->m_edge_tag)
continue;
const bool bBoundaryEdge = (1 == e->m_face_count) && (nullptr != e->m_face2[0].Face());
const bool bMovedBoundaryEdge = bBoundaryEdge && e->m_status.RuntimeMark();
//const bool bStationaryBoundaryEdge = bBoundaryEdge && false == e->m_status.RuntimeMark();
++total_count;
if (0 == e->m_face_count)
++wire_count;
else if (1 == e->m_face_count)
++bdry_count;
else if (2 == e->m_face_count)
++interior_count;
else if (2 == e->m_face_count)
++nonman_count;
unsigned int moved_face_count = 0;
unsigned int stationary_face_count = 0;
for (unsigned short evi = 0; evi < e->m_face_count; evi++)
{
const ON_SubDFace* f = e->Face(evi);
if (nullptr == f)
{
ON_SUBD_ERROR("Edge has null face.");
return ON_SubD::EdgeTag::Unset;
}
if (f->m_status.RuntimeMark())
++moved_face_count;
else
++stationary_face_count;
}
if (moved_face_count > 0 && stationary_face_count > 0)
{
// This is edge is between a moved face and a stationary face.
// It will "split" into two edges
++split_count;
}
else if (moved_face_count > 0)
{
// This edge and all faced currently attached to it are moving.
if (bExtrudeBoundaries && bMovedBoundaryEdge)
++moved_to_smooth_count;
else
++moved_to_crease_count;
}
else if (stationary_face_count > 0)
{
// This edge is moving and all faced currently attached to it are stationary.
// A new face will be attached to this edge
if (bExtrudeBoundaries && bMovedBoundaryEdge)
{
// This boundary edge will move and a new face will be createed between it
// and the face the edge is currently attached to.
++moved_to_crease_count;
}
else
++stationary_count; // this crease (interior or boundary) and its attached face or faces do not move
}
}
if (total_count != wire_count + split_count + moved_to_smooth_count + moved_to_crease_count + stationary_count)
{
ON_SUBD_ERROR("Bug in counting code above or invalid topology near this vertex.");
return ON_SubD::EdgeTag::Unset;
}
if (ON_SubD::VertexTag::Dart == v->m_vertex_tag)
{
if (
1 == total_count
&& 1 == interior_count
&& 0 == bdry_count
&& 0 == wire_count
&& 0 == nonman_count
)
{
if (0 == split_count)
{
if (1 == moved_to_crease_count && 0 == moved_to_smooth_count && 0 == stationary_count)
{
// dart crease and both attached faces are moving
moved_vertex_tag = ON_SubD::VertexTag::Dart;
stationary_vertex_tag = ON_SubD::VertexTag::Smooth;
return ON_SubD::EdgeTag::Smooth;
}
else if (0 == moved_to_crease_count && 0 == moved_to_smooth_count && 1 == stationary_count)
{
// dart crease and both attached faces are stationary
moved_vertex_tag = ON_SubD::VertexTag::Smooth;
stationary_vertex_tag = ON_SubD::VertexTag::Dart;
return ON_SubD::EdgeTag::Smooth;
}
}
else if (1 == split_count)
{
// Along the dart crease, one attached face is moving and the other is stationary.
// The dart crease will get split into two darts and the new edge will be smooth.
moved_vertex_tag = ON_SubD::VertexTag::Dart;
stationary_vertex_tag = ON_SubD::VertexTag::Dart;
return ON_SubD::EdgeTag::SmoothX; // will X becomes Smooth on 1st subdivision
}
}
ON_SUBD_ERROR("Unexpected dart vertex edge tags - bug in counting code above or current tags or topology are not invalid.");
return ON_SubD::EdgeTag::Unset;
}
/////////////////////////////////////////////////////////////////////////////////////////////
//
// The rest of this function is for the case when ON_SubD::VertexTag::Crease == v->m_vertex_tag
//
if (1 == wire_count)
{
moved_vertex_tag = ON_SubD::VertexTag::Crease;
stationary_vertex_tag = ON_SubD::VertexTag::Crease;
return ON_SubD::EdgeTag::Crease;
}
if (2 == wire_count)
{
moved_vertex_tag = ON_SubD::VertexTag::Crease;
stationary_vertex_tag = ON_SubD::VertexTag::Crease;
return ON_SubD::EdgeTag::SmoothX; // will X becomes Smooth on 1st subdivision
}
if (0 != wire_count || 0 != nonman_count)
{
// If extrusions involving nonmanifold regions need to be supported, lots of changes are required.
ON_SUBD_ERROR("Currently, non-manifold cases are not supported.");
return ON_SubD::EdgeTag::Unset;
}
for(;;)
{
if (
ON_SubD::VertexTag::Crease == v->m_vertex_tag
&& 2 == (split_count + moved_to_crease_count + moved_to_smooth_count + stationary_count)
)
{
if (2 == interior_count && 0 == bdry_count)
break;
if (0 == interior_count && 2 == bdry_count)
break;
}
ON_SUBD_ERROR("Unexpected crease vertex edge tags - bug in counting code above or current tags or topology are not invalid.");
return ON_SubD::EdgeTag::Unset;
}
if (0 == interior_count && 2 == bdry_count)
{
// The vertex v is currently a boundary crease vertex and split_count always 0 in this case.
//
// Since 2 = split_count + stationary_count + moved_to_crease_count + moved_to_smooth_count,
// stationary_count = 0,1,2 is used as the next "case reduction" filter.
if (0 != split_count || 2 != (stationary_count + moved_to_crease_count + moved_to_smooth_count) )
{
ON_SUBD_ERROR("Bug in boundary crease case counting code above.");
return ON_SubD::EdgeTag::Unset;
}
if (0 == stationary_count)
{
// both attached crease edges are moving
// 3 possibilities (moved_to_crease_count,moved_to_smooth_count) = (2,0), (0,2), or (1,1)
if (2 == moved_to_crease_count )
{
moved_vertex_tag = ON_SubD::VertexTag::Crease;
stationary_vertex_tag = ON_SubD::VertexTag::Smooth;
return ON_SubD::EdgeTag::Smooth;
}
if (2 == moved_to_smooth_count )
{
// attached faces are moving and moved edges will be copied.
// The moved edges will convert from crease to smooth and the stationary copies will become creases.
// New faces will be created between the moved edge and the stationary copy.
moved_vertex_tag = ON_SubD::VertexTag::Smooth;
stationary_vertex_tag = ON_SubD::VertexTag::Crease;
return ON_SubD::EdgeTag::Smooth;
}
if ( 1 == moved_to_crease_count && 1 == moved_to_smooth_count )
{
// new edge runs from moved crease to crease that replaces moved to smooth edge
moved_vertex_tag = ON_SubD::VertexTag::Crease;
stationary_vertex_tag = ON_SubD::VertexTag::Crease;
return ON_SubD::EdgeTag::Crease;
}
}
else if (1 == stationary_count)
{
// 1 attached crease edge is moving and the other attached crease edge is stationary
// 2 possibilities (moved_to_crease_count,moved_to_smooth_count) = (1,0) or (0,1)
if (1 == moved_to_crease_count && 0 == moved_to_smooth_count)
{
// moved edge continues to be a boundary (and crease) edge.
// The new edge connecting the stationary crease and the moved edge is a crease.
moved_vertex_tag = ON_SubD::VertexTag::Crease;
stationary_vertex_tag = ON_SubD::VertexTag::Crease;
return ON_SubD::EdgeTag::Crease;
}
if (0 == moved_to_crease_count && 1 == moved_to_smooth_count)
{
// moved crease becomes interior smooth edge
// The new edge connecting the stationary crease and the moved edge is a crease.
moved_vertex_tag = ON_SubD::VertexTag::Smooth;
stationary_vertex_tag = ON_SubD::VertexTag::Crease;
return ON_SubD::EdgeTag::Smooth;
}
}
else if (2 == stationary_count)
{
// neither attached crease edge is moving
// 1 possibility (moved_to_crease_count,moved_to_smooth_count) = (0,0)
moved_vertex_tag = ON_SubD::VertexTag::Smooth;
stationary_vertex_tag = ON_SubD::VertexTag::Crease;
return ON_SubD::EdgeTag::Smooth;
}
}
else if (2 == interior_count && 0 == bdry_count)
{
// The vertex v is currently an interior crease vertex and moved_to_smooth_count always 0 in this case.
// (move_to_smooth_count>0 requires a boundary edge)
//
// Since 2 = split_count + stationary_count + moved_to_crease_count + moved_to_smooth_count,
// split_count = 0,1,2 is used as the next "case reduction" filter.
if (0 != moved_to_smooth_count || 2 != (split_count + stationary_count + moved_to_crease_count) )
{
ON_SUBD_ERROR("Bug in interior crease case counting code above.");
return ON_SubD::EdgeTag::Unset;
}
if (0 == split_count)
{
// stationary_count can be 0, 1 or 2.
if (0 == stationary_count)
{
// both creases and all attached faces are moving
moved_vertex_tag = ON_SubD::VertexTag::Crease;
stationary_vertex_tag = ON_SubD::VertexTag::Smooth;
return ON_SubD::EdgeTag::Smooth;
}
else if (1 == stationary_count)
{
// TODO
}
else if (2 == stationary_count)
{
// both creases and all attached faces are stationary
moved_vertex_tag = ON_SubD::VertexTag::Smooth;
stationary_vertex_tag = ON_SubD::VertexTag::Crease;
return ON_SubD::EdgeTag::Smooth;
}
}
else if (1 == split_count)
{
// stationary_count can be 0 or 1
if (0 == stationary_count)
{
// 1 = moved_to_crease_count, 0 = moved_to_smooth_count.
moved_vertex_tag = ON_SubD::VertexTag::Dart;
stationary_vertex_tag = ON_SubD::VertexTag::Crease;
return ON_SubD::EdgeTag::SmoothX; // will X becomes Smooth on 1st subdivision
}
else if (1 == stationary_count)
{
// moved_to_crease_count = 0 and moved_to_smooth_count = 0
moved_vertex_tag = ON_SubD::VertexTag::Dart;
stationary_vertex_tag = ON_SubD::VertexTag::Crease;
return ON_SubD::EdgeTag::SmoothX; // will X becomes Smooth on 1st subdivision
}
}
else if (2 == split_count)
{
// stationary_count = 0, moved_to_crease_count = 0, and moved_to_smooth_count = 0
moved_vertex_tag = ON_SubD::VertexTag::Crease;
stationary_vertex_tag = ON_SubD::VertexTag::Crease;
return ON_SubD::EdgeTag::SmoothX; // will X becomes Smooth on 1st subdivision
}
}
ON_SUBD_ERROR("Unexpected crease vertex edge tags - bug in counting code above or current tags or topology are not invalid.");
return ON_SubD::EdgeTag::Unset;
}
static bool Internal_NonManifoldEdgeWillBeCreated( const ON_SubDVertex* v )
{
if (nullptr == v || false == v->m_status.RuntimeMark())
return false;
v->m_status.ClearRuntimeMark();
const unsigned int vertex_edge_count = v->m_edge_count;
unsigned int boundary_count = 0;
for (unsigned int vei = 0; vei < vertex_edge_count; vei++)
{
const ON_SubDEdge* e = v->Edge(vei);
if (nullptr == e || 0 == e->m_face_count)
continue;
if (e->m_face_count > 2)
return true;
const ON_SubDFace* f = e->Face(0);
const bool b0 = (nullptr != f) ? f->m_status.RuntimeMark() : false;
f = (e->m_face_count > 1) ? e->Face(1) : nullptr;
const bool b1 = (nullptr != f) ? f->m_status.RuntimeMark() : false;
if (b0 == b1)
continue;
boundary_count++;
if (boundary_count > 2)
return true;
}
return false;
}
unsigned int ON_SubD::ExtrudeComponents(
const ON_Xform& xform,
const ON_SubDComponentPtr* cptr_list,
size_t cptr_count
)
{
const bool bExtrudeBoundaries = true;
const bool bPermitNonManifoldEdgeCreation = false;
const ON_SubD::EdgeTag original_edge_tag = ON_SubD::EdgeTag::Unset;
const ON_SubD::EdgeTag moved_edge_tag = ON_SubD::EdgeTag::Unset;
return ExtrudeComponents(xform, cptr_list, cptr_count, bExtrudeBoundaries, bPermitNonManifoldEdgeCreation, original_edge_tag, moved_edge_tag);
}
unsigned int ON_SubD::ExtrudeComponents(
const ON_Xform& xform,
const ON_SubDComponentPtr* cptr_list,
size_t cptr_count,
bool bExtrudeBoundaries,
bool bPermitNonManifoldEdgeCreation,
ON_SubD::EdgeTag original_edge_tag,
ON_SubD::EdgeTag moved_edge_tag
)
{
if (nullptr == cptr_list || cptr_count <= 0)
return 0;
return Internal_ExtrudeComponents(
xform,
cptr_list,
cptr_count,
bExtrudeBoundaries,
bPermitNonManifoldEdgeCreation,
original_edge_tag,
moved_edge_tag
);
}
unsigned int ON_SubD::Extrude(const ON_Xform & xform)
{
if (IsSolid())
return 0;
const bool bExtrudeBoundaries = true;
const bool bPermitNonManifoldEdgeCreation = false;
return Internal_ExtrudeComponents(
xform,
nullptr,
0,
bExtrudeBoundaries,
bPermitNonManifoldEdgeCreation,
ON_SubD::EdgeTag::Unset, ON_SubD::EdgeTag::Unset
);
}
unsigned int ON_SubD::Internal_ExtrudeComponents(
const ON_Xform& xform,
const ON_SubDComponentPtr* cptr_list,
size_t cptr_count,
bool bExtrudeBoundaries,
bool bPermitNonManifoldEdgeCreation,
ON_SubD::EdgeTag original_edge_tag,
ON_SubD::EdgeTag moved_edge_tag
)
{
const bool bHaveCptrList = (cptr_count > 0 && nullptr != cptr_list);
bool bExtrudeAll = false;
if (false == bHaveCptrList && 0 == cptr_count)
{
bool bIsManifold = false;
bool bIsOriented = false;
bool bHasBoundary = false;
int solid_orientation = 0;
ActiveLevel().GetTopologicalAttributes(bIsManifold, bIsOriented, bHasBoundary, solid_orientation);
bExtrudeAll = bHasBoundary;
}
if (
false == xform.IsValidAndNotZeroAndNotIdentity()
|| (false == bHaveCptrList && false == bExtrudeAll)
)
return 0;
// The extrusion is initially calculated as if bot original_edge_tag and moved_edge_tag
// are Unset. A post process (will be added later to) sets the tags as specified
if (ON_SubD::EdgeTag::Crease != original_edge_tag && ON_SubD::EdgeTag::Smooth != original_edge_tag)
original_edge_tag = ON_SubD::EdgeTag::Unset;
if (ON_SubD::EdgeTag::Crease != moved_edge_tag && ON_SubD::EdgeTag::Smooth != moved_edge_tag)
moved_edge_tag = ON_SubD::EdgeTag::Unset;
ON_SubDComponentMarksClearAndRestore mark_guard(*this);
// Marks very vertex touching a component in the cptr_list.
// Skips applying the transform because it is the identity.
unsigned int list_vertex_count = 0;
unsigned int list_edge_count = 0;
unsigned int list_face_count = 0;
const unsigned int marked_vertex_count = Internal_MarkExtrudeComponents(
*this,
cptr_list, cptr_count,
bExtrudeBoundaries,
list_vertex_count, list_edge_count, list_face_count
);
unsigned int moved_face_count = 0; // moved face count
unsigned int new_face_count = 0; // number of new faces on moved boundary
for (;;)
{
if (0 == marked_vertex_count)
break;
// Set moved_faces[] = list of faces that will move.
ON_SimpleArray<const ON_SubDFace*> moved_faces(list_face_count + 128);
ON_SubDFaceIterator fit(*this);
for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace())
{
const unsigned int face_vertex_count = f->m_edge_count;
if (face_vertex_count < 3 || false == f->m_status.RuntimeMark())
continue;
moved_faces.Append(f);
}
moved_face_count = moved_faces.UnsignedCount();
// Mark edges on the boundary of the moved subset.
// Set moved_edges[] = list of edges that are either
// 1) between a moved face and a stationary face
// 2) are a boundary edge to be extruded.
ON_SimpleArray<const ON_SubDEdge*> moved_edges(list_edge_count + list_vertex_count);
ON_SimpleArray<ON_Internal_ExtrudedSide> new_sides(64);
ON_SubDEdgeIterator eit(*this);
for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge())
{
const bool bExtrudedBoundaryEdge
= bExtrudeBoundaries
&& e->m_status.RuntimeMark()
&& 1 == e->m_face_count
&& ON_SubD::EdgeTag::Crease == e->m_edge_tag
;
bool bExtrudeEdge = bExtrudedBoundaryEdge;
e->m_status.ClearRuntimeMark();
bool bMarkedFace = false;
bool bUnmarkedFace = false;
const unsigned int edge_face_count = e->m_face_count;
for (unsigned int efi = 0; efi < edge_face_count; efi++)
{
const ON_SubDFace* f = e->Face(efi);
if (nullptr == f)
continue;
if (f->m_status.RuntimeMark())
bMarkedFace = true; // f is in the subset of moved faces.
else
bUnmarkedFace = true; // f is in the subset of stationary faces.
if (bMarkedFace && bUnmarkedFace)
{
// e is on the boundary between the subset of moved faces and stationary faces.
bExtrudeEdge = true;
break;
}
}
if (bExtrudeEdge)
{
if (false == bMarkedFace)
moved_edges.Append(e); // e is not part of a moved face's boundary
e->m_status.SetRuntimeMark();
ON_Internal_ExtrudedSide& newside = new_sides.AppendNew();
newside.m_marked_edge = const_cast<ON_SubDEdge*>(e);
newside.m_bExtrudedBoundaryEdge = bExtrudedBoundaryEdge;
newside.m_bHasMovedFace = bMarkedFace;
newside.m_bHasStationaryFace = bUnmarkedFace;
newside.m_original_marked_edge_tag = e->m_edge_tag;
}
}
new_face_count = new_sides.UnsignedCount();
if (0 == new_face_count)
{
// no new faces will be made
if (moved_face_count > 0)
{
// Every face is moving
Transform(xform);
}
break;
}
if (false == bPermitNonManifoldEdgeCreation)
{
bool bNonManifoldEdgeWillBeCreated = false;
for (unsigned int fi = 0; fi < moved_face_count; fi++)
{
const ON_SubDFace* f = moved_faces[fi];
const unsigned int face_vertex_count = f->m_edge_count;
for (unsigned int fvi = 0; fvi < face_vertex_count; fvi++)
{
bNonManifoldEdgeWillBeCreated = Internal_NonManifoldEdgeWillBeCreated(f->Vertex(fvi));
if (bNonManifoldEdgeWillBeCreated)
break;
}
if (bNonManifoldEdgeWillBeCreated)
break;
}
if (bNonManifoldEdgeWillBeCreated)
break; // not allowd to create non-manifold edges
for (unsigned int ei = 0; ei < moved_edges.UnsignedCount(); ei++)
{
const ON_SubDEdge* e = moved_edges[ei];
for (unsigned int evi = 0; evi < 2; evi++)
{
bNonManifoldEdgeWillBeCreated = Internal_NonManifoldEdgeWillBeCreated(e->m_vertex[evi]);
if (bNonManifoldEdgeWillBeCreated)
break;
}
if (bNonManifoldEdgeWillBeCreated)
break;
}
if (bNonManifoldEdgeWillBeCreated)
break; // not allowd to create non-manifold edges
}
// clear vertex marks.
ClearComponentMarks(true, false, false, nullptr);
// Duplicate vertices that are on an edge between a marked and unmarked face
// or a moved boundary edge
ON_SimpleArray<ON_Internal_ExtrudedVertexPair> vertex_pairs(new_face_count+8);
for (unsigned int i = 0; i < new_face_count; i++)
{
const ON_SubDEdge* e = new_sides[i].m_marked_edge;
for (unsigned int evi = 0; evi < 2; evi++)
{
ON_SubDVertex* v = const_cast<ON_SubDVertex*>(e->m_vertex[evi]);
if (nullptr == v)
continue;
if (v->m_status.RuntimeMark())
continue;
// Mark this vertex. It will eventually get moved
v->m_status.SetRuntimeMark();
ON_Internal_ExtrudedVertexPair& vpair = vertex_pairs.AppendNew();
vpair.m_marked_vertex = v;
ON_SubD::VertexTag moved_vertex_tag = ON_SubD::VertexTag::Unset; // the original vertex gets moved
ON_SubD::VertexTag stationary_vertex_tag = ON_SubD::VertexTag::Unset; // (this one get allocated)
ON_SubD::EdgeTag connecting_edge_tag = ON_SubD::EdgeTag::Unset; // from stationary to moved vertex
switch (v->m_vertex_tag)
{
case ON_SubD::VertexTag::Dart:
connecting_edge_tag = Internal_ConnectingEdgeTagAtVertex(bExtrudeBoundaries, v, moved_vertex_tag, stationary_vertex_tag);
break;
case ON_SubD::VertexTag::Crease:
connecting_edge_tag = Internal_ConnectingEdgeTagAtVertex(bExtrudeBoundaries, v, moved_vertex_tag, stationary_vertex_tag);
break;
case ON_SubD::VertexTag::Corner:
moved_vertex_tag = v->m_vertex_tag;
stationary_vertex_tag = v->m_vertex_tag;
connecting_edge_tag = ON_SubD::EdgeTag::Crease;
break;
case ON_SubD::VertexTag::Smooth:
moved_vertex_tag = ON_SubD::VertexTag::Smooth;
stationary_vertex_tag = ON_SubD::VertexTag::Smooth;
connecting_edge_tag = ON_SubD::EdgeTag::Smooth;
break;
default:
moved_vertex_tag = ON_SubD::VertexTag::Unset;
stationary_vertex_tag = ON_SubD::VertexTag::Unset;
connecting_edge_tag = ON_SubD::EdgeTag::Unset;
break;
}
// original vertex will eventually be moved.
v->m_vertex_tag = moved_vertex_tag;
// new vertex will become part of the stationary subset.
// It is not marked.
vpair.m_unmarked_vertex = this->AddVertex(stationary_vertex_tag, v->m_P);
// transform the marked boundary vertex
v->Transform(false, xform);
// edge from stationary subset to moved subset.
ON_SubDEdge* connecting_edge = this->AddEdge(connecting_edge_tag, vpair.m_unmarked_vertex, vpair.m_marked_vertex);
vpair.m_new_side = connecting_edge;
}
}
// sort vertex pairs so they can be located by the original vertex id.
vertex_pairs.QuickSort(ON_Internal_ExtrudedVertexPair::CompareMarkedVertexId);
// remove unmarked faces from marked vertices
for (unsigned int i = 0; i < vertex_pairs.UnsignedCount(); i++)
{
ON_SubDVertex* marked_vertex = vertex_pairs[i].m_marked_vertex;
ON_SubDVertex* unmarked_vertex = vertex_pairs[i].m_unmarked_vertex;
const unsigned int vertex_face_count0 = marked_vertex->m_face_count;
GrowVertexFaceArray(unmarked_vertex, vertex_face_count0);
marked_vertex->m_face_count = 0;
for (unsigned int vfi = 0; vfi < vertex_face_count0; vfi++)
{
const ON_SubDFace* f = marked_vertex->m_faces[vfi];
if (nullptr == f )
continue;
ON_SubDVertex* v
= (f->m_status.RuntimeMark())
? marked_vertex
: unmarked_vertex;
v->m_faces[v->m_face_count] = f;
v->m_face_count++;
}
}
// build new side edges
ON_Internal_ExtrudedVertexPair key[2];
for (unsigned int i = 0; i < new_face_count; i++)
{
const ON_SubDEdge* e = new_sides[i].m_marked_edge;
for (unsigned int evi = 0; evi < 2; evi++)
{
key[evi].m_marked_vertex = const_cast<ON_SubDVertex*>(e->m_vertex[evi]);
const int i0 =
(nullptr != key[evi].m_marked_vertex)
? vertex_pairs.BinarySearch(&key[evi], ON_Internal_ExtrudedVertexPair::CompareMarkedVertexId)
: -1;
if (i0 < 0)
{
key[evi] = ON_Internal_ExtrudedVertexPair::Unset;
continue;
}
key[evi] = vertex_pairs[i0];
}
const ON_SubD::EdgeTag unmarked_edge_tag = new_sides[i].UnmarkedEdgeTag(key[0].m_unmarked_vertex, key[1].m_unmarked_vertex);
new_sides[i].m_unmarked_edge = this->AddEdge(unmarked_edge_tag, key[0].m_unmarked_vertex, key[1].m_unmarked_vertex);
new_sides[i].m_new_side0 = key[0].m_new_side;
new_sides[i].m_new_side1 = key[1].m_new_side;
}
// Mark everything a moved face touches
// including interior edges and vertices.
// Transform any vertices that are not already marked.
for (unsigned int i = 0; i < moved_face_count; i++)
{
const ON_SubDFace* f = moved_faces[i];
const unsigned int face_edge_count = f->m_edge_count;
for (unsigned int fei = 0; fei < face_edge_count; ++fei)
{
const ON_SubDEdge* e = f->Edge(fei);
if (nullptr == e)
continue;
e->m_status.SetRuntimeMark();
for (unsigned int evi = 0; evi < 2; evi++)
{
ON_SubDVertex* v = const_cast<ON_SubDVertex*>(e->m_vertex[evi]);
if (nullptr !=v && false == v->m_status.RuntimeMark())
{
v->Transform(false, xform);
v->m_status.SetRuntimeMark();
}
}
}
}
// Mark everything vertex a moved edge touches
// Transform any vertices that are not already marked.
for (unsigned int i = 0; i < moved_edges.UnsignedCount(); i++)
{
const ON_SubDEdge* e = moved_edges[i];
const unsigned int edge_face_count = e->m_face_count;
for (unsigned int efi = 0; efi < edge_face_count; ++efi)
{
e->m_status.SetRuntimeMark();
for (unsigned int evi = 0; evi < 2; evi++)
{
ON_SubDVertex* v = const_cast<ON_SubDVertex*>(e->m_vertex[evi]);
if (nullptr !=v && false == v->m_status.RuntimeMark())
{
v->Transform(false, xform);
v->m_status.SetRuntimeMark();
}
}
}
}
// For the original boundary vertrex, move unmarked edges to use the new vertex.
for (unsigned int i = 0; i < vertex_pairs.UnsignedCount(); i++)
{
Internal_SetEdgeVertices(*this, vertex_pairs[i]);
}
// build new side faces
for (unsigned int i = 0; i < new_face_count; i++)
{
Internal_AddNewFace(*this, new_sides[i]);
}
// Any edge touching the vertices in vertex_pairs[] may need its tag adjusted
// because those vertices may have had their tags adjusted.
// Some edges get checked twice, but adding code to check just once
// takes more time than checking twice.
for (unsigned int i = 0; i < vertex_pairs.UnsignedCount(); i++)
{
for (unsigned int j = 0; j < 2; j++)
{
const ON_SubDVertex* v = (0 == j) ? vertex_pairs[i].m_marked_vertex : vertex_pairs[i].m_unmarked_vertex;
if (nullptr == v || nullptr == v->m_edges)
continue;
for (unsigned int vei = 0; vei < v->m_edge_count; vei++)
{
ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(v->m_edges[vei].m_ptr);
if (nullptr == e)
continue;
const ON_SubD::EdgeTag adjusted_etag = Internal_AdjustedEdgeTag(e);
if (ON_SubD::EdgeTag::Unset != adjusted_etag && e->m_edge_tag != adjusted_etag)
e->m_edge_tag = adjusted_etag;
}
}
}
// remove cached subdivision calculations
ClearEvaluationCache();
// Calculate vertex tags, edge tags, edge sector weights.
this->UpdateAllTagsAndSectorCoefficients(true);
break;
}
#if defined(ON_DEBUG)
IsValid();
#endif
// TODO - run through new_sides[] array and attempt to adjust tags
//if (ON_SubD::EdgeTag::Unset != original_edge_tag || ON_SubD::EdgeTag::Unset != moved_edge_tag)
//{
//
//}
// number of moved faces and new boundary faces
return moved_face_count + new_face_count;
}
unsigned int ON_SubD::SetVertexTags(
const ON_COMPONENT_INDEX* ci_list,
size_t ci_count,
ON_SubD::VertexTag vertex_tag
)
{
if (
ON_SubD::VertexTag::Smooth != vertex_tag
&& ON_SubD::VertexTag::Crease != vertex_tag
&& ON_SubD::VertexTag::Corner != vertex_tag
)
return 0;
if (
ci_count <= 0
|| nullptr == ci_list
|| VertexCount() <= 0
)
return 0;
ON_SimpleArray<ON_SubDComponentPtr> cptr_list;
if (ComponentPtrFromComponentIndex(ci_list,ci_count,cptr_list) <= 0)
return 0; // nothing to change
return SetVertexTags(
cptr_list.Array(),
cptr_list.UnsignedCount(),
vertex_tag
);
}
unsigned int ON_SubD::SetVertexTags(
const ON_SubDComponentPtr* cptr_list,
size_t cptr_count,
ON_SubD::VertexTag vertex_tag
)
{
if (
ON_SubD::VertexTag::Smooth != vertex_tag
&& ON_SubD::VertexTag::Crease != vertex_tag
&& ON_SubD::VertexTag::Corner != vertex_tag
)
return 0;
if (cptr_count <= 0 || nullptr == cptr_list)
return 0;
ON_SubDComponentMarksClearAndRestore mark_guard(*this);
const bool bNewVertexTagIsSmooth = (ON_SubD::VertexTag::Smooth == vertex_tag);
// count and mark vertex candidates
// mark edges that may need to have their tag changed
unsigned int candidate_count = 0;
for (size_t i = 0; i < cptr_count; i++)
{
ON_SubDVertex* vertex = cptr_list[i].Vertex();
if (nullptr == vertex)
continue;
if (vertex->m_vertex_tag == vertex_tag)
continue;
if (ON_SubD::VertexTag::Corner != vertex_tag)
{
// new vertex_tag is Smooth or Crease
if (nullptr == vertex->m_edges || vertex->m_edge_count < 2)
continue;
const ON_SubDVertexEdgeProperties ep = vertex->EdgeProperties();
if (ep.m_nonmanifold_edge_count > 0)
continue; // nonmanifold edge
if (ep.m_boundary_edge_count + ep.m_wire_edge_count > 2)
continue;
if (ON_SubD::VertexTag::Smooth == vertex_tag)
{
if (ep.m_interior_edge_count != vertex->m_edge_count)
continue;
}
else if (ON_SubD::VertexTag::Crease == vertex_tag)
{
if (2 == ep.m_crease_edge_count)
{
// attempt change - further refinement may be needed here
}
else if (2 == ep.m_boundary_edge_count)
{
// attempt change - further refinement may be needed here
}
else if (2 == ep.m_wire_edge_count)
{
// attempt change - further refinement may be needed here
}
else
{
// dont' attempt change - further refinement may be needed here
continue;
}
}
}
candidate_count++;
vertex->m_status.SetRuntimeMark();
if (nullptr != vertex->m_edges)
{
if (ON_SubD::VertexTag::Corner == vertex_tag)
{
const unsigned int crease_count = vertex->EdgeCount(ON_SubD::EdgeTag::Crease);
if (2 == crease_count)
continue; // do not crease additional edges
}
for (unsigned short vei = 0; vei < vertex->m_edge_count; vei++)
{
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(vertex->m_edges[vei].m_ptr);
if (nullptr == edge)
continue;
if (bNewVertexTagIsSmooth)
{
// new vertex_tag is Smooth
if (edge->IsSmoothNotX())
continue;
}
else
{
// new vertex_tag is Crease or Corner
if (edge->IsCrease())
continue;
}
// This edge tag will need to be changed
edge->m_status.SetRuntimeMark();
}
}
}
if (0 == candidate_count)
return 0;
bool bUpdateTags = (ON_SubD::VertexTag::Crease != vertex_tag);
// This for loop is used when new vertex_tag is ON_SubD::VertexTag::Crease.
for (int pass = 0; pass < 2 && false == bUpdateTags; pass++)
{
// More careful analysis is neeeded to accurately mark smooth edges that will become creases
ON_SubDEdgeIterator eit(*this);
for (const ON_SubDEdge* edge = eit.FirstEdge(); nullptr != edge; edge = eit.NextEdge())
{
if (false == edge->m_status.RuntimeMark())
continue;
edge->m_status.ClearRuntimeMark();
if (false == edge->IsSmooth())
continue;
const ON_SubDVertex* v[2] = { edge->m_vertex[0], edge->m_vertex[1] };
if (nullptr == v[0] || nullptr == v[1])
continue;
const ON_SubD::VertexTag vtag[2] = {
(v[0]->m_status.RuntimeMark() ? vertex_tag : v[0]->m_vertex_tag),
(v[1]->m_status.RuntimeMark() ? vertex_tag : v[1]->m_vertex_tag)
};
// At least one of v[0] and v[1] had m_vertex_tag changed.
ON_SubD::EdgeTag edge_tag;
for (;;)
{
if (2 != edge->m_face_count)
{
edge_tag = ON_SubD::EdgeTag::Crease;
break;
}
if (2 == v[0]->m_edge_count && (ON_SubD::VertexTag::Crease == vtag[0] || ON_SubD::VertexTag::Corner == vtag[0]))
{
edge_tag = ON_SubD::EdgeTag::Crease;
break;
}
if (2 == v[1]->m_edge_count && (ON_SubD::VertexTag::Crease == vtag[1] || ON_SubD::VertexTag::Corner == vtag[1]))
{
edge_tag = ON_SubD::EdgeTag::Crease;
break;
}
if (
(ON_SubD::VertexTag::Crease == vtag[0] || ON_SubD::VertexTag::Corner == vtag[0] || ON_SubD::VertexTag::Dart == vtag[0])
&&
(ON_SubD::VertexTag::Crease == vtag[1] || ON_SubD::VertexTag::Corner == vtag[1] || ON_SubD::VertexTag::Dart == vtag[1])
)
{
edge_tag = ON_SubD::EdgeTag::Crease;
break;
}
edge_tag = ON_SubD::EdgeTag::Smooth;
break;
}
if (ON_SubD::EdgeTag::Crease == edge_tag)
edge->m_status.SetRuntimeMark();
}
// make sure new crease vertices will have the right number of creased edges
bUpdateTags = true;
for (size_t i = 0; i < cptr_count; i++)
{
ON_SubDVertex* vertex = cptr_list[i].Vertex();
if (nullptr == vertex)
continue;
if (false == vertex->m_status.RuntimeMark())
continue;
unsigned int crease_count = 0;
unsigned int marked_count = 0;
for (unsigned short vei = 0; vei < vertex->m_edge_count; vei++)
{
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(vertex->m_edges[vei].m_ptr);
if (nullptr == edge)
continue;
if (edge->IsCrease())
++crease_count;
else if (edge->m_status.RuntimeMark())
++marked_count;
}
if (crease_count + marked_count <= 2 && (0 != crease_count || 0 != marked_count))
continue;
if (pass > 0)
return 0;
bUpdateTags = false;
if (2 == crease_count)
{
for (unsigned short vei = 0; vei < vertex->m_edge_count; vei++)
{
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(vertex->m_edges[vei].m_ptr);
if (nullptr != edge)
edge->m_status.ClearRuntimeMark();
}
}
else
{
vertex->m_status.ClearRuntimeMark();
candidate_count--;
}
}
if (0 == candidate_count)
return 0;
}
if (false == bUpdateTags)
return 0;
unsigned int changed_vertex_count = 0;
for (size_t i = 0; i < cptr_count; i++)
{
ON_SubDVertex* vertex = cptr_list[i].Vertex();
if (nullptr == vertex)
continue;
if (false == vertex->m_status.RuntimeMark())
continue;
changed_vertex_count++;
vertex->m_vertex_tag = vertex_tag;
vertex->VertexModifiedNofification();
for (unsigned short vei = 0; vei < vertex->m_edge_count; vei++)
{
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(vertex->m_edges[vei].m_ptr);
if (nullptr == edge)
continue;
if ( false == bNewVertexTagIsSmooth
&& edge->m_status.RuntimeMark()
&& false == edge->IsCrease()
)
{
const_cast<ON_SubDEdge*>(edge)->m_edge_tag = ON_SubD::EdgeTag::Crease;
edge->EdgeModifiedNofification();
}
edge->m_status.SetRuntimeMark();
const ON_SubDVertex* other_vertex = edge->OtherEndVertex(vertex);
other_vertex->m_status.SetRuntimeMark();
if ( false == bNewVertexTagIsSmooth
&& ON_SubD::EdgeTag::Crease == edge->m_edge_tag
&& other_vertex->IsSmooth()
)
{
const_cast<ON_SubDVertex*>(other_vertex)->m_vertex_tag = ON_SubD::VertexTag::Dart;
other_vertex->VertexModifiedNofification();
}
}
}
if (0 == changed_vertex_count)
return 0;
ON_SubDEdgeIterator eit(*this);
for (const ON_SubDEdge* edge = eit.FirstEdge(); nullptr != edge; edge = eit.NextEdge())
{
if (false == edge->m_status.RuntimeMark())
continue;
const ON_SubDVertex* v[2] = { edge->m_vertex[0], edge->m_vertex[1] };
if (nullptr == v[0] || nullptr == v[1])
continue;
ON_SubD::EdgeTag edge_tag;
if (v[0]->IsDartOrCreaseOrCorner() && v[1]->IsDartOrCreaseOrCorner())
edge_tag = ON_SubD::EdgeTag::Crease;
else
edge_tag = ON_SubD::EdgeTag::Smooth;
if (edge->m_edge_tag == edge_tag)
continue;
const_cast<ON_SubDEdge*>(edge)->m_edge_tag = edge_tag;
edge->EdgeModifiedNofification();
}
ON_SubDVertexIterator vit(*this);
for (const ON_SubDVertex* vertex = vit.FirstVertex(); nullptr != vertex; vertex = vit.NextVertex())
{
if (false == vertex->m_status.RuntimeMark())
continue;
const unsigned int crease_count = vertex->EdgeCount(ON_SubD::EdgeTag::Crease);
ON_SubD::VertexTag vtag = vertex->m_vertex_tag;
if (2 == crease_count)
{
if ( false == vertex->IsCreaseOrCorner() )
vtag = ON_SubD::VertexTag::Crease;
}
else if (1 == crease_count)
vtag = ON_SubD::VertexTag::Dart;
else if (crease_count > 2)
vtag = ON_SubD::VertexTag::Corner;
else
vtag = ON_SubD::VertexTag::Smooth;
if (vertex->m_vertex_tag == vtag)
continue;
const_cast<ON_SubDVertex*>(vertex)->m_vertex_tag = vtag;
vertex->ClearSavedSubdivisionPoints();
}
ClearEvaluationCache();
UpdateAllTagsAndSectorCoefficients(false);
return changed_vertex_count;
}
unsigned int ON_SubD::SetEdgeTags(
const ON_COMPONENT_INDEX* ci_list,
size_t ci_count,
ON_SubD::EdgeTag edge_tag
)
{
if (ON_SubD::EdgeTag::Smooth != edge_tag && ON_SubD::EdgeTag::Crease != edge_tag)
return 0;
if (
ci_count <= 0
|| nullptr == ci_list
|| EdgeCount() <= 0
)
return 0;
ON_SimpleArray<ON_SubDComponentPtr> cptr_list;
if (ComponentPtrFromComponentIndex(ci_list,ci_count,cptr_list) <= 0)
return 0; // nothing to change
return SetEdgeTags(
cptr_list.Array(),
cptr_list.UnsignedCount(),
edge_tag
);
}
unsigned int ON_SubD::SetEdgeTags(
const ON_SubDComponentPtr* cptr_list,
size_t cptr_count,
ON_SubD::EdgeTag edge_tag
)
{
if (ON_SubD::EdgeTag::Smooth != edge_tag && ON_SubD::EdgeTag::Crease != edge_tag)
return 0;
if (
cptr_count <= 0
|| nullptr == cptr_list
|| EdgeCount() <= 0
)
return 0;
unsigned int changed_edge_count = 0;
const bool bChangeToSmooth = (ON_SubD::EdgeTag::Smooth == edge_tag) ? true : false;
for (size_t i = 0; i < cptr_count; i++)
{
ON_SubDEdge* edge = cptr_list[i].Edge();
if (nullptr == edge)
continue;
if (bChangeToSmooth == edge->IsSmooth())
continue;
if (bChangeToSmooth && 2 != edge->FaceCount())
continue;
edge->EdgeModifiedNofification();
changed_edge_count++;
edge->m_edge_tag = edge_tag;
edge->UnsetSectorCoefficientsForExperts();
for (int evi = 0; evi < 2; evi++)
{
ON_SubDVertex* v = const_cast<ON_SubDVertex*>(edge->m_vertex[evi]);
if (nullptr == v)
continue;
v->m_vertex_tag = ON_SubD::VertexTag::Unset;
v->ClearSavedSubdivisionPoints();
}
}
if (0 == changed_edge_count)
return 0;
ClearEvaluationCache();
ON_SubDVertexIterator vit(*this);
for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex())
{
if (ON_SubD::VertexTag::Unset != v->m_vertex_tag)
continue;
unsigned crease_count = 0;
const unsigned vertex_edge_count = v->EdgeCount();
for (unsigned vei = 0; vei < vertex_edge_count; vei++)
{
const ON_SubDEdge* e = v->Edge(vei);
if (nullptr == e)
continue;
if (e->IsCrease())
{
crease_count++;
if (crease_count > 2)
break;
}
}
ON_SubD::VertexTag vertex_tag;
switch (crease_count)
{
case 0:
vertex_tag = ON_SubD::VertexTag::Smooth;
break;
case 1:
vertex_tag = ON_SubD::VertexTag::Dart;
break;
case 2:
vertex_tag = ON_SubD::VertexTag::Crease;
break;
default:
vertex_tag = ON_SubD::VertexTag::Corner;
break;
}
if (v->m_vertex_tag != vertex_tag)
{
const_cast<ON_SubDVertex*>(v)->m_vertex_tag = vertex_tag;
v->ClearSavedSubdivisionPoints();
}
}
ON_SubDEdgeIterator eit(*this);
for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge())
{
ON_SubD::EdgeTag e_tag = e->m_edge_tag;
if (nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1])
e_tag = ON_SubD::EdgeTag::Unset;
else if (ON_SubD::EdgeTag::Smooth == e_tag
&& ON_SubD::VertexTag::Smooth != e->m_vertex[0]->m_vertex_tag
&& ON_SubD::VertexTag::Smooth != e->m_vertex[1]->m_vertex_tag)
e_tag = ON_SubD::EdgeTag::Unset;
if (e_tag != e->m_edge_tag)
{
const_cast<ON_SubDEdge*>(e)->m_edge_tag = e_tag;
e->UnsetSectorCoefficientsForExperts();
e->ClearSavedSubdivisionPoints();
}
}
UpdateAllTagsAndSectorCoefficients(false);
return changed_edge_count;
}
unsigned int ON_SubD::RemoveAllCreases()
{
unsigned int changed_count = 0;
ON_SubDEdgeIterator eit(*this);
for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge())
{
if ( false == e->IsCrease() || 2 != e->m_face_count)
continue;
const_cast<ON_SubDEdge*>(e)->m_edge_tag = ON_SubD::EdgeTag::Smooth;
e->UnsetSectorCoefficientsForExperts();
for (int evi = 0; evi < 2; evi++)
{
if (nullptr == e->m_vertex[evi])
continue;
const_cast<ON_SubDVertex*>(e->m_vertex[evi])->m_vertex_tag = ON_SubD::VertexTag::Unset;
}
++changed_count;
}
if (changed_count > 0)
{
this->DestroyRuntimeCache(true);
this->UpdateAllTagsAndSectorCoefficients(true);
}
return changed_count;
}
const ON_SubDEdgePtr ON_SubDEdgeChain::EdgeChainNeighbor(ON_SubDEdgePtr starting_edge, ON_ChainDirection search_direction, ON_SubD::ChainType chain_type)
{
return ON_SubDEdgeChain::EdgeChainNeighbor(
starting_edge,
search_direction,
chain_type,
false,
ON_ComponentStatus::NoneSet,
ON_ComponentStatus::NoneSet
);
}
const ON_SubDEdgePtr ON_SubDEdgeChain::EdgeChainNeighbor(
ON_SubDEdgePtr starting_edge,
ON_ChainDirection search_direction,
ON_SubD::ChainType chain_type,
bool bEnableStatusCheck,
ON_ComponentStatus status_pass,
ON_ComponentStatus status_fail
)
{
for (;;)
{
if (ON_ChainDirection::Previous != search_direction && ON_ChainDirection::Next != search_direction)
break;
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(starting_edge.m_ptr);
if (nullptr == edge)
break;
bool bReverse = (ON_ChainDirection::Previous == search_direction);
if (0 != ON_SUBD_EDGE_DIRECTION(starting_edge.m_ptr))
bReverse = !bReverse;
const ON_SubDVertex* v = edge->m_vertex[bReverse ? 0 : 1];
if (nullptr == v)
break;
if (v->m_edge_count <= 1 || nullptr == v->m_edges)
break;
const bool bIsSmooth = edge->IsSmooth();
const bool bIsCrease = edge->IsCrease() || 2 != edge->m_face_count;
if (bIsSmooth != (bIsCrease?false:true))
break;
if (ON_SubD::ChainType::EqualEdgeAndVertexTag == chain_type)
{
if (bIsSmooth)
{
if (ON_SubD::VertexTag::Smooth != v->m_vertex_tag)
break;
}
else
{
if (ON_SubD::VertexTag::Crease != v->m_vertex_tag)
break;
}
}
// Look for a single neighbor with same crease/smooth property and same face count
// This lets chains turn the right way when there are both creases and smooth
// edges.
const ON_SubDEdge* nxt = nullptr;
for (unsigned short vei = 0; vei < v->m_edge_count; vei++)
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(v->m_edges[vei].m_ptr);
if (edge == e)
continue;
if (bIsSmooth != e->IsSmooth())
continue;
if (bIsCrease != e->IsCrease())
continue;
if (e->m_face_count != edge->m_face_count)
continue;
if (e->m_vertex[0] != v && e->m_vertex[1] != v)
continue; // bogus edge
if (nullptr == nxt)
{
nxt = e;
continue;
}
// ambiguous options here
nxt = nullptr;
break;
}
const int nxt_connecting_vertex_index = (ON_ChainDirection::Next == search_direction) ? 0 : 1;
if (nullptr != nxt)
{
if (false == bEnableStatusCheck || ON_ComponentStatus::StatusCheck(nxt->m_status, status_pass, status_fail))
return ON_SubDEdgePtr::Create(nxt, (v == nxt->m_vertex[nxt_connecting_vertex_index]) ? 0 : 1);
}
if (2 != edge->m_face_count || 4 != v->m_edge_count)
break;
const ON_SubDFace* f[2] = { ON_SUBD_FACE_POINTER(edge->m_face2[0].m_ptr),ON_SUBD_FACE_POINTER(edge->m_face2[1].m_ptr) };
if (nullptr == f[0] || nullptr == f[1] || f[0] == f[1])
break;
for (unsigned short vei = 0; vei < v->m_edge_count; vei++)
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(v->m_edges[vei].m_ptr);
if (edge == e)
continue;
if (2 != e->m_face_count)
continue;
if (e->m_vertex[0] != v && e->m_vertex[1] != v)
continue; // bogus edge
const ON_SubDFace* nxtf[2] = { ON_SUBD_FACE_POINTER(e->m_face2[0].m_ptr),ON_SUBD_FACE_POINTER(e->m_face2[1].m_ptr) };
if (nullptr == nxtf[0] || nullptr == nxtf[1] || nxtf[0] == nxtf[1])
continue;
if (f[0] == nxtf[0] || f[1] == nxtf[0])
continue;
if (f[0] == nxtf[1] || f[1] == nxtf[1])
continue;
if (nullptr == nxt)
{
nxt = e;
continue;
}
// ambiguous options here
nxt = nullptr;
break;
}
if (nullptr != nxt)
{
if (bIsSmooth != nxt->IsSmooth())
{
if ( ON_SubD::ChainType::EqualEdgeTag == chain_type || ON_SubD::ChainType::EqualEdgeAndVertexTag == chain_type)
break;
}
if (false == bEnableStatusCheck || ON_ComponentStatus::StatusCheck(nxt->m_status, status_pass, status_fail))
return ON_SubDEdgePtr::Create(nxt, (v == nxt->m_vertex[nxt_connecting_vertex_index]) ? 0 : 1);
}
break;
}
return ON_SubDEdgePtr::Null;
}
void ON_UniqueTester::Block::DeleteBlock(Block* blk)
{
if (nullptr != blk)
onfree(blk);
}
ON_UniqueTester::Block* ON_UniqueTester::Block::NewBlock()
{
size_t sz1 = sizeof(Block);
while (0 != sz1 % 8)
sz1++;
size_t sz2 = ON_UniqueTester::Block::BlockCapacity * sizeof(m_a[0]);
void* p = onmalloc(sz1 + sz2);
Block* blk = new (p) Block();
blk->m_a = (ON__UINT_PTR*)((char*)(p)) + sz1;
return blk;
}
int ON_UniqueTester::Block::Compare(ON__UINT_PTR* lhs, ON__UINT_PTR* rhs)
{
if (*lhs < *rhs)
return -1;
if (*lhs > *rhs)
return 1;
return 0;
}
bool ON_UniqueTester::Block::InBlock(size_t sorted_count,ON__UINT_PTR x) const
{
if (nullptr != m_a && m_count > 0)
{
if ( sorted_count > 0 && x >= m_a[0] && x <= m_a[sorted_count - 1])
{
if (nullptr != bsearch(&x, m_a, sorted_count, sizeof(m_a[0]), (int(*)(const void*, const void*))ON_UniqueTester::Block::Compare))
return true;
}
if (sorted_count < m_count)
{
const ON__UINT_PTR* p = m_a + sorted_count;
const ON__UINT_PTR* p1 = m_a + m_count;
while (p < p1)
{
if (x == *p++)
return true;
}
}
}
return false;
}
void ON_UniqueTester::Block::SortBlock()
{
ON_qsort(m_a, m_count, sizeof(m_a[0]), (int(*)(const void*, const void*))ON_UniqueTester::Block::Compare);
}
void ON_UniqueTester::Internal_CopyFrom(
const ON_UniqueTester & src
)
{
m_block_list = nullptr;
m_sorted_count = 0;
Block* first_blk = nullptr;
for ( Block* src_blk = src.m_block_list; nullptr != src_blk; src_blk = src_blk->m_next)
{
Block* blk = Block::NewBlock();
memcpy( blk->m_a, src_blk->m_a, src_blk->m_count * sizeof(blk->m_a[0]) );
blk->m_count = src_blk->m_count;
if (nullptr == first_blk)
{
first_blk = blk;
}
else
{
blk->m_next = m_block_list;
m_block_list = blk;
}
}
if (nullptr != first_blk)
{
if (src.m_sorted_count != first_blk->m_count)
first_blk->SortBlock();
first_blk->m_next = m_block_list;
m_block_list = first_blk;
m_sorted_count = first_blk->m_count;
}
}
void ON_UniqueTester::Internal_Destroy()
{
Block* nxt = m_block_list;
m_block_list = nullptr;
m_sorted_count = 0;
for ( Block* blk = nxt; nullptr != blk; blk = nxt)
{
nxt = blk->m_next;
Block::DeleteBlock(blk);
}
}
ON_UniqueTester::~ON_UniqueTester()
{
Internal_Destroy();
}
ON_UniqueTester::ON_UniqueTester(const ON_UniqueTester& src)
{
Internal_CopyFrom(src);
}
ON_UniqueTester& ON_UniqueTester::operator=(const ON_UniqueTester& src)
{
if (this != &src)
{
Internal_Destroy();
Internal_CopyFrom(src);
}
return *this;
}
bool ON_UniqueTester::InList(ON__UINT_PTR x) const
{
size_t sorted_count = m_sorted_count;
for ( const Block* blk = m_block_list; nullptr != blk; blk = blk->m_next)
{
if (blk->InBlock(sorted_count, x))
return true;
sorted_count = ON_UniqueTester::Block::BlockCapacity;
}
return false;
}
bool ON_UniqueTester::AddToList(ON__UINT_PTR x)
{
if (nullptr != m_block_list && m_sorted_count + 50 == m_block_list->m_count)
{
m_block_list->SortBlock();
m_sorted_count = m_block_list->m_count;
}
if (InList(x))
return false;
Internal_AddValue(x);
return true;
}
void ON_UniqueTester::ExpertAddNewToList(ON__UINT_PTR x)
{
Internal_AddValue(x);
}
void ON_UniqueTester::Internal_AddValue(ON__UINT_PTR x)
{
if (nullptr == m_block_list || ON_UniqueTester::Block::BlockCapacity == m_block_list->m_count)
{
if (nullptr != m_block_list && m_sorted_count < ON_UniqueTester::Block::BlockCapacity)
m_block_list->SortBlock();
ON_UniqueTester::Block* blk = ON_UniqueTester::Block::NewBlock();
blk->m_next = m_block_list;
m_block_list = blk;
m_sorted_count = 0;
}
m_block_list->m_a[m_block_list->m_count++] = x;
if ( 1 == m_block_list->m_count
|| (m_sorted_count+1 == m_block_list->m_count && x > m_block_list->m_a[m_sorted_count-1])
)
++m_sorted_count;
}
void ON_UniqueTester::ClearList()
{
Internal_Destroy();
}
unsigned int ON_UniqueTester::Count() const
{
size_t count = 0;
for ( const Block* blk = m_block_list; nullptr != blk; blk = blk->m_next)
{
count += blk->m_count;
}
return (unsigned int)count;
}
const ON_SimpleArray<ON_SubDEdgePtr>& ON_SubDEdgeChain::EdgeChain() const
{
return m_edge_chain;
}
const ON_SubD& ON_SubDEdgeChain::SubD() const
{
return m_subd_ref.SubD();
}
const ON_SubDRef ON_SubDEdgeChain::SubDRef() const
{
return m_subd_ref;
}
bool ON_SubDEdgeChain::InChain(
const ON_SubDEdgePtr edge_ptr
) const
{
return InChain(ON_SUBD_EDGE_POINTER(edge_ptr.m_ptr));
}
bool ON_SubDEdgeChain::InChain(
const ON_SubDEdge* edge
) const
{
return (nullptr == edge) ? false : m_unique_tester.InList((ON__UINT_PTR)edge);
}
bool ON_SubDEdgeChain::InChain(
const ON_SubDVertex* vertex
) const
{
return (nullptr == vertex) ? false : m_unique_tester.InList((ON__UINT_PTR)vertex);
}
bool ON_SubDEdgeChain::IsClosedLoop() const
{
const unsigned int count = m_edge_chain.UnsignedCount();
return (count >= 3 && m_edge_chain[0].RelativeVertex(0) == m_edge_chain[count - 1].RelativeVertex(1));
}
bool ON_SubDEdgeChain::IsConvexLoop(bool bStrictlyConvex) const
{
if (false == IsClosedLoop())
return false;
const unsigned int count = m_edge_chain.UnsignedCount();
ON_SimpleArray<ON_3dPoint> points(count);
for (unsigned int i = 0; i < count; ++i)
{
const ON_SubDVertex* v = m_edge_chain[i].RelativeVertex(0);
if (nullptr == v)
return false;
points.Append(v->ControlNetPoint());
}
if ( false == (points[0] != points[count - 1]) )
return false;
return ON_IsConvexPolyline(points, bStrictlyConvex);
}
unsigned int ON_SubDEdgeChain::BeginEdgeChain(
ON_SubDRef subd_ref,
const ON_SubDEdge* initial_edge
)
{
return ON_SubDEdgeChain::BeginEdgeChain(subd_ref, ON_SubDEdgePtr::Create(initial_edge, 0));
}
unsigned int ON_SubDEdgeChain::BeginEdgeChain(
ON_SubDRef subd_ref,
const ON_SimpleArray<const ON_SubDEdge* >& initial_edge_chain
)
{
return BeginEdgeChain(subd_ref, initial_edge_chain.Array(), initial_edge_chain.UnsignedCount());
}
unsigned int ON_SubDEdgeChain::BeginEdgeChain(
ON_SubDRef subd_ref,
const ON_SubDEdge*const* initial_edge_chain,
size_t edge_count
)
{
ClearEdgeChain();
if (
edge_count <= 0
|| subd_ref.SubD().IsEmpty()
|| subd_ref.SubD().EdgeCount() < (unsigned int)edge_count
)
return 0;
if ( 1 == edge_count)
return ON_SubDEdgeChain::BeginEdgeChain(subd_ref, ON_SubDEdgePtr::Create(initial_edge_chain[0], 0));
const ON_SubDEdge* e0 = initial_edge_chain[0];
if (nullptr == e0 || nullptr == e0->m_vertex[0] || nullptr == e0->m_vertex[1] )
return 0;
const ON_SubDEdge* e1 = initial_edge_chain[1];
if (nullptr == e1 || nullptr == e1->m_vertex[0] || nullptr == e1->m_vertex[1] )
return 0;
ON_SubDEdgePtr eptr = ON_SubDEdgePtr::Create(e0, (e0->m_vertex[1] == e1->m_vertex[0] || e0->m_vertex[1] == e1->m_vertex[1]) ? 0 : 1);
ON_SimpleArray<ON_SubDEdgePtr> eptr_chain(edge_count);
eptr_chain.Append(eptr);
const ON_SubDVertex* v = eptr.RelativeVertex(1);
for (unsigned int i = 1; i < edge_count; i++)
{
e1 = initial_edge_chain[i];
if (nullptr == e1 || nullptr == e1->m_vertex[0] || nullptr == e1->m_vertex[1] )
return 0;
if (v != e1->m_vertex[0] && v != e1->m_vertex[1])
return 0;
eptr = ON_SubDEdgePtr::Create(e1, (v == e1->m_vertex[0]) ? 0 : 1);
eptr_chain.Append(eptr);
}
return ON_SubDEdgeChain::BeginEdgeChain(subd_ref,eptr_chain);
}
unsigned int ON_SubDEdgeChain::BeginEdgeChain(
ON_SubDRef subd_ref,
ON_SubDEdgePtr eptr
)
{
return ON_SubDEdgeChain::BeginEdgeChain(subd_ref, &eptr, 1);
}
unsigned int ON_SubDEdgeChain::BeginEdgeChain(
ON_SubDRef subd_ref,
const ON_SimpleArray<ON_SubDEdgePtr>& initial_edge_chain
)
{
return ON_SubDEdgeChain::BeginEdgeChain(subd_ref, initial_edge_chain.Array(), initial_edge_chain.UnsignedCount() );
}
unsigned int ON_SubDEdgeChain::BeginEdgeChain(
ON_SubDRef subd_ref,
const ON_SubDEdgePtr* initial_edge_chain,
size_t edge_count
)
{
ClearEdgeChain();
m_subd_ref = subd_ref;
if (edge_count <= 0 || m_subd_ref.SubD().IsEmpty())
return 0;
if ( ((size_t)subd_ref.SubD().EdgeCount()) < edge_count )
return 0;
m_edge_chain.Reserve(edge_count + 128);
const ON_SubDVertex* first_vertex = nullptr;
const ON_SubDVertex* last_vertex = nullptr;
for (size_t i = 0; i < edge_count; i++)
{
const ON_SubDEdgePtr eptr = initial_edge_chain[i];
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr.m_ptr);
if (nullptr == e)
continue;
if (m_unique_tester.InList((ON__UINT_PTR)e))
continue;
const ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(eptr.m_ptr);
const ON_SubDVertex* v[2] = { e->m_vertex[edir], e->m_vertex[1 - edir] };
if (nullptr == v[0] || nullptr == v[1] || v[0] == v[1] )
continue;
if (nullptr == first_vertex)
{
first_vertex = v[0];
last_vertex = v[1];
m_unique_tester.ExpertAddNewToList((ON__UINT_PTR)first_vertex);
m_unique_tester.ExpertAddNewToList((ON__UINT_PTR)last_vertex);
}
else
{
if (last_vertex != v[0])
continue;
if (v[1] != first_vertex)
{
if (false == m_unique_tester.AddToList((ON__UINT_PTR)v[1]))
continue;
}
}
m_edge_chain.Append(eptr);
m_unique_tester.ExpertAddNewToList((ON__UINT_PTR)e);
last_vertex = v[1];
if (last_vertex == first_vertex)
break;
};
return m_edge_chain.UnsignedCount();
}
void ON_SubDEdgeChain::ClearEdgeChain()
{
m_edge_chain.SetCount(0);
m_unique_tester.ClearList();
}
unsigned int ON_SubDEdgeChain::EdgeCount() const
{
return m_edge_chain.UnsignedCount();
}
void ON_SubDEdgeChain::SetStatusCheck(
bool bEnableStatusCheck,
ON_ComponentStatus status_check_pass,
ON_ComponentStatus status_check_fail
)
{
m_bEnableStatusCheck = bEnableStatusCheck ? true : false;
m_status_check_pass = status_check_pass;
m_status_check_fail = status_check_fail;
}
bool ON_SubDEdgeChain::StatusCheckEnabled() const
{
return m_bEnableStatusCheck;
}
void ON_SubDEdgeChain::Reverse()
{
ON_SubDEdgeChain::ReverseEdgeChain(m_edge_chain);
}
const ON_SubDEdgePtr ON_SubDEdgeChain::FirstEdgePtr() const
{
return m_edge_chain.UnsignedCount() > 0 ? m_edge_chain[0] : ON_SubDEdgePtr::Null;
}
const ON_SubDEdgePtr ON_SubDEdgeChain::LastEdgePtr() const
{
return m_edge_chain.UnsignedCount() > 0 ? *(m_edge_chain.Last()) : ON_SubDEdgePtr::Null;
}
const ON_SubDEdgePtr ON_SubDEdgeChain::EdgePtr(int edge_index) const
{
return (edge_index >= 0 && edge_index < m_edge_chain.Count()) ? m_edge_chain[edge_index] : ON_SubDEdgePtr::Null;
}
const ON_SubDEdge* ON_SubDEdgeChain::FirstEdge() const
{
return FirstEdgePtr().Edge();
}
const ON_SubDEdge* ON_SubDEdgeChain::LastEdge() const
{
return LastEdgePtr().Edge();
}
const ON_SubDEdge* ON_SubDEdgeChain::Edge(int edge_index) const
{
return EdgePtr(edge_index).Edge();
}
const ON_SubDVertex* ON_SubDEdgeChain::FirstVertex() const
{
return Vertex(0);
}
const ON_SubDVertex* ON_SubDEdgeChain::LastVertex() const
{
return Vertex(m_edge_chain.Count());
}
const ON_SubDVertex* ON_SubDEdgeChain::Vertex(int vertex_index) const
{
const int edge_count = m_edge_chain.Count();
if ( vertex_index >= 0 && vertex_index <= edge_count && edge_count > 0 )
{
return
(vertex_index == edge_count)
? m_edge_chain[edge_count - 1].RelativeVertex(1)
: m_edge_chain[vertex_index].RelativeVertex(0);
}
return nullptr;
}
unsigned int ON_SubDEdgeChain::AddOneNeighbor(
ON_ChainDirection direction,
ON_SubD::ChainType chain_type
)
{
const unsigned int count0 = m_edge_chain.UnsignedCount();
if (count0 <= 0 || IsClosedLoop() )
return 0;
ON_SubDEdgePtr eptr;
const ON_SubDEdge* e;
const ON_SubDVertex* v;
const ON_SubDVertex* chain_ends[2] = { FirstVertex() ,LastVertex() };
eptr
= (ON_ChainDirection::Previous != direction)
? ON_SubDEdgeChain::EdgeChainNeighbor(LastEdgePtr(), ON_ChainDirection::Next, chain_type, m_bEnableStatusCheck, m_status_check_pass, m_status_check_fail)
: ON_SubDEdgePtr::Null;
e = eptr.Edge();
v = eptr.RelativeVertex(0);
if ( nullptr != v && v == chain_ends[1] && false == InChain(e) )
{
v = eptr.RelativeVertex(1);
if (v == chain_ends[0] || m_unique_tester.AddToList((ON__UINT_PTR)v))
{
m_unique_tester.ExpertAddNewToList((ON__UINT_PTR)e);
m_edge_chain.Append(eptr);
}
}
eptr
= (ON_ChainDirection::Next != direction)
? ON_SubDEdgeChain::EdgeChainNeighbor(FirstEdgePtr(), ON_ChainDirection::Previous, chain_type, m_bEnableStatusCheck, m_status_check_pass, m_status_check_fail)
: ON_SubDEdgePtr::Null;
e = eptr.Edge();
v = eptr.RelativeVertex(1);
if ( nullptr != v && v == chain_ends[0] && false == InChain(e) )
{
v = eptr.RelativeVertex(0);
if (v == chain_ends[1] || m_unique_tester.AddToList((ON__UINT_PTR)v))
{
m_unique_tester.ExpertAddNewToList((ON__UINT_PTR)e);
m_edge_chain.Insert(0, eptr);
}
}
return m_edge_chain.UnsignedCount() - count0;
}
unsigned int ON_SubDEdgeChain::AddAllNeighbors(
ON_ChainDirection direction,
ON_SubD::ChainType chain_type
)
{
const unsigned int count0 = m_edge_chain.UnsignedCount();
if (count0 <= 0 || IsClosedLoop())
return 0;
if (ON_ChainDirection::Previous != direction)
while (1 == AddOneNeighbor(ON_ChainDirection::Next, chain_type)) {}
if (ON_ChainDirection::Next != direction)
while (1 == AddOneNeighbor(ON_ChainDirection::Previous, chain_type)) {}
return m_edge_chain.UnsignedCount() - count0;
}
unsigned int ON_SubDEdgeChain::AddEdge(
const ON_SubDEdge* edge
)
{
const unsigned int count0 = m_edge_chain.UnsignedCount();
if (count0 <= 0)
return 0;
if (
nullptr == edge
|| nullptr == edge->m_vertex[0]
|| nullptr == edge->m_vertex[1]
|| edge->m_vertex[0] == edge->m_vertex[1]
)
return 0;
const ON_SubDVertex* v[2] = { FirstVertex(),LastVertex() };
if (v[0] == v[1])
return 0;
if ( m_bEnableStatusCheck && false == ON_ComponentStatus::StatusCheck(edge->m_status, m_status_check_pass, m_status_check_fail))
return 0;
ON_SubDEdgePtr eptr = ON_SubDEdgePtr::Null;
if (v[1] == edge->m_vertex[0])
eptr = ON_SubDEdgePtr::Create(edge, 0);
else if (v[1] == edge->m_vertex[1])
eptr = ON_SubDEdgePtr::Create(edge, 1);
else if (v[0] == edge->m_vertex[1])
eptr = ON_SubDEdgePtr::Create(edge, 0);
else if (v[0] == edge->m_vertex[0])
eptr = ON_SubDEdgePtr::Create(edge, 1);
else
return 0;
if (m_unique_tester.InList((ON__UINT_PTR)edge))
return 0;
if (v[1] == eptr.RelativeVertex(0) )
{
if (v[0] == eptr.RelativeVertex(1) || m_unique_tester.AddToList((ON__UINT_PTR)eptr.RelativeVertex(1)))
{
m_edge_chain.Append(eptr);
m_unique_tester.ExpertAddNewToList((ON__UINT_PTR)edge);
}
}
else if (v[0] == eptr.RelativeVertex(1) )
{
if (v[1] == eptr.RelativeVertex(0) || m_unique_tester.AddToList((ON__UINT_PTR)eptr.RelativeVertex(0)))
{
m_edge_chain.Insert(0, eptr);
m_unique_tester.ExpertAddNewToList((ON__UINT_PTR)edge);
}
}
return m_edge_chain.UnsignedCount() - count0;
}
unsigned int ON_SubDEdgeChain::RemoveEdges(
const ON_SubDEdge* first_edge,
const ON_SubDEdge* last_edge
)
{
unsigned int count0 = m_edge_chain.UnsignedCount();
unsigned int i0 = 0;
unsigned int i1 = count0;
if (nullptr != first_edge)
{
while (i0 < count0 && first_edge != m_edge_chain[i0].Edge())
++i0;
}
if (nullptr != last_edge)
{
while (i1 > i0 && last_edge != m_edge_chain[i1 - 1].Edge())
--i1;
}
const unsigned int count1 = i1 - i0;
if (count1 >= count0)
return 0;
if (i0 > 0)
{
for (unsigned int i = i0; i < i1; i++)
m_edge_chain[i - i0] = m_edge_chain[i];
}
m_edge_chain.SetCount(count1);
m_unique_tester.ClearList();
for (unsigned int i = 0; i < count1; i++)
{
m_unique_tester.AddToList((ON__UINT_PTR)m_edge_chain[i].Edge());
m_unique_tester.AddToList((ON__UINT_PTR)m_edge_chain[i].RelativeVertex(0));
}
if ( FirstVertex() != LastVertex() )
m_unique_tester.AddToList((ON__UINT_PTR)LastVertex());
return count0 - count1;
}
void ON_SubDEdgeChain::ReverseEdgeChain(
ON_SimpleArray< ON_SubDEdgePtr >& edge_chain
)
{
ON_SubDEdgeChain::ReverseEdgeChain(edge_chain.Array(), edge_chain.UnsignedCount());
}
void ON_SubDEdgeChain::ReverseEdgeChain(
ON_SubDEdgePtr* edge_chain,
size_t edge_count
)
{
if (edge_count <= 0 || nullptr == edge_chain)
return;
ON_SubDEdgePtr* p0 = edge_chain;
ON_SubDEdgePtr* p1 = p0 + (edge_count - 1);
while ( p0 < p1)
{
ON_SubDEdgePtr eptr = p0->Reversed();
*p0 = p1->Reversed();
*p1 = eptr;
++p0;
--p1;
}
if (p0 == p1)
*p0 = p0->Reversed();
}
bool ON_SubDEdgeChain::IsValidEdgeChain(
const ON_SimpleArray< ON_SubDEdgePtr >& edge_chain,
bool bCheckForDuplicateEdges
)
{
return ON_SubDEdgeChain::IsValidEdgeChain(edge_chain.Array(), edge_chain.UnsignedCount(), bCheckForDuplicateEdges);
}
bool ON_SubDEdgeChain::IsValidEdgeChain(
const ON_SubDEdgePtr* edge_chain,
size_t edge_count,
bool bCheckForDuplicateEdges
)
{
if (edge_count <= 0)
return true;
if (nullptr == edge_chain)
return false;
const ON_SubDVertex* first_vertex = edge_chain->RelativeVertex(0);
if (nullptr == first_vertex)
return false;
const ON_SubDVertex* v = first_vertex;
const ON_SubDEdgePtr* p0 = edge_chain;
const ON_SubDEdgePtr* p1 = edge_chain+1;
for (const ON_SubDEdgePtr* p = p0; p < p1; ++p)
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(p->m_ptr);
if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1])
return false;
ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(p->m_ptr);
const ON_SubDVertex* v0 = e->m_vertex[edir];
const ON_SubDVertex* v1 = e->m_vertex[1 - edir];
if (v0 != v || nullptr == v1 || v0 == v1)
return false;
v = v1;
}
if (bCheckForDuplicateEdges)
{
const ON_SubDVertex* last_vertex = v;
ON_UniqueTester tester;
for (const ON_SubDEdgePtr* p = p0; p < p1; ++p)
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(p->m_ptr);
if (false == tester.AddToList((ON__UINT_PTR)e))
return false; // duplicate edge
if (false == tester.AddToList((ON__UINT_PTR)e->m_vertex[ON_SUBD_EDGE_DIRECTION(p->m_ptr)]))
return false; // duplicate vertex
}
if (first_vertex != last_vertex)
{
if (false == tester.AddToList((ON__UINT_PTR)last_vertex))
return false; // duplicate vertex
}
}
return true;
}
class ON_SubDMeshImpl* ON_SubDMesh::SubLimple() const
{
return m_impl_sp.get();
}
unsigned int ON_SubDMesh::SubLimpleUseCount() const
{
return (unsigned int)(m_impl_sp.use_count());
}
bool ON_SubD::IsSolid() const
{
bool bIsManifold = false;
bool bIsOriented = false;
bool bHasBoundary = false;
int solid_orientation = 0;
ActiveLevel().GetTopologicalAttributes(bIsManifold, bIsOriented, bHasBoundary, solid_orientation);
return (bIsManifold && bIsOriented && false == bHasBoundary);
}
int ON_SubD::SolidOrientation() const
{
bool bIsManifold = false;
bool bIsOriented = false;
bool bHasBoundary = false;
int solid_orientation = 0;
ActiveLevel().GetTopologicalAttributes(bIsManifold, bIsOriented, bHasBoundary, solid_orientation);
return solid_orientation;
}
bool ON_SubD::IsManifold( bool& bIsOriented, bool& bHasBoundary ) const
{
bool bIsManifold = false;
bIsOriented = false;
bHasBoundary = false;
int solid_orientation = 0;
ActiveLevel().GetTopologicalAttributes(bIsManifold, bIsOriented, bHasBoundary, solid_orientation);
return bIsManifold;
}
bool ON_SubD::IsManifold() const
{
bool bIsOriented = false;
bool bHasBoundary = false;
return IsManifold(bIsOriented, bHasBoundary);
}
class ON_SubDEdgePtrLink
{
public:
ON_SubDEdgePtrLink() = default;
~ON_SubDEdgePtrLink() = default;
ON_SubDEdgePtrLink(const ON_SubDEdgePtrLink&) = default;
ON_SubDEdgePtrLink& operator=(const ON_SubDEdgePtrLink&) = default;
public:
static const ON_SubDEdgePtrLink Empty;
public:
ON_SubDEdgePtr m_ep = ON_SubDEdgePtr::Null;
unsigned int m_index = 0;
unsigned int m_nbr_index = 0;
static int CompareVertex(
const ON_SubDEdgePtrLink* lhs,
const ON_SubDEdgePtrLink* rhs
)
{
const ON_SubDVertex* lhs_v = lhs->m_ep.RelativeVertex(0);
const ON_SubDVertex* rhs_v = rhs->m_ep.RelativeVertex(0);
if (lhs_v < rhs_v)
return -1;
if (lhs_v > rhs_v)
return 1;
return 0;
}
static int CompareIndex(
const ON_SubDEdgePtrLink* lhs,
const ON_SubDEdgePtrLink* rhs
)
{
const unsigned int lhs_i = lhs->m_index;
const unsigned int rhs_i = rhs->m_index;
if (lhs_i < rhs_i)
return -1;
if (lhs_i > rhs_i)
return 1;
return 0;
}
static void Resolve3OrMoreEdges(
const unsigned int unset_nbr1_index,
unsigned int count,
const ON_SubDVertex* v,
ON_SubDEdgePtrLink* links
);
};
const ON_SubDEdgePtrLink ON_SubDEdgePtrLink::Empty;
void ON_SubDEdgePtrLink::Resolve3OrMoreEdges(
const unsigned int unset_nbr1_index,
unsigned int count,
const ON_SubDVertex* v,
ON_SubDEdgePtrLink* links
)
{
// If the case can't be resolved by Resolve3OrMoreEdges(),
// then the vertex will not appear in the middle of a chain.
if (count < 3 || nullptr == v || count != (unsigned int)v->m_edge_count)
return;
switch (count)
{
case 3:
if (false == v->IsCrease() && false == v->IsDart())
return;
break;
case 4:
if (false == v->IsCrease() && false == v->IsSmooth())
return;
break;
default:
if (false == v->IsCrease())
return;
break;
}
const ON_SubDEdge* link_edges[4] = {};
const ON_SubDEdge* vertex_edges[4] = {};
unsigned int crease_edge_count = 0;
unsigned int smooth_edge_count = 0;
unsigned int smooth_edge_link_index[4] = {};
unsigned int crease_edge_link_index[4] = {};
for (unsigned int j = 0; j < count; j++)
{
const ON_SubDEdge* e = links[j].m_ep.Edge();
if (nullptr == e)
return;
const ON_SubDEdge* ve = v->Edge(j);
if (nullptr == ve)
return;
if (j < 4)
{
link_edges[j] = e;
vertex_edges[j] = ve;
}
if (e->IsSmooth() && 2 == e->m_face_count)
{
if ( smooth_edge_count < 4)
smooth_edge_link_index[smooth_edge_count] = j;
++smooth_edge_count;
}
else if (e->IsCrease())
{
if (crease_edge_count < 4)
crease_edge_link_index[crease_edge_count] = j;
++crease_edge_count;
}
else
return;
}
if ( 2 == crease_edge_count && v->IsCrease() )
{
// Link the two creased edges.
// The vertex will be interior in a chain and the edges
// will be next to each other.
links[crease_edge_link_index[0]].m_nbr_index = links[crease_edge_link_index[1]].m_index;
links[crease_edge_link_index[1]].m_nbr_index = links[crease_edge_link_index[0]].m_index;
if (1 == smooth_edge_count)
{
// this edge will be at the end of a chain.
links[smooth_edge_link_index[0]].m_nbr_index = unset_nbr1_index;
}
}
if (2 == smooth_edge_count)
{
// Link the two smooth edges.
// The vertex will be interior in a chain and the edges
// will be next to each other.
links[smooth_edge_link_index[0]].m_nbr_index = links[smooth_edge_link_index[1]].m_index;
links[smooth_edge_link_index[1]].m_nbr_index = links[smooth_edge_link_index[0]].m_index;
if (1 == crease_edge_count)
{
// this edge will be at the end of a chain
links[crease_edge_link_index[0]].m_nbr_index = unset_nbr1_index;
}
}
if (
4 != count
|| 4 != smooth_edge_count
|| 0 != crease_edge_count
|| 4 != v->m_face_count
|| 4 != v->m_edge_count
|| false == v->IsSmooth()
)
return;
// make sure vertex_edges[] and link_edges[] are the same list.
unsigned int match_count = 0;
for (unsigned int j = 0; j == match_count && j < count; j++)
{
for (unsigned int k = 0; k < count; k++)
{
if (vertex_edges[k] == link_edges[j])
{
vertex_edges[k] = nullptr;
match_count++;
break;
}
}
}
if (match_count != count)
return;
// vertex has 4 faces and 4 smooth edges. Link opposite edges.
const ON_SubDFace* edge_faces[4][2];
for (unsigned int j = 0; j < 4; j++)
{
edge_faces[j][0] = link_edges[j]->Face(0);
edge_faces[j][1] = link_edges[j]->Face(1);
if (nullptr == edge_faces[j][0] || nullptr == edge_faces[j][1])
return;
}
ON_2udex pairs[2];
unsigned int pair_count = 0;
ON_2udex pair;
for (pair.i = 0; pair.i < 4; ++pair.i) for (pair.j = pair.i+1; pair.j < 4; ++pair.j)
{
if (
edge_faces[pair.i][0] != edge_faces[pair.j][0]
&& edge_faces[pair.i][0] != edge_faces[pair.j][1]
&& edge_faces[pair.i][1] != edge_faces[pair.j][0]
&& edge_faces[pair.i][1] != edge_faces[pair.j][1]
)
{
// the associated edges share no faces.
if ( pair_count < 2)
pairs[pair_count] = pair;
if (++pair_count > 2)
break;
}
}
if (2 == pair_count)
{
links[pairs[0].i].m_nbr_index = links[pairs[0].j].m_index;
links[pairs[0].j].m_nbr_index = links[pairs[0].i].m_index;
links[pairs[1].i].m_nbr_index = links[pairs[1].j].m_index;
links[pairs[1].j].m_nbr_index = links[pairs[1].i].m_index;
}
return;
}
unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains(
const ON_SimpleArray< const ON_SubDEdge* >& unsorted_edges,
unsigned int minimum_chain_length,
ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges
)
{
const unsigned int unsorted_edge_count = unsorted_edges.Count();
ON_SimpleArray< ON_SubDEdgePtr > unsorted_eptrs(unsorted_edge_count);
for (unsigned i = 0; i < unsorted_edge_count; ++i)
{
const ON_SubDEdge* e = unsorted_edges[i];
if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1] || e->m_vertex[0] == e->m_vertex[1])
continue;
ON_SubDEdgePtr eptr = ON_SubDEdgePtr::Create(e, 0);
if (1 == e->m_face_count && 0 == ON_SUBD_FACE_DIRECTION(e->m_face2[0].m_ptr))
eptr = eptr.Reversed();
unsorted_eptrs.Append(eptr);
}
return ON_SubDEdgeChain::SortEdgesIntoEdgeChains(unsorted_eptrs, minimum_chain_length, sorted_edges);
}
unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains(
const ON_SimpleArray< ON_SubDEdgePtr >& unsorted_edges,
unsigned int minimum_chain_length,
ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges
)
{
// NOTE:
// unsorted_edges[] and sorted_edges[] may reference the same array.
////const ON_SubDEdge* ee[2] = {};
////const ON_SubDVertex* vv[2] = {};
const unsigned int unsorted_edge_count = unsorted_edges.Count();
if (unsorted_edge_count <= 0)
{
sorted_edges.SetCount(0);
return 0;
}
ON_SimpleArray< ON_SubDEdgePtrLink > links(2*unsorted_edge_count);
const unsigned int unset_nbr1_index = 0xFFFFFFFEU;
const unsigned int unset_nbrX_index = unset_nbr1_index+1;
ON_SubDEdgePtrLink epl;
epl.m_nbr_index = unset_nbrX_index;
for (unsigned int i = 0; i < unsorted_edge_count; i++)
{
ON_SubDEdgePtr ep = unsorted_edges[i];
const ON_SubDEdge* e = ep.Edge();
if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1] || e->m_vertex[0] == e->m_vertex[1])
continue;
// unsorted_edges[i] has 2 links with m_index = 2*i and m_index = 2*+1.
// links with even m_index have opposite orientation as unsorted_edges[].
// links[2*i].m_ep.RelativeVertex(0) = unsorted_edges[i].RelativeVertex(1)
epl.m_ep = ep.Reversed();
links.Append(epl);
++epl.m_index;
////ee[0] = epl.m_ep.Edge();
////vv[1] = epl.m_ep.RelativeVertex(0);
// links with odd m_index have same orientation as unsorted_edges[].
// links[2*i+1].m_ep.RelativeVertex(0) = unsorted_edges[i].RelativeVertex(0)
epl.m_ep = ep;
links.Append(epl);
++epl.m_index;
////ee[1] = epl.m_ep.Edge();
////vv[0] = epl.m_ep.RelativeVertex(0);
////if (e != ee[0] || e != ee[1] || vv[0] == vv[1])
//// return false;
}
// NOTE:
// unsorted_edges[] and sorted_edges[] may reference the same array.
// At this point, I'm finished with unsorted_edges[] so it's ok to
// modify sorted_edges[] here.
sorted_edges.SetCount(0);
sorted_edges.Reserve(unsorted_edge_count);
// sort links by ON_SubDEdgePtr.RelativeVertex(0) and set m_nbr_index
links.QuickSort(ON_SubDEdgePtrLink::CompareVertex);
// link_count = even number
const unsigned int link_count = links.UnsignedCount();
unsigned int i1 = link_count;
for (unsigned int i0 = 0; i0 < link_count; i0 = i1)
{
ON_SubDEdgePtrLink& epl0 = links[i0];
const ON_SubDVertex* v = epl0.m_ep.RelativeVertex(0);
for (i1 = i0 + 1; i1 < link_count; ++i1)
{
if (0 != ON_SubDEdgePtrLink::CompareVertex(&epl0, &links[i1]))
break;
}
if (nullptr == v)
{
ON_SUBD_ERROR("Bug in code that creates the links[] array.");
continue;
}
if (v->IsCorner())
{
// These edges will be at the ends of chains.
while (i0 < i1)
links[i0++].m_nbr_index = unset_nbr1_index;
continue;
}
if (i0 + 1 == i1)
{
// The vertex is referenced by exactly 1 edge in unsorted_edges[]
// This edge will appear in sorted_edges[] at the start or end of a chain.
epl0.m_nbr_index = unset_nbr1_index;
continue;
}
if (i0 + 2 == i1)
{
// This vertex is referenced by exactly 2 edges in unsorted_edges[].
// The vertex will be in the interior of a chain and the edges will
// appear in sorted_edges[] next to each other in the same chain.
ON_SubDEdgePtrLink& epl1 = links[i0 + 1];
epl0.m_nbr_index = epl1.m_index;
epl1.m_nbr_index = epl0.m_index;
continue;
}
// The vertex referenced by 3 or more edges in unsorted_edges[].
// If the case cannot be resolved by Resolve3OrMoreEdges(),
// then this vertex will not be in the interior of a chain.
ON_SubDEdgePtrLink::Resolve3OrMoreEdges(
unset_nbr1_index,
i1 - i0,
v,
links.Array() + i0
);
}
// Sort links[] by m_index valut to restore links[] to its original order.
links.QuickSort(ON_SubDEdgePtrLink::CompareIndex);
ON_SubDEdgePtrLink* links_array = links.Array();
unsigned chain_count = 0;
ON_SimpleArray<ON_SubDEdgePtr> chain(unsorted_edge_count);
for (unsigned int i = 0; i < link_count; ++i)
{
// epl0 and epl1 are the links for edges[i/2]
const ON_SubDEdgePtrLink epl0 = links_array[i];
links_array[i].m_ep = ON_SubDEdgePtr::Null;
const ON_SubDEdgePtrLink epl1 = links_array[++i];
links_array[i].m_ep = ON_SubDEdgePtr::Null;
if (nullptr == epl0.m_ep.Edge())
continue; // this edge has already been inserted in sorted_edges[].
chain.SetCount(0);
// Add edges that come "before" edges[i/2] to chain[]
epl = epl1;
for (;;)
{
if (epl.m_nbr_index >= unset_nbr1_index)
break;
unsigned int j = epl.m_nbr_index;
unsigned int j1 = (0 == (j % 2)) ? (j + 1) : (j - 1);
// epl = "previous" link
epl = links_array[j1];
links_array[j].m_ep = ON_SubDEdgePtr::Null;
links_array[j1].m_ep = ON_SubDEdgePtr::Null;
if (nullptr == epl.m_ep.Edge())
break;
////ee[0] = epl.m_ep.Edge();
////vv[0] = epl.m_ep.RelativeVertex(0);
////vv[1] = epl.m_ep.RelativeVertex(1);
////if (vv[0] == vv[1] || nullptr == ee[0])
//// return false;
chain.Append(epl.m_ep);
}
const bool bClosedChain = (epl.m_index == epl1.m_index);
const bool bReverseFinalChain
= false == bClosedChain
&& unset_nbr1_index == epl.m_nbr_index
&& (0 == (epl.m_index % 2));
if (false == bClosedChain)
{
chain.Reverse();
}
////ee[0] = epl1.m_ep.Edge();
////vv[0] = epl1.m_ep.RelativeVertex(0);
////vv[1] = epl1.m_ep.RelativeVertex(1);
////if (vv[0] == vv[1] || nullptr == ee[0])
//// return false;
chain.Append(epl1.m_ep); // matches input edge orientation
if (bClosedChain)
{
// put edges[i/2] at the start of the closed chain.
chain.Reverse();
}
else
{
// Add edges that come "after" edges[i/2] to chain[]
epl = epl0;
for (;;)
{
if (epl.m_nbr_index >= unset_nbr1_index)
break;
unsigned int j = epl.m_nbr_index;
unsigned int j1 = (0 == (j % 2)) ? (j + 1) : (j - 1);
// epl = "next" link
epl = links_array[j1];
links_array[j].m_ep = ON_SubDEdgePtr::Null;
links_array[j1].m_ep = ON_SubDEdgePtr::Null;
if (nullptr == epl.m_ep.Edge())
break;
////ee[0] = epl.m_ep.Edge();
////vv[0] = epl.m_ep.RelativeVertex(1);
////vv[1] = epl.m_ep.RelativeVertex(0);
////if (vv[0] == vv[1] || nullptr == ee[0])
//// return false;
chain.Append(epl.m_ep.Reversed());
}
if (bReverseFinalChain)
chain.Reverse();
}
const unsigned int chain_edge_count = chain.UnsignedCount();
if (chain_edge_count > 0)
{
for (;;)
{
if (chain_edge_count < 3)
break;
const ON_SubDVertex* c0 = chain[0].RelativeVertex(0);
if (nullptr == c0)
break;
if (c0->IsCorner())
break;
const ON_SubDVertex* c1 = chain[chain_edge_count-1].RelativeVertex(1);
if (c0 != c1)
break;
const ON_SubDEdge* e0 = chain[0].Edge();
if (nullptr == e0)
break;
const ON_SubDEdge* e1 = chain[chain_edge_count-1].Edge();
if (nullptr == e1)
break;
const bool bSmooth = e0->IsSmooth();
if (bSmooth != e1->IsSmooth())
break;
if (bSmooth && c0->IsCrease())
break;
// Check for an embedded crease vertex.
for (unsigned int k = 1; k < chain_edge_count; ++k)
{
const ON_SubDVertex* v = chain[k].RelativeVertex(0);
if (nullptr == v)
break;
const ON_SubDEdge* e = chain[k].Edge();
if (nullptr == e)
break;
if (
bSmooth != e->IsSmooth()
|| (bSmooth && v->IsCreaseOrCorner())
)
{
// shift chain[] so it begins at chain[k];
ON_SimpleArray<ON_SubDEdgePtr> tail;
tail.Append(k, chain.Array());
for (unsigned n = k; n < chain_edge_count; ++n)
chain[n - k] = chain[n];
chain.SetCount(chain_edge_count - k);
chain.Append(tail.Count(), tail.Array());
break;
}
}
break;
}
if (chain.UnsignedCount() >= minimum_chain_length)
{
++chain_count;
sorted_edges.Append(chain.Count(), chain.Array());
}
}
if ( link_count == 2*sorted_edges.UnsignedCount() )
break; // we've used all the links - no need to "skip over the rest".
}
return chain_count;
}
#if defined(ON_SUBD_CENSUS)
//////////////////////////////////////////////////////////////////////////
//
// ON_CensusCounter
//
class ON_PointerHashElement
{
public:
ON__UINT_PTR m_sn = 0;
ON__UINT_PTR m_ptr = 0;
ON_PointerHashElement* m_next = nullptr;
};
class ON_PointerHashTable
{
public:
void Add(ON_CensusCounter::Class c, ON__UINT_PTR ptr);
void Remove(ON_CensusCounter::Class c, ON__UINT_PTR ptr);
ON__UINT_PTR SerialNumber(ON_CensusCounter::Class c, ON__UINT_PTR ptr) const;
enum
{
hash_count = 1024
};
ON_PointerHashElement* m_table[(unsigned int)ON_CensusCounter::Class::count][hash_count];
unsigned int m_count = 0;
static bool TheOneExists();
static ON_PointerHashTable* TheOne();
static void DestroyTheOne();
private:
ON_PointerHashTable();
~ON_PointerHashTable();
ON_PointerHashTable(const ON_PointerHashTable&) = delete;
ON_PointerHashTable& operator=(const ON_PointerHashTable&) = delete;
static unsigned int PointerHash(ON__UINT_PTR ptr);
ON_FixedSizePool m_fsp;
static ON_PointerHashTable* m_the_one;
};
ON_PointerHashTable* ON_PointerHashTable::m_the_one = nullptr;
ON_PointerHashTable::ON_PointerHashTable()
{
memset(m_table,0,sizeof(m_table));
m_fsp.Create(sizeof(ON_PointerHashElement),0,0);
}
ON_PointerHashTable::~ON_PointerHashTable()
{
memset(m_table,0,sizeof(m_table));
m_count = 0;
m_fsp.Destroy();
}
bool ON_PointerHashTable::TheOneExists()
{
return (nullptr != ON_PointerHashTable::m_the_one);
}
ON_PointerHashTable* ON_PointerHashTable::TheOne()
{
if (nullptr == ON_PointerHashTable::m_the_one)
{
ON_PointerHashTable::m_the_one = new ON_PointerHashTable();
}
return ON_PointerHashTable::m_the_one;
}
void ON_PointerHashTable::DestroyTheOne()
{
if (nullptr != ON_PointerHashTable::m_the_one)
{
delete ON_PointerHashTable::m_the_one;
ON_PointerHashTable::m_the_one = nullptr;
}
}
unsigned int ON_PointerHashTable::PointerHash(ON__UINT_PTR ptr)
{
return (ON_CRC32(0, sizeof(ptr),&ptr) % ON_PointerHashTable::hash_count);
}
void ON_PointerHashTable::Add(ON_CensusCounter::Class c, ON__UINT_PTR ptr)
{
static ON__UINT_PTR sn = 0;
// not thread safe - a crude debugging tool
ON_PointerHashElement* phe = (ON_PointerHashElement*)m_fsp.AllocateDirtyElement();
phe->m_sn = ++sn;
phe->m_ptr = ptr;
const unsigned int hash_dex = ON_PointerHashTable::PointerHash(ptr);
phe->m_next = m_table[(unsigned int)c][hash_dex];
m_table[(unsigned int)c][hash_dex] = phe;
m_count++;
}
void ON_PointerHashTable::Remove(ON_CensusCounter::Class c, ON__UINT_PTR ptr)
{
const unsigned int hash_dex = ON_PointerHashTable::PointerHash(ptr);
ON_PointerHashElement* phe = m_table[(unsigned int)c][hash_dex];
for (ON_PointerHashElement* phe0 = nullptr; nullptr != phe; phe = phe->m_next)
{
if (ptr == phe->m_ptr)
{
if ( nullptr == phe0 )
m_table[(unsigned int)c][hash_dex] = phe->m_next;
else
phe0 = phe->m_next;
m_fsp.ReturnElement(phe);
m_count--;
return;
}
phe0 = phe;
}
// pointer not in the table
ON_SubDIncrementErrorCount();
return;
}
ON__UINT_PTR ON_PointerHashTable::SerialNumber(ON_CensusCounter::Class c, ON__UINT_PTR ptr) const
{
const unsigned int hash_dex = ON_PointerHashTable::PointerHash(ptr);
for ( ON_PointerHashElement* phe = m_table[(unsigned int)c][hash_dex]; nullptr != phe; phe = phe->m_next)
{
if (ptr == phe->m_ptr)
return phe->m_sn;
}
// pointer not in the table
return 0;
}
void ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class c, ON__UINT_PTR ptr)
{
ON_PointerHashTable::TheOne()->Add(c,ptr);
}
void ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class c, ON__UINT_PTR ptr)
{
ON_PointerHashTable::TheOne()->Remove(c,ptr);
}
void ON_CensusCounter::Clear()
{
ON_PointerHashTable::DestroyTheOne();
}
void ON_CensusCounter::CensusReport(
class ON_TextLog& text_log
)
{
ON_PointerHashTable* ht = ON_PointerHashTable::TheOneExists() ? ON_PointerHashTable::TheOne() : nullptr;
if ( nullptr == ht )
{
text_log.Print("No class census information exists.\n");
return;
}
text_log.Print("%d items exist.\n",ht->m_count);
for (unsigned int i = 0; i < (unsigned int)ON_CensusCounter::Class::count; i++)
{
bool bPrintClassName = true;
const char* sClassName = nullptr;
const ON_CensusCounter::Class c = (ON_CensusCounter::Class)i;
switch (c)
{
case ON_CensusCounter::Class::subd:
sClassName = "ON_SubD";
break;
case ON_CensusCounter::Class::subd_impl:
sClassName = "ON_SubDimple";
break;
case ON_CensusCounter::Class::subd_limit_mesh:
sClassName = "ON_SubDMesh";
break;
case ON_CensusCounter::Class::subd_limit_mesh_impl:
sClassName = "ON_SubDMeshImpl";
break;
case ON_CensusCounter::Class::subd_ref:
sClassName = "ON_SubDef";
break;
default:
sClassName = "Bug in ON_CensusCounter";
break;
}
for (unsigned int j = 0; j < ON_PointerHashTable::hash_count; j++)
{
for (ON_PointerHashElement* e = ht->m_table[i][j]; nullptr != e; e = e->m_next)
{
const unsigned int sn = (unsigned int)e->m_sn;
if (bPrintClassName)
{
text_log.Print("\n\n%s census:\n",sClassName);
bPrintClassName = false;
}
switch (c)
{
case ON_CensusCounter::Class::subd:
{
const ON_SubD* subd = (const ON_SubD*)e->m_ptr;
if ( subd == &ON_SubD::Empty )
text_log.Print("ON_SubD::Empty (%u) ", sn);
else
text_log.Print("ON_SubD(%u) ", sn);
const ON_SubDimple* dimple = subd->SubDimple();
if ( nullptr == dimple )
text_log.Print(" ON_SubDimple(nullptr)\n");
else
{
unsigned int dimple_sn = (unsigned int)ht->SerialNumber(ON_CensusCounter::Class::subd_impl, (ON__UINT_PTR)dimple);
text_log.Print(" ON_SubDimple(%u)x%u\n", dimple_sn, subd->SubDimpleUseCount());
}
}
break;
case ON_CensusCounter::Class::subd_impl:
{
text_log.Print("ON_SubDimple(%u)\n", sn);
}
break;
case ON_CensusCounter::Class::subd_limit_mesh:
{
const ON_SubDMesh* subd_limit_mesh = (const ON_SubDMesh*)e->m_ptr;
if ( subd_limit_mesh == &ON_SubDMesh::Empty )
text_log.Print("ON_SubDMesh::Empty (%u) ", sn);
else
text_log.Print("ON_SubDMesh(%u) ", sn);
const class ON_SubDMeshImpl* limple = subd_limit_mesh->SubLimple();
if ( nullptr == limple )
text_log.Print(" ON_SubDMeshImpl(nullptr)\n");
else
{
unsigned int limple_sn = (unsigned int)ht->SerialNumber(ON_CensusCounter::Class::subd_limit_mesh_impl, (ON__UINT_PTR)limple);
text_log.Print(" ON_SubDMeshImpl(%u)x%u\n", limple_sn, subd_limit_mesh->SubLimpleUseCount());
}
}
break;
case ON_CensusCounter::Class::subd_limit_mesh_impl:
{
const ON_SubDMeshImpl* subd_limple = (const ON_SubDMeshImpl*)e->m_ptr;
text_log.Print("ON_SubDMeshImpl(%u)", sn);
std::shared_ptr<ON_SubDimple> limple_sp(subd_limple->m_subdimple_wp.lock());
const ON_SubDimple* dimple = limple_sp.get();
if ( nullptr == dimple )
text_log.Print(" ON_SubDimple(nullpr)\n");
else
{
unsigned int dimple_sn = (unsigned int)ht->SerialNumber(ON_CensusCounter::Class::subd_impl, (ON__UINT_PTR)dimple);
text_log.Print(" ON_SubDimple(%u)x%u+1\n", dimple_sn, (unsigned int)(limple_sp.use_count()-1));
}
}
break;
case ON_CensusCounter::Class::subd_ref:
{
const ON_SubDRef* subd_ref = (const ON_SubDRef*)e->m_ptr;
const ON_SubD* subd = &subd_ref->SubD();
ON__UINT_PTR subd_sn = ht->SerialNumber(ON_CensusCounter::Class::subd, (ON__UINT_PTR)subd);
text_log.Print("ON_SubDRef(%u) ON_SubD(%u)x%u\n", sn, subd_sn, subd_ref->ReferenceCount());
}
break;
}
}
}
}
}
static ON__UINT_PTR ON_SubDCensusCounter_This(ON_SubDCensusCounter* p)
{
const ON_SubD* pSubD = (const ON_SubD*)((unsigned char*)p - sizeof(ON_Geometry));
return (ON__UINT_PTR)pSubD;
}
ON_SubDCensusCounter::ON_SubDCensusCounter() ON_NOEXCEPT
{
ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd,ON_SubDCensusCounter_This(this));
}
ON_SubDCensusCounter::~ON_SubDCensusCounter() ON_NOEXCEPT
{
ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd,ON_SubDCensusCounter_This(this));
}
ON_SubDCensusCounter::ON_SubDCensusCounter(const ON_SubDCensusCounter&) ON_NOEXCEPT
{
ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd,ON_SubDCensusCounter_This(this));
}
ON_SubDRefCensusCounter::ON_SubDRefCensusCounter() ON_NOEXCEPT
{
ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_ref,(ON__UINT_PTR)this);
}
ON_SubDRefCensusCounter::~ON_SubDRefCensusCounter() ON_NOEXCEPT
{
ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd_ref,(ON__UINT_PTR)this);
}
ON_SubDRefCensusCounter::ON_SubDRefCensusCounter(const ON_SubDRefCensusCounter&) ON_NOEXCEPT
{
ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_ref,(ON__UINT_PTR)this);
}
ON_SubDImpleCensusCounter::ON_SubDImpleCensusCounter() ON_NOEXCEPT
{
ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_impl,(ON__UINT_PTR)this);
}
ON_SubDImpleCensusCounter::~ON_SubDImpleCensusCounter() ON_NOEXCEPT
{
ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd_impl,(ON__UINT_PTR)this);
}
ON_SubDImpleCensusCounter::ON_SubDImpleCensusCounter(const ON_SubDImpleCensusCounter&) ON_NOEXCEPT
{
ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_impl,(ON__UINT_PTR)this);
}
ON_SubDMeshCensusCounter::ON_SubDMeshCensusCounter() ON_NOEXCEPT
{
ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh,(ON__UINT_PTR)this);
}
ON_SubDMeshCensusCounter::~ON_SubDMeshCensusCounter() ON_NOEXCEPT
{
ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd_limit_mesh,(ON__UINT_PTR)this);
}
ON_SubDMeshCensusCounter::ON_SubDMeshCensusCounter(const ON_SubDMeshCensusCounter&) ON_NOEXCEPT
{
ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh,(ON__UINT_PTR)this);
}
ON_SubDMeshCensusCounter::ON_SubDMeshCensusCounter() ON_NOEXCEPT
{
ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh_impl,(ON__UINT_PTR)this);
}
ON_SubDMeshCensusCounter::~ON_SubDMeshCensusCounter() ON_NOEXCEPT
{
ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd_limit_mesh_impl,(ON__UINT_PTR)this);
}
ON_SubDMeshCensusCounter::ON_SubDMeshCensusCounter(const ON_SubDMeshCensusCounter&) ON_NOEXCEPT
{
ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh_impl,(ON__UINT_PTR)this);
}
#endif