mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-17 14:56:02 +08:00
8068 lines
235 KiB
C++
8068 lines
235 KiB
C++
/* $NoKeywords: $ */
|
|
/*
|
|
//
|
|
// Copyright (c) 1993-2014 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>.
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
*/
|
|
|
|
|
|
#if 0
|
|
#define ON_SUBD_CENSUS
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_CensusCounter
|
|
//
|
|
// This tool is used to study memory leaks and other shared ptr issues.
|
|
// The classes have no size but effect performance. Use only in
|
|
// debugging situations. Never ship a release with ON_SUBD_CENSUS defined.
|
|
//
|
|
class ON_SUBD_CLASS ON_CensusCounter
|
|
{
|
|
public:
|
|
enum class Class : unsigned int
|
|
{
|
|
unset = 0,
|
|
subd = 1,
|
|
subd_impl = 2,
|
|
subd_limit_mesh = 3,
|
|
subd_limit_mesh_impl = 4,
|
|
subd_ref = 5,
|
|
|
|
count
|
|
};
|
|
|
|
static void RegisterBirth(ON_CensusCounter::Class,ON__UINT_PTR);
|
|
|
|
static void RegisterDeath(ON_CensusCounter::Class,ON__UINT_PTR);
|
|
|
|
static void CensusReport(
|
|
class ON_TextLog&
|
|
);
|
|
|
|
static void Clear();
|
|
};
|
|
|
|
class ON_SUBD_CLASS ON_SubDCensusCounter
|
|
{
|
|
public:
|
|
ON_SubDCensusCounter() ON_NOEXCEPT;
|
|
~ON_SubDCensusCounter() ON_NOEXCEPT;
|
|
ON_SubDCensusCounter(const ON_SubDCensusCounter&) ON_NOEXCEPT;
|
|
ON_SubDCensusCounter& operator=(const ON_SubDCensusCounter&) = default;
|
|
//ON_SubDCensusCounter( ON_SubDCensusCounter&& ) ON_NOEXCEPT;
|
|
//ON_SubDCensusCounter& operator=( ON_SubDCensusCounter&& ) ON_NOEXCEPT;
|
|
};
|
|
|
|
class ON_SUBD_CLASS ON_SubDRefCensusCounter
|
|
{
|
|
public:
|
|
ON_SubDRefCensusCounter() ON_NOEXCEPT;
|
|
~ON_SubDRefCensusCounter() ON_NOEXCEPT;
|
|
ON_SubDRefCensusCounter(const ON_SubDRefCensusCounter&) ON_NOEXCEPT;
|
|
ON_SubDRefCensusCounter& operator=(const ON_SubDRefCensusCounter&) = default;
|
|
//ON_SubDRefCensusCounter( ON_SubDRefCensusCounter&& ) ON_NOEXCEPT;
|
|
//ON_SubDRefCensusCounter& operator=( ON_SubDRefCensusCounter&& ) ON_NOEXCEPT;
|
|
};
|
|
|
|
|
|
class ON_SUBD_CLASS ON_SubDImpleCensusCounter
|
|
{
|
|
public:
|
|
ON_SubDImpleCensusCounter() ON_NOEXCEPT;
|
|
~ON_SubDImpleCensusCounter() ON_NOEXCEPT;
|
|
ON_SubDImpleCensusCounter(const ON_SubDImpleCensusCounter&) ON_NOEXCEPT;
|
|
ON_SubDImpleCensusCounter& operator=(const ON_SubDImpleCensusCounter&) = default;
|
|
//ON_SubDImplCensusCounter( ON_SubDImplCensusCounter&& ) ON_NOEXCEPT;
|
|
//ON_SubDImplCensusCounter& operator=( ON_SubDImplCensusCounter&& ) ON_NOEXCEPT;
|
|
};
|
|
|
|
class ON_SUBD_CLASS ON_SubDLimitMeshCensusCounter
|
|
{
|
|
public:
|
|
ON_SubDLimitMeshCensusCounter() ON_NOEXCEPT;
|
|
~ON_SubDLimitMeshCensusCounter() ON_NOEXCEPT;
|
|
ON_SubDLimitMeshCensusCounter(const ON_SubDLimitMeshCensusCounter&) ON_NOEXCEPT;
|
|
ON_SubDLimitMeshCensusCounter& operator=(const ON_SubDLimitMeshCensusCounter&) = default;
|
|
//ON_SubDLimitMeshCensusCounter( ON_SubDLimitMeshCensusCounter&& ) ON_NOEXCEPT;
|
|
//ON_SubDLimitMeshCensusCounter& operator=( ON_SubDLimitMeshCensusCounter&& ) ON_NOEXCEPT;
|
|
};
|
|
|
|
|
|
class ON_SUBD_CLASS ON_SubDLimitMeshImplCensusCounter
|
|
{
|
|
public:
|
|
ON_SubDLimitMeshImplCensusCounter() ON_NOEXCEPT;
|
|
~ON_SubDLimitMeshImplCensusCounter() ON_NOEXCEPT;
|
|
ON_SubDLimitMeshImplCensusCounter(const ON_SubDLimitMeshImplCensusCounter&) ON_NOEXCEPT;
|
|
ON_SubDLimitMeshImplCensusCounter& operator=(const ON_SubDLimitMeshImplCensusCounter&) ON_NOEXCEPT;
|
|
ON_SubDLimitMeshImplCensusCounter( ON_SubDLimitMeshImplCensusCounter&& ) ON_NOEXCEPT;
|
|
ON_SubDLimitMeshImplCensusCounter& operator=( ON_SubDLimitMeshImplCensusCounter&& ) ON_NOEXCEPT;
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Definition of subdivision surface
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
#if !defined(OPENNURBS_SUBD_INC_)
|
|
#define OPENNURBS_SUBD_INC_
|
|
|
|
class ON_CLASS ON_SubDVertexPtr
|
|
{
|
|
public:
|
|
// For performance reasons, m_ptr is not initialized and no constructors are declared
|
|
// or implemented. If you require initialization, then use x = ON_SubDVertexPtr::Null
|
|
// or x = ON_SubDVertexPtr::Create(...).
|
|
ON__UINT_PTR m_ptr;
|
|
|
|
static const ON_SubDVertexPtr Null;
|
|
|
|
bool IsNull() const;
|
|
|
|
class ON_SubDVertex* Vertex() const;
|
|
|
|
ON__UINT_PTR VertexPtrMark() const;
|
|
|
|
ON_ComponentStatus Status() const;
|
|
|
|
static
|
|
class ON_SubDVertexPtr Create(
|
|
const class ON_SubDVertex* vertex
|
|
);
|
|
|
|
/*
|
|
Parameters:
|
|
vertex - [in]
|
|
mark - [in]
|
|
zero or one
|
|
*/
|
|
static
|
|
class ON_SubDVertexPtr Create(
|
|
const class ON_SubDVertex* vertex,
|
|
ON__UINT_PTR vertex_mark
|
|
);
|
|
|
|
static
|
|
class ON_SubDVertexPtr Create(
|
|
const class ON_SubDComponentPtr& vertex_component
|
|
);
|
|
};
|
|
|
|
class ON_CLASS ON_SubDEdgePtr
|
|
{
|
|
public:
|
|
// For performance reasons, m_ptr is not initialized and no constructors are declared
|
|
// or implemented. If you require initialization, then use x = ON_SubDEdgePtr::Null
|
|
// or x = ON_SubDEdgePtr::Create(...).
|
|
ON__UINT_PTR m_ptr;
|
|
|
|
static const ON_SubDEdgePtr Null;
|
|
|
|
bool IsNull() const;
|
|
|
|
class ON_SubDEdge* Edge() const;
|
|
|
|
ON__UINT_PTR EdgeDirection() const;
|
|
|
|
ON_ComponentStatus Status() const;
|
|
|
|
/*
|
|
Returns:
|
|
A pointer to the same edge with the direction flipped
|
|
*/
|
|
ON_SubDEdgePtr Reversed() const;
|
|
|
|
static ON_SubDEdgePtr Create(
|
|
const class ON_SubDEdge* edge,
|
|
ON__UINT_PTR direction
|
|
);
|
|
|
|
static ON_SubDEdgePtr Create(
|
|
const class ON_SubDComponentPtr& edge_component
|
|
);
|
|
};
|
|
|
|
class ON_CLASS ON_SubDFacePtr
|
|
{
|
|
public:
|
|
// For performance reasons, m_ptr is not initialized and no constructors are declared
|
|
// or implemented. If you require initialization, then use x = ON_SubDFacePtr::Null
|
|
// or x = ON_SubDFacePtr::Create(...).
|
|
ON__UINT_PTR m_ptr;
|
|
|
|
static const ON_SubDFacePtr Null;
|
|
|
|
bool IsNull() const;
|
|
|
|
class ON_SubDFace* Face() const;
|
|
|
|
ON__UINT_PTR FaceDirection() const;
|
|
|
|
ON_ComponentStatus Status() const;
|
|
|
|
static
|
|
class ON_SubDFacePtr Create(
|
|
const class ON_SubDFace* face,
|
|
ON__UINT_PTR direction
|
|
);
|
|
|
|
static ON_SubDFacePtr Create(
|
|
const class ON_SubDComponentPtr& face_component
|
|
);
|
|
};
|
|
|
|
class ON_CLASS ON_SubDComponentPtr
|
|
{
|
|
public:
|
|
// For performance reasons, m_ptr is not initialized and no constructors are declared
|
|
// or implemented. If you require initialization, then use x = ON_SubDComponentPtr::Null
|
|
// or x = ON_SubDComponentPtr::Create(...).
|
|
ON__UINT_PTR m_ptr;
|
|
|
|
static const ON_SubDComponentPtr Null; // // nullptr, type = unset, mark = 0
|
|
|
|
enum class Type : unsigned char
|
|
{
|
|
Unset = 0,
|
|
Vertex = 2,
|
|
Edge = 4,
|
|
Face = 6
|
|
};
|
|
|
|
static ON_SubDComponentPtr::Type ComponentPtrTypeFromUnsigned(
|
|
unsigned int component_pointer_type_as_unsigned
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
ON_SubDComponentPtr::Type::Vertex
|
|
< ON_SubDComponentPtr::Type::Edge
|
|
< ON_SubDComponentPtr::Type::Face
|
|
< ON_SubDComponentPtr::Type::Unset
|
|
< invalid
|
|
*/
|
|
static int CompareComponentPtrType(
|
|
ON_SubDComponentPtr::Type a,
|
|
ON_SubDComponentPtr::Type b
|
|
);
|
|
|
|
static int CompareType(
|
|
const ON_SubDComponentPtr* a,
|
|
const ON_SubDComponentPtr* b
|
|
);
|
|
|
|
static int Compare(
|
|
const ON_SubDComponentPtr* a,
|
|
const ON_SubDComponentPtr* b
|
|
);
|
|
|
|
|
|
/*
|
|
Returns:
|
|
True if the ComponentBase() pointer is nullptr. Note that type and mark may be set.
|
|
*/
|
|
bool IsNull() const;
|
|
bool IsNotNull() const;
|
|
|
|
ON_SubDComponentPtr::Type ComponentType() const;
|
|
|
|
class ON_SubDComponentBase* ComponentBase() const;
|
|
class ON_SubDVertex* Vertex() const;
|
|
class ON_SubDEdge* Edge() const;
|
|
class ON_SubDFace* Face() const;
|
|
|
|
ON_SubDVertexPtr VertexPtr() const;
|
|
ON_SubDEdgePtr EdgePtr() const;
|
|
ON_SubDFacePtr FacePtr() const;
|
|
|
|
ON_COMPONENT_INDEX ComponentIndex() const;
|
|
|
|
/*
|
|
Returns:
|
|
0 or 1.
|
|
The interpretation of the mark varies depending on the context.
|
|
For vertices, this is the vertex mark.
|
|
For edges, this is generally an index into ON_SubDEdge.m_vertex[]
|
|
or a direction flag with 1 indicating a reversed direction.
|
|
For face, this is generally an orientation flag with 1 indicating
|
|
a reversed (clockwise) orientation.
|
|
*/
|
|
ON__UINT_PTR ComponentMark() const;
|
|
|
|
ON_ComponentStatus Status() const;
|
|
|
|
/*
|
|
Returns:
|
|
1: status changed.
|
|
0: status not changed.
|
|
*/
|
|
unsigned int SetStates(
|
|
ON_ComponentStatus states_to_set
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
1: status changed.
|
|
0: status not changed.
|
|
*/
|
|
unsigned int ClearStates(
|
|
ON_ComponentStatus states_to_clear
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Makes "this" an exact copy of status.
|
|
Parameters:
|
|
status - [in]
|
|
Returns:
|
|
1: status changed.
|
|
0: status not changed.
|
|
*/
|
|
unsigned int SetStatus(
|
|
ON_ComponentStatus status
|
|
);
|
|
|
|
ON_SubDComponentPtr ClearMark() const;
|
|
|
|
ON_SubDComponentPtr SetMark() const;
|
|
|
|
ON_SubDComponentPtr ToggleMark() const;
|
|
|
|
static
|
|
const ON_SubDComponentPtr CreateNull(
|
|
ON_SubDComponentPtr::Type component_type,
|
|
bool bMark
|
|
);
|
|
|
|
static
|
|
const ON_SubDComponentPtr Create(
|
|
const class ON_SubDVertex* vertex
|
|
);
|
|
|
|
static
|
|
const ON_SubDComponentPtr Create(
|
|
const class ON_SubDEdge* edge
|
|
);
|
|
|
|
static
|
|
const ON_SubDComponentPtr Create(
|
|
const class ON_SubDFace* face
|
|
);
|
|
|
|
static
|
|
const ON_SubDComponentPtr Create(
|
|
const class ON_SubDVertex* vertex,
|
|
ON__UINT_PTR vertex_mark
|
|
);
|
|
|
|
static
|
|
const ON_SubDComponentPtr Create(
|
|
const class ON_SubDEdge* edge,
|
|
ON__UINT_PTR edge_mark
|
|
);
|
|
|
|
static
|
|
const ON_SubDComponentPtr Create(
|
|
const class ON_SubDFace* face,
|
|
ON__UINT_PTR face_mark
|
|
);
|
|
|
|
static
|
|
const ON_SubDComponentPtr Create(
|
|
ON_SubDVertexPtr vertexptr
|
|
);
|
|
|
|
static
|
|
const ON_SubDComponentPtr Create(
|
|
ON_SubDEdgePtr edgeptr
|
|
);
|
|
|
|
static
|
|
const ON_SubDComponentPtr Create(
|
|
ON_SubDFacePtr faceptr
|
|
);
|
|
|
|
wchar_t* ToString(
|
|
wchar_t* s,
|
|
size_t s_capacity
|
|
) const;
|
|
|
|
const ON_wString ToString() const;
|
|
|
|
};
|
|
|
|
#if defined(OPENNURBS_SUBD_WIP)
|
|
|
|
#if 1
|
|
// SuD is exported from opennurbs DLL
|
|
#define ON_SUBD_CLASS ON_CLASS
|
|
#else
|
|
// SuD is not exported from opennurbs DLL
|
|
#define ON_SUBD_CLASS
|
|
#endif
|
|
|
|
class ON_SUBD_CLASS ON_SubDComponentRegionIndex
|
|
{
|
|
public:
|
|
ON_SubDComponentRegionIndex() = default;
|
|
~ON_SubDComponentRegionIndex() = default;
|
|
ON_SubDComponentRegionIndex(const ON_SubDComponentRegionIndex&) = default;
|
|
ON_SubDComponentRegionIndex& operator=(const ON_SubDComponentRegionIndex&) = default;
|
|
|
|
public:
|
|
enum : unsigned short
|
|
{
|
|
/// Capacity of the m_index[] array;
|
|
IndexCapacity = 9
|
|
};
|
|
|
|
// All values are zero
|
|
static const ON_SubDComponentRegionIndex Zero;
|
|
|
|
// All values are 0xFFFF
|
|
static const ON_SubDComponentRegionIndex Unset;
|
|
|
|
/*
|
|
Description:
|
|
Compares subdivision counts. If the counts are the same, compares m_indices[].
|
|
*/
|
|
static int Compare(
|
|
const ON_SubDComponentRegionIndex* lhs,
|
|
const ON_SubDComponentRegionIndex* rhs
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Compares subdivision indices for minimum(lhs->m_subdivision_count,rhs->m_subdivision_count).
|
|
*/
|
|
static int CompareMinimumSubregion(
|
|
const ON_SubDComponentRegionIndex* lhs,
|
|
const ON_SubDComponentRegionIndex* rhs
|
|
);
|
|
|
|
unsigned short Index(
|
|
unsigned short i
|
|
) const;
|
|
|
|
unsigned short m_subdivision_count = 0;
|
|
|
|
// If m_subdivision_count > 0, then m_index[0], ..., m_index[m_subdivision_count-1]
|
|
// identifies a subregion of the level 0 component.
|
|
//
|
|
// Faces with quad subdivision:
|
|
// m_index[n] is the subdivision quad for the region that contains
|
|
// the parent face's corner at face->m_vertex[m_index[n]].
|
|
// Edges
|
|
// m_region_index[n] = 0 indicates the beginning half of the parent edge.
|
|
// (begins at edge->Vertex(0))
|
|
// m_region_index[n] = 1 indicates the ending half of the parent edge.
|
|
// (ends at edge->Vertex(1))
|
|
//
|
|
// When a component is created during a subdivision step, the value 0xFFFF
|
|
// is used to mark the non-existent regions at earlier subdivision levels.
|
|
// For example, if a level 1 edge is created by connecting
|
|
// a level0 edge subdivision point (middle-ish of the edge)
|
|
// to a level0 face subdivision point (center-ish of the face),
|
|
// then the level 1 edge would have
|
|
// m_level0_component = ON_SubDComponentPtr::CreateNull(ON_SubDComponentPtr::Type::Edge, bReversed),
|
|
// (m_level0_component.IsNull() will be true)
|
|
// m_level0_component_id = ON_SubDComponentRegion::NewTransientId()
|
|
// m_subdivision_count = 1,
|
|
// m_region_index[0] = 0xFFFF.
|
|
//
|
|
unsigned short m_index[ON_SubDComponentRegionIndex::IndexCapacity] = {};
|
|
|
|
void Push(
|
|
unsigned int region_index
|
|
);
|
|
|
|
void Pop();
|
|
|
|
/*
|
|
Description:
|
|
Get a string of the form .a.b.c .a.b.c = m_index[] values.
|
|
*/
|
|
wchar_t* ToString(
|
|
wchar_t* s,
|
|
size_t s_capacity
|
|
) const;
|
|
|
|
const ON_wString ToString() const;
|
|
|
|
/*
|
|
Description:
|
|
Encodes ON_SubDComponentRegionIndex information in 32 bits.
|
|
(m_subdivision_count) << 24
|
|
| (0x00FF0000 & ((m_region_index[0]) << 16))
|
|
| (m_region_index[1] & 0x0003) << (14)
|
|
| (m_region_index[2] & 0x0003) << (12)
|
|
...
|
|
| (m_index[m_subdivision_count] & 0x0003) <<(16-(2*m_subdivision_count))
|
|
Remarks:
|
|
This is useful when quick compare and sorting of regions is required,
|
|
m_subdivision_count < 256, m_index[0] < 256, m_index[1] < 4, ..., m_index[m_subdivision_count] < 4
|
|
Regions of N-gons with N < 256 and regions of edges
|
|
satisify these condition when m_subdivision_count < 256
|
|
(which is always in real world situations).
|
|
*/
|
|
ON__UINT32 ToCompressedRegionIndex() const;
|
|
|
|
static const ON_SubDComponentRegionIndex FromCompressedRegionIndex(
|
|
ON__UINT32 compressed_region_index
|
|
);
|
|
|
|
static ON__UINT32 ToCompressedRegionIndex(
|
|
unsigned short subdivision_count,
|
|
const unsigned short* region_index
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Decompress a 32-bit region.
|
|
Parameters:
|
|
region32 - [in]
|
|
Value returned from To32BitRegion().
|
|
subdivision_count - [out]
|
|
Subdivision count
|
|
region_index[] - out
|
|
Region indices. The region_index[] array must have a capcity of at
|
|
least ON_SubDComponentRegion::region_index_capacity elements.
|
|
*/
|
|
static void FromCompressedRegionIndex(
|
|
ON__UINT32 compressed_region_index,
|
|
unsigned short* subdivision_count,
|
|
unsigned short* region_index
|
|
);
|
|
};
|
|
|
|
class ON_SUBD_CLASS ON_SubDComponentRegion
|
|
{
|
|
public:
|
|
ON_SubDComponentRegion() = default;
|
|
~ON_SubDComponentRegion() = default;
|
|
ON_SubDComponentRegion(const ON_SubDComponentRegion&) = default;
|
|
ON_SubDComponentRegion& operator=(const ON_SubDComponentRegion&) = default;
|
|
|
|
public:
|
|
static const ON_SubDComponentRegion Create(
|
|
const class ON_SubDFace* level0_face
|
|
);
|
|
|
|
static const ON_SubDComponentRegion Create(
|
|
unsigned int component_id,
|
|
ON_SubDComponentPtr::Type component_type,
|
|
bool bComponentMark
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Creates a region that can be used to identify a component
|
|
created at a certain level of subdivision that does not
|
|
come from dividing a component from the previous level.
|
|
For example, Catmull Clark subdivision edges on level N+1
|
|
that run from the level N edge subdivision point to the
|
|
level N face subdivision point.
|
|
|
|
m_level0_component = ON_SubDComponentPtr::CreateNull(component_type, bComponentMark),
|
|
(m_level0_component.IsNull() will be true)
|
|
m_level0_component_id = ON_SubDComponentRegion::NewTransientId()
|
|
m_subdivision_count = subdivision_count,
|
|
m_region_index[0, ..., (subdivision_count-1)] = 0xFFFF.
|
|
|
|
*/
|
|
static const ON_SubDComponentRegion CreateSubdivisionRegion(
|
|
ON_SubDComponentPtr::Type component_type,
|
|
bool bComponentMark,
|
|
unsigned short subdivision_count,
|
|
bool bAssignTransientId
|
|
);
|
|
|
|
public:
|
|
static const ON_SubDComponentRegion Empty;
|
|
|
|
public:
|
|
ON_SubDComponentPtr m_level0_component = ON_SubDComponentPtr::Null;
|
|
|
|
unsigned int m_level0_component_id = 0;
|
|
|
|
unsigned short SubdivisionCount() const;
|
|
|
|
ON_SubDComponentRegionIndex m_region_index;
|
|
|
|
/*
|
|
Returns:
|
|
True if m_level0_component_id is a transient id.
|
|
*/
|
|
bool IsTransientId() const;
|
|
|
|
/*
|
|
Returns:
|
|
True if m_level0_component_id is the id of a persistent ON_SubD level 0 component.
|
|
*/
|
|
bool IsPersistentId() const;
|
|
|
|
/*
|
|
Description:
|
|
Compares
|
|
m_level0_component.ComponentType(),
|
|
m_level0_component_id,
|
|
m_level0_component.ComponentMark(),
|
|
the entire sub region,
|
|
and m_level0_component.m_ptr.
|
|
*/
|
|
static int Compare(
|
|
const ON_SubDComponentRegion* lhs,
|
|
const ON_SubDComponentRegion* rhs
|
|
);
|
|
|
|
/*
|
|
Descriptions:
|
|
Compares
|
|
m_level0_component.ComponentType(),
|
|
m_level0_component_id,
|
|
m_level0_component.ComponentMark().
|
|
*/
|
|
static int CompareTypeIdMark(
|
|
const ON_SubDComponentRegion* lhs,
|
|
const ON_SubDComponentRegion* rhs
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Compares
|
|
m_level0_component.ComponentType(),
|
|
m_level0_component_id,
|
|
m_level0_component.ComponentMark(),
|
|
and the m_region_index[] values for the
|
|
minimum subdivision count lhs and rhs.
|
|
*/
|
|
static int CompareTypeIdMarkMinimumSubregion(
|
|
const ON_SubDComponentRegion* lhs,
|
|
const ON_SubDComponentRegion* rhs
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Compares
|
|
m_level0_component.ComponentType(),
|
|
m_level0_component_id,
|
|
m_level0_component.ComponentMark(),
|
|
and the entire sub region.
|
|
*/
|
|
static int CompareTypeIdMarkSubregion(
|
|
const ON_SubDComponentRegion* lhs,
|
|
const ON_SubDComponentRegion* rhs
|
|
);
|
|
|
|
void SetLevel0Component(
|
|
ON_SubDComponentPtr component_ptr
|
|
);
|
|
|
|
void SetLevel0Face(
|
|
const ON_SubDFace* face
|
|
);
|
|
|
|
void SetLevel0EdgePtr(
|
|
const ON_SubDEdgePtr edge_ptr
|
|
);
|
|
|
|
void SetLevel0Vertex(
|
|
const ON_SubDVertex* vertex
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
region_index - [in]
|
|
If m_level0_component identifies an edge, region_index is 0 or 1,
|
|
and the edge is reversed (1=m_level0_component.ComponentMark()),
|
|
then PushAbsolute(1-region_index) is called.
|
|
In every other case, PushAbsolute(region_index) is called.
|
|
*/
|
|
void PushAdjusted(
|
|
unsigned int region_index
|
|
);
|
|
|
|
/*
|
|
Parameters:
|
|
region_index - [in]
|
|
If m_level0_component identifies a face, then region_index is the index of the
|
|
corner vertex for the subdivision quad.
|
|
If m_level0_component identifies an edge, then region_index must be 0 or 1.
|
|
Description:
|
|
Increments if m_subdivision_count and appends region_index to m_region_index[]
|
|
(m_region_index[m_subdivision_count++] = region_index)
|
|
*/
|
|
void PushAbsolute(
|
|
unsigned int region_index
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Get a string of the form fN.a.b.c where N = m_level0_face-m_id, a.b.c = m_region_index[] values.
|
|
*/
|
|
wchar_t* ToString(
|
|
wchar_t* s,
|
|
size_t s_capacity
|
|
) const;
|
|
|
|
const ON_wString ToString() const;
|
|
|
|
void Pop();
|
|
|
|
bool IsEmptyRegion() const;
|
|
|
|
enum : unsigned int
|
|
{
|
|
TransientIdBit = 0x80000000U
|
|
};
|
|
|
|
|
|
/*
|
|
Returns:
|
|
A value that can be used to identify transient subdivision components that do not
|
|
exist in the persistent levels of a SubD.
|
|
Transient ids always satisfy (ON_SubDComponentRegion::TransientIdBit & transient_id) is not zero and
|
|
(~ON_SubDComponentRegion::TransientIdBit & transient_id) is not zero.
|
|
Remarks:
|
|
Transient ids are used to identify subdivision components at levels that do
|
|
not persist in the ON_SubD. They are unique within the context where they are
|
|
being used. They generally vary with each repetition of a calcultion in that
|
|
context.
|
|
*/
|
|
static const unsigned int NewTransientId();
|
|
|
|
/*
|
|
Description:
|
|
Resets the value used to generate transient ids.
|
|
This is useful during debugging session so that transient id
|
|
values are predictable. Otherwise, use of this function
|
|
should be avoided.
|
|
*/
|
|
static void ResetTransientId();
|
|
|
|
/*
|
|
Parameters:
|
|
id - [in]
|
|
Value to test to see if it is a transient subd component id.
|
|
Returns:
|
|
True if (ON_SubDComponentRegion::TransientIdBit & id) is not zero and
|
|
(~ON_SubDComponentRegion::TransientIdBit & id) is not zero.
|
|
Remarks:
|
|
Transient ids are used to identify subdivision components at levels that do
|
|
not persist in the ON_SubD. They are unique within the context where they are
|
|
being used. They generally vary with each repetition of a calcultion in that
|
|
context.
|
|
*/
|
|
static bool IsTransientId(unsigned int id);
|
|
|
|
/*
|
|
Parameters:
|
|
id - [in]
|
|
Value to test to see if it is a transient subd component id.
|
|
Returns:
|
|
If the id is a transient id, then its id value is returned.
|
|
Otherwise, 0 is returned.
|
|
Remarks:
|
|
Transient ids are used to identify subdivision components at levels that do
|
|
not persist in the ON_SubD. They are unique within the context where they are
|
|
being used. They generally vary with each repetition of a calcultion in that
|
|
context.
|
|
*/
|
|
static unsigned int TransientId(unsigned int id);
|
|
|
|
/*
|
|
Parameters:
|
|
id - [in]
|
|
Value to test to see if it is a persitsent subd component id.
|
|
Returns:
|
|
True if (ON_SubDComponentRegion::TransientIdBit & id) is not zero and
|
|
(~ON_SubDComponentRegion::TransientIdBit & id) is not zero.
|
|
Remarks:
|
|
Transient ids are used to identify subdivision components at levels that do
|
|
not persist in the ON_SubD. They are unique within the context where they are
|
|
being used. They generally vary with each repetition of a calcultion in that
|
|
context.
|
|
*/
|
|
static bool IsPersistentId(unsigned int id);
|
|
};
|
|
|
|
class ON_SUBD_CLASS ON_SubDFaceRegion
|
|
{
|
|
public:
|
|
ON_SubDFaceRegion() = default;
|
|
~ON_SubDFaceRegion() = default;
|
|
ON_SubDFaceRegion(const ON_SubDFaceRegion&) = default;
|
|
ON_SubDFaceRegion& operator=(const ON_SubDFaceRegion&) = default;
|
|
|
|
static const ON_SubDFaceRegion Empty;
|
|
|
|
public:
|
|
// Identifies a region of an ON_SubDFace
|
|
ON_SubDComponentRegion m_face_region;
|
|
|
|
// When the face region is a quad, m_edge_region[4] identifies regions of ON_SubDEdge elements.
|
|
// When the face region is a sub-quad, these edges may be null or have null ON_SubDEdge pointers
|
|
// and the ids will be zero or ON_SubDComponentRegion::IsTransientId() will be true.
|
|
// When ON_SubDComponentRegion::IsTransientId() is true, the id does not identify
|
|
// a persistent edge in the ON_SubD.
|
|
ON_SubDComponentRegion m_edge_region[4];
|
|
|
|
unsigned int m_level0_edge_count = 0;
|
|
|
|
// If set, these are the vertice ids at the region's limit surface corners.
|
|
// m_vertex_id[] is mutable because these values appear during recursive calculations.
|
|
// When the face region is a sub-quad, these ids will be zero or ON_SubDComponentRegion::IsTransientId()
|
|
// will be true.
|
|
// When ON_SubDComponentRegion::IsTransientId() is true, the id does not identify
|
|
// a persistent vertex in the ON_SubD.
|
|
mutable unsigned int m_vertex_id[4] = {};
|
|
|
|
public:
|
|
void Push(unsigned int quadrant_index);
|
|
|
|
bool IsValid(
|
|
bool bSilentError
|
|
) const;
|
|
|
|
wchar_t* ToString(
|
|
wchar_t* s,
|
|
size_t s_capacity
|
|
) const;
|
|
|
|
const ON_wString ToString() const;
|
|
};
|
|
|
|
|
|
class ON_SUBD_CLASS ON_SubDFaceRegionAndNurbs
|
|
{
|
|
public:
|
|
ON_SubDFaceRegionAndNurbs() = default;
|
|
~ON_SubDFaceRegionAndNurbs() = default;
|
|
ON_SubDFaceRegionAndNurbs(const ON_SubDFaceRegionAndNurbs&) = default;
|
|
ON_SubDFaceRegionAndNurbs& operator=(const ON_SubDFaceRegionAndNurbs&) = default;
|
|
|
|
static const ON_SubDFaceRegionAndNurbs Empty;
|
|
|
|
public:
|
|
ON_SubDFaceRegion m_face_region;
|
|
// This pointer is not managed by ON_SubDFaceRegionAndNurbs
|
|
class ON_NurbsSurface* m_nurbs_surface = nullptr;
|
|
};
|
|
|
|
|
|
/*
|
|
Description:
|
|
The ON_SubDComponentLocation enum is used when an ON_SubD component
|
|
is referenced and it is important to distinguish between the
|
|
component's location in the SubD control net and its location
|
|
in the SubD limit surface.
|
|
*/
|
|
enum class ON_SubDComponentLocation : unsigned char
|
|
{
|
|
Unset = 0,
|
|
ControlNet = 1,
|
|
LimitSurface = 2
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubD
|
|
//
|
|
class ON_SUBD_CLASS ON_SubD : public ON_Geometry
|
|
{
|
|
ON_OBJECT_DECLARE(ON_SubD);
|
|
|
|
#if defined(ON_SUBD_CENSUS)
|
|
private:
|
|
ON_SubDCensusCounter m_census_counter;
|
|
#endif
|
|
|
|
public:
|
|
static const ON_SubD Empty;
|
|
|
|
enum : unsigned int
|
|
{
|
|
maximum_subd_level = 128 // uses as a sanity check on input parameters
|
|
};
|
|
|
|
#pragma region RH_C_SHARED_ENUM [ON_SubD::VertexTag] [Rhino.Geometry.SubD.SubDVertexTag] [nested:byte]
|
|
/// <summary>
|
|
/// SubD::VertexTag identifies the type of subdivision vertex. Different tags use
|
|
/// different subdivision algorithms to determine where the subdivision point and
|
|
/// limit point are located. There are toplological constraints that restrict which
|
|
/// tags can be assigned.
|
|
/// </summary>
|
|
enum class VertexTag : unsigned char
|
|
{
|
|
///<summary>
|
|
/// Not a valid vertex tag and the default value for ON_SubDVertex::m_vertex_tag.
|
|
/// This encourages developers to thoughtfully initialize ON_SubDVertex::m_vertex_tag.
|
|
///</summary>
|
|
Unset = 0,
|
|
|
|
///<summary>
|
|
/// Must be an interior vertex.
|
|
/// All edges ending at a smooth vertex must be tagged as ON_SubD::EdgeTag::Smooth.
|
|
///</summary>
|
|
Smooth = 1,
|
|
|
|
///<summary>
|
|
/// Can be an iterior or a boundary vertex.
|
|
/// Exactly two edges ending at a crease vertex must be tagged as ON_SubD::EdgeTag::Crease.
|
|
/// All other edges ending at a crease must be tagged as tagON_SubD::EdgeTag::smooth.
|
|
///</summary>
|
|
Crease = 2,
|
|
|
|
///<summary>
|
|
/// Can be an iterior, boundary or isolated vertex.
|
|
/// The location of a corner vertex is fixed.
|
|
/// The all subdivision points and the limit point are at the initial vertex location.
|
|
/// The edges ending at a corner vertex can be smooth or crease edges.
|
|
///</summary>
|
|
Corner = 3,
|
|
|
|
///<summary>
|
|
/// Must be an interior vertex.
|
|
/// Exactly one edge ending at a dart vertex must be tagged as ON_SubD::EdgeTag::Smooth.
|
|
/// All other edges ending at a dart vertex must be tagged as tagON_SubD::EdgeTag::smooth.
|
|
///</summary>
|
|
Dart = 4
|
|
};
|
|
#pragma endregion
|
|
|
|
static ON_SubD::VertexTag VertexTagFromUnsigned(
|
|
unsigned int vertex_tag_as_unsigned
|
|
);
|
|
|
|
/*
|
|
Parameters:
|
|
vertex_tag - [in]
|
|
Returns:
|
|
True if vertex_tag is Smooth, Crease, Corner, or Dart.
|
|
False otherwise.
|
|
*/
|
|
static bool VertexTagIsSet(
|
|
ON_SubD::VertexTag vertex_tag
|
|
);
|
|
|
|
#pragma region RH_C_SHARED_ENUM [ON_SubD::EdgeTag] [Rhino.Geometry.SubD.SubDEdgeTag] [nested:byte]
|
|
/// <summary>
|
|
/// SubD::EdgeTag identifies the type of subdivision edge. Different tags use
|
|
/// different subdivision algorithms to determine where the subdivision point is located.
|
|
/// </summary>
|
|
enum class EdgeTag : unsigned char
|
|
{
|
|
///<summary>
|
|
/// Not a valid edge tag and the default value for ON_SubDEdge::m_edge_tag.
|
|
/// This encourages developers to thoughtfully initialize ON_SubDEdge::m_edge_tag.
|
|
///</summary>
|
|
Unset = 0,
|
|
|
|
///<summary>
|
|
/// One or two of the edge's vertices must be tagged as ON_SubD::VertexTag::Smooth.
|
|
/// The edge must have exactly two faces.
|
|
///</summary>
|
|
Smooth = 1,
|
|
|
|
///<summary>
|
|
/// Both of the edge's vertices must be tagged as not ON_SubD::VertexTag::Smooth.
|
|
/// The edge can have any number of faces.
|
|
///</summary>
|
|
Crease = 2,
|
|
|
|
///<summary>
|
|
/// Reserved for version 2 of the ON_SubD project.
|
|
/// Currently this tag is not used and is invalid.
|
|
///
|
|
/// FUTURE: The edge is a "soft crease" or "semi-sharp".
|
|
/// At lease one end vertex must be tagged as ON_SubD::VertexTag::Smooth
|
|
/// The edge must have exactly two faces.
|
|
/// The value of ON_SubDEdge::m_sharpness controls how
|
|
/// soft/hard the edge appears.
|
|
/// ON_SubDEdge::m_sharpness = 0 is identical to ON_SubD::EdgeTag::Smooth.
|
|
/// ON_SubDEdge::m_sharpness = 1 is identical to ON_SubD::EdgeTag::Crease.
|
|
///</summary>
|
|
Sharp = 3,
|
|
|
|
///<summary>
|
|
/// This tag appears only on edges that have exactly two neighboring faces
|
|
/// and neither end vertex is tagged as ON_SubD::VertexTag::Smooth.
|
|
/// The level 1 subdivision point for a level 0 edge tagged as ON_SubD::EdgeTag::X
|
|
/// is the standard smooth edge subdivision point.
|
|
/// When subdivided, the new subdivision vertex will be tagged
|
|
/// as ON_SubD::VertexTag::Smooth and the subdivided edges will
|
|
/// be tagged as ON_SubD::EdgeTag::Smooth. Thus, the tag ON_SubD::EdgeTag::X
|
|
/// should only appear at level 0.
|
|
/// This tag exists because the ON_SubD subdivision
|
|
/// algorithm requires any edge with both end vertices
|
|
/// tagged as not smooth must be subdivided at its midpoint.
|
|
/// Sector iterators treat "X" edges as smooth.
|
|
/// Both edge end weights must be set so the smooth
|
|
/// subdivided edges will be valid.
|
|
///</summary>
|
|
X = 4
|
|
};
|
|
#pragma endregion
|
|
|
|
static ON_SubD::EdgeTag EdgeTagFromUnsigned(
|
|
unsigned int edge_tag_as_unsigned
|
|
);
|
|
|
|
|
|
/*
|
|
Parameters:
|
|
edge_tag - [in]
|
|
Returns:
|
|
True if edge_tag is Smooth, Crease, Sharp, or X.
|
|
False otherwise.
|
|
*/
|
|
static bool EdgeTagIsSet(
|
|
ON_SubD::EdgeTag edge_tag
|
|
);
|
|
|
|
|
|
#pragma region RH_C_SHARED_ENUM [ON_SubD::FacetType] [Rhino.Geometry.SubD.SubDFacetType] [nested:byte]
|
|
/// <summary>
|
|
/// SubD::FacetType reports the default facet type for subdivision algorithms.
|
|
/// </summary>
|
|
enum class FacetType : unsigned char
|
|
{
|
|
///<summary> Not a valid facet type. </summary>
|
|
Unset = 0,
|
|
|
|
///<summary> Triangle </summary>
|
|
Tri = 3,
|
|
|
|
///<summary> Quadrangle </summary>
|
|
Quad = 4
|
|
};
|
|
#pragma endregion
|
|
|
|
static ON_SubD::FacetType FacetTypeFromUnsigned(
|
|
unsigned int facet_type_as_unsigned
|
|
);
|
|
|
|
//enum class VertexEdgeOrder : unsigned char
|
|
//{
|
|
// unset = 0,
|
|
// radial, // The ON_SubDVertex edge and face information satisfies:
|
|
// // 1) m_face_count = m_edge_count or m_face_count+1 == m_edge_count
|
|
// // 2) m_faces[i] is between m_edges[i] and m_edges[(i+1)%m_edge_count]
|
|
// // 3) When 0 < i < m_edge_count-1, m_edges[i].m_edge_count = 2
|
|
// // and m_edges[i].m_face2[] references m_faces[i-1] and m_faces[i]
|
|
// // in an unspecified order.
|
|
// notradial // one of the conditions conditions for radial is not satisfied.
|
|
//};
|
|
|
|
//static ON_SubD::VertexEdgeOrder VertexEdgeOrderFromUnsigned(
|
|
// unsigned int vertex_edge_order_as_unsigned
|
|
// );
|
|
|
|
#pragma region RH_C_SHARED_ENUM [ON_SubD::VertexFacetType] [Rhino.Geometry.SubD.VertexFacetType] [nested:byte]
|
|
|
|
///<summary>Summarizes the number of edges in faces in the whole object.</summary>
|
|
enum class VertexFacetType : unsigned char
|
|
{
|
|
///<summary>Not a valid vertex face type.</summary>
|
|
Unset = 0,
|
|
|
|
///<summary>All faces are triangular.</summary>
|
|
Tri = 3,
|
|
|
|
///<summary>All faces are quads.</summary>
|
|
Quad = 4,
|
|
|
|
///<summary>Edge count of faces is constant and > 4.</summary>
|
|
Ngon = 5,
|
|
|
|
///<summary>Edge count of faces is not constant.</summary>
|
|
Mixed = 0xFF
|
|
};
|
|
#pragma endregion
|
|
|
|
static ON_SubD::VertexFacetType VertexFacetTypeFromUnsigned(
|
|
unsigned int vertex_facet_type_as_unsigned
|
|
);
|
|
|
|
#pragma region RH_C_SHARED_ENUM [ON_SubD::SubDType] [Rhino.Geometry.SubD.SubDType] [nested:byte]
|
|
/// <summary>
|
|
/// Subdivision algorithm.
|
|
/// </summary>
|
|
enum class SubDType : unsigned char
|
|
{
|
|
///<summary>
|
|
/// Not a valid subdivision type.
|
|
///</summary>
|
|
Unset = 0,
|
|
|
|
///<summary>
|
|
/// Built-in Loop-Warren triangle with Bernstein-Levin-Zorin creases and darts.
|
|
///</summary>
|
|
TriLoopWarren = 3,
|
|
|
|
///<summary>
|
|
/// Built-in Catmull-Clark quad with Bernstein-Levin-Zorin creases and darts.
|
|
///</summary>
|
|
QuadCatmullClark = 4,
|
|
|
|
///<summary>
|
|
/// Custom triangle face algorithm. (Not built-in. Provided for use by 3rd party developers.)
|
|
///</summary>
|
|
CustomTri = 5,
|
|
|
|
///<summary>
|
|
/// Custom quad facet algorithm. (Not built-in. Provided for use by 3rd party developers.)
|
|
///</summary>
|
|
CustomQuad = 6,
|
|
|
|
///<summary>
|
|
/// Custom algorithm. (Not built-in. Provided for use by 3rd party developers.)
|
|
///</summary>
|
|
Custom = 7
|
|
|
|
// All values must be <= 31; i.e., (((unsigned char)0xE0U) & subd_type)) must be zero.
|
|
};
|
|
#pragma endregion
|
|
|
|
static ON_SubD::SubDType SubDTypeFromUnsigned(
|
|
unsigned int subd_type_as_unsigned
|
|
);
|
|
|
|
static ON_SubD::SubDType DefaultSubDType();
|
|
|
|
static unsigned int FacetEdgeCount(
|
|
ON_SubD::FacetType facet_type
|
|
);
|
|
|
|
static unsigned int FacetEdgeCount(
|
|
ON_SubD::SubDType subd_type
|
|
);
|
|
|
|
/*
|
|
Parameters:
|
|
sit - [in]
|
|
vertex sector iterator
|
|
component_ring_capacity - [in]
|
|
capacity of component_ring[] array
|
|
1 + center_vertex.m_edge_count + center_vertex.m_face_count
|
|
will be large enough.
|
|
component_ring - [out]
|
|
A sorted list of ON_SubDComponentPtr values are returned in component_ring[]
|
|
component_ring[0] is the central vertex.
|
|
component_ring[1] and subsequent components with odd indices are sector edges.
|
|
component_ring[2] and subsequent components with even indices are sector faces.
|
|
For edge components (i is odd), component_ring[i].ComponentMark() is the index of
|
|
the center vertex in ON_SubDEge.m_vertex[].
|
|
Returns:
|
|
Number of components set in component_ring[].
|
|
|
|
0: failure
|
|
|
|
>= 4 and even:
|
|
component_ring[0] = center vertex
|
|
component_ring[1] = starting crease edge
|
|
component_ring[2] = starting face
|
|
... zero or more interior smooth edge, face pairs ...
|
|
component_ring[component_count-1] = ending crease edge
|
|
|
|
>= 5 and odd:
|
|
component_ring[0] = vit.CenterVertex()
|
|
component_ring[1] = first edge (smooth)
|
|
component_ring[2] = first face
|
|
... zero or more smooth edge, face, pairs ...
|
|
component_ring[component_count-2] = last edge (smooth)
|
|
component_ring[component_count-1] = last face
|
|
|
|
Example:
|
|
unsinged int component_ring_count = GetVertexComponentRing(vit,component_ring);
|
|
unsinged int N = component_ring_count/2; // number of edges in ring
|
|
const bool bSectorHasCreaseBoundary = (0 == (component_ring_count % 2));
|
|
*/
|
|
static unsigned int GetSectorComponentRing(
|
|
const class ON_SubDSectorIterator& sit,
|
|
size_t component_ring_capacity,
|
|
ON_SubDComponentPtr* component_ring
|
|
);
|
|
|
|
/*
|
|
Parameters:
|
|
sit - [in]
|
|
vertex sector iterator
|
|
component_ring - [out]
|
|
A sorted listof ON_SubDComponentPtr values are returned in component_ring[]
|
|
|
|
|
|
|
|
Returns:
|
|
Number of components set in component_ring[].
|
|
|
|
0: failure
|
|
|
|
>= 4 and even:
|
|
component_ring[0] = vit.CenterVertex()
|
|
component_ring[1] = starting crease edge
|
|
component_ring[2] = starting face
|
|
... zero or more interior smooth edge, face pairs ...
|
|
component_ring[component_count-1] = ending crease edge
|
|
|
|
>= 5 and odd:
|
|
component_ring[0] = center vertex
|
|
component_ring[1] = first edge (smooth)
|
|
component_ring[2] = first face
|
|
... zero or more smooth edge, face, pairs ...
|
|
component_ring[component_count-2] = last edge (smooth)
|
|
component_ring[component_count-1] = last face
|
|
|
|
Example:
|
|
unsinged int component_ring_count = GetVertexComponentRing(vit,component_ring);
|
|
unsinged int N = component_ring_count/2; // number of edges in ring
|
|
const bool bSectorHasCreaseBoundary = (0 == (component_ring_count % 2));
|
|
*/
|
|
static unsigned int GetSectorComponentRing(
|
|
const class ON_SubDSectorIterator& sit,
|
|
ON_SimpleArray<ON_SubDComponentPtr>& component_ring
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
Number of edges in an component ring returned by ON_SubD::GetVertexComponentRing();
|
|
*/
|
|
static unsigned int ComponentRingEdgeCount(
|
|
size_t component_ring_count
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
Number of faces in an component ring returned by ON_SubD::GetVertexComponentRing();
|
|
*/
|
|
static unsigned int ComponentRingFaceCount(
|
|
size_t component_ring_count
|
|
);
|
|
|
|
static bool ComponentRingIsValid(
|
|
size_t component_ring_count,
|
|
const ON_SubDComponentPtr* component_ring
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
Number of points in the subdivision ring or 0 if the call fails.
|
|
*/
|
|
static unsigned int GetSectorSubdivsionPointRing(
|
|
ON_SubD::SubDType subd_type,
|
|
size_t component_ring_count,
|
|
const ON_SubDComponentPtr* component_ring,
|
|
size_t point_ring_capacity,
|
|
size_t point_ring_stride,
|
|
double* point_ring
|
|
);
|
|
|
|
static unsigned int GetSectorSubdivisionPointRing(
|
|
ON_SubD::SubDType subd_type,
|
|
size_t component_ring_count,
|
|
const ON_SubDComponentPtr* component_ring,
|
|
ON_SimpleArray<ON_3dPoint>& subd_point_ring
|
|
);
|
|
|
|
static unsigned int GetSectorPointRing(
|
|
ON_SubD::SubDType subd_type,
|
|
bool bSubdivideIfNeeded,
|
|
size_t component_ring_count,
|
|
const ON_SubDComponentPtr* component_ring,
|
|
size_t subd_point_ring_capacity,
|
|
size_t subd_point_ring_stride,
|
|
double* subd_point_ring
|
|
);
|
|
|
|
static unsigned int GetSectorPointRing(
|
|
ON_SubD::SubDType subd_type,
|
|
bool bSubdivideIfNeeded,
|
|
size_t component_ring_count,
|
|
const ON_SubDComponentPtr* component_ring,
|
|
ON_SimpleArray<ON_3dPoint>& point_ring
|
|
);
|
|
|
|
static unsigned int GetSectorPointRing(
|
|
ON_SubD::SubDType subd_type,
|
|
bool bSubdivideIfNeeded,
|
|
const class ON_SubDSectorIterator& sit,
|
|
size_t point_ring_capacity,
|
|
size_t point_ring_stride,
|
|
double* point_ring
|
|
);
|
|
|
|
static unsigned int GetSectorPointRing(
|
|
ON_SubD::SubDType subd_type,
|
|
bool bSubdivideIfNeeded,
|
|
const class ON_SubDSectorIterator& sit,
|
|
ON_SimpleArray<ON_3dPoint>& point_ring
|
|
);
|
|
|
|
/*
|
|
Parameters:
|
|
subd_type - [in]
|
|
A quad based subdivision algorithm.
|
|
bFirstPass - [in]
|
|
If bFirstPass is true and the components are in standard form for the vertex
|
|
and subdivision type, then locations of the component vertices opposite the
|
|
center vertex are returned in the point ring.
|
|
bSecondPass - [in]
|
|
If bSecondtPass is true and the first pass is disable or does not succeed,
|
|
then the component subdivision locations are returned in the point ring.
|
|
vertex0 - [in]
|
|
If not null, then vertex0->m_edges and vertex0->m_faces must
|
|
be radially sorted and span a single sector and component_ring[]
|
|
is ignored.
|
|
component_ring_count - [in]
|
|
If vertex0 is null, then component_ring_count specifies the number
|
|
of components in the component_ring[] array.
|
|
component_ring[] - [in]
|
|
If vertex0 is null, then component_ring[0] is the central vertex,
|
|
component_ring[1] and subsequent components with odd indices are
|
|
sector edges, component_ring[2] and subsequent components with even
|
|
indices are sector faces, all sorted radially.
|
|
point_ring_stride - [in]
|
|
point_ring - [out]
|
|
point locations are returned here.
|
|
Returns:
|
|
Number of points in the subdivision ring or 0 if the call fails.
|
|
The number of points is
|
|
1 + ON_SubD::ComponentRingEdgeCount(component_ring_count) + ON_SubD::ComponentRingFaceCount(component_ring_count).
|
|
Remarks:
|
|
No validation checking is performed. This function will crash
|
|
if the input is not valid. Call GetSubdivisionPointRing() if
|
|
you want a crash proof call.
|
|
*/
|
|
static unsigned int GetQuadSectorPointRing(
|
|
ON_SubD::SubDType subd_type,
|
|
bool bFirstPass,
|
|
bool bSecondPass,
|
|
const class ON_SubDVertex* vertex0,
|
|
size_t component_ring_count,
|
|
const class ON_SubDComponentPtr* component_ring,
|
|
size_t point_ring_stride,
|
|
double* point_ring
|
|
);
|
|
|
|
/*
|
|
Parameters:
|
|
subd_type - [in]
|
|
A tri based subdivision algorithm.
|
|
bFirstPass - [in]
|
|
If bFirstPass is true and the components are in standard form for the vertex
|
|
and subdivision type, then locations of the component vertices opposite the
|
|
center vertex are returned in the point ring.
|
|
bSecondPass - [in]
|
|
If bSecondPass is true and the first pass is disable or does not succeed,
|
|
then the component subdivision locations are returned in the point ring.
|
|
vertex0 - [in]
|
|
If not null, then vertex0->m_edges and vertex0->m_faces must
|
|
be radially sorted and span a single sector and component_ring[]
|
|
is ignored.
|
|
component_ring_count - [in]
|
|
If vertex0 is null, then component_ring_count specifies the number
|
|
of components in the component_ring[] array.
|
|
component_ring[] - [in]
|
|
If vertex0 is null, then component_ring[0] is the central vertex,
|
|
component_ring[1] and subsequent components with odd indices are
|
|
sector edges, component_ring[2] and subsequent components with even
|
|
indices are sector faces, all sorted radially.
|
|
point_ring_stride - [in]
|
|
point_ring - [out]
|
|
point locations are returned here.
|
|
Returns:
|
|
Number of points in the subdivision ring or 0 if the call fails.
|
|
The number of points is 1 + ON_SubD::ComponentRingEdgeCount(component_ring_count).
|
|
Remarks:
|
|
No validation checking is performed. This function will crash
|
|
if the input is not valid. Call GetSubdivisionPointRing() if
|
|
you want a crash proof call.
|
|
*/
|
|
static unsigned int GetTriSectorPointRing(
|
|
ON_SubD::SubDType subd_type,
|
|
bool bFirstPass,
|
|
bool bSecondPass,
|
|
const class ON_SubDVertex* vertex0,
|
|
size_t component_ring_count,
|
|
const class ON_SubDComponentPtr* component_ring,
|
|
size_t point_ring_stride,
|
|
double* point_ring
|
|
);
|
|
|
|
static const class ON_SubDVertex* SubdivideSector(
|
|
ON_SubD::SubDType subd_type,
|
|
const class ON_SubDVertex* center_vertex,
|
|
size_t component_ring_count,
|
|
const class ON_SubDComponentPtr* component_ring,
|
|
class ON_SubD_FixedSizeHeap& fsh
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
true if sector_edge_count is valid for the vertex type
|
|
*/
|
|
static bool IsValidSectorEdgeCount(
|
|
ON_SubD::VertexTag vertex_tag,
|
|
unsigned int sector_edge_count
|
|
);
|
|
|
|
static bool IsValidSectorFaceCount(
|
|
ON_SubD::VertexTag vertex_tag,
|
|
unsigned int sector_face_count
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
Type of facets the basic subdivision algorithm requires.
|
|
ON_SubD::FacetType::Quad if subd_type is ON_SubD::SubDType::TriLoopWarren.
|
|
ON_SubD::FacetType::Tri if subd_type is ON_SubD::SubDType::QuadCatmullClark.
|
|
ON_SubD::FacetType::Unset otherwise.
|
|
Remark:
|
|
All built in subdivision algorithm will handle faces with 3 or more edges.
|
|
*/
|
|
static ON_SubD::FacetType FacetTypeFromSubDType(
|
|
ON_SubD::SubDType subd_type
|
|
);
|
|
|
|
static ON_SubD::SubDType SubDTypeFromFacetType(
|
|
ON_SubD::FacetType facet_type
|
|
);
|
|
|
|
static bool PointRingHasFacePoints(
|
|
ON_SubD::SubDType subd_type
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
true if facet_type is ON_SubD::FacetType::Tri or ON_SubD::FacetType::Quad.
|
|
*/
|
|
static bool IsQuadOrTriFacetType(
|
|
ON_SubD::FacetType facet_type
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
true if subd_type is ON_SubD::SubDType::TriLoopWarren or ON_SubD::SubDType::QuadCatmullClark.
|
|
*/
|
|
static bool IsQuadOrTriSubDType(
|
|
ON_SubD::SubDType subd_type
|
|
);
|
|
|
|
ON_SubD() ON_NOEXCEPT;
|
|
virtual ~ON_SubD();
|
|
|
|
/*
|
|
Description:
|
|
Creates an independent copy of src.
|
|
*/
|
|
ON_SubD( const ON_SubD& src );
|
|
|
|
/*
|
|
Description:
|
|
Creates an independent copy of src.
|
|
*/
|
|
ON_SubD& operator=(const ON_SubD& src);
|
|
|
|
#if defined(ON_HAS_RVALUEREF)
|
|
// rvalue copy constructor
|
|
ON_SubD( ON_SubD&& ) ON_NOEXCEPT;
|
|
|
|
// The rvalue assignment operator calls ON_Object::operator=(ON_Object&&)
|
|
// which could throw exceptions. See the implementation of
|
|
// ON_Object::operator=(ON_Object&&) for details.
|
|
ON_SubD& operator=( ON_SubD&& );
|
|
#endif
|
|
|
|
/*
|
|
Description:
|
|
The subdivision information referenced by src_subd will be shared with this
|
|
Remarks:
|
|
ON_Geometry base class information, like ON_UserData, is not copied or shared.
|
|
*/
|
|
void ShareContentsFrom(
|
|
ON_SubD& subd
|
|
);
|
|
|
|
static void SwapContents(
|
|
ON_SubD& a,
|
|
ON_SubD& b
|
|
);
|
|
|
|
//virtual
|
|
void MemoryRelocate() override;
|
|
|
|
//virtual
|
|
bool IsValid( class ON_TextLog* text_log = nullptr ) const override;
|
|
|
|
//virtual
|
|
void Dump(
|
|
ON_TextLog&
|
|
) const override;
|
|
|
|
//virtual
|
|
unsigned int SizeOf() const override;
|
|
|
|
//virtual
|
|
ON__UINT32 DataCRC(
|
|
ON__UINT32 current_remainder
|
|
) const override;
|
|
|
|
//virtual
|
|
bool Write(
|
|
ON_BinaryArchive& archive
|
|
) const override;
|
|
|
|
//virtual
|
|
bool Read(
|
|
ON_BinaryArchive& archive
|
|
) override;
|
|
|
|
//virtual
|
|
ON::object_type ObjectType() const override;
|
|
|
|
|
|
//virtual
|
|
unsigned int ClearComponentStates(
|
|
ON_ComponentStatus states_to_clear
|
|
) const override;
|
|
|
|
//virtual
|
|
unsigned int GetComponentsWithSetStates(
|
|
ON_ComponentStatus states_filter,
|
|
bool bAllEqualStates,
|
|
ON_SimpleArray< ON_COMPONENT_INDEX >& components
|
|
) const override;
|
|
|
|
//virtual
|
|
unsigned int SetComponentStates(
|
|
ON_COMPONENT_INDEX component_index,
|
|
ON_ComponentStatus states_to_set
|
|
) const override;
|
|
|
|
//virtual
|
|
unsigned int ClearComponentStates(
|
|
ON_COMPONENT_INDEX component_index,
|
|
ON_ComponentStatus states_to_clear
|
|
) const override;
|
|
|
|
//virtual
|
|
unsigned int SetComponentStatus(
|
|
ON_COMPONENT_INDEX component_index,
|
|
ON_ComponentStatus status_to_copy
|
|
) const override;
|
|
|
|
//virtual
|
|
ON_AggregateComponentStatus AggregateComponentStatus() const override;
|
|
|
|
//virtual
|
|
void MarkAggregateComponentStatusAsNotCurrent() const override;
|
|
|
|
//virtual
|
|
bool DeleteComponents(
|
|
const ON_COMPONENT_INDEX* ci_list,
|
|
size_t ci_count
|
|
) override;
|
|
|
|
/*
|
|
Remarks:
|
|
For ON_SubD objects, ClearBoundingBox() and DestroyRuntimeCache()
|
|
are identical.
|
|
*/
|
|
//virtual
|
|
void DestroyRuntimeCache(
|
|
bool bDelete = true
|
|
) override;
|
|
|
|
//virtual
|
|
int Dimension() const override;
|
|
|
|
// virtual ON_Geometry GetBBox override
|
|
bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override;
|
|
|
|
// virtual ON_Geometry GetTightBoundingBox override
|
|
bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override;
|
|
|
|
/*
|
|
Description:
|
|
Clears all saved information that depends on vertex locations,
|
|
subdivision algorithms, vertex or edge tags, or control net topology.
|
|
If you modify any of the above, then call ClearBoundingBox().
|
|
Remarks:
|
|
For ON_SubD objects, ClearBoundingBox() and DestroyRuntimeCache()
|
|
are identical.
|
|
*/
|
|
//virtual
|
|
void ClearBoundingBox() override;
|
|
|
|
//virtual
|
|
bool Transform(
|
|
const ON_Xform& xform
|
|
) override;
|
|
|
|
//virtual
|
|
bool IsDeformable() const override;
|
|
|
|
//virtual
|
|
bool MakeDeformable() override;
|
|
|
|
//virtual
|
|
bool SwapCoordinates(
|
|
int i,
|
|
int j
|
|
) override;
|
|
|
|
|
|
|
|
//virtual
|
|
bool HasBrepForm() const override;
|
|
|
|
//virtual
|
|
ON_Brep* BrepForm(
|
|
ON_Brep* brep = nullptr
|
|
) const override;
|
|
|
|
//virtual
|
|
ON_COMPONENT_INDEX ComponentIndex() const override;
|
|
|
|
//virtual
|
|
bool EvaluatePoint(
|
|
const class ON_ObjRef& objref,
|
|
ON_3dPoint& P
|
|
) const override;
|
|
|
|
/*
|
|
Description:
|
|
Uses the input mesh to define the level zero control polygon.
|
|
Parameters:
|
|
level_zero_mesh - [in]
|
|
from_mesh_parameters - [in]
|
|
To get the smoothest possible result, pass nullptr
|
|
or ON_SubDFromMeshOptions::Smooth. To get a sub-D with interior
|
|
creases use other static ON_SubDFromMeshOptions values or
|
|
create one with custom settings.
|
|
*/
|
|
static ON_SubD* CreateFromMesh(
|
|
const class ON_Mesh* level_zero_mesh,
|
|
const class ON_SubDFromMeshOptions* from_mesh_parameters,
|
|
ON_SubD* subd
|
|
);
|
|
|
|
unsigned int DumpTopology(
|
|
ON_TextLog&
|
|
) const;
|
|
|
|
unsigned int DumpTopology(
|
|
ON_2udex vertex_id_range,
|
|
ON_2udex edge_id_range,
|
|
ON_2udex face_id_range,
|
|
ON_TextLog&
|
|
) const;
|
|
|
|
/*
|
|
Description:
|
|
Discard all contents of this ON_SubD.
|
|
Remarks:
|
|
More efficient than Destroy() if this ON_SubD will be reused soon.
|
|
*/
|
|
void Clear();
|
|
|
|
/*
|
|
Description:
|
|
Delete all contents release all memory used by this ON_SubD.
|
|
*/
|
|
void Destroy();
|
|
|
|
ON_SubD::SubDType ActiveLevelSubDType() const;
|
|
|
|
/*
|
|
Returns:
|
|
The number of explicitly computed levels that are currently available.
|
|
A value of 0 indicates this SubD is empty.
|
|
*/
|
|
unsigned int LevelCount() const;
|
|
|
|
/*
|
|
Returns:
|
|
If the SubD is not empty, then the index of the active level is returned. This value will be < LevelCount().
|
|
If the SubD is empty, then ON_UNSET_UINT_INDEX is returned.
|
|
*/
|
|
unsigned int ActiveLevelIndex() const;
|
|
|
|
/*
|
|
Description:
|
|
Remove subdivision levels
|
|
Paramters:
|
|
max_level_index - [in]
|
|
Remove all levels after max_level_index
|
|
*/
|
|
void ClearHigherSubdivisionLevels(
|
|
unsigned int max_level_index
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Remove subdivision levels
|
|
Paramters:
|
|
min_level_index - [in]
|
|
Remove all levels before min_level_index
|
|
*/
|
|
void ClearLowerSubdivisionLevels(
|
|
unsigned int min_level_index
|
|
);
|
|
|
|
bool IsEmpty() const;
|
|
|
|
|
|
/*
|
|
Description:
|
|
Get aggregate edge demographics for the subd.
|
|
Returns:
|
|
Bitwise or of ON_ComponentAttributes::EdgeFlags values for every edge in the subd.
|
|
*/
|
|
unsigned int EdgeFlags() const;
|
|
|
|
/////////////////////////////////////////////////////////
|
|
//
|
|
// Component (Vertex, Edge, Face) access
|
|
//
|
|
ON_SubDComponentPtr ComponentPtrFromComponentIndex(
|
|
ON_COMPONENT_INDEX component_index
|
|
) const;
|
|
|
|
unsigned int ComponentPtrFromComponentIndex(
|
|
const ON_COMPONENT_INDEX* ci_list,
|
|
size_t ci_count,
|
|
ON_SimpleArray<ON_SubDComponentPtr>& cptr_list
|
|
) const;
|
|
|
|
unsigned int ComponentPtrFromComponentIndex(
|
|
const ON_COMPONENT_INDEX* ci_list,
|
|
size_t ci_count,
|
|
bool bIncludeVertices,
|
|
bool bIncludeEdges,
|
|
bool bIncludeFaces,
|
|
ON_SimpleArray<ON_SubDComponentPtr>& cptr_list
|
|
) const;
|
|
|
|
/////////////////////////////////////////////////////////
|
|
//
|
|
// Vertex access
|
|
//
|
|
|
|
unsigned int VertexCount() const;
|
|
|
|
const class ON_SubDVertex* FirstVertex() const;
|
|
|
|
class ON_SubDVertexIterator VertexIterator() const;
|
|
|
|
class ON_SubDVertexArray VertexArray() const;
|
|
|
|
/*
|
|
Parameters:
|
|
vertex_id - [in]
|
|
Returns:
|
|
If vertex_id identifies a valid vertex in this ON_SubD, then
|
|
a pointer to that vertex is returned.
|
|
Otherwise, nullptr is returned.
|
|
*/
|
|
const class ON_SubDVertex* VertexFromId(
|
|
unsigned int vertex_id
|
|
) const;
|
|
|
|
/////////////////////////////////////////////////////////
|
|
//
|
|
// Edge access
|
|
//
|
|
|
|
unsigned int EdgeCount() const;
|
|
|
|
const class ON_SubDEdge* FirstEdge() const;
|
|
|
|
class ON_SubDEdgeIterator EdgeIterator() const;
|
|
|
|
class ON_SubDEdgeArray EdgeArray() const;
|
|
|
|
/*
|
|
Parameters:
|
|
edge_id - [in]
|
|
Returns:
|
|
If edge_id identifies a valid edge in this ON_SubD, then
|
|
a pointer to that edge is returned.
|
|
Otherwise, nullptr is returned.
|
|
*/
|
|
const class ON_SubDEdge* EdgeFromId(
|
|
unsigned int edge_id
|
|
) const;
|
|
|
|
/////////////////////////////////////////////////////////
|
|
//
|
|
// Face access
|
|
//
|
|
|
|
unsigned int FaceCount() const;
|
|
|
|
const class ON_SubDFace* FirstFace() const;
|
|
|
|
class ON_SubDFaceIterator FaceIterator() const;
|
|
|
|
class ON_SubDFaceArray FaceArray() const;
|
|
|
|
/*
|
|
Parameters:
|
|
face_id - [in]
|
|
Returns:
|
|
If face_id identifies a valid face in this ON_SubD, then
|
|
a pointer to that face is returned.
|
|
Otherwise, nullptr is returned.
|
|
*/
|
|
const class ON_SubDFace* FaceFromId(
|
|
unsigned int face_id
|
|
) const;
|
|
|
|
/////////////////////////////////////////////////////////
|
|
//
|
|
// Component (vertex, edge, face) state ( selected, highlighted, ... ) tools
|
|
// NOTE:
|
|
// All component status settings are mutable
|
|
// All are copied.
|
|
// None are saved.
|
|
//
|
|
|
|
/*
|
|
Parameters:
|
|
states_filter - [in]
|
|
bAllEqualStates - [in]
|
|
If a state is set in states_filter, all active level components
|
|
with the same state set will be included in the
|
|
components_with_set_states[] array.
|
|
If bAllEqualStates is true, then ON_ComponentStatus::AllEqualStates()
|
|
is used to test for inclusion.
|
|
If bAllEqualStates is false, then ON_ComponentStatus::SomeEqualStates()
|
|
is used to test for inclusion.
|
|
components_with_set_states - [out]
|
|
Returns:
|
|
Number of returned components.
|
|
*/
|
|
unsigned int GetComponentsWithSetStates(
|
|
ON_ComponentStatus states_filter,
|
|
bool bAllEqualStates,
|
|
ON_SimpleArray< ON_SubDComponentPtr >& components_with_set_states
|
|
) const;
|
|
|
|
|
|
/*
|
|
Description:
|
|
Set states on an individual component.
|
|
Parameters:
|
|
component_ptr - [in]
|
|
The states will be set on this component.
|
|
states_to_set - [in]
|
|
If a state is set in the states_to_set parameter, the same
|
|
state will be set on the component.
|
|
Returns:
|
|
0: no state settings changed on the component.
|
|
1: some state setting changed on the component.
|
|
*/
|
|
unsigned int SetComponentStates(
|
|
ON_SubDComponentPtr component_ptr,
|
|
ON_ComponentStatus states_to_set
|
|
) const;
|
|
|
|
/*
|
|
Description:
|
|
Clear states on an individual component.
|
|
Parameters:
|
|
component_ptr - [in]
|
|
The states will be cleared on this component.
|
|
states_to_clear - [in]
|
|
If a state is set in the states_to_clear parameter, the same
|
|
state will be cleared on the component.
|
|
Returns:
|
|
0: no state settings changed on the component.
|
|
1: some state setting changed on the component.
|
|
*/
|
|
unsigned int ClearComponentStates(
|
|
ON_SubDComponentPtr component_ptr,
|
|
ON_ComponentStatus states_to_clear
|
|
) const;
|
|
|
|
/*
|
|
Description:
|
|
Copy status settings to an individual component.
|
|
Parameters:
|
|
component_ptr - [in]
|
|
The states will be copied to this component.
|
|
status_to_copy - [in]
|
|
Returns:
|
|
1: some state settings changed on the component.
|
|
1: some state setting changed on the component.
|
|
*/
|
|
unsigned int SetComponentStatus(
|
|
ON_SubDComponentPtr component_ptr,
|
|
ON_ComponentStatus status_to_copy
|
|
) const;
|
|
|
|
bool DeleteComponents(
|
|
const ON_SubDComponentPtr* cptr_list,
|
|
size_t cptr_count
|
|
);
|
|
|
|
|
|
/////////////////////////////////////////////////////////
|
|
//
|
|
// Editing tools
|
|
//
|
|
|
|
unsigned int MergeColinearEdges(
|
|
double distance_tolerance,
|
|
double maximum_aspect,
|
|
double sin_angle_tolerance
|
|
);
|
|
|
|
ON_SubDEdgePtr MergeEdges(
|
|
ON_SubDEdgePtr eptr0,
|
|
ON_SubDEdgePtr eptr1
|
|
);
|
|
|
|
static bool EdgesCanBeMerged(
|
|
ON_SubDEdgePtr eptr0,
|
|
ON_SubDEdgePtr eptr1
|
|
);
|
|
|
|
// returns true if all facets are consistently oriented
|
|
bool IsOriented(
|
|
unsigned int level_index
|
|
) const;
|
|
|
|
// reverses the orientation of all facets
|
|
bool ReverseOrientation(
|
|
unsigned int level_index
|
|
) const;
|
|
|
|
// Attempts to orient all facet to match the first facet.
|
|
bool Orient(
|
|
unsigned int level_index
|
|
) const;
|
|
|
|
/*
|
|
Description:
|
|
Interior vertices (smooth and dart) must have at least three faces.
|
|
Concave corner vertices must have at least two faces.
|
|
*/
|
|
bool RepairInvalidSectors(
|
|
unsigned int level_index
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Split and edge.
|
|
The input edge is modifed to terminate at the input vertex.
|
|
The new edge begins at the input vertex and ends at the final vertex
|
|
of the original input edge.
|
|
edge - [in]
|
|
edge to split.
|
|
vertex_location - [in]
|
|
location of inserted vertex.
|
|
If vertex_location == ON_ON_3dPoint::UnsetPoint,
|
|
then the edge's midpoint is used.
|
|
Returns:
|
|
A pointer to the new edge or nullptr if the input is not valid.
|
|
*/
|
|
const class ON_SubDEdge* SplitEdge(
|
|
class ON_SubDEdge* edge,
|
|
ON_3dPoint vertex_location
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Split a face into two faces by inserting and edge connecting the
|
|
specified vertices.
|
|
Parameters:
|
|
face - [in]
|
|
A face with at least four edges.
|
|
fvi0 - [in]
|
|
fvi1 - [in]
|
|
Indices of the inserted edge ends.
|
|
Returns:
|
|
A pointer to the inserted edge.
|
|
The inserted edge runs from face->Vertex(fvi0) to face->Vertex(fvi1).
|
|
ON_SubDEdge.Face(0) is the original face and ON_SubDEdge::Face(1) is
|
|
the added face.
|
|
The first edge of the input face remains the first edge of face.
|
|
The inserted edge is the first edge of the added face.
|
|
*/
|
|
const class ON_SubDEdge* SplitFace(
|
|
class ON_SubDFace* face,
|
|
unsigned int fvi0,
|
|
unsigned int fvi1
|
|
);
|
|
|
|
const class ON_SubDVertex* TriangulateFace(
|
|
class ON_SubDFace* face
|
|
);
|
|
|
|
const class ON_SubDFace* MergeFaces(
|
|
class ON_SubDEdge* edge
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Updates vertex tag, edge tag, and edge coefficient values
|
|
on the active level.
|
|
|
|
After completing custom editing operations that modify the
|
|
topology of the SubD control net or changing values of
|
|
vertex or edge tags, the tag and sector coefficients
|
|
information on nearby components in the edited areas
|
|
need to be updated.
|
|
|
|
Parameters:
|
|
bUnsetValuesOnly - [in]
|
|
If true, the update is restricted to vertices tagged as
|
|
ON_SubD::VertexTag::Unset and edges tagged as ON_SubD::EdgeTag::Unset.
|
|
|
|
Returns:
|
|
Number of vertices and edges that were changed during the update.
|
|
*/
|
|
unsigned int UpdateAllTagsAndSectorCoefficients(
|
|
bool bUnsetValuesOnly
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
This tool if for expert users writing advanced editing tools.
|
|
After completing custom editing operations that modify the
|
|
topology of the SubD control net or changing values of
|
|
vertex or edge tags, the tag and sector coefficients
|
|
information on nearby components in the edited areas
|
|
need to be updated.
|
|
Parameters:
|
|
bUnsetTagsOnly - [in]
|
|
If bUnsetTagsOnly is true, then only unset tags and
|
|
ill be updated.
|
|
If bUnsetTagsOnly is false, then all tags and
|
|
will be checked and updated as needed.
|
|
Returns:
|
|
Number of vertices that changed during the update.
|
|
Remarks:
|
|
It is easiest to call UpdateTagsAndSectorCoefficients().
|
|
*/
|
|
unsigned int UpdateVertexTags(
|
|
bool bUnsetVertexTagsOnly
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
This tool if for expert users writing advanced editing tools.
|
|
After completing custom editing operations that modify the
|
|
topology of the SubD control net or changing values of
|
|
vertex or edge tags, the tag and sector coefficients
|
|
information on nearby components in the edited areas
|
|
need to be updated.
|
|
Parameters:
|
|
bUnsetValuesOnly - [in]
|
|
If bUnsetValuesOnly is true, then only unset tags and
|
|
sector weights will be updated.
|
|
If bUnsetValuesOnly is false, then all tags and
|
|
sector weights will be checked and updated as needed.
|
|
Returns:
|
|
Number of edges that had a tag value changed or sector
|
|
coefficient set to ON_SubDSectorType::UnsetSectorWeight.
|
|
Remarks:
|
|
It is easiest to call UpdateTagsAndSectorCoefficients().
|
|
*/
|
|
unsigned int UpdateEdgeTags(
|
|
bool bUnsetEdgeTagsOnly
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
This tool if for expert users writing advanced editing tools.
|
|
After completing custom editing operations that modify the
|
|
topology of the SubD control net or changing values of
|
|
vertex or edge tags, the tag and sector coefficients
|
|
information on nearby components in the edited areas
|
|
need to be updated.
|
|
Parameters:
|
|
bUnsetValuesOnly - [in]
|
|
If bUnsetValuesOnly is true, then only unset tags and
|
|
sector weights will be updated.
|
|
If bUnsetValuesOnly is false, then all tags and
|
|
sector weights will be checked and updated as needed.
|
|
Returns:
|
|
Number of edges that had a tag value changed or sector
|
|
coefficient set to ON_SubDSectorType::UnsetSectorWeight.
|
|
Remarks:
|
|
It is easiest to call UpdateTagsAndSectorCoefficients().
|
|
*/
|
|
unsigned int UpdateEdgeSectorCoefficients(
|
|
bool bUnsetSectorCoefficientsOnly
|
|
);
|
|
|
|
/*
|
|
Descripiton:
|
|
Clears the ON_ComponentState
|
|
*/
|
|
unsigned int ClearComponentMarks(
|
|
bool bClearVertexMarks,
|
|
bool bClearEdgeMarks,
|
|
bool bClearFaceMarks,
|
|
ON_SimpleArray< const class ON_SubDComponentBase* >* marked_component_list
|
|
) const;
|
|
|
|
unsigned int SetComponentMarks(
|
|
bool bClearBeforeSet,
|
|
const ON_SimpleArray< const class ON_SubDComponentBase* >& marked_component_list
|
|
) const;
|
|
|
|
unsigned int GetMarkedComponents(
|
|
bool bIncludeVertices,
|
|
bool bIncludeEdges,
|
|
bool bIncludeFaces,
|
|
ON_SimpleArray< const class ON_SubDComponentBase* >& marked_component_list
|
|
) const;
|
|
|
|
|
|
/*
|
|
Description:
|
|
Transforms the SubD components in ci_list[].
|
|
Parameters:
|
|
xform - [in]
|
|
ci_list - [in]
|
|
ci_count - [in]
|
|
Returns:
|
|
Number of vertex locations that changed.
|
|
*/
|
|
unsigned int TransformComponents(
|
|
const ON_Xform& xform,
|
|
const ON_COMPONENT_INDEX* ci_list,
|
|
size_t ci_count
|
|
);
|
|
|
|
unsigned int TransformComponents(
|
|
const ON_Xform& xform,
|
|
const ON_SubDComponentPtr* cptr_list,
|
|
size_t cptr_count
|
|
);
|
|
|
|
unsigned int ExtrudeComponents(
|
|
const ON_Xform& xform,
|
|
const ON_COMPONENT_INDEX* ci_list,
|
|
size_t ci_count,
|
|
bool bPermitNonManifoldEdgeCreation,
|
|
ON_SubD::EdgeTag original_edge_tag,
|
|
ON_SubD::EdgeTag moved_edge_tag
|
|
);
|
|
|
|
unsigned int ExtrudeComponents(
|
|
const ON_Xform& xform,
|
|
const ON_SubDComponentPtr* cptr_list,
|
|
size_t cptr_count,
|
|
bool bPermitNonManifoldEdgeCreation,
|
|
ON_SubD::EdgeTag original_edge_tag,
|
|
ON_SubD::EdgeTag moved_edge_tag
|
|
);
|
|
|
|
/////*
|
|
////Description:
|
|
//// Apply the built-in triangle subdivision subdivision algorithm globally.
|
|
////Returns:
|
|
//// New level.
|
|
////*/
|
|
////unsigned int TriSubdivision();
|
|
|
|
////unsigned int GetSector(
|
|
//// const class ON_SubDFace* face,
|
|
//// ON__UINT_PTR face_vertex_index,
|
|
//// class ON_SubDVertex& sector
|
|
//// ) const;
|
|
|
|
////unsigned int GetSector(
|
|
//// const class ON_SubDVertex* vertex,
|
|
//// const ON_SubDFace* face,
|
|
//// class ON_SubDVertex& sector
|
|
//// ) const;
|
|
|
|
////unsigned int GetSector(
|
|
//// const ON_SubDVertex* vertex,
|
|
//// ON_SubDFacePtr face_ptr,
|
|
//// class ON_SubDVertex& sector
|
|
//// ) const;
|
|
|
|
////unsigned int GetSector(
|
|
//// const class ON_SubDVertex* vertex,
|
|
//// const class ON_SubDEdge* smooth_edge,
|
|
//// ON_SubDVertex& sector
|
|
//// ) const;
|
|
|
|
////unsigned int GetSector(
|
|
//// const ON_SubDEdge* smooth_edge,
|
|
//// ON__UINT_PTR smooth_edge_end_index,
|
|
//// ON_SubDVertex& sector
|
|
//// ) const;
|
|
|
|
////unsigned int GetSector(
|
|
//// ON_SubDEdgePtr smooth_edge_ptr,
|
|
//// class ON_SubDVertex& sector
|
|
//// ) const;
|
|
|
|
/*
|
|
Description:
|
|
Apply the built-in subdivision algorithm and save the results
|
|
in this ON_SubD.
|
|
Parameters:
|
|
subd_type - [in]
|
|
unset will use the current subdivision type.
|
|
level_index - [in]
|
|
Level where subdivision starts
|
|
count - [in] > 0
|
|
Number of times to subdivide.
|
|
Returns:
|
|
Number of subdivision steps that succeeded.
|
|
(= count when everything works, < count when input is not valid)
|
|
*/
|
|
bool Subdivide(
|
|
ON_SubD::SubDType subd_type,
|
|
unsigned int level_index,
|
|
unsigned int count
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
Active level subdivison type.
|
|
*/
|
|
bool SetSubDType(
|
|
ON_SubD::SubDType subd_type
|
|
);
|
|
|
|
class ON_SubDVertex* AddVertex(
|
|
ON_SubD::VertexTag vertex_tag,
|
|
const double* P
|
|
);
|
|
|
|
/*
|
|
Parameters:
|
|
edge_face_count - [in]
|
|
Number of faces the edge will eventually have.
|
|
Pass 0 if the value is not known.
|
|
v0 - [in]
|
|
starting vertex
|
|
v1 - [in]
|
|
ending vertex
|
|
Returns:
|
|
If edge_face_count > 0x7FFFU, then ON_SubD::EdgeTag::Unset is returned.
|
|
|
|
If edge_face_count is 1 or >= 3, then ON_SubD::EdgeTag::Crease is returned.
|
|
|
|
If both vertex tags are ON_SubD::VertexTag::Smooth, then ON_SubD::EdgeTag::Smooth is returned.
|
|
|
|
If edge_face_count is 1 and both vertex tags are ON_SubD::VertexTag::Crease or ON_SubD::VertexTag::Corner,
|
|
then ON_SubD::EdgeTag::Crease is returned.
|
|
|
|
If edge_face_count is 2 and both vertex tags are set and both are not ON_SubD::VertexTag::Smooth,
|
|
then ON_SubD::EdgeTag::X is returned.
|
|
|
|
Otherwise, ON_SubD::EdgeTag::Unset is returned.
|
|
*/
|
|
static ON_SubD::EdgeTag EdgeTagFromContext(
|
|
unsigned int edge_face_count,
|
|
const ON_SubD::VertexTag v0_tag,
|
|
const ON_SubD::VertexTag v1_tag
|
|
);
|
|
|
|
static ON_SubD::EdgeTag EdgeTagFromContext(
|
|
unsigned int edge_face_count,
|
|
const ON_SubDVertex* v0,
|
|
const ON_SubDVertex* v1
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Add an edge to the subd.
|
|
Parameters:
|
|
edge_tag - [in]
|
|
ON_SubD::EdgeTag::Unset
|
|
Edge tag is not known at this time.
|
|
ON_SubD::EdgeTag::Smooth
|
|
Smooth edge. If both vertices are tagged as not smooth, the
|
|
tag on the returned edge will be ON_SubD::EdgeTag::X. This
|
|
tag is changed to ON_SubD::EdgeTag::Smooth on the first
|
|
subdivision step.
|
|
ON_SubD::EdgeTag::Crease.
|
|
Crease edge. Both vertices must be tagged as not smooth.
|
|
v0 - [in]
|
|
v1 - [in]
|
|
The edge begins at v0 and ends at v1.
|
|
The edge will be on the same level as the vertices.
|
|
Returns:
|
|
Pointer to the allocated edge.
|
|
Remarks:
|
|
ON_SubD::EdgeTagFromContext() can be used to determine edge
|
|
tag values in simple situations.
|
|
*/
|
|
class ON_SubDEdge* AddEdge(
|
|
ON_SubD::EdgeTag edge_tag,
|
|
class ON_SubDVertex* v0,
|
|
class ON_SubDVertex* v1
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Expert use tool to add an edge with precomputed sector coefficients.
|
|
Parameters:
|
|
edge_tag - [in]
|
|
This expert user function does not automatically set the edge tag.
|
|
v0 - [in]
|
|
v0_sector_coefficient - [in]
|
|
v1 - [in]
|
|
v1_sector_coefficient - [in]
|
|
The edge begins at v0 and ends at v1.
|
|
The edge will be on the same level as the vertices.
|
|
The edges sector weights are set
|
|
*/
|
|
class ON_SubDEdge* AddEdgeWithSectorCoefficients(
|
|
ON_SubD::EdgeTag edge_tag,
|
|
class ON_SubDVertex* v0,
|
|
double v0_sector_coefficient,
|
|
class ON_SubDVertex* v1,
|
|
double v1_sector_coefficient
|
|
);
|
|
|
|
class ON_SubDFace* AddFace(
|
|
unsigned int edge_count,
|
|
const class ON_SubDEdgePtr* edge
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Expert user tool to insert an edge in the face's edge array.
|
|
Parameters:
|
|
face - [in]
|
|
edge - [in]
|
|
edge_direction -[in]
|
|
i - [in]
|
|
index where the edge should be inserted.
|
|
Returns:
|
|
true if successful.
|
|
Remarks:
|
|
This tool is used during construction or editing of a SubD and the
|
|
connection is added even if the result is an invalid face or edge.
|
|
It is up to the expert user to make enough changes to create a valid SubD.
|
|
*/
|
|
bool AddFaceEdgeConnection(
|
|
ON_SubDFace* face,
|
|
unsigned int i,
|
|
ON_SubDEdge* edge,
|
|
ON__UINT_PTR edge_direction
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Expert user tool to insert an edge in the face's edge array.
|
|
Parameters:
|
|
face - [in]
|
|
eptr - [in]
|
|
direction must be set correctly
|
|
i - [in]
|
|
index where the edge should be inserted.
|
|
Returns:
|
|
true if successful.
|
|
Remarks:
|
|
This tool is used during construction or editing of a SubD and the
|
|
connection is added even if the result is an invalid face or edge.
|
|
It is up to the expert user to make enough changes to create a valid SubD.
|
|
*/
|
|
bool AddFaceEdgeConnection(
|
|
ON_SubDFace* face,
|
|
unsigned int i,
|
|
ON_SubDEdgePtr eptr
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Expert user tool to insert an edge in the face's edge array.
|
|
Parameters:
|
|
face - [in]
|
|
edge - [in]
|
|
edge to remove
|
|
Returns:
|
|
true if successful.
|
|
Remarks:
|
|
This tool is used during construction or editing of a SubD and the
|
|
connection is removed even if the result is an invalid face or edge.
|
|
It is up to the expert user to make enough changes to create a valid SubD.
|
|
*/
|
|
bool RemoveFaceEdgeConnection(
|
|
ON_SubDFace* face,
|
|
ON_SubDEdge* edge
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Expert user tool to insert an edge in the face's edge array.
|
|
Parameters:
|
|
face - [in]
|
|
i - [in]
|
|
index where the edge should be removed.
|
|
removed_edge - [out]
|
|
removed edge
|
|
Remarks:
|
|
This tool is used during construction or editing of a SubD and the
|
|
connection is removed even if the result is an invalid face or edge.
|
|
It is up to the expert user to make enough changes to create a valid SubD.
|
|
*/
|
|
bool RemoveFaceEdgeConnection(
|
|
ON_SubDFace* face,
|
|
unsigned int i
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Expert user tool to insert an edge in the face's edge array.
|
|
Parameters:
|
|
face - [in]
|
|
i - [in]
|
|
index where the edge should be removed.
|
|
removed_edge - [out]
|
|
removed edge
|
|
Remarks:
|
|
This tool is used during construction or editing of a SubD and the
|
|
connection is removed even if the result is an invalid face or edge.
|
|
It is up to the expert user to make enough changes to create a valid SubD.
|
|
*/
|
|
bool RemoveFaceEdgeConnection(
|
|
ON_SubDFace* face,
|
|
unsigned int i,
|
|
ON_SubDEdgePtr& removed_edge
|
|
);
|
|
|
|
bool GrowVertexEdgeArray(
|
|
ON_SubDVertex* v,
|
|
size_t capacity
|
|
);
|
|
bool GrowVertexFaceArray(
|
|
ON_SubDVertex* v,
|
|
size_t capacity
|
|
);
|
|
bool GrowEdgeFaceArray(
|
|
ON_SubDEdge* e,
|
|
size_t capacity
|
|
);
|
|
bool GrowFaceEdgeArray(
|
|
ON_SubDFace* f,
|
|
size_t capacity
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Get the limit surface mesh for this subD.
|
|
Parameters:
|
|
minimum_display_density - [in]
|
|
Returns:
|
|
A mesh of the subdivision limit surface.
|
|
Remarks:
|
|
The mesh is a reference counted mesh managed by this ON_SubD.
|
|
*/
|
|
class ON_SubDLimitMesh LimitSurfaceMesh() const;
|
|
|
|
ON_SubDLimitMesh UpdateLimitSurfaceMesh(
|
|
unsigned int minimum_display_density
|
|
) const;
|
|
|
|
void ClearLimitSurfaceMesh() const;
|
|
|
|
void ClearEvaluationCache() const;
|
|
|
|
/*
|
|
Description:
|
|
Get an ON_Mesh of the subdivision limit surface
|
|
Parameters:
|
|
display_parameters - [in]
|
|
mesh - [in]
|
|
If not null, the returned mesh will be stored on
|
|
the input class.
|
|
Returns:
|
|
A mesh of the subdivision limit surface.
|
|
*/
|
|
class ON_Mesh* GetLimitSurfaceMesh(
|
|
const class ON_SubDDisplayParameters& display_parameters,
|
|
class ON_Mesh* mesh
|
|
) const;
|
|
|
|
|
|
/*
|
|
Description:
|
|
Get a mesh of the subdivision control net.
|
|
Parameters:
|
|
level_index - [in] (>=0)
|
|
mesh - [in]
|
|
If not null, the returned mesh will be stored on
|
|
the input class.
|
|
Returns:
|
|
The subdivision level as a mesh.
|
|
*/
|
|
class ON_Mesh* GetControlNetMesh(
|
|
class ON_Mesh* mesh
|
|
) const;
|
|
|
|
|
|
/*
|
|
Description:
|
|
Get the limit surface mesh as a set of fragments.
|
|
Parameters:
|
|
display_parameters - [in]
|
|
|
|
fragment_callback_context - [in]
|
|
first parameter for the FragmentCallback function
|
|
|
|
fragment_callback_function - [in]
|
|
A function pointer with prototype:
|
|
|
|
bool fragment_callback_function(
|
|
void *fragment_callback_context,
|
|
const class ON_SubDLimitMeshFragment* fragment
|
|
);
|
|
|
|
For each fragment that is produced, fragment_callback_function() is called.
|
|
You must copy the retuned fragment if you want to keep it for future use.
|
|
If fragment_callback_function returns false, the calculation is canceled.
|
|
Returns:
|
|
Number of fragments produced.
|
|
*/
|
|
unsigned int GetLimitSurfaceMeshInFragments(
|
|
const class ON_SubDDisplayParameters& display_parameters,
|
|
ON__UINT_PTR fragment_callback_context,
|
|
bool(*fragment_callback_function)(ON__UINT_PTR , const class ON_SubDLimitMeshFragment*)
|
|
) const;
|
|
|
|
/*
|
|
Returns:
|
|
The number of limit surface mesh fragments (ON_SubDLimitMeshFragment) that
|
|
GetLimitSurfaceMeshFragments() will produce.
|
|
*/
|
|
unsigned int LimitSurfaceMeshFragmentCount() const;
|
|
|
|
|
|
/*
|
|
Description:
|
|
Get the limit surface as a set of bicubic patch fragments.
|
|
Parameters:
|
|
display_parameters - [in]
|
|
|
|
callback_context - [in]
|
|
first parameter for the callback functions
|
|
|
|
begin_face_callback_function - [in]
|
|
nullptr or a function pointer with prototype:
|
|
|
|
bool begin_face_callback_function(
|
|
void *fragment_callback_context,
|
|
const class ON_SubDFaceRegion& face_region,
|
|
);
|
|
|
|
At the beginning of each quad face, this function is called.
|
|
If the original SubD face is a quad, then face_region identifies that quad.
|
|
If the original SubD face is not a quad, then face_region identifies the
|
|
level 1 subdivision quad. The face region information is useful in building
|
|
a correspondence between the original SubD and the Nurbs patches.
|
|
If begin_face_callback_function returns false, the calculation is canceled.
|
|
|
|
fragment_callback_function - [in]
|
|
A function pointer with prototype:
|
|
|
|
bool fragment_callback_function(
|
|
void *fragment_callback_context,
|
|
const class ON_SubDLimitNurbsFragment* fragment
|
|
);
|
|
|
|
For each fragment that is produced, fragment_callback_function() is called.
|
|
You must copy the retuned fragment if you want to keep it for future use.
|
|
If fragment_callback_function returns false, the calculation is canceled.
|
|
Returns:
|
|
Number of fragments produced.
|
|
*/
|
|
unsigned int GetLimitSurfaceNurbsFragments(
|
|
const class ON_SubDDisplayParameters& display_parameters,
|
|
ON__UINT_PTR callback_context,
|
|
bool(*begin_face_callback_function)(ON__UINT_PTR ,const class ON_SubDFaceRegion&),
|
|
bool(*fragment_callback_function)(ON__UINT_PTR ,const class ON_SubDLimitNurbsFragment*)
|
|
) const;
|
|
|
|
#pragma region RH_C_SHARED_ENUM [ON_SubD::NurbsSurfaceType] [Rhino.Geometry.SubD.NurbsSurfaceType] [nested:byte]
|
|
/// <summary>
|
|
/// ON_SubD::NurbsSurfaceType specifies what type of NURBS surfaces are returned by ON_SubD.GetLimitSurfaceNurbs()
|
|
/// </summary>
|
|
enum class NurbsSurfaceType : unsigned char
|
|
{
|
|
///<summary>
|
|
/// Not a valid type. Used to indicate the type has not been set and to encourage developers to explicitly specify a type.
|
|
/// When in doubt, specify NurbsSurfaceType::Large.
|
|
///</summary>
|
|
Unset = 0,
|
|
|
|
///<summary>
|
|
/// A single NURBS surface will be created for each SubD quad. Near extraordinary vertices, the surfaces may
|
|
/// have lots of knots.
|
|
///</summary>
|
|
Large = 1,
|
|
|
|
///<summary>
|
|
/// NURBS surfaces will be as large as possible without the addition of extra knots.
|
|
/// Near extraordinary vertices, the surfaces may
|
|
/// have lots of knots.
|
|
/// This option is prefered when a user wants larger NURBS surfaces but not at the cost of addtional NURBS control points.
|
|
///</summary>
|
|
Medium = 2,
|
|
|
|
///<summary>
|
|
/// NURBS surfaces will not be merged and will have clamped knots.
|
|
///</summary>
|
|
Small = 3,
|
|
|
|
///<summary>
|
|
/// NURBS surfaces will not be merged and will have unclamped uniform knots.
|
|
/// This is useful as a starting point for customized merging and modifying
|
|
/// continuity at extraordinary vertices.
|
|
///</summary>
|
|
Unprocessed = 4
|
|
};
|
|
#pragma endregion
|
|
|
|
/*
|
|
Description:
|
|
Get the SubD limit surface as bicubic NURBS surfaces.
|
|
|
|
Parameters:
|
|
display_parameters - [in]
|
|
Determines the density of NURBS patches near extraordinary vertices.
|
|
|
|
nurbs_surface_type - [in]
|
|
Controls the size and knot properties of the returned NURBS sufaces.
|
|
|
|
callback_context - [in]
|
|
first parameter for the callback functions
|
|
|
|
nurbs_callback_function - [in]
|
|
nullptr or a function pointer with prototype:
|
|
|
|
bool nurbs_callback_function(
|
|
ON__UINT_PTR callback_context,
|
|
const ON_SubDComponentRegion& subd_face_region,
|
|
const ON_SubDComponentRegion subd_edge_region[4],
|
|
const class ON_NurbsSurface*& nurbs_surface
|
|
);
|
|
|
|
For each NURBS surface that is produced, nurbs_callback_function() is called.
|
|
|
|
subd_face_region identifies the region of the SubD that this NURBS surface
|
|
models.
|
|
|
|
subd_edge_region[4] identifies the edges the NURBS surface abuts in
|
|
the order South,East,North,West.
|
|
|
|
When an edge is a subdivision edge, then
|
|
subd_edge_region[i].m_level0_component.ComponentType() is ON_SubDComponentPtr::Type::Edge,
|
|
subd_edge_region[i].m_level0_component.ComponentBase() is nullptr, and
|
|
ON_SubDComponentRegion::IsTransientId(subd_edge_region[i].m_level0_component_id) is true
|
|
|
|
The nurbs_surface pointer points to an ON_NurbsSurface on the heap.
|
|
You must take responsibility for managing this surface and deleting it
|
|
at the appropriate time.
|
|
|
|
Returns:
|
|
Number of NURBS surfaces returned.
|
|
|
|
*/
|
|
unsigned int GetLimitSurfaceNurbs(
|
|
const class ON_SubDDisplayParameters& display_parameters,
|
|
ON_SubD::NurbsSurfaceType nurbs_surface_type,
|
|
ON__UINT_PTR callback_context,
|
|
bool(*nurbs_callback_function)(ON__UINT_PTR , const class ON_SubDFaceRegion&, class ON_NurbsSurface*)
|
|
) const;
|
|
|
|
|
|
/*
|
|
Description:
|
|
Get the SubD limit surface as a list of bicubic NURBS patches.
|
|
Parameters:
|
|
display_parameters - [in]
|
|
Determines the density of NURBS patches near extraordinary vertices.
|
|
nurbs_surface_type - [in]
|
|
Controls the size and knot properties of the returned NURBS sufaces.
|
|
patches - [out]
|
|
The bicubic NURBS patches are appended to this array.
|
|
Returns:
|
|
Number of patches appended to patches[]
|
|
*/
|
|
unsigned int GetLimitSurfaceNurbs(
|
|
const class ON_SubDDisplayParameters& display_parameters,
|
|
ON_SubD::NurbsSurfaceType nurbs_surface_type,
|
|
ON_SimpleArray< ON_NurbsSurface* >& patches
|
|
) const;
|
|
|
|
unsigned int GetLimitSurfaceNurbs(
|
|
const class ON_SubDDisplayParameters& display_parameters,
|
|
ON_SubD::NurbsSurfaceType nurbs_surface_type,
|
|
ON_SimpleArray< ON_SubDFaceRegionAndNurbs >& patches
|
|
) const;
|
|
|
|
ON_Brep* GetLimitSurfaceNurbs(
|
|
const class ON_SubDDisplayParameters& display_parameters,
|
|
ON_Brep* destination_brep
|
|
) const;
|
|
|
|
public:
|
|
/*
|
|
Description:
|
|
Pretend this function and ON_SubDimple do not exist.
|
|
Returns:
|
|
Something that you are pretending does not exist.
|
|
Remarks:
|
|
It is intentional that the definition of ON_SubDimple class is not
|
|
available in the opennurbs library interface (not in a header file).
|
|
The size and design of ON_SubDimple will change constantly.
|
|
If you choose to hack and whack so you can dereference an
|
|
ON_SubDimple* pointer, then your code will crash unpredictably.
|
|
*/
|
|
const class ON_SubDimple* SubDimple() const;
|
|
const class ON_SubDLevel& ActiveLevel() const;
|
|
unsigned int SubDimpleUseCount() const;
|
|
|
|
void ShareDimple(const ON_SubD&);
|
|
void SwapDimple(ON_SubD&);
|
|
|
|
void ShareDimple(const class ON_SubDLimitMeshImpl&);
|
|
void SwapDimple(class ON_SubDLimitMeshImpl& );
|
|
|
|
private:
|
|
class ON_SubDimple* SubDimple(bool bCreateIfNeeded);
|
|
class ON_SubDLevel const * ActiveLevelConstPointer() const;
|
|
class ON_SubDLevel* ActiveLevelPointer();
|
|
|
|
void CopyHelper(const ON_SubD&);
|
|
|
|
private:
|
|
friend class ON_SubDRef;
|
|
#pragma ON_PRAGMA_WARNING_PUSH
|
|
#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 )
|
|
// C4251: ... needs to have dll-interface to be used by clients of class ...
|
|
// m_subdimple_sp is private and all code that manages m_subdimple_sp is explicitly implemented in the DLL.
|
|
private:
|
|
std::shared_ptr<class ON_SubDimple> m_subdimple_sp;
|
|
#pragma ON_PRAGMA_WARNING_POP
|
|
|
|
public:
|
|
// The ON_SubD code increments ON_SubD::ErrorCount everytime something
|
|
// unexpected happens. This is useful for debugging.
|
|
static unsigned int ErrorCount;
|
|
};
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDRef
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDRef
|
|
{
|
|
#if defined(ON_SUBD_CENSUS)
|
|
private:
|
|
ON_SubDRefCensusCounter m_census_counter;
|
|
#endif
|
|
|
|
public:
|
|
static const ON_SubDRef Empty;
|
|
|
|
ON_SubDRef() ON_NOEXCEPT;
|
|
~ON_SubDRef();
|
|
ON_SubDRef(const ON_SubDRef& src) ON_NOEXCEPT;
|
|
ON_SubDRef& operator=(const ON_SubDRef& src);
|
|
|
|
#if defined(ON_HAS_RVALUEREF)
|
|
// rvalue copy constructor
|
|
ON_SubDRef( ON_SubDRef&& ) ON_NOEXCEPT;
|
|
// rvalue assignment operator
|
|
ON_SubDRef& operator=( ON_SubDRef&& );
|
|
#endif
|
|
|
|
const class ON_SubD& SubD() const;
|
|
|
|
/*
|
|
Returns:
|
|
Number of references to the ON_SubD, including the one by this ON_SubDRef.
|
|
*/
|
|
unsigned int ReferenceCount() const;
|
|
|
|
/*
|
|
Description:
|
|
Allocates a new ON_SubD and has this ON_SubDRef reference it.
|
|
*/
|
|
class ON_SubD& NewSubD();
|
|
|
|
|
|
/*
|
|
Description:
|
|
Allocates a new ON_SubD and has this ON_SubDRef reference it.
|
|
*/
|
|
class ON_SubD& CopySubD(
|
|
const ON_SubDRef& src
|
|
);
|
|
class ON_SubD& CopySubD(
|
|
const ON_SubD& src
|
|
);
|
|
|
|
class ON_SubD& UniqueSubD();
|
|
|
|
/*
|
|
Description:
|
|
Remove this reference to the managed ON_SubD.
|
|
If this is the last reference, then the managed ON_SubD is deleted.
|
|
*/
|
|
void Clear();
|
|
|
|
public:
|
|
class ON_SubDVertexIterator VertexIterator() const;
|
|
class ON_SubDEdgeIterator EdgeIterator() const;
|
|
class ON_SubDFaceIterator FaceIterator() const;
|
|
|
|
/*
|
|
Description:
|
|
Expert user function to have this ON_SubDRef manage the lifetime of subd.
|
|
Parameters:
|
|
subd - [in/out]
|
|
subd must point to an ON_SubD that was constructed on the heap using
|
|
an operator new call with a public ON_SubD constructor.
|
|
Returns:
|
|
a pointer to the managed subd or nullptr subd in not valid.
|
|
Example:
|
|
ON_SubD* subd = new ON_SubD(...);
|
|
ON_SubDRef subr;
|
|
ON_SubD* managed_subd = subdr.SetSubD(subd);
|
|
// subd = nullptr
|
|
// managed_subd = pointer you can use
|
|
*/
|
|
class ON_SubD* SetSubDForExperts(
|
|
class ON_SubD*& subd
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Expert user function to have this ON_SubDRef reference the
|
|
contents of an existing ON_SubD.
|
|
Do not use if user data on the referenced subd needs to be accessed.
|
|
Parameters:
|
|
subd - [in]
|
|
Any subd on the heap or the stack.
|
|
Returns:
|
|
true if successful.
|
|
*/
|
|
static ON_SubDRef CreateReferenceForExperts(
|
|
const ON_SubD& subd
|
|
);
|
|
|
|
private:
|
|
/*
|
|
Description:
|
|
Expert user function to have this ON_SubDRef reference the
|
|
contents of an existing ON_SubD.
|
|
Do not use if user data on the referenced subd needs to be accessed.
|
|
Parameters:
|
|
subd - [in]
|
|
Any subd on the heap or the stack.
|
|
*/
|
|
ON_SubDRef(
|
|
const class ON_SubD& subd
|
|
);
|
|
|
|
private:
|
|
#pragma ON_PRAGMA_WARNING_PUSH
|
|
#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 )
|
|
// C4251: ... needs to have dll-interface to be used by clients of class ...
|
|
// m_subd_sp is private and all code that manages m_subd_sp is explicitly implemented in the DLL.
|
|
private:
|
|
std::shared_ptr<class ON_SubD> m_subd_sp;
|
|
#pragma ON_PRAGMA_WARNING_POP
|
|
};
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDSectorType
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDSectorType
|
|
{
|
|
public:
|
|
ON_SubDSectorType() = default;
|
|
ON_SubDSectorType(const ON_SubDSectorType&) = default;
|
|
ON_SubDSectorType& operator=(const ON_SubDSectorType&) = default;
|
|
|
|
static const ON_SubDSectorType Empty;
|
|
|
|
bool IsValid() const;
|
|
|
|
unsigned int SectorTypeHash() const;
|
|
|
|
static int Compare(const ON_SubDSectorType*, const ON_SubDSectorType*);
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Sector Weights
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// In the comment below,
|
|
// F = number of faces in the sector,
|
|
// E = number of edges in the sector.
|
|
//
|
|
// There are five valid sector configurations of edges and faces. In all
|
|
// configurations, the edges have one end at the center vertex and the
|
|
// faces have one corner at the center vertex.
|
|
//
|
|
// SMOOTH
|
|
// 1) The center vertex is smooth.
|
|
// 2) F >= 2
|
|
// 3) E = F
|
|
// 4) Every edge is smooth.
|
|
// 5) Every edge is an edge of two different faces in the sector.
|
|
//
|
|
// DART
|
|
// 1) The center vertex is a dart.
|
|
// 2) F >= 2
|
|
// 3) E = F
|
|
// 4) One edge is a crease.
|
|
// 5) The crease edge is an edge of two geometrically adjacent sector faces.
|
|
//
|
|
// DART* (The same as "DART", but the crease edge has been duplicated.)
|
|
// 1) The center vertex is a dart.
|
|
// 2) F >= 2
|
|
// 3) E = F+1
|
|
// 4) Two edges are creases that have the same end locations.
|
|
// 5) Each crease edge is an edge of a single face in the sector,
|
|
// these faces are different and are geometrically adjacent.
|
|
//
|
|
// BOUNDED
|
|
// 1) The center vertex is a crease or corner vertex.
|
|
// 2) F >= 2
|
|
// 3) E = F+1
|
|
// 4) Two edges are crease edges that have different vertices at their ends.
|
|
// 5) Each crease edge is an edge of a single face in the sector,
|
|
// these faces are different and not geometrically adjacent.
|
|
//
|
|
// BOUNDED*
|
|
// 1) The center vertex is a crease or corner vertex.
|
|
// 2) F = 1
|
|
// 3) E = 2
|
|
// 4) The edges are crease edges that have different vertices at their ends.
|
|
// 5) The edges a edges of the face.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// The sector weight is used when subdividing smooth edges in sectors
|
|
// with a DART, DART* or BOUNDED configuration. In these cases the
|
|
// sector weight is a value strictly between 0.0 and 1.0 that depends on
|
|
// 1) the center vertex tag (crease, corner or dart),
|
|
// 2) the value of F,
|
|
// 3) and when the center vertex is a corner, the angle between
|
|
// the boundary edges.
|
|
//
|
|
// The sector weight is ignored when dividing smooth edges in SMOOTH sectors.
|
|
// The sector weight is ignored when subdividing crease edges.
|
|
//
|
|
// For a smooth edge in a sector with a DART, DART* or BOUNDED configuration,
|
|
// with w = sector weight, C = location of the center vertex
|
|
// and P = location of the smooth vertex at the other end
|
|
// of the smooth edge, the point
|
|
//
|
|
// Q = 3/4 * (w*C + (1-w)*P)
|
|
//
|
|
// is the contribution of C and P to the edge's subdivision point.
|
|
//
|
|
// When a smooth edge has smooth vertices at both ends located
|
|
// at A and B, the contribution of A and B to the edge's subdivision
|
|
// point is
|
|
//
|
|
// Q = 3/8 * (A + B) = 3/4 * (1/2*A + 1/2*B)
|
|
//
|
|
// A crease edge's subdivision point is alwasy the edge's midpoint.
|
|
/*
|
|
Description:
|
|
Calculates sector weight value for the sector type
|
|
identified by this ON_SubDSectorType.
|
|
Returns:
|
|
w: 0.0 <= w < 1.0
|
|
w = sector theta value.
|
|
ON_SubDSectorType::ErrorSectorWeight
|
|
This ON_SubDSectorType is not valid and the calculation failed.
|
|
*/
|
|
double SectorWeight() const;
|
|
|
|
|
|
ON_SubD::SubDType SubDType() const;
|
|
|
|
ON_SubD::FacetType FacetType() const;
|
|
|
|
unsigned int FacetEdgeCount() const;
|
|
|
|
ON_SubD::VertexTag VertexTag() const;
|
|
|
|
|
|
unsigned int EdgeCount() const;
|
|
|
|
unsigned int FaceCount() const;
|
|
|
|
/*
|
|
Returns:
|
|
Number of points in the point ring.
|
|
For quad subds, this is 1 + FaceCount() + EdgeCount().
|
|
For tri subds, this is 1 + EdgeCount().
|
|
*/
|
|
unsigned int PointRingCount() const;
|
|
|
|
/*
|
|
Returns:
|
|
1 + FaceCount() + EdgeCount()
|
|
*/
|
|
unsigned int ComponentRingCount() const;
|
|
|
|
/*
|
|
Returns:
|
|
If the sector vertex tag is ON_SubD::VertexTag::Corner,
|
|
the angle between the corner crease boundary edges is
|
|
returned.
|
|
Otherwise, ON_SubDSectorType::ErrorCornerSectorAngle is returned.
|
|
*/
|
|
double CornerSectorAngleRadians() const;
|
|
|
|
/*
|
|
Returns:
|
|
a value >= 0 and <= ON_SubDSectorType::MaximumAngleIndex
|
|
*/
|
|
unsigned int CornerSectorAngleIndex() const;
|
|
|
|
/*
|
|
Description:
|
|
An angle index value of ON_SubDSectorType::MaximumAngleIndex indicates
|
|
the angle is 2pi radians.
|
|
*/
|
|
static const unsigned int MaximumAngleIndex; // = 72
|
|
|
|
/*
|
|
Parameters:
|
|
angle_radians - [in] (0.0 <= angle_radians <= 2*ON_PI
|
|
The angle between the bounding crease edges
|
|
Returns:
|
|
angle_index: >= 0 and <= ON_SubDSectorType::MaximumCornerSectorIndex
|
|
| angle_radians - angle_index/M * 2pi | <= 1/2 * 1/M * 2pi,
|
|
where M = ON_SubDSectorType::MaximumAngleIndex
|
|
ON_UNSET_UINT_INDEX
|
|
angle_radians is not valid and the calculation failed.
|
|
*/
|
|
static unsigned int AngleIndexFromAngleRadians(
|
|
double angle_radians
|
|
);
|
|
|
|
/*
|
|
Convert and angle index into radians
|
|
Parameters:
|
|
angle_index - [in]
|
|
0 to ON_SubDSectorType::MaximumAngleIndex.
|
|
Returns:
|
|
If angle_index is valid, the corresponding angle in radians is returned.
|
|
= angle_index / ON_SubDSectorType::MaximumAngleIndex * 2 * ON_PI
|
|
(double division performed)
|
|
Otherwise ON_UNSET_VALUE is returned.
|
|
*/
|
|
static double AngleRadiansFromAngleIndex(
|
|
unsigned int angle_index
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
True if this is a smooth interior vertex sector
|
|
*/
|
|
bool IsSmoothSector() const;
|
|
|
|
|
|
/*
|
|
Returns:
|
|
True if this is a dart interior vertex sector
|
|
*/
|
|
bool IsDartSector() const;
|
|
|
|
|
|
/*
|
|
Returns:
|
|
True if this is a crease vertex sector
|
|
*/
|
|
bool IsCreaseSector() const;
|
|
|
|
/*
|
|
Returns:
|
|
True if this is a corner vertex sector
|
|
*/
|
|
bool IsCornerSector() const;
|
|
|
|
/*
|
|
Returns:
|
|
True if this is a convex corner vertex sector (sector angle <= pi)
|
|
*/
|
|
bool IsConvexCornerSector() const;
|
|
|
|
/*
|
|
Returns:
|
|
True if this is a concave corner vertex sector (sector angle > pi)
|
|
*/
|
|
bool IsConcaveCornerSector() const;
|
|
|
|
/*
|
|
Parameters:
|
|
sector_boundary_edge0_ptr - [in]
|
|
sector_boundary_edge1_ptr - [in]
|
|
Crease edges that bound the sector containing the smooth edge.
|
|
The edge direction must identify the corner vertex.
|
|
Returns:
|
|
tagged end angle for a smooth edge that
|
|
1) ends at a vertex tagged on ON_SubD::VertexTag::Corner
|
|
2) has two adjacent faces.
|
|
3) lies in a sector bounded by 2 distinct crease edges.
|
|
*/
|
|
static double CornerSectorAngleRadiansFromEdges(
|
|
ON_SubDEdgePtr sector_boundary_edge0_ptr,
|
|
ON_SubDEdgePtr sector_boundary_edge1_ptr
|
|
);
|
|
|
|
static bool IsValidCornerSectorAngleRadians(
|
|
double corner_sector_angle_radians
|
|
);
|
|
|
|
static double ClampCornerSectorAngleRadians(
|
|
double corner_sector_angle_radians
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
Number of subdivision points in a sector ring
|
|
facet_type vertex_tag ring count
|
|
tri smooth N+1
|
|
tri crease N+2
|
|
quad smooth 2N+1
|
|
quad crease 2N+2
|
|
(2 * valence + 1) for quad subds
|
|
(valence + 1) for tri subds
|
|
*/
|
|
static unsigned int SectorPointRingCountFromEdgeCount(
|
|
ON_SubD::SubDType subd_type,
|
|
ON_SubD::VertexTag vertex_tag,
|
|
unsigned int sector_edge_count
|
|
);
|
|
|
|
static unsigned int SectorPointRingCountFromFaceCount(
|
|
ON_SubD::SubDType subd_type,
|
|
ON_SubD::VertexTag vertex_tag,
|
|
unsigned int sector_face_count
|
|
);
|
|
|
|
static unsigned int SectorFaceCountFromEdgeCount(
|
|
ON_SubD::VertexTag vertex_tag,
|
|
unsigned int sector_edge_count
|
|
);
|
|
|
|
static unsigned int SectorEdgeCountFromFaceCount(
|
|
ON_SubD::VertexTag vertex_tag,
|
|
unsigned int sector_face_count
|
|
);
|
|
|
|
static unsigned int MinimumSectorEdgeCount(
|
|
ON_SubD::VertexTag vertex_tag
|
|
);
|
|
|
|
static unsigned int MinimumSectorFaceCount(
|
|
ON_SubD::VertexTag vertex_tag
|
|
);
|
|
|
|
public:
|
|
/*
|
|
Returns:
|
|
ON_SubDSectorType::IgnoredSectorWeight
|
|
*/
|
|
static double SmoothSectorWeight();
|
|
|
|
/*
|
|
Parameters:
|
|
face_type - [in]
|
|
sector_face_count - [in]
|
|
number of faces in the smooth sector.
|
|
Returns:
|
|
0:
|
|
failed to caclulate weight
|
|
ON_SubDSectorType::UnsetSectorWeight:
|
|
subd_type was set ON_SubD::SubDType::Unset
|
|
and was required to calculate the weight.
|
|
This typically happens when a SubD control net is being
|
|
created and a facet type is not specified.
|
|
The weights will be calculated at the first subdivision.
|
|
0 < w < 1:
|
|
1/2 + 1/3*cos(tagged end angle) for quadrangle facets
|
|
1/3 + 1/3*cos(tagged end angle) for triangle facets
|
|
Remarks:
|
|
This is a useful tool when calling AddEdge while a subdivision
|
|
level is being constructed.
|
|
*/
|
|
static double CreaseSectorWeight(
|
|
ON_SubD::SubDType subd_type,
|
|
unsigned int sector_face_count
|
|
);
|
|
|
|
static double DartSectorWeight(
|
|
ON_SubD::SubDType subd_type,
|
|
unsigned int sector_face_count
|
|
);
|
|
|
|
static double CornerSectorWeight(
|
|
ON_SubD::SubDType subd_type,
|
|
unsigned int sector_face_count,
|
|
double corner_sector_angle_radians
|
|
);
|
|
|
|
// This value is is used to set sector angles when the
|
|
// actual value is not needed. This occurs at both ends
|
|
// of a creased edge and when the end of a smooth edge
|
|
// is a smooth vertex.
|
|
static const double IgnoredCornerSectorAngle; // = 0.0;
|
|
|
|
// This value is used to set sector weights that could not be
|
|
// correctly set because something in the calculation failed.
|
|
// It is typically used when an invalid component in SubD object
|
|
// was needed to calculate the weight.
|
|
static const double UnsetCornerSectorAngle; // = -8881.0;
|
|
|
|
// This value is indicate a corner sector angle calculation failed.
|
|
static const double ErrorCornerSectorAngle; // = -9991.0;
|
|
|
|
|
|
// This value is used for smooth sector thetas
|
|
static const double SmoothSectorTheta; // = 0.5*ON_PI
|
|
|
|
// This value is used to indicate a sector theta needs to be set
|
|
static const double UnsetSectorTheta; // = -8882.0;
|
|
|
|
// This value is used to indicate a sector theta calculation failed.
|
|
static const double ErrorSectorTheta; // = -9992.0;
|
|
|
|
|
|
// This value is is used to set sector weights when the
|
|
// actual value is not needed. This occurs at both ends
|
|
// of a creased edge and when the end of a smooth edge
|
|
// is a smooth vertex.
|
|
static const double IgnoredSectorWeight; // = 0.0;
|
|
|
|
// This value is used to mark sector weights that need to be
|
|
// set in the future when more information is available.
|
|
// It is typically used when creating a subD control net
|
|
// and the facet type is not known. Any value < 0.0 and not
|
|
// equal to ON_UNSET_VALUE would work. The fact that the actual
|
|
// value is -999.0 has no other significance.
|
|
static const double UnsetSectorWeight; // = -8883.0;
|
|
|
|
// This value is indicate a sector weight calculation failed.
|
|
static const double ErrorSectorWeight; // = -9993.0;
|
|
|
|
static bool IsValidSectorWeightValue(
|
|
double weight_value,
|
|
bool bAllowUnsetTaggedEndWeight
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
ON_SubDSectorType::ErrorSectorWeight and calls debug breakpoint
|
|
*/
|
|
static double SectorWeightCalculationError();
|
|
|
|
|
|
/*
|
|
Description:
|
|
Create a ON_SubDSectorType from a ON_SubDSectorIterator.
|
|
Parameters:
|
|
subd_type - [in]
|
|
vertex_tag - [in]
|
|
sector_face_count - [in]
|
|
Number of faces in the sector.
|
|
corner_sector_angle_radians - [in]
|
|
If vertex_tag is ON_SubD::VertexTag::Corner, this
|
|
parameter is the angle between the crease edges
|
|
that bound the corner.
|
|
If vertex_tag is not ON_SubD::VertexTag::Corner,
|
|
this parameter is ignored.
|
|
Returns:
|
|
An ON_SubDSectorType for the case the input parameters
|
|
identify.
|
|
*/
|
|
static ON_SubDSectorType Create(
|
|
ON_SubD::SubDType subd_type,
|
|
ON_SubD::VertexTag vertex_tag,
|
|
unsigned int sector_face_count,
|
|
double corner_sector_angle_radians
|
|
);
|
|
|
|
|
|
/*
|
|
Description:
|
|
Create a ON_SubDSectorType from a ON_SubDSectorIterator.
|
|
Parameters:
|
|
subd_type - [in]
|
|
sit - [in]
|
|
Returns:
|
|
An ON_SubDSectorType for the sector identified by sit.
|
|
*/
|
|
static ON_SubDSectorType Create(
|
|
ON_SubD::SubDType subd_type,
|
|
const ON_SubDSectorIterator& sit
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Create a ON_SubDSectorType for the sector containing the face.
|
|
Parameters:
|
|
subd_type - [in]
|
|
face - [in]
|
|
face_vertex_index - [in]
|
|
face->Vertex(face_vertex_index) will be the sector's
|
|
center vertex.
|
|
Returns:
|
|
An ON_SubDSectorType for the sector containing the face.
|
|
*/
|
|
static ON_SubDSectorType Create(
|
|
ON_SubD::SubDType subd_type,
|
|
const class ON_SubDFace* face,
|
|
unsigned int face_vertex_index
|
|
);
|
|
|
|
static ON_SubDSectorType Create(
|
|
ON_SubD::SubDType subd_type,
|
|
const class ON_SubDFace* face,
|
|
const class ON_SubDVertex* vertex
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Create a ON_SubDSectorType for the sector containing the edge.
|
|
Parameters:
|
|
subd_type - [in]
|
|
edge - [in]
|
|
edge_vertex_index - [in]
|
|
edge->Vertex(edge_vertex_index) will be the sector's
|
|
center vertex.
|
|
Returns:
|
|
An ON_SubDSectorType for the sector containing the edge.
|
|
*/
|
|
static ON_SubDSectorType Create(
|
|
ON_SubD::SubDType subd_type,
|
|
const class ON_SubDEdge* edge,
|
|
unsigned int edge_vertex_index
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Create a smooth ON_SubDSectorType.
|
|
Parameters:
|
|
subd_type - [in]
|
|
sector_face_count - [in]
|
|
Number of faces in the sector.
|
|
Returns:
|
|
An ON_SubDSectorType for the smooth sector case specified
|
|
by the input parameters.
|
|
*/
|
|
static ON_SubDSectorType CreateSmoothSectorType(
|
|
ON_SubD::SubDType subd_type,
|
|
unsigned int sector_face_count
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Create a crease ON_SubDSectorType.
|
|
Parameters:
|
|
subd_type - [in]
|
|
sector_face_count - [in]
|
|
Number of faces in the sector.
|
|
Returns:
|
|
An ON_SubDSectorType for the crease sector case specified
|
|
by the input parameters.
|
|
*/
|
|
static ON_SubDSectorType CreateCreaseSectorType(
|
|
ON_SubD::SubDType subd_type,
|
|
unsigned int sector_face_count
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Create a dart ON_SubDSectorType.
|
|
Parameters:
|
|
subd_type - [in]
|
|
sector_face_count - [in]
|
|
Number of faces in the sector.
|
|
Returns:
|
|
An ON_SubDSectorType for the dart sector case specified
|
|
by the input parameters.
|
|
*/
|
|
static ON_SubDSectorType CreateDartSectorType(
|
|
ON_SubD::SubDType subd_type,
|
|
unsigned int sector_face_count
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Create a corner ON_SubDSectorType.
|
|
Parameters:
|
|
subd_type - [in]
|
|
sector_face_count - [in]
|
|
Number of faces in the sector.
|
|
corner_sector_angle_radians - [in]
|
|
The angle between the crease edges that bound the corner.
|
|
Returns:
|
|
An ON_SubDSectorType for the corner sector case specified
|
|
by the input parameters.
|
|
*/
|
|
static ON_SubDSectorType CreateCornerSectorType(
|
|
ON_SubD::SubDType subd_type,
|
|
unsigned int sector_face_count,
|
|
double sector_corner_angle_radians
|
|
);
|
|
|
|
static int Compare(
|
|
const ON_SubDSectorType& a,
|
|
const ON_SubDSectorType& b
|
|
);
|
|
|
|
|
|
/*
|
|
Description:
|
|
Get the subdivision matrix for the default subdivison algorithms
|
|
used by ON_SubD.
|
|
|
|
The matrix coefficents are ordered so that the matrix acts on
|
|
the left of the points returned by ON_SubDSectorIterator::GetVertexRing().
|
|
|
|
For an interior vertex (smooth or dart), the coefficents are ordered
|
|
so that one iteration of subdivision is given by:
|
|
ON_SubD::SubDType::TriLoopWarren case:
|
|
S*Transpose(V, E[0], E[1], ..., E[N-1])
|
|
ON_SubD::SubDType::QuadCatmullClark case:
|
|
S*Transpose(V, E[0], F[0], E[1], F[1], ..., E[N-1], F[N-1]).
|
|
For a dart vertex, E[0] is the point at the end of the creased edge.
|
|
|
|
|
|
For a boundary vertex (crease or corner), the coefficents are ordered
|
|
so that one iteration of subdivision is given by:
|
|
ON_SubD::SubDType::TriLoopWarren case:
|
|
S*Transpose(V, E[0], E[1], ..., E[N-1]).
|
|
ON_SubD::SubDType::QuadCatmullClark case:
|
|
S*Transpose(V, E[0], F[0], E[1], F[1], ..., F[N-2], E[N-1]).
|
|
|
|
N = edge valence = number of edges in the sector.
|
|
E[i] = end of i-th edge radiating from V.
|
|
In the ON_SubD::SubDType::QuadCatmullClark case, F[i] = point on the quad
|
|
that is opposite V.
|
|
The edges and faces are ordered radially so that the face for F[i]
|
|
lies between the edges for E[i] and E[(i+1)%N].
|
|
|
|
Parameters:
|
|
matrix_capacity - [in]
|
|
S[] can store any RxR matrix with R <= matrix_capacity.
|
|
S - [out]
|
|
subdivision matrix
|
|
Matrix coefficent (i,j) = S[i][j]
|
|
0 <= i < R, 0 <= j < R, R = ON_SubDSectorType.PointRingCount()
|
|
|
|
Returns:
|
|
R > 0:
|
|
R = PointRingCount() and S is the RxR subdivision matrix for the sector type.
|
|
0: Invalid input
|
|
*/
|
|
unsigned int GetSubdivisionMatrix(
|
|
size_t matrix_capacity,
|
|
double** S
|
|
) const;
|
|
|
|
/*
|
|
Parameters:
|
|
S_capacity - [in]
|
|
Number of elements in S[] array
|
|
S - [out]
|
|
subdivision matrix.
|
|
Matrix coefficent (i,j) = S[i*R + j],
|
|
0 <= i < R, 0 <= j < R, R = ON_SubDSectorType.PointRingCount()
|
|
Returns:
|
|
0: Invalid input.
|
|
>0: Number of rows and columns in S.
|
|
This number is always ON_SubDSectorType.PointRingCount().
|
|
*/
|
|
unsigned int GetSubdivisionMatrix(
|
|
size_t S_capacity,
|
|
double* S
|
|
) const;
|
|
|
|
/*
|
|
Description:
|
|
All the subdivision matrices for the ON_SubD built-in
|
|
subdivision algorithms have eigenvalues (1, lambda1, lambda2, e4, ..., eR),
|
|
where 1 > lambda1 >= lambda2 > |e4| >= ... >= |eR| > 0.
|
|
|
|
The subdominant eigenvalue is lambda1 and,
|
|
with one exception, lambda1 = lambda2.
|
|
The exception is described in the description of
|
|
ON_SubDSectorType::SubdominantEigenvalueMulitiplicity().
|
|
|
|
Returns:
|
|
> 0.0:
|
|
The subdominant eigenvalue for the subdivision matrix.
|
|
|
|
ON_UNSET_VALUE:
|
|
This ON_SubDSectorType is not valid.
|
|
*/
|
|
double SubdominantEigenvalue() const;
|
|
|
|
/*
|
|
Returns:
|
|
0:
|
|
The sector type is not set.
|
|
|
|
2:
|
|
The subdominant eigenvalue has algebraic and geometric multiplicty = 2.
|
|
This is the most common case.
|
|
|
|
1:
|
|
The subdominant eigenvalue has algebraic and geometric multiplicty = 1.
|
|
This occures in Catmull-Clark subdivision at a crease vertex with
|
|
two crease edges and a single face. The subdivision matrix for this
|
|
case is
|
|
S is a 4 x 4 matrix with rows =
|
|
(3/4, 1/8, 0, 1/8),
|
|
(1/2, 1/2, 0, 0),
|
|
(1/4, 1/4, 1/4, 1/4),
|
|
(1/2, 0, 0, 1/2).
|
|
S has 4 real eigenvalues = (1, 1/2, 1/4, 1/4), all wtih
|
|
geometric multiplicity = 1.
|
|
The three eigenvectors are
|
|
(1, 1, 1, 1), (0, -1, 0, 1), (0, 0, 1, 0).
|
|
*/
|
|
unsigned int SubdominantEigenvalueMulitiplicity() const;
|
|
|
|
/*
|
|
Description:
|
|
With one exception, which is described below,
|
|
all the subdivision matrices for the ON_SubD built-in
|
|
subdivision algorithms have eigenvalues (1, lambda, lambda, e4, ..., eR),
|
|
where lambda is real, 1 > lambda > |e4| >= ... >= |eR| > 0, and the
|
|
geometric dimension of the lambda eigenspace is 2 (there are two
|
|
linearly independent lambda eigenvectors).
|
|
|
|
The subdominant eigenvalue is lamda. This function returns an
|
|
orthogonal basis, (E1, E2), for the subdominant eigenspace.
|
|
|
|
An eigenvector for the dominant eigen value 1 has is (1,1,...,1).
|
|
The domainant eignevector is orthogonal to the subdominant eigenspace.
|
|
|
|
Put another way,
|
|
0 = E1[0] + ... + E1[R-1]
|
|
0 = E2[0] + ... + E2[R-1]
|
|
0 = E1[0]*E2[0] + ... + E1[R-1]*E2[R-1]
|
|
|
|
Exceptional case:
|
|
The Catmull-Clark subdivision matrix for a crease vertex with
|
|
two crease edges and a single face is a special case.
|
|
In this exceptional, this function returns
|
|
lambda = 1/2, E1 = {0,-1,0,-1} and E2 = {1, -2, -5, -2}.
|
|
For more information about the exceptional case, see the description of
|
|
ON_SubDSectorType::SubdominantEigenvalueMulitiplicity().
|
|
|
|
Parameters:
|
|
E1_capacity - [in]
|
|
Capacity of the E1[] array.
|
|
E1 - [out]
|
|
E2_capacity - [in]
|
|
Capacity of the E2[] array.
|
|
E2 - [out]
|
|
When E1_capacity > 0 and E2_capacity > 0, two orthoganal eigenvectors
|
|
spanning the subdivision matrix subdominant eigenspace are returned
|
|
in E1[] and E2[].
|
|
If one of E1_capacity or E2_capacity is > 0, then both must be > 0.
|
|
|
|
Returns:
|
|
ON_UNSET_VALUE: Invalid input.
|
|
e > 0.0 and e < 1.0:
|
|
subdominant eigenvalue.
|
|
*/
|
|
double GetSubdominantEigenvectors(
|
|
size_t E1_capacity,
|
|
double* E1,
|
|
size_t E2_capacity,
|
|
double* E2
|
|
) const;
|
|
|
|
/*
|
|
Parameters:
|
|
LPev_capacity - [in]
|
|
Capacity of the LPev[] array.
|
|
LPev - [out]
|
|
When LPev_capacity > 0, then the limit surface point evaluation coefficients are
|
|
returned in LPev[]. Otherwise LPev is ignored.
|
|
LT0ev_capacity - [in]
|
|
Capacity of the LPev[] array.
|
|
LT0ev - [out]
|
|
LT1ev_capacity - [in]
|
|
Capacity of the LPev[] array.
|
|
LT1ev - [out]
|
|
When LT0ev_capacity > 0 and LT1ev_capacity > 0, then the limit surface
|
|
tangent coefficients are returned in LT0ev[] and LT1ev[]. Otherwise,
|
|
LT0ev[] and LT1ev[] are ignored.
|
|
If one of LT0ev_capacity or LT1ev_capacity is > 0, then both must be > 0.
|
|
Returns:
|
|
0: Invalid input.
|
|
>0: Number of evaluation coefficients in the L*ev[] arrays.
|
|
This number is always ON_SubDSectorType.PointRingCount().
|
|
*/
|
|
unsigned int GetLimitSurfaceEvaluationCoefficients(
|
|
size_t LPev_capacity,
|
|
double* LPev,
|
|
size_t LT0ev_capacity,
|
|
double* LT0ev,
|
|
size_t LT1ev_capacity,
|
|
double* LT1ev
|
|
) const;
|
|
|
|
// LimitSurfaceNormalSign() is a debugging tool - slow and not useful in general
|
|
double LimitSurfaceNormalSign() const;
|
|
|
|
bool LimitEvaluationCoefficientsAvailable() const;
|
|
|
|
/*
|
|
Parameters:
|
|
eigenvalues_capacity - [in]
|
|
Capacity of the eigenvalues[] array.
|
|
Must be 0 or >= PointRingCount()
|
|
eigenvalues - [out]
|
|
If 0 = eigenvalues_capacity, eigenvalues must be nullptr.
|
|
If eigenvalues_capacity > 0, is specifies the capactiy
|
|
of the eigenvalues[] array.
|
|
Returns:
|
|
R > 0:
|
|
A complete set of eigenvalues is available for this sector type.
|
|
The eigenvalues are (1, lambda, lambda, e3, ..., eR), where
|
|
1 > lambda > e3 >= ... >= eR > 0.
|
|
0:
|
|
Invalid input or the eigenvalues for this sector typoe are not available.
|
|
*/
|
|
unsigned int GetAllEigenvalues(
|
|
size_t eigenvalues_capacity,
|
|
double* eigenvalues
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
The subdivision matrix for all cases is known.
|
|
A complete set of eigenvalues are available for some cases.
|
|
Parameters:
|
|
facet_type - [in]
|
|
vertex_tag - [in]
|
|
sector_edge_count - [in]
|
|
The input parameters identify the subdivision case.
|
|
Returns:
|
|
R > 0: Eigenvalues are known. There subdivison matrix is R x R.
|
|
0: Eigenvalues for this case are not known.
|
|
*/
|
|
static unsigned int AllEigenvaluesAvailableKnown(
|
|
ON_SubD::SubDType subd_type,
|
|
ON_SubD::VertexTag vertex_tag,
|
|
unsigned int sector_edge_count
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Create a partial subdivison sector around vertex.
|
|
The resulting ON_SubD has an outer ring with smooth edges and vertices,
|
|
which is not valid as a stand-alone subd. This is typically used for
|
|
testing.
|
|
Parameters:
|
|
radius - [in]
|
|
The center vertex is located at (0,0,0),
|
|
If radius > 0.0, then the end of the first edge is at (radius,0,0),
|
|
subsequent edges are in a radial array and quad face points, if any,
|
|
are 2*radius from the origin.
|
|
sector_angle_radians - [in]
|
|
If radius > 0,
|
|
this->VertexTag() is ON_SubD::VertexTag::Crease,
|
|
crease_sector_angle_radians > 0.0 and
|
|
crease_sector_angle_radians < 2.0*ON_PI,
|
|
then this will be the angle between the crease boundary edges.
|
|
In all other cases, crease_sector_angle_radians is ignored.
|
|
subd - [in]
|
|
If subd is not null, the vertex ring is put in this
|
|
subd.
|
|
Returns:
|
|
a pointer to the vertex ring
|
|
nullptr is returned if the input is not valid.
|
|
*/
|
|
ON_SubD* SectorRingSubD(
|
|
double radius,
|
|
double sector_angle_radians,
|
|
ON_SubD* subd
|
|
) const;
|
|
|
|
private:
|
|
ON_SubD::SubDType m_subd_type = ON_SubD::SubDType::Unset;
|
|
ON_SubD::FacetType m_facet_type = ON_SubD::FacetType::Unset;
|
|
ON_SubD::VertexTag m_vertex_tag = ON_SubD::VertexTag::Unset;
|
|
unsigned char m_reserved1 = 0;
|
|
unsigned int m_hash = 0; // SetHash() sets this field, SectorTypeHash() returns its value.
|
|
unsigned int m_corner_sector_angle_index = 0; // >= 0 and <= ON_SubDSectorType::MaximumAngleIndex
|
|
unsigned int m_sector_face_count = 0;
|
|
double m_sector_weight = 0.0;
|
|
double m_sector_theta = 0.0;
|
|
double m_corner_sector_angle_radians = 0.0;
|
|
|
|
private:
|
|
void SetHash();
|
|
|
|
/*
|
|
Description:
|
|
Calculates sector theta value for the sector type
|
|
identified by this ON_SubDSectorType.
|
|
Returns:
|
|
theta: 0.0 <= theta <= ON_PI
|
|
The sector theta value.
|
|
ON_SubDSectorType::ErrorSectorTheta
|
|
This ON_SubDSectorType is not valid and the calculation failed.
|
|
*/
|
|
double SectorTheta() const;
|
|
|
|
/*
|
|
Parameters:
|
|
sector_face_count - [in] >= 1
|
|
Number of faces in the crease sector.
|
|
Returns:
|
|
theta: 0.0 < theta <= ON_PI
|
|
sector theta value for a crease sector with sector_face_count faces.
|
|
ON_SubDSectorType::ErrorSectorTheta
|
|
sector_face_count is not valid and the calculation failed.
|
|
*/
|
|
static double CreaseSectorTheta(
|
|
unsigned int sector_face_count
|
|
);
|
|
|
|
/*
|
|
Parameters:
|
|
sector_face_count - [in] >= 2
|
|
Number of faces in the dart sector.
|
|
Returns:
|
|
theta: 0.0 < theta <= ON_PI
|
|
sector theta value for a dart sector with sector_face_count faces.
|
|
ON_SubDSectorType::ErrorSectorTheta
|
|
sector_face_count is not valid and the calculation failed.
|
|
*/
|
|
static double DartSectorTheta(
|
|
unsigned int sector_face_count
|
|
);
|
|
|
|
/*
|
|
Parameters:
|
|
sector_face_count - [in] >= 2
|
|
Number of faces in the dart sector.
|
|
corner_sector_angle_radians - [in] (0.0 <= corner_sector_angle_radians <= 2*ON_PI
|
|
The angle between the bounding crease edges
|
|
Returns:
|
|
theta: 0.0 < theta <= ON_PI/2
|
|
sector theta value for the corner sector.
|
|
ON_SubDSectorType::ErrorSectorTheta
|
|
sector_face_count or corner_sector_angle_radians were not valid
|
|
and the calculation failed.
|
|
*/
|
|
static double CornerSectorThetaFromCornerAngle(
|
|
unsigned int sector_face_count,
|
|
double corner_sector_angle_radians
|
|
);
|
|
|
|
/*
|
|
Parameters:
|
|
subd_type - [in]
|
|
sector_theta - [in]
|
|
value from one of the sector theta functions.
|
|
ON_SubDEdge::SectorTheta()
|
|
ON_SubDEdge::SmoothSectorTheta()
|
|
ON_SubDEdge::CreaseSectorTheta()
|
|
ON_SubDEdge::CornerSectorTheta()
|
|
ON_SubDEdge::DartSectorTheta()
|
|
Returns:
|
|
0:
|
|
failed to caclulate weight
|
|
ON_UNSET_VALUE:
|
|
subd_type was set ON_SubD::SubDType::Unset
|
|
and the tagged end weight cannot be calculated until
|
|
the facet type is known. This typically happens
|
|
when a SubD control net is being created and
|
|
a facet type is not specified. The weights will
|
|
be calculated at the first subdivision.
|
|
0 < w < 1:
|
|
If ON_SubD::SubDType::QuadCatmullClark == subd_type,
|
|
then the returned value is
|
|
1/2 + 1/3*cos(sector_angle_radians). (1/6 <= w <= 5/6)
|
|
If ON_SubD::SubDType::TriLoopWarren == subd_type,
|
|
then the returned value is
|
|
1/3 + 1/3*cos(sector_angle_radians). (0 < w <= 2/3)
|
|
Remarks:
|
|
This is a useful tool when calling AddEdge while a subdivision
|
|
level is being constructed.
|
|
*/
|
|
static double SectorWeightFromTheta(
|
|
ON_SubD::SubDType subd_type,
|
|
double sector_theta
|
|
);
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDLimitMeshFragment
|
|
//
|
|
// Meshes of ON_SubD limit surface are calculated in fragments.
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDLimitMeshFragmentGrid
|
|
{
|
|
public:
|
|
// No construction for performance reasons.
|
|
// If you require initialization, use = ON_SubDLimitMeshFragmentGrid::Empty
|
|
//
|
|
//ON_SubDLimitMeshFragmentGrid() = default;
|
|
//~ON_SubDLimitMeshFragmentGrid() = default;
|
|
//ON_SubDLimitMeshFragmentGrid(const ON_SubDLimitMeshFragmentGrid&) = default;
|
|
//ON_SubDLimitMeshFragmentGrid& operator=(const ON_SubDLimitMeshFragmentGrid&) = default;
|
|
static const ON_SubDLimitMeshFragmentGrid Empty;
|
|
|
|
/*
|
|
Description:
|
|
Get mesh facet quads that index into a grid of points.
|
|
Parameters:
|
|
side_segment_count - [in]
|
|
number quads in each row and column of the quad grid.
|
|
side_segment_count >= 1
|
|
side_segment_count <= ON_SubDLimitMesh::MaximumSideSegmentCount
|
|
side_segment_count must be a power of 2
|
|
|
|
level_of_detail - [in]
|
|
0: quad count = maximum quad count = (side_count x side_count)
|
|
1: quad count = 1/4 maximum quad count
|
|
1: quad count = 1/16 maximum quad count
|
|
...
|
|
If 4^level_of_detail > maximum quad count, then a single quad is returned.
|
|
*/
|
|
static ON_SubDLimitMeshFragmentGrid Quads(
|
|
unsigned int side_segment_count,
|
|
unsigned int level_of_detail
|
|
);
|
|
|
|
static ON_SubDLimitMeshFragmentGrid Tris(
|
|
unsigned int side_segment_count,
|
|
unsigned int level_of_detail
|
|
);
|
|
|
|
static ON_SubDLimitMeshFragmentGrid Facets(
|
|
ON_SubD::FacetType facet_type,
|
|
unsigned int side_segment_count,
|
|
unsigned int level_of_detail
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Get mesh facet quads that index into a grid of points.
|
|
Parameters:
|
|
side_segment_count - [in]
|
|
number quads in each row and column of the quad grid
|
|
with the highest level of detail (level_of_detail = 0)
|
|
side_count must be a power of 2
|
|
level_of_detail - [in]
|
|
Desired level of detail of the returned grid
|
|
0: highest (side_count x side_count) quads
|
|
1: 1/4 density (side_count x side_count)/4 quads
|
|
2: 1/16 density (side_count x side_count)/16 quads
|
|
...
|
|
side_count-2: 4 quads
|
|
side_count-1: 1 quad
|
|
>= side_count: 1 quad
|
|
Returns:
|
|
Number of quads in the grid.
|
|
*/
|
|
static unsigned int SetQuads(
|
|
unsigned int side_segment_count,
|
|
unsigned int level_of_detail,
|
|
size_t quad_capacity,
|
|
size_t quad_stride,
|
|
unsigned int* quads,
|
|
size_t side_capacity,
|
|
size_t side_stride,
|
|
unsigned int* sides
|
|
);
|
|
|
|
|
|
unsigned int SideSegmentCount() const;
|
|
|
|
/*
|
|
Description:
|
|
The GridId() is persistent and unique based on the contents of the
|
|
grid. It is intended to be used in render applications that store
|
|
copies of ON_SubDLimitMeshFragmentGrid settings in things like
|
|
vertex object buffers and want a reliable way to index into them.
|
|
The Empty grid has id = 0;
|
|
Returns:
|
|
0:
|
|
when the grid is empty
|
|
32*n + 2*lod + t:
|
|
t = 0 for quads and 1 for tris,
|
|
(0 <= n <= 8) m_F_count = 2^(2n),
|
|
(0 <= lod <= 8) m_F_level_of_detail = lod
|
|
Remarks:
|
|
m_F_count is always
|
|
*/
|
|
unsigned int GridId() const;
|
|
|
|
/*
|
|
Returns:
|
|
3 for tris, 4 for quads, 0 for unset.
|
|
*/
|
|
unsigned int GridFacetSideCount() const;
|
|
|
|
bool GetGridParameters(
|
|
unsigned int grid_point_index,
|
|
double grid_parameters[2]
|
|
) const;
|
|
|
|
|
|
private:
|
|
unsigned char m_reserved;
|
|
|
|
public:
|
|
|
|
ON_SubD::FacetType m_F_type;
|
|
unsigned char m_side_segment_count; // = 2^n for non-empty grids (0 <= n <= 8)
|
|
unsigned short m_F_count; // = m_side_count*m_side_count
|
|
unsigned short m_F_level_of_detail; // 0 = highest, > 0 = reduced
|
|
unsigned short m_F_stride;
|
|
const unsigned int* m_F;
|
|
const unsigned int* m_S; // [4*m_side_segment_count + 1] indices that form the polyline boundary.
|
|
const ON_SubDLimitMeshFragmentGrid* m_prev_level_of_detail; // nullptr or the previous level with more facets.
|
|
const ON_SubDLimitMeshFragmentGrid* m_next_level_of_detail; // nullptr or the next level with fewer facets.
|
|
};
|
|
|
|
class ON_SUBD_CLASS ON_SubDLimitMeshFragment
|
|
{
|
|
public:
|
|
// No construction for performance reasons.
|
|
// If you require initialization, use = ON_SubDLimitMeshFragment::Empty
|
|
//
|
|
//ON_SubDLimitMeshFragment() = default;
|
|
//~ON_SubDLimitMeshFragment() = default;
|
|
//ON_SubDLimitMeshFragment(const ON_SubDLimitMeshFragment&) = default;
|
|
//ON_SubDLimitMeshFragment& operator=(const ON_SubDLimitMeshFragment&) = default;
|
|
|
|
// Every field of ON_SubDLimitMeshFragment::Empty is zero.
|
|
static const ON_SubDLimitMeshFragment Empty;
|
|
|
|
static const unsigned int MaximumSideSegmentCount;
|
|
|
|
/*
|
|
Returns:
|
|
side_segment_count = 2^display_density
|
|
*/
|
|
static unsigned int SideSegmentCountFromDisplayDensity(
|
|
unsigned int display_density
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
base 2 log of side_segment_count.
|
|
Remarks:
|
|
side_segment_count = 2^display_density
|
|
*/
|
|
static unsigned int DisplayDensityFromSideSegmentCount(
|
|
unsigned int side_segment_count
|
|
);
|
|
|
|
/*
|
|
Parameters:
|
|
facet_type - [in]
|
|
ON_SubD::FacetType::Quad or ON_SubD::FacetType::Tri
|
|
|
|
display_density - [in]
|
|
>= 0
|
|
Returns:
|
|
total number of points in the limit mesh fragment.
|
|
Remarks:
|
|
The number of points is the same for quad or tri subdivision limit
|
|
mesh fragments, even though one is a rectanglular collection of
|
|
quads and the other is a trianglular collection of triangles.
|
|
*/
|
|
static unsigned int PointCountFromDisplayDensity(
|
|
ON_SubD::FacetType facet_type,
|
|
unsigned int display_density
|
|
);
|
|
|
|
/*
|
|
Parameters:
|
|
display_density - [in]
|
|
>= 0
|
|
Returns:
|
|
total number of faces in the limit mesh fragment.
|
|
*/
|
|
static unsigned int FaceCountFromDisplayDensity(
|
|
unsigned int display_density
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
true if side_segment_count is valid.
|
|
Otherwise 0 is returned.
|
|
*/
|
|
static bool SideSegmentCountIsValid(
|
|
unsigned int side_segment_count
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
If side_segment_count is valide, then (side_segment_count+1) is returned.
|
|
Otherwise 0 is returned.
|
|
*/
|
|
static unsigned int SidePointCountFromSideCount(
|
|
unsigned int side_segment_count
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
If side_segment_count is valide, then (side_segment_count+1)^2 is returned.
|
|
Otherwise 0 is returned.
|
|
*/
|
|
static unsigned int QuadGridPointCountFromSideCount(
|
|
unsigned int side_segment_count
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
If side_segment_count is valide, then side_segment_count^2 is returned.
|
|
Otherwise 0 is returned.
|
|
*/
|
|
static unsigned int QuadGridQuadCountFromSideCount(
|
|
unsigned int side_segment_count
|
|
);
|
|
|
|
public:
|
|
const class ON_SubDFace* m_face;
|
|
|
|
// m_face_vertex_index[] stores the information needed for the Vertex()
|
|
// and Edge() functions to work.
|
|
//
|
|
// If m_face is nullptr, then m_face_vertex_index[] has no meaning.
|
|
// If m_face is not nullptr and a corner of the grid is on a face
|
|
// vertex, then the corresponding m_face_vertex_index[] value
|
|
// will be <= ON_SubDFace::MaximumEdgeCount and m_face->Vertex(m_face_vertex_index[])
|
|
// is the vertex. Otherwise, the corresponding m_face_vertex_index[] value
|
|
// will be > ON_SubDFace::MaximumEdgeCount. For partial fragments,
|
|
// only some m_face_vertex_index[] identify vertices and the grid extends
|
|
// halfway along the neighboring face edges are
|
|
unsigned short m_face_vertex_index[4];
|
|
|
|
/*
|
|
Parameters:
|
|
grid_side_index - [in]
|
|
0 to 3 for quad grids.
|
|
0 to 2 for tri grids
|
|
Returns:
|
|
The subd edge that is on the identified side of the grid.
|
|
*/
|
|
const class ON_SubDEdgePtr EdgePtr(
|
|
unsigned int grid_side_index
|
|
) const;
|
|
const class ON_SubDEdge* Edge(
|
|
unsigned int grid_side_index
|
|
) const;
|
|
|
|
const class ON_SubDVertexPtr VertexPtr(
|
|
unsigned int grid_corner_index
|
|
) const;
|
|
const class ON_SubDVertex* Vertex(
|
|
unsigned int grid_corner_index
|
|
) const;
|
|
|
|
ON_3dPoint CornerPoint(
|
|
unsigned int grid_corner_index
|
|
) const;
|
|
|
|
/*
|
|
Returns:
|
|
Status of the face.
|
|
*/
|
|
ON_ComponentStatus Status() const;
|
|
|
|
/*
|
|
Returns:
|
|
True if this fragment covers a subset of a face.
|
|
*/
|
|
bool IsSubFragment() const;
|
|
|
|
/*
|
|
Returns:
|
|
True if this fragment covers an entier subd face.
|
|
*/
|
|
bool IsCompleteFragment() const;
|
|
|
|
bool Transform(
|
|
const ON_Xform& xform
|
|
);
|
|
|
|
unsigned short m_face_fragment_count; // Number of fragments that will be delivered for this face.
|
|
unsigned short m_face_fragment_index; // First fragment has index = 0. Last fragment has index = m_face_fragment_count-1.
|
|
|
|
// For quad based subdivision algorithms, the mesh fragment
|
|
// is a tesselation of a rectangular shaped surface,
|
|
// there are m_side_count quad edges along each side of the tesselation,
|
|
// there are a total of m_side_count X m_side_count quads, and
|
|
// m_P_count = (m_side_count+1)*(m_side_count+1).
|
|
//
|
|
// For trangle based subdivision algorithms, the mesh fragment
|
|
// is a tesselation of a triangular shaped surface,
|
|
// there are m_side_count triangle edges along each side of the tesselation,
|
|
// there are a total of m_side_count X m_side_count triangles, and
|
|
// m_P_count = (m_side_count+1)*(m_side_count+2)/2.
|
|
//
|
|
|
|
// Number of points
|
|
unsigned short m_P_count;
|
|
unsigned short m_P_capacity;
|
|
|
|
// points
|
|
size_t m_P_stride;
|
|
// The memory m_P references is managed by some other class or function.
|
|
// Never modify the values in m_P.
|
|
double* m_P;
|
|
|
|
// surface normals parallel to m_P[] array
|
|
size_t m_N_stride;
|
|
// If m_N is not nullptr, then it can accomodate up to m_P_capacity normals.
|
|
// The memory m_N references is managed by some other class or function.
|
|
// Never modify the values in m_N.
|
|
double* m_N;
|
|
|
|
// quads or tris
|
|
ON_SubDLimitMeshFragmentGrid m_grid; //
|
|
|
|
ON_BoundingBox m_bbox;
|
|
|
|
ON_SubDLimitMeshFragment* m_next_fragment;
|
|
ON_SubDLimitMeshFragment* m_prev_fragment;
|
|
};
|
|
|
|
class ON_SUBD_CLASS ON_SubDManagedLimitMeshFragment : public ON_SubDLimitMeshFragment
|
|
{
|
|
public:
|
|
ON_SubDManagedLimitMeshFragment() ON_NOEXCEPT;
|
|
~ON_SubDManagedLimitMeshFragment() ON_NOEXCEPT;
|
|
ON_SubDManagedLimitMeshFragment(const ON_SubDManagedLimitMeshFragment&) ON_NOEXCEPT;
|
|
ON_SubDManagedLimitMeshFragment& operator=(const ON_SubDManagedLimitMeshFragment&) ON_NOEXCEPT;
|
|
|
|
static ON_SubDManagedLimitMeshFragment Create(const ON_SubDLimitMeshFragment& src) ON_NOEXCEPT;
|
|
|
|
#if defined(ON_HAS_RVALUEREF)
|
|
// rvalue copy constructor
|
|
ON_SubDManagedLimitMeshFragment( ON_SubDManagedLimitMeshFragment&& ) ON_NOEXCEPT;
|
|
|
|
// rvalue assignment operator
|
|
ON_SubDManagedLimitMeshFragment& operator=( ON_SubDManagedLimitMeshFragment&& ) ON_NOEXCEPT;
|
|
#endif
|
|
|
|
void Clear() ON_NOEXCEPT;
|
|
|
|
void Destroy() ON_NOEXCEPT;
|
|
|
|
bool ReserveCapacity(
|
|
ON_SubD::FacetType facet_type,
|
|
unsigned int mesh_density
|
|
) ON_NOEXCEPT;
|
|
|
|
private:
|
|
void CopyHelper(const ON_SubDLimitMeshFragment& src);
|
|
size_t m_storage_capacity = 0;
|
|
double* m_storage = nullptr;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDDisplayParameters
|
|
//
|
|
// A collection of parameters that are passed to functions that
|
|
// calculate a various representations of the limit surface.
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDDisplayParameters
|
|
{
|
|
public:
|
|
static const ON_SubDDisplayParameters Empty;
|
|
|
|
// Parameters for the default limit surface display mesh.
|
|
static const ON_SubDDisplayParameters DefaultDisplayMeshParameters;
|
|
|
|
/*
|
|
Description:
|
|
In most applications, the caller sets the mesh_density
|
|
and leaves the other parameters set to the default
|
|
values.
|
|
*/
|
|
static ON_SubDDisplayParameters CreateFromDisplayDensity(
|
|
unsigned int display_density
|
|
);
|
|
|
|
ON_SubDDisplayParameters() = default;
|
|
~ON_SubDDisplayParameters() = default;
|
|
ON_SubDDisplayParameters(const ON_SubDDisplayParameters&) = default;
|
|
ON_SubDDisplayParameters& operator=(const ON_SubDDisplayParameters&) = default;
|
|
|
|
unsigned int m_display_density = 0;
|
|
|
|
bool m_bUseMultipleThreads = false;
|
|
ON_Terminator* m_terminator = nullptr;
|
|
// optional progress reporting
|
|
ON_ProgressReporter* m_progress_reporter = nullptr;
|
|
ON_Interval m_progress_reporter_interval = ON_Interval::ZeroToOne;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDLimitMesh
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDLimitMesh
|
|
{
|
|
#if defined(ON_SUBD_CENSUS)
|
|
private:
|
|
ON_SubDLimitMeshCensusCounter m_census_counter;
|
|
#endif
|
|
|
|
public:
|
|
static const ON_SubDLimitMesh Empty;
|
|
|
|
/*
|
|
Returns:
|
|
A runtime number that changes if the limit mesh content changes.
|
|
0: Empty limit mesh
|
|
Remarks:
|
|
This is a runtime number. It is not saved in archives and begins
|
|
at 1 with each new runtime instance of the opennurbs library.
|
|
*/
|
|
unsigned int ContentSerialNumber() const;
|
|
|
|
enum : unsigned int
|
|
{
|
|
DefaultDisplayDensity = 4, // default limit mesh density 16x16 quads per SubD quad 16 = 2^4
|
|
MaximumDisplayDensity = 8 // 8 (256x256 quads per SubD quad 256 = 2^8)
|
|
//MaximumLevelOfDetail = 0, // 0 = most facets per fragment
|
|
//MinimumLevelOfDetail = 8 // 8 = fewest facets per fragment
|
|
};
|
|
|
|
static ON_SubDLimitMesh* Create(
|
|
const ON_SubD& subd,
|
|
const class ON_SubDDisplayParameters& limit_mesh_parameters,
|
|
ON_SubDLimitMesh* destination_mesh
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
This version is for expert users who want to take
|
|
responsibility for managing the subd and limit mesh
|
|
*/
|
|
static ON_SubDLimitMesh* Create(
|
|
ON_SubDFaceIterator fit,
|
|
const class ON_SubDDisplayParameters& limit_mesh_parameters,
|
|
ON_SubDLimitMesh* destination_mesh
|
|
);
|
|
|
|
ON_SubDLimitMesh() = default;
|
|
~ON_SubDLimitMesh() = default;
|
|
ON_SubDLimitMesh(const ON_SubDLimitMesh&) = default;
|
|
ON_SubDLimitMesh& operator=(const ON_SubDLimitMesh&) = default;
|
|
|
|
|
|
#if defined(ON_HAS_RVALUEREF)
|
|
// rvalue copy constructor
|
|
ON_SubDLimitMesh( ON_SubDLimitMesh&& ) ON_NOEXCEPT;
|
|
// rvalue assignment operator
|
|
ON_SubDLimitMesh& operator=( ON_SubDLimitMesh&& );
|
|
#endif
|
|
|
|
ON_SubDLimitMesh Copy() const;
|
|
|
|
ON_SubDLimitMesh& CopyFrom(
|
|
const ON_SubDLimitMesh& src
|
|
);
|
|
|
|
static void Swap(
|
|
ON_SubDLimitMesh& a,
|
|
ON_SubDLimitMesh& b
|
|
);
|
|
|
|
bool Transform(
|
|
const ON_Xform& xform
|
|
);
|
|
|
|
unsigned int DisplayDensity() const;
|
|
ON_SubDDisplayParameters DisplayParameters() const;
|
|
unsigned int FragmentCount() const;
|
|
const ON_SubDLimitMeshFragment* FirstFragment() const; // linked list of mesh fragments
|
|
|
|
/*
|
|
Description:
|
|
If the subd referenced by m_subd_ref changes, then call
|
|
Update to update the limit mesh.
|
|
*/
|
|
bool Update(
|
|
bool bShareUpdate
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
The ON__INT_PTRs in the tree are const ON_SubDLimitMeshFragment* pointers.
|
|
*/
|
|
const ON_RTree& FragmentTree() const;
|
|
|
|
/*
|
|
Description:
|
|
Clears everything.
|
|
*/
|
|
void Clear();
|
|
|
|
/*
|
|
Description:
|
|
If the tree is not needed and memory resources are tight, then call ClearTree()
|
|
to remove the RTree.
|
|
*/
|
|
void ClearTree();
|
|
|
|
bool IsEmpty() const;
|
|
|
|
ON_SubD::FacetType GridType() const;
|
|
|
|
ON_BoundingBox BoundingBox() const;
|
|
|
|
bool GetTightBoundingBox(
|
|
ON_BoundingBox& bbox,
|
|
bool bGrowBox,
|
|
const ON_Xform* xform
|
|
) const;
|
|
|
|
ON_SubDRef SubDRef() const;
|
|
ON_SubD SubD() const;
|
|
|
|
public:
|
|
/*
|
|
Description:
|
|
Pretend this function and ON_SubDLimitMeshImpl do not exist.
|
|
Returns:
|
|
Something that you are pretending does not exist.
|
|
Remarks:
|
|
It is intentional that the definition of ON_SubDLimitMeshImpl class is not
|
|
available in the opennurbs library interface (not in a header file).
|
|
The size and design of ON_SubDLimitMeshImpl will change constantly.
|
|
If you choose to hack and whack so you can dereference an
|
|
ON_SubDLimitMeshImpl* pointer, then your code will crash unpredictably.
|
|
*/
|
|
class ON_SubDLimitMeshImpl* SubLimple() const;
|
|
unsigned int SubLimpleUseCount() const;
|
|
|
|
private:
|
|
#pragma ON_PRAGMA_WARNING_PUSH
|
|
#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 )
|
|
friend class ON_SubDLimitMeshImpl;
|
|
// C4251: ... needs to have dll-interface to be used by clients of class ...
|
|
// m_impl_sp is private and all code that manages m_impl_sp is explicitly implemented in the DLL.
|
|
private:
|
|
std::shared_ptr< class ON_SubDLimitMeshImpl > m_impl_sp;
|
|
#pragma ON_PRAGMA_WARNING_POP
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class ON_SUBD_CLASS ON_SubDLimitNurbsFragment
|
|
{
|
|
public:
|
|
ON_SubDLimitNurbsFragment() = default;
|
|
~ON_SubDLimitNurbsFragment() = default;
|
|
ON_SubDLimitNurbsFragment(const ON_SubDLimitNurbsFragment&) = default;
|
|
ON_SubDLimitNurbsFragment& operator=(const ON_SubDLimitNurbsFragment&) = default;
|
|
|
|
public:
|
|
// No construction for performance reasons.
|
|
// If you require initialization, use = ON_SubDLimitMeshFragment::Empty
|
|
//
|
|
//ON_SubDLimitMeshFragment() = default;
|
|
//~ON_SubDLimitMeshFragment() = default;
|
|
//ON_SubDLimitMeshFragment(const ON_SubDLimitMeshFragment&) = default;
|
|
//ON_SubDLimitMeshFragment& operator=(const ON_SubDLimitMeshFragment&) = default;
|
|
|
|
// Every field of ON_SubDLimitNurbsFragment::Empty is zero.
|
|
static const ON_SubDLimitNurbsFragment Empty;
|
|
|
|
// Every m_patch_cv[][][] value is ON_UNSET_VALUE.
|
|
// Every other field of ON_SubDLimitNurbsFragment::Unset is zero.
|
|
static const ON_SubDLimitNurbsFragment Unset;
|
|
|
|
// Every m_patch_cv[][][] value is ON_DBL_QNAN.
|
|
// Every other field of ON_SubDLimitNurbsFragment::Unset is zero.
|
|
static const ON_SubDLimitNurbsFragment Nan;
|
|
|
|
|
|
#pragma region RH_C_SHARED_ENUM [ON_SubDLimitNurbsFragment::PatchTypeXYX] [Rhino.Geometry.SubD.PatchFragmentTypeXYX] [internal:nested:byte]
|
|
enum class Type : unsigned char
|
|
{
|
|
///<summary>Not a valid patch type.</summary>
|
|
Unset = 0,
|
|
|
|
///<summary>This fragment is a single 4x4 bicubic span covering the entire SubD region identified by m_face_region.</summary>
|
|
BicubicSingle = 1,
|
|
|
|
///<summary>The bispans in this fragment are quadrants of the SubD region identified by m_face_region.</summary>
|
|
BicubicQuadrant = 4
|
|
};
|
|
#pragma endregion
|
|
|
|
#pragma region RH_C_SHARED_ENUM [ON_SubDLimitNurbsFragment::BispanType] [Rhino.Geometry.SubD.FragmentBispanType] [internal:nested:byte]
|
|
enum class BispanType : unsigned char
|
|
{
|
|
///<summary>No bispan.</summary>
|
|
None = 0,
|
|
|
|
///<summary>Entire region identified by m_face_region is an exact bicubic patch.</summary>
|
|
Exact = 1,
|
|
|
|
///<summary>Entire region identified by m_face_region is approximately a bicubic patch.</summary>
|
|
Approximate = 2
|
|
};
|
|
#pragma endregion
|
|
|
|
public:
|
|
// knot vector is uniform and not clamped. For example {-2,-1,0,1,2,3,4}.
|
|
double m_patch_cv[5][5][3];
|
|
|
|
// m_face_region identifies what part of the SubD is represented by this patch
|
|
ON_SubDFaceRegion m_face_region;
|
|
|
|
ON_SubDLimitNurbsFragment::Type m_type = ON_SubDLimitNurbsFragment::Type::Unset;
|
|
|
|
// m_patch_type[] reports what type of patch is returned in m_patch_cv[]
|
|
// m_patch_type[0] = the state for the uniform bi-cubic patch:
|
|
// CV[i][j] = m_patch_cv[i][j] (0 <= i <= 3, 0 <= j <= 3)
|
|
// knot[0] = {-2,-1,0,1,2,3}
|
|
// knot[1] = {-2,-1,0,1,2,3}
|
|
// m_patch_type[1] = the state for the uniform bi-cubic patch:
|
|
// CV[i][j] = m_patch_cv[i+1][j] (0 <= i <= 3, 0 <= j <= 3)
|
|
// knot[0] = {-1,0,1,2,3,4}
|
|
// knot[1] = {-2,-1,0,1,2,3}
|
|
// m_patch_type[2] = the state for the uniform bi-cubic patch:
|
|
// CV[i][j] = m_patch_cv[i+1][j+1] (0 <= i <= 3, 0 <= j <= 3)
|
|
// knot[0] = {-1,0,1,2,3,4}
|
|
// knot[1] = {-1,0,1,2,3,4}
|
|
// m_patch_type[3] = the state for the uniform bi-cubic patch:
|
|
// CV[i][j] = m_patch_cv[i][j+1] (0 <= i <= 3, 0 <= j <= 3)
|
|
// knot[0] = {-2,-1,0,1,2,3}
|
|
// knot[1] = {-1,0,1,2,3,4}
|
|
//
|
|
// Single bicubic bezier covers the region:
|
|
// m_patch_type[] = {Bicubic or ApproximateBicubic, Ignore, Ignore, Ignore}
|
|
// Bicubic quadrants
|
|
// Each m_patch_type[] element is one of BicubicQuadrant, ApproximateBicubicQuadrant, or None.
|
|
ON_SubDLimitNurbsFragment::BispanType m_bispan_type[4] = {};
|
|
|
|
|
|
private:
|
|
// number of edges in the level 0 SubD face.
|
|
unsigned char m_reserved1 = 0;
|
|
unsigned short m_reserved2 = 0;
|
|
|
|
public:
|
|
/*
|
|
True if there are no set bispans.
|
|
*/
|
|
bool IsEmpty() const;
|
|
|
|
/*
|
|
Returns:
|
|
0: unset NURBS fragment
|
|
1: This NURBS fragment uses a single bicubic span to model the region identified by m_face_region.
|
|
4: This NURBS fragment uses 4 bicubic spans to model the 4 quadrants of the region identified by m_face_region.
|
|
*/
|
|
unsigned int MaximumBispanCount() const;
|
|
|
|
/*
|
|
Returns:
|
|
0 to 4 indicating the number of bicubic spans that are set in
|
|
this NURBS fragment.
|
|
Remarks:
|
|
If 0 < SetBispanCount() == MaximumBispanCount(),
|
|
then the entire region identified by m_face_region is modeled by
|
|
this NURBS fragment.
|
|
|
|
If 0 < SetBispanCount() < MaximumBispanCount(),
|
|
then that many quadrants of the region identified by m_face_region
|
|
are modeled by this set bispans in this NURBS fragment.
|
|
*/
|
|
unsigned int SetBispanCount() const;
|
|
|
|
|
|
/*
|
|
Returns:
|
|
0 to 4 indicating the number of unset bicubic spans in "this".
|
|
*/
|
|
unsigned int UnsetBispanCount() const;
|
|
|
|
/*
|
|
Returns:
|
|
One or more of the bispans approximate the SubD surface.
|
|
*/
|
|
bool IsApproximate() const;
|
|
|
|
ON_NurbsSurface* GetSurface(
|
|
ON_NurbsSurface* destination_surface
|
|
) const;
|
|
|
|
ON_NurbsSurface* GetQuadrantSurface(
|
|
unsigned int quadrant_index,
|
|
ON_NurbsSurface* destination_surface
|
|
) const;
|
|
|
|
|
|
private:
|
|
unsigned short m_reserved = 0;
|
|
};
|
|
|
|
|
|
class ON_SUBD_CLASS ON_SubDSectorLimitPoint
|
|
{
|
|
public:
|
|
// For performance reasons, the default the data members are
|
|
// not initialized by the default constructor
|
|
// Use = ON_SubDSectorLimitPoint::Unset when initialization is required
|
|
static const ON_SubDSectorLimitPoint Unset; // all doubles are ON_UNSET_VALUE, all pointer are nullptr
|
|
static const ON_SubDSectorLimitPoint Nan; // all doubles are ON_DBL_QNAN, all pointer are nullptr
|
|
static const ON_SubDSectorLimitPoint Zero; // all doubles are 0.0, all pointer are nullptr
|
|
|
|
/*
|
|
Returns:
|
|
true if m_limitP[0] is a nan (like ON_DBL_QNAN).
|
|
false otherwise.
|
|
*/
|
|
bool IsUnset() const;
|
|
|
|
/*
|
|
Returns:
|
|
true if m_limitP[0] is ON_UNSET_VALUE.
|
|
false otherwise.
|
|
*/
|
|
bool IsNan() const;
|
|
|
|
/*
|
|
Returns:
|
|
true if all coordinates are zero.
|
|
false otherwise.
|
|
*/
|
|
bool IsZero() const;
|
|
|
|
/*
|
|
Returns:
|
|
true if all coordinates are valid doubles and the tangents and normal have at least
|
|
one nonzero coordinate.
|
|
false otherwise.
|
|
*/
|
|
bool IsSet() const;
|
|
|
|
bool Transform(
|
|
const ON_Xform& xform
|
|
);
|
|
|
|
// limit surface point, tangents and normal
|
|
double m_limitP[3]; // point
|
|
double m_limitT1[3]; // first unit tangent
|
|
double m_limitT2[3]; // second unit tangent
|
|
double m_limitN[3]; // unit normal (same direction as m_limitT1 x m_limitT2)
|
|
|
|
// When an ON_SubDVertex has a single sector, these pointers are both null.
|
|
// When an ON_SubDVertex has a multiple sectors,
|
|
// m_sector_face is the "first" face in the sector and
|
|
// m_next_sector_limit_point is used to create a linked list.
|
|
// (The "first" face in a sector is the one ON_SubDSectorIterator.IncrementToCrease(-1) returns.)
|
|
const class ON_SubDSectorLimitPoint* m_next_sector_limit_point; // nullptr for vertices with one sector
|
|
const class ON_SubDFace* m_sector_face; // nullptr for vertices with one sector
|
|
};
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDComponentBase
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDComponentBase
|
|
{
|
|
public:
|
|
static const ON_SubDComponentBase Unset;
|
|
|
|
///*
|
|
//Returns:
|
|
// True if component is not nullptr, component->m_id > 0 and component->m_archive_id != ON_UNSET_UINT_INDEX.
|
|
//*/
|
|
//static bool IsActive(
|
|
// const ON_SubDComponentBase* component
|
|
// );
|
|
|
|
public:
|
|
ON_SubDComponentBase() = default;
|
|
~ON_SubDComponentBase() = default;
|
|
ON_SubDComponentBase(const ON_SubDComponentBase&) = default;
|
|
ON_SubDComponentBase& operator=(const ON_SubDComponentBase&) = default;
|
|
|
|
public:
|
|
// The audience for this comment is anybody who wants to change the data
|
|
// fields in ON_SubDComponentBase. Everyone else should ignore this comment.
|
|
// ON_SubD components come from ON_FixedSizePool and ON_SubD code
|
|
// uses ON_FixedSizePool.ReturnElement. The first sizeof(void*) bytes
|
|
// must be a data field that is not referenced in returned elements.
|
|
// Since a returned element cannot have a "next level vertex",
|
|
// m_subd_point1 is a good data member to put first.
|
|
|
|
// m_subd_point1 points to the next level's vertex when this component
|
|
// has been subdivided using an algorithm like Catmull-Clark or Loop-Warren.
|
|
const class ON_SubDVertex* m_subd_point1 = nullptr;
|
|
|
|
public:
|
|
// The audience for this comment is anybody who wants to change the data
|
|
// fields in ON_SubDComponentBase. Everyone else should ignore this comment.
|
|
// It is critical that the offset of m_id in ON_SubDComponentBase be >= sizeof(void*).
|
|
// ON_SubD components come from ON_FixedSizePool and ON_SubD code
|
|
// use ON_FixedSizePool.ElementFromId and ON_FixedSizePool.ReturnElement.
|
|
// Once assigned, m_id is never changed and that allows ON_SubD component
|
|
// indices to work.
|
|
|
|
// Id assigned to this component. Never modify this value. It is assigned
|
|
// by allocators and used to find the component from an ON_COMPONENT_INDEX.
|
|
unsigned int m_id = 0;
|
|
|
|
private:
|
|
// The m_archive_id must be immediately after the m_id field.
|
|
mutable unsigned int m_archive_id = 0;
|
|
|
|
public:
|
|
unsigned short m_level = 0;
|
|
|
|
public:
|
|
mutable ON_ComponentStatus m_status = ON_ComponentStatus::NoneSet;
|
|
|
|
public:
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// Saved subdivision point
|
|
//
|
|
/*
|
|
Description:
|
|
Set the saved subdivision point.
|
|
Parameters:
|
|
subdivision_point_type - [in]
|
|
Specifies subdivision algorithm.
|
|
Use ON_SubD::SubDType::Unset to clear the cache.
|
|
subdivision_point - [in]
|
|
includes displacement if it exists
|
|
Returns:
|
|
true if successful
|
|
*/
|
|
bool SetSavedSubdivisionPoint(
|
|
ON_SubD::SubDType subd_type,
|
|
const double subdivision_point[3]
|
|
) const;
|
|
|
|
bool GetSavedSubdivisionPoint(
|
|
ON_SubD::SubDType subd_type,
|
|
double subdivision_point[3]
|
|
) const;
|
|
|
|
ON_SubD::SubDType SavedSubdivisionPointType() const;
|
|
|
|
/*
|
|
Description:
|
|
Clears saved subdivision information for this component.
|
|
*/
|
|
void ClearSavedSubdivisionPoint() const;
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// displacement applied to subdivision point
|
|
//
|
|
bool SetDisplacement(
|
|
ON_SubD::SubDType subd_type,
|
|
const double displacement[3]
|
|
);
|
|
|
|
bool GetDisplacement(
|
|
ON_SubD::SubDType subd_type,
|
|
double displacement[3]
|
|
) const;
|
|
|
|
ON_SubD::SubDType DisplacementType() const;
|
|
|
|
void ClearDisplacement() const;
|
|
|
|
protected:
|
|
// GetSubdivisionPoint( bUseSavedSubdivisionPoint=true ) can change the value of m_saved_points_flags
|
|
// m_saved_points_flags & 0x1F = ON_SubD::SubDType value
|
|
// m_saved_points_flags & 0x40 != 0 if m_cache_subd_P is set.
|
|
// m_saved_points_flags & 0x80 != 0 if m_displacementV is set.
|
|
// GetLimitPoint( bUseSavedLimitPoint=true ) can change the value of m_saved_points_flags
|
|
// m_saved_points_flags & 0x20 != 0 if ON_subDVertex.m_limit* values are set.
|
|
mutable unsigned char m_saved_points_flags = 0U;
|
|
|
|
public:
|
|
|
|
// All the faces with the same nonzero value of m_group_id are in the same "group".
|
|
// SDK interface on ON_SubD will be added after we get a better idea of how this
|
|
// feature will be used.
|
|
unsigned int m_group_id = 0U;
|
|
|
|
protected:
|
|
// GetSubdivisionPoint( bUseSavedSubdivisionPoint=true ) can change the value of m_cache_subd_P
|
|
mutable double m_saved_subd_point1[3]; // saved subdivision point
|
|
|
|
protected:
|
|
// optional displacement applied to standard subdivision point.
|
|
double m_displacement_V[3];
|
|
|
|
public:
|
|
/*
|
|
Description:
|
|
Pretend ArchiveId() and SetArchiveId() do not exist.
|
|
Returns:
|
|
The ArchiveId is a value set and used by ON_BinaryArchive Read() and Write()
|
|
functions and copy constructors and operator=().
|
|
A public interface is supplied because it is not practical to use friends.
|
|
Remarks:
|
|
A value of ON_UNSET_UINT_INDEX indicates the component is not in use.
|
|
*/
|
|
unsigned int ArchiveId() const
|
|
{
|
|
return m_archive_id;
|
|
}
|
|
|
|
void SetArchiveId(
|
|
unsigned int archive_id
|
|
) const
|
|
{
|
|
// m_archive_id is mutable
|
|
if ( ON_UNSET_UINT_INDEX != archive_id )
|
|
m_archive_id = archive_id;
|
|
}
|
|
|
|
protected:
|
|
void CopyBaseFrom(
|
|
const ON_SubDComponentBase* src
|
|
);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDVertex
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDVertex : public ON_SubDComponentBase
|
|
{
|
|
public:
|
|
static const ON_SubDVertex Empty;
|
|
|
|
bool Write (
|
|
class ON_BinaryArchive& archive
|
|
) const;
|
|
|
|
static bool Read (
|
|
class ON_BinaryArchive& archive,
|
|
class ON_SubD& subd,
|
|
class ON_SubDVertex*& vertex
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Apply a tranxfomration matrix to vertex geometry information.
|
|
Parameters:
|
|
bTransformationSavedSubdivisionPoint - [in]
|
|
If the transformation is being applied to every vertex, edge and
|
|
face in every level of a subdivision object, and the transformation
|
|
is an isometry (rotation, translation, ...), a uniform scale, or a
|
|
composition of these types, then set
|
|
bTransformationSavedSubdivisionPoint = true to apply the
|
|
transformation to saved subdivision and saved limit point information.
|
|
In all other cases, set bTransformationSavedSubdivisionPoint = false
|
|
and any saved subdivision points or saved limit points will be
|
|
deleted. When in doubt, pass false.
|
|
|
|
xform - [in]
|
|
*/
|
|
bool Transform(
|
|
bool bTransformationSavedSubdivisionPoint,
|
|
const class ON_Xform& xform
|
|
);
|
|
|
|
bool SetLocation(
|
|
ON_3dPoint location,
|
|
bool bClearNeighborhoodCache
|
|
);
|
|
|
|
|
|
ON_BoundingBox ControlNetBoundingBox() const;
|
|
ON_BoundingBox LimitSurfaceBoundingBox(
|
|
const ON_SubD& subd
|
|
) const;
|
|
|
|
public:
|
|
ON_COMPONENT_INDEX ComponentIndex() const;
|
|
ON_SubDComponentPtr ComponentPtr() const;
|
|
|
|
public:
|
|
const class ON_SubDVertex* m_prev_vertex = nullptr; // linked list of vertices on this level
|
|
const class ON_SubDVertex* m_next_vertex = nullptr; // linked list of vertices on this level
|
|
|
|
public:
|
|
ON_SubD::VertexTag m_vertex_tag = ON_SubD::VertexTag::Unset;
|
|
|
|
|
|
private:
|
|
//ON_SubD::VertexEdgeOrder m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset;
|
|
unsigned char m_reserved1 = 0;
|
|
unsigned short m_reserved2 = 0;
|
|
unsigned int m_reserved3 = 0;
|
|
|
|
public:
|
|
unsigned short m_edge_count = 0;
|
|
unsigned short m_face_count = 0;
|
|
|
|
unsigned short m_edge_capacity = 0;
|
|
unsigned short m_face_capacity = 0;
|
|
|
|
public:
|
|
// Array of m_edge_count edges.
|
|
// m_edge[i].EdgeDirection() indicates which edge end is located at this vertex
|
|
// If m_edge_capacity > 0, m_edge_capacity is the number of elements that
|
|
// may be used in m_edges[].
|
|
class ON_SubDEdgePtr* m_edges = nullptr;
|
|
|
|
// Array of m_face_count faces.
|
|
// If m_face_capacity > 0, m_face_capacity is the number of elements that
|
|
// may be used in m_faces[].
|
|
const class ON_SubDFace** m_faces = nullptr;
|
|
|
|
public:
|
|
double m_P[3]; // vertex location
|
|
|
|
private:
|
|
// Cached limit point and limit normal
|
|
// GetLimitPoint( bUseSavedLimitPoint=true ) can change the value of m_limitP_type
|
|
// If the limit point is set and vertex has a single sector, then
|
|
// m_limit_point.m_sector_face = nullptr and m_limit_point.m_next_sector_limit_point = nullptr.
|
|
// If the limit point is set and vertex has a multiple sectors, then
|
|
// m_limit_point.m_sector_face = first face in the sector.
|
|
// If multiple limit points are set, then are in a linked list
|
|
// traversed using the ON_SubDSectorLimitPointm_next_sector_limit_point.
|
|
// The second and any additional limit points are managed by a fixed size pool.
|
|
// Calling ClearLimitPoint() will return these to the pool.
|
|
mutable ON_SubDSectorLimitPoint m_limit_point = ON_SubDSectorLimitPoint::Unset;
|
|
|
|
public:
|
|
static const unsigned int MaximumEdgeCount;
|
|
static const unsigned int MaximumFaceCount;
|
|
|
|
static int CompareUnorderedEdges(
|
|
const ON_SubDVertex* a,
|
|
const ON_SubDVertex* b
|
|
);
|
|
|
|
static int CompareUnorderedFaces(
|
|
const ON_SubDVertex* a,
|
|
const ON_SubDVertex* b
|
|
);
|
|
|
|
static int CompareUnorderedEdgesAndFaces(
|
|
const ON_SubDVertex* a,
|
|
const ON_SubDVertex* b
|
|
);
|
|
|
|
///*
|
|
//Description:
|
|
// Sort the m_edges[] and m_faces[] arrays so radial groups are together.
|
|
// After the sorting is completed, m_vertex_edge_order is set to recored
|
|
// the current sorting state and its value is returned.
|
|
// The sorting is done unconditionally.
|
|
//*/
|
|
//ON_SubD::VertexEdgeOrder SortEdges();
|
|
|
|
unsigned int EdgeCount(
|
|
ON_SubD::EdgeTag edge_tag
|
|
) const;
|
|
|
|
unsigned int EdgeCount() const;
|
|
|
|
const class ON_SubDEdge* Edge(
|
|
unsigned int i
|
|
) const;
|
|
|
|
const ON_SubDEdgePtr EdgePtr(
|
|
unsigned int i
|
|
) const;
|
|
|
|
ON__UINT_PTR EdgeDirection(
|
|
unsigned int i
|
|
) const;
|
|
|
|
unsigned int EdgeArrayIndex(
|
|
const ON_SubDEdge* edge
|
|
) const;
|
|
|
|
unsigned int FaceCount() const;
|
|
|
|
const class ON_SubDFace* Face(
|
|
unsigned int i
|
|
) const;
|
|
|
|
unsigned int FaceArrayIndex(
|
|
const ON_SubDFace* face
|
|
) const;
|
|
|
|
ON_SubD::FacetType FirstFaceFacetType() const;
|
|
|
|
/*
|
|
Returns
|
|
true if m_vertex_tag is ON_SubD::VertexTag::Crease, ON_SubD::VertexTag::Corner or ON_SubD::VertexTag::Dart.
|
|
*/
|
|
bool IsTagged() const;
|
|
|
|
///*
|
|
//Parameters:
|
|
// subd_type - [in]
|
|
// Specifies subdivision algorithm
|
|
// vertex_tag_filter - [in]
|
|
// If vertex_tag is not ON_SubD::VertexTag::Unset and vertex_tag != m_vertex_tag,
|
|
// then false is returned. This parameter can be used when a smooth or crease
|
|
// vertex is explicity required.
|
|
// bTestFaces - [in]
|
|
// If true, and the edge and face count tests succeed, then the faces in the
|
|
// vertex m_faces[] array are tested to insure they are
|
|
// quads (ccquad subdivisiontype) or tris (lwtri subdivisiontype).
|
|
//Returns:
|
|
// If m_vertex_tag is ON_SubD::Vertex::Tag::smooth,
|
|
// and the number of edges = number of faces,
|
|
// and there are 4 (ccquad subdivisiontype) or 6 (lwtri subdivisiontype) edges,
|
|
// and bTestFaces is false or the faces pass the face test,
|
|
// then true is returned.
|
|
//
|
|
// If m_vertex_tag is ON_SubD::Vertex::Tag::crease,
|
|
// and the number of edges = 1 + number of faces,
|
|
// and there are 3 (ccquad subdivisiontype) or 4 (lwtri subdivisiontype) edges,
|
|
// and bTestFaces is false or the faces pass the face test,
|
|
// then true is returned.
|
|
|
|
// In all other cases, false is returned.
|
|
//*/
|
|
//bool IsOrdinary(
|
|
// ON_SubD::SubDType subd_type,
|
|
// ON_SubD::VertexTag vertex_tag_filter,
|
|
// bool bTestFaces
|
|
// ) const;
|
|
|
|
|
|
/*
|
|
Returns:
|
|
True if m_vertex_tag is ON_SubD::VertexTag::Smooth.
|
|
*/
|
|
bool IsSmooth() const;
|
|
|
|
/*
|
|
Returns:
|
|
True if m_vertex_tag is ON_SubD::VertexTag::Crease.
|
|
*/
|
|
bool IsCrease() const;
|
|
|
|
/*
|
|
Returns:
|
|
True if m_vertex_tag is ON_SubD::VertexTag::Corner.
|
|
*/
|
|
bool IsCorner() const;
|
|
|
|
/*
|
|
Returns:
|
|
True if m_vertex_tag is ON_SubD::VertexTag::Dart.
|
|
*/
|
|
bool IsDart() const;
|
|
|
|
/*
|
|
Returns:
|
|
True if m_vertex_tag is ON_SubD::VertexTag::Smooth or ON_SubD::VertexTag::Crease.
|
|
*/
|
|
bool IsSmoothOrCrease() const;
|
|
|
|
/*
|
|
Returns:
|
|
True if m_vertex_tag is ON_SubD::VertexTag::Crease or ON_SubD::VertexTag::Corner.
|
|
*/
|
|
bool IsCreaseOrCorner() const;
|
|
|
|
/*
|
|
Returns:
|
|
True if m_vertex_tag is ON_SubD::VertexTag::Crease or ON_SubD::VertexTag::Corner or ON_SubD::VertexTag::Dart.
|
|
*/
|
|
bool IsCreaseOrCornerOrDart() const;
|
|
|
|
/*
|
|
Returns:
|
|
True if m_vertex_tag is ON_SubD::VertexTag::Smooth or ON_SubD::VertexTag::Dart.
|
|
*/
|
|
bool IsSmoothOrDart() const;
|
|
|
|
/*
|
|
Description:
|
|
A "standard" vertex is one where the standard subdivsion matrix for that vertex
|
|
can be used to calculate the subdivision point.
|
|
This function is desinged to be useful for testing and debugging code when
|
|
a certain situation is expected to exist. It is not used for evaluation
|
|
because it is too slow.
|
|
|
|
Returns:
|
|
True if the subdivison point of the vertex can be calulated using the standard
|
|
subdivion matrix for the vertex type and face count.
|
|
|
|
Remarks:
|
|
If the vertex is tagged and has multiple sectors, like an interior
|
|
crease or corner vertex, then this function will return false.
|
|
In this situation, it is possible that the vertex, as the center of a
|
|
sector, is standard.
|
|
*/
|
|
bool IsStandard(
|
|
ON_SubD::SubDType subd_type
|
|
) const;
|
|
|
|
/*
|
|
Parameters:
|
|
subdivision_point_type - [in]
|
|
Selects subdivision algorithm. Must be either
|
|
ON_SubD::SubDType::TriLoopWarren or ON_SubD::SubDType::QuadCatmullClark.
|
|
bUseSavedSubdivisionPoint - [in]
|
|
If there is a saved subdivision point and bUseSavedSubdivisionPoint
|
|
is true, then the saved value is returned.
|
|
subdivision_point - [out]
|
|
Returns:
|
|
true if successful
|
|
*/
|
|
bool GetSubdivisionPoint(
|
|
ON_SubD::SubDType subdivision_point_type,
|
|
bool bUseSavedSubdivisionPoint,
|
|
double subdivision_point[3]
|
|
) const;
|
|
|
|
/*
|
|
Parameters:
|
|
facet_type - [in]
|
|
Selects subdivision algorithm.
|
|
bUseSavedLimitPoint - [in]
|
|
If there is a saved limit point and normal and bUseSavedLimitPoint
|
|
is true, then the saved value is used.
|
|
limit_point - [out]
|
|
Returns:
|
|
true if successful
|
|
*/
|
|
bool GetLimitPoint(
|
|
ON_SubD::SubDType subd_type,
|
|
const ON_SubDFace* sector_face,
|
|
bool bUseSavedLimitPoint,
|
|
class ON_SubDSectorLimitPoint& limit_point
|
|
) const;
|
|
|
|
/*
|
|
Description:
|
|
If there is a saved limit point, then its location is returned in limit_point[].
|
|
Parameters:
|
|
limit_point - [out]
|
|
Returns:
|
|
ON_SubD::SubDType::Unset: No saved limit point. The input value of limit_point[] is not changed.
|
|
Otherwise: The type of saved SubD limit point and its location is returned in limit_point[].
|
|
*/
|
|
ON_SubD::SubDType GetSavedLimitPointLocation(
|
|
double limit_point[3]
|
|
) const;
|
|
|
|
/*
|
|
Description:
|
|
Save limit point and limit normal for future use.
|
|
Parameters:
|
|
subd_type - [in]
|
|
limit_point - [in]
|
|
limit_normal - [in]
|
|
Returns:
|
|
true if successful
|
|
*/
|
|
bool SetSavedLimitPoint(
|
|
ON_SubD::SubDType subd_type,
|
|
const ON_SubDSectorLimitPoint limit_point
|
|
) const;
|
|
|
|
void ClearSavedLimitPoints() const;
|
|
|
|
/*
|
|
Returns:
|
|
ON_SubD::SubDType::TriLoopWarren
|
|
The vertex limit point and normal are saved for Loop trianglular subdivision.
|
|
ON_SubD::SubDType::QuadCatmullClark
|
|
The vertex limit point and normal are saved for Catmull-Clark quad subdivision.
|
|
ON_SubD::SubDType::Unset
|
|
The vertex limit point and normal are not saved.
|
|
*/
|
|
ON_SubD::SubDType SavedLimitPointType() const;
|
|
|
|
/*
|
|
Description:
|
|
Report what type of facet or facets use this vertex.
|
|
*/
|
|
ON_SubD::VertexFacetType VertexFacetTypes() const;
|
|
|
|
|
|
/*
|
|
Description:
|
|
Call this function if the vertex is modified and it will clear any
|
|
cached subdivision information that needs to be recalculated.
|
|
*/
|
|
void VertexModifiedNofification() const;
|
|
|
|
private:
|
|
static bool GetQuadPoint(
|
|
const class ON_SubDVertex* vertex, // smooth or dart
|
|
bool bUseSavedSubdivisionPoint,
|
|
double vertex_point[3]
|
|
);
|
|
|
|
static bool GetTriPoint(
|
|
const class ON_SubDVertex* vertex, // smooth or dart
|
|
bool bUseSavedSubdivisionPoint,
|
|
double vertex_point[3]
|
|
);
|
|
|
|
static unsigned int GetFacePointSum(
|
|
const ON_SubDFace* face,
|
|
const ON_SubDVertex* vertex,
|
|
double* facePsum // sum of points that are not immediately adjacent to vertex
|
|
);
|
|
|
|
|
|
/*
|
|
Description:
|
|
Used for smooth and dart vertices when there are faces
|
|
that use the vertex have different numbers of sides.
|
|
This typically happen when a quad subd control net is
|
|
being subdivided for the first time.
|
|
Parameters:
|
|
vertex - [in]
|
|
vertex_point - [out]
|
|
Returns:
|
|
true if successful
|
|
*/
|
|
static bool GetGeneralQuadSubdivisionPoint(
|
|
const class ON_SubDVertex* vertex,
|
|
bool bUseSavedSubdivisionPoint,
|
|
double vertex_point[3]
|
|
);
|
|
|
|
private:
|
|
friend class ON_SubDArchiveIdMap;
|
|
void CopyFrom(
|
|
const ON_SubDVertex* src,
|
|
bool bCopyEdgeArray,
|
|
bool bCopyFaceArray,
|
|
bool bCopyLimitPointList
|
|
);
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDEdge
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDEdge : public ON_SubDComponentBase
|
|
{
|
|
public:
|
|
static const ON_SubDEdge Empty;
|
|
|
|
bool Write (
|
|
class ON_BinaryArchive& archive
|
|
) const;
|
|
|
|
static bool Read (
|
|
class ON_BinaryArchive& archive,
|
|
class ON_SubD& subd,
|
|
class ON_SubDEdge*& edge
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Apply a tranxfomration matrix to vertex geometry information.
|
|
|
|
Parameters:
|
|
bTransformationSavedSubdivisionPoint - [in]
|
|
If the transformation is being applied to every vertex, edge and
|
|
face in every level of a subdivision object, and the transformation
|
|
is an isometry (rotation, translation, ...), a uniform scale, or a
|
|
composition of these types, then set
|
|
bTransformationSavedSubdivisionPoint = true to apply the
|
|
transformation to saved subdivision and saved limit point information.
|
|
In all other cases, set bTransformationSavedSubdivisionPoint = false
|
|
and any saved subdivision points or saved limit points will be
|
|
deleted. When in doubt, pass false.
|
|
|
|
xform - [in]
|
|
*/
|
|
bool Transform(
|
|
bool bTransformationSavedSubdivisionPoint,
|
|
const class ON_Xform& xform
|
|
);
|
|
|
|
ON_BoundingBox ControlNetBoundingBox() const;
|
|
ON_BoundingBox LimitSurfaceBoundingBox(
|
|
const ON_SubD& subd
|
|
) const;
|
|
|
|
|
|
/*
|
|
Description:
|
|
Call this function if the edge is modified and it will clear any
|
|
cached subdivision information that needs to be recalculated.
|
|
*/
|
|
void EdgeModifiedNofification() const;
|
|
|
|
|
|
public:
|
|
ON_COMPONENT_INDEX ComponentIndex() const;
|
|
ON_SubDComponentPtr ComponentPtr() const;
|
|
|
|
public:
|
|
const class ON_SubDEdge* m_prev_edge = nullptr; // linked list of edges on this level
|
|
const class ON_SubDEdge* m_next_edge = nullptr; // linked list of edges on this level
|
|
|
|
public:
|
|
// When checking the edge tag, it is important to consider what
|
|
// should happen in the ON_SubD::EdgeTag::X case. It is strongly
|
|
// suggested that you use the member functions ON_SubDEdge::IsSmooth()
|
|
// and ON_SubDEdge::IsCrease() instead of comparing m_edge_tag to
|
|
// ON_SubD::EdgeTag values.
|
|
ON_SubD::EdgeTag m_edge_tag = ON_SubD::EdgeTag::Unset;
|
|
|
|
private:
|
|
unsigned char m_reserved1 = 0;
|
|
unsigned short m_reserved2 = 0;
|
|
|
|
public:
|
|
// Array of m_face_count faces.
|
|
//
|
|
// The first two are in m_face2[0] and m_face2[1].
|
|
// When m_face_count > 3, the third and additional faces
|
|
// are in m_facex[0], ..., m_facex[m_face_count-3];
|
|
//
|
|
// The value of ON_SubDFacePtr.FaceDirection() is 0 if the
|
|
// edge's natural orientation from m_vertex[0] to m_vertex[1]
|
|
// agrees with the face's boundary orientation.
|
|
//
|
|
// The value of ON_SubDFacePtr.FaceDirection() is 1 if the
|
|
// edge's natural orientation from m_vertex[0] to m_vertex[1]
|
|
// is opposited the face's boundary orientation.
|
|
static const unsigned int MaximumFaceCount;
|
|
unsigned short m_face_count = 0;
|
|
unsigned short m_facex_capacity = 0;
|
|
ON_SubDFacePtr m_face2[2];
|
|
ON_SubDFacePtr* m_facex = nullptr;
|
|
|
|
// m_vertex[0] = vertex at the start of the edge.
|
|
// m_vertex[1] = vertex at the end of the edge.
|
|
const class ON_SubDVertex* m_vertex[2];
|
|
|
|
// If the value of vertex->m_vertex_tag is not ON_SubD::VertexTag::Smooth,
|
|
// then that vertex is "tagged".
|
|
//
|
|
// If the value of m_edge_tag is ON_SubD::EdgeTag::Crease,
|
|
// then m_sector_coefficient[] should be {0,0}.
|
|
// In any case m_sector_coefficient[] values are ignored and the
|
|
// midpoint of the edge is the location of the edge.s subdivision point.
|
|
// The edge's subdivision vertex will be tagged as ON_SubD::VertexTag::Crease
|
|
// and both subdivision edges will be tagged as ON_SubD::EdgeTag::Crease.
|
|
//
|
|
// If the value of m_edge_tag is ON_SubD::EdgeTag::Smooth
|
|
// and neither end vertex is tagged, then m_sector_coefficient[] should be {0,0}.
|
|
// In any case m_sector_coefficient[] values are ignored on smooth edges
|
|
// with smooth vertices at both ends.
|
|
// The edge's subdivision vertex will be tagged as ON_SubD::VertexTag::Smooth
|
|
// and both subdivision edges will be tagged as ON_SubD::EdgeTag::Smooth.
|
|
//
|
|
// If the value of m_edge_tag is ON_SubD::EdgeTag::Smooth and
|
|
// exactly one end vertex is tagged, then the m_sector_coefficient[]
|
|
// value for the tagged end is calculated by ON_SubDSectorType::SectorWeight().
|
|
// tagged_weight*tagged_vertex + (1.0 - tagged_weight)*untagged_vertex
|
|
// is used when combining the edge ends.
|
|
// The edge's subdivision vertex will be tagged as ON_SubD::VertexTag::Smooth
|
|
// and both subdivision edges will be tagged as ON_SubD::EdgeTag::Smooth.
|
|
//
|
|
// If the value of m_edge_tag is ON_SubD::EdgeTag::X, then the edge
|
|
// must have exactly two neighboring faces,
|
|
// both vertices must be tagged and the m_sector_coefficient[]
|
|
// values are calculated by ON_SubDSectorType::SectorWeight().
|
|
// When the edge is subdivided, the midpoint of the edge is the
|
|
// location of the edge.s subdivision point.
|
|
// The edge's subdivision vertex will be tagged as ON_SubD::VertexTag::Smooth
|
|
// and both subdivision edges will be tagged as ON_SubD::EdgeTag::Smooth.
|
|
//
|
|
// If the value of m_edge_tag is ON_SubD::EdgeTag::Smooth
|
|
// and both end vertices are tagged, that is a severe error
|
|
// condition and the edge is subdivided at its midpoint.
|
|
//
|
|
// If the value of m_edge_tag is ON_SubD::EdgeTag::X
|
|
// and both end vertices are not tagged, that is a severe error
|
|
// condition and the edge is subdivided at its midpoint.
|
|
double m_sector_coefficient[2];
|
|
|
|
// If m_edge_tag is not ON_SubD::EdgeTag::Sharp, then m_sharpness is ignored.
|
|
// If m_edge_tag is ON_SubD::EdgeTag::Sharp, then m_sharpness controls how hard/soft
|
|
// the edge appears.
|
|
// The behavior of a "sharp" edge with m_sharpness = 1 is identical to a crease edge.
|
|
// A "sharp" edge with m_sharpness = 0 is identical to a smooth edge.
|
|
// For this reason, m_sharpness must be > 0 and < 1.
|
|
double m_sharpness = 0.0;
|
|
|
|
public:
|
|
unsigned int FaceCount() const;
|
|
|
|
ON_SubDFacePtr FacePtr(
|
|
unsigned int i
|
|
) const;
|
|
|
|
ON_SubDFacePtr FacePtr(
|
|
const class ON_SubDFace* f
|
|
) const;
|
|
|
|
const class ON_SubDFace* Face(
|
|
unsigned int i
|
|
) const;
|
|
|
|
ON__UINT_PTR FaceDirection(
|
|
unsigned int i
|
|
) const;
|
|
|
|
unsigned int FaceArrayIndex(
|
|
const class ON_SubDFace* f
|
|
) const;
|
|
|
|
/*
|
|
Description:
|
|
Expert user tool to remove a face from the edges's face array.
|
|
Remarks:
|
|
Does not modify the face. If the edge is referenced in the face's edge array,
|
|
then the edge must be removed from the face's edge array.
|
|
*/
|
|
bool RemoveFaceFromArray(
|
|
const ON_SubDFace* f
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Expert user tool to remove a face from the edges's face array.
|
|
Remarks:
|
|
Does not modify the face. If the edge is referenced in the face's edge array,
|
|
then the edge must be removed from the face's edge array.
|
|
*/
|
|
bool RemoveFaceFromArray(
|
|
unsigned int i,
|
|
ON_SubDFacePtr& removed_face
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Expert user tool to add a face from the edges's face array.
|
|
Remarks:
|
|
Does not modify the face. If the edge is not referenced in the face's edge array,
|
|
then the edge must be inserted in the correct location in the faces array.
|
|
If you are creating a non-manifold SubD, you must first reserve m_facex[]
|
|
capacity by calling ON_SubD::GrowEdgeFaceArray().
|
|
*/
|
|
bool AddFaceToArray(
|
|
ON_SubDFacePtr face_ptr
|
|
);
|
|
|
|
const class ON_SubDVertex* Vertex(
|
|
unsigned int i
|
|
) const;
|
|
|
|
/*
|
|
Description:
|
|
Return the vertex at the other end of the edge.
|
|
Parameters:
|
|
vertex - [in]
|
|
A vertex referenced in the edge's m_vertex[] array.
|
|
Returns:
|
|
If vertex is not nullptr and exactly one of m_vertex[] is equal to vertex,
|
|
then the other m_vertex[] pointer is returned.
|
|
In any other case, nullptr is returned.
|
|
*/
|
|
const ON_SubDVertex* OtherEndVertex(
|
|
const ON_SubDVertex* vertex
|
|
) const;
|
|
|
|
/*
|
|
Description:
|
|
Return the neighboring face.
|
|
Parameters:
|
|
face - [in]
|
|
A face referenced in the edge's m_face2[] array.
|
|
bStopAtCrease - [in]
|
|
If true and if m_edge_tag = ON_SubD::EdgeTag::Crease,
|
|
then nullptr is returned.
|
|
Returns:
|
|
If the m_face_count = 2,
|
|
m_edge_tag is smooth or x or passes the crease tag test,
|
|
one of m_face2[0,1] points a face, then
|
|
the neighboring face is returned.
|
|
In any other case, nullptr is returned.
|
|
*/
|
|
const ON_SubDFace* NeighborFace(
|
|
const ON_SubDFace* face,
|
|
bool bStopAtCrease
|
|
) const;
|
|
|
|
/*
|
|
Description:
|
|
Return the neighboring face.
|
|
Parameters:
|
|
face - [in]
|
|
A face referenced in the edge's m_face2[] array.
|
|
bStopAtCrease - [in]
|
|
If true and if m_edge_tag = ON_SubD::EdgeTag::Crease,
|
|
then nullptr is returned.
|
|
Returns:
|
|
If the m_face_count = 2,
|
|
m_edge_tag is smooth or x or passes the crease tag test,
|
|
one of m_face2[0,1] points a face, then
|
|
the neighboring face is returned.
|
|
In any other case, ON_SubDFacePtr::Null is returned.
|
|
*/
|
|
const ON_SubDFacePtr NeighborFacePtr(
|
|
const ON_SubDFace* face,
|
|
bool bStopAtCrease
|
|
) const;
|
|
|
|
|
|
|
|
/*
|
|
Returns:
|
|
True if m_edge_tag is ON_SubD::EdgeTag::Smooth.
|
|
bEdgeTagXresult if m_edge_tag is ON_SubD::EdgeTag::X.
|
|
False in all other cases.
|
|
*/
|
|
bool IsSmooth(
|
|
bool bEdgeTagXresult
|
|
) const;
|
|
|
|
/*
|
|
Returns:
|
|
True if m_edge_tag is ON_SubD::EdgeTag::Crease.
|
|
bEdgeTagXresult if m_edge_tag is ON_SubD::EdgeTag::X.
|
|
False in all other cases.
|
|
*/
|
|
bool IsCrease(
|
|
bool bEdgeTagXresult
|
|
) const;
|
|
|
|
/*
|
|
Returns:
|
|
0: end vertices are not tagged as darts
|
|
1: one end vertex is tagged as a dart.
|
|
2: both end vertices are tagged as a darts.
|
|
*/
|
|
unsigned int DartCount() const;
|
|
|
|
/*
|
|
Returns:
|
|
bitwise or of applicable ON_ComponentAttributes::EdgeFlags values.
|
|
*/
|
|
unsigned int EdgeFlags() const;
|
|
|
|
/*
|
|
Parameters:
|
|
subdivision_point_type - [in]
|
|
Selects subdivision algorithm. Must be either
|
|
ON_SubD::SubDType::TriLoopWarren or ON_SubD::SubDType::QuadCatmullClark.
|
|
bUseSavedSubdivisionPoint - [in]
|
|
If there is a saved subdivision point and bUseSavedSubdivisionPoint
|
|
is true, then the saved value is returned.
|
|
subdivision_point - [out]
|
|
Returns:
|
|
true if successful
|
|
*/
|
|
bool GetSubdivisionPoint(
|
|
ON_SubD::SubDType subdivision_point_type,
|
|
bool bUseSavedSubdivisionPoint,
|
|
double subdivision_point[3]
|
|
) const;
|
|
|
|
/*
|
|
Parameters:
|
|
edge_vertex_index - [in]
|
|
0 or 1
|
|
edge_ptr0 - [out]
|
|
edge_ptr1 - [out]
|
|
Crease edges that bound the sector containing this edge.
|
|
The direction value of the edge pointer identifies the end
|
|
of the sector boundary edge this->at m_vertex[edge_vertex_index].
|
|
Returns:
|
|
Number of faces in the sector.
|
|
*/
|
|
unsigned int GetSectorBoundaryEdges(
|
|
unsigned int edge_vertex_index,
|
|
ON_SubDEdgePtr* edge_ptr0,
|
|
ON_SubDEdgePtr* edge_ptr1
|
|
) const;
|
|
|
|
/*
|
|
Returns:
|
|
0: m_vertex[0] is tagged and m_vertex[1] is not tagged.
|
|
1: m_vertex[0] is tagged and m_vertex[1] is not tagged.
|
|
2: m_vertex[0] and m_vertex[1] are both tagged.
|
|
3: neither m_vertex[0] nor m_vertex[1] is tagged.
|
|
*/
|
|
unsigned int TaggedEndIndex() const;
|
|
|
|
private:
|
|
static unsigned int GetFacePointSum(
|
|
const ON_SubDFace* face,
|
|
const ON_SubDEdge* edge,
|
|
double* facePsum // sum of face vertex points not on the edge
|
|
);
|
|
|
|
private:
|
|
friend class ON_SubDArchiveIdMap;
|
|
void CopyFrom(
|
|
const ON_SubDEdge* src,
|
|
bool bReverseEdge,
|
|
bool bCopyVertexArray,
|
|
bool bCopyFaceArray
|
|
);
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDFace
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDFace : public ON_SubDComponentBase
|
|
{
|
|
public:
|
|
static const ON_SubDFace Empty;
|
|
|
|
bool Write (
|
|
class ON_BinaryArchive& archive
|
|
) const;
|
|
|
|
static bool Read (
|
|
class ON_BinaryArchive& archive,
|
|
class ON_SubD& subd,
|
|
class ON_SubDFace*& face
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Apply a tranxfomration matrix to vertex geometry information.
|
|
|
|
Parameters:
|
|
bTransformationSavedSubdivisionPoint - [in]
|
|
If the transformation is being applied to every vertex, edge and
|
|
face in every level of a subdivision object, and the transformation
|
|
is an isometry (rotation, translation, ...), a uniform scale, or a
|
|
composition of these types, then set
|
|
bTransformationSavedSubdivisionPoint = true to apply the
|
|
transformation to saved subdivision and saved limit point information.
|
|
In all other cases, set bTransformationSavedSubdivisionPoint = false
|
|
and any saved subdivision points or saved limit points will be
|
|
deleted. When in doubt, pass false.
|
|
|
|
xform - [in]
|
|
*/
|
|
bool Transform(
|
|
bool bTransformationSavedSubdivisionPoint,
|
|
const class ON_Xform& xform
|
|
);
|
|
|
|
ON_BoundingBox ControlNetBoundingBox() const;
|
|
ON_BoundingBox LimitSurfaceBoundingBox(
|
|
const ON_SubD& subd
|
|
) const;
|
|
|
|
|
|
ON_COMPONENT_INDEX ComponentIndex() const;
|
|
ON_SubDComponentPtr ComponentPtr() const;
|
|
|
|
/*
|
|
Description:
|
|
Call this function if the face is modified and it will clear any
|
|
cached subdivision information that needs to be recalculated.
|
|
*/
|
|
void FaceModifiedNofification() const;
|
|
|
|
|
|
public:
|
|
const class ON_SubDFace* m_prev_face = nullptr; // linked list of faces on this level
|
|
const class ON_SubDFace* m_next_face = nullptr; // linked list of faces on this level
|
|
|
|
public:
|
|
unsigned int m_zero_face_id = 0; // id of level zero face
|
|
unsigned int m_parent_face_id = 0; // id of previous level face
|
|
|
|
private:
|
|
unsigned int m_reserved = 0; // id of previous level face
|
|
|
|
public:
|
|
// Array of m_edge_count edges that form the boundary of the face.
|
|
// The edges are in ordered to form a continuous loop.
|
|
//
|
|
// The first four are in m_edge4[0], ..., m_edge4[3].
|
|
// When m_edge_count > 4, the fifth and additional edges
|
|
// are in m_edgex[0], ..., m_edgex[m_edge_count-5];
|
|
//
|
|
// The value of ON_SubDEdgePtr.EdgeDirection() is 0 if the
|
|
// edge's natural orientation from m_vertex[0] to m_vertex[1]
|
|
// agrees with the face's boundary orientation.
|
|
//
|
|
// The value of ON_SubDEdgePtr.EdgeDirection() is 1 if the
|
|
// edge's natural orientation from m_vertex[0] to m_vertex[1]
|
|
// is opposited the face's boundary orientation.
|
|
static const unsigned int MaximumEdgeCount;
|
|
unsigned short m_edge_count = 0;
|
|
unsigned short m_edgex_capacity = 0;
|
|
|
|
ON_SubDEdgePtr m_edge4[4];
|
|
ON_SubDEdgePtr* m_edgex = nullptr;
|
|
|
|
public:
|
|
unsigned int EdgeCount() const;
|
|
|
|
ON_SubDEdgePtr EdgePtr(
|
|
unsigned int i
|
|
) const;
|
|
|
|
ON_SubDEdgePtr EdgePtr(
|
|
const class ON_SubDEdge* e
|
|
) const;
|
|
|
|
const class ON_SubDVertex* Vertex(
|
|
unsigned int i
|
|
) const;
|
|
|
|
unsigned int VertexIndex(
|
|
const ON_SubDVertex* vertex
|
|
) const;
|
|
|
|
const class ON_SubDEdge* Edge(
|
|
unsigned int i
|
|
) const;
|
|
|
|
ON__UINT_PTR EdgeDirection(
|
|
unsigned int i
|
|
) const;
|
|
|
|
unsigned int EdgeArrayIndex(
|
|
const ON_SubDEdge* e
|
|
) const;
|
|
|
|
/*
|
|
Description:
|
|
Expert user tool to remove an edge from the face's edge array.
|
|
Remarks:
|
|
Does not modify the edge. If the face is referenced in the edge's face array,
|
|
then the face must be removed from the edge's face array.
|
|
*/
|
|
bool RemoveEdgeFromArray(
|
|
const ON_SubDEdge* e
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Expert user tool to remove an edge from the face's edge array.
|
|
Remarks:
|
|
Does not modify the edge. If the face is referenced in the edge's face array,
|
|
then the face must be removed from the edge's face array.
|
|
*/
|
|
bool RemoveEdgeFromArray(
|
|
unsigned int i,
|
|
ON_SubDEdgePtr& removed_edge
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Expert user tool to replace one edge with another in the face's edge array.
|
|
Parameters:
|
|
edge_to_remove - [in]
|
|
edge_to_insert - [in]
|
|
The inserted edge is assigned the same boundary orientation as the
|
|
removed edge.
|
|
Remarks:
|
|
Does not modify the edge. The corresponding reference to this face must
|
|
be removed from the first edge and added to the second edge.
|
|
*/
|
|
bool ReplaceEdgeInArray(
|
|
unsigned int fei0,
|
|
ON_SubDEdge* edge_to_remove,
|
|
ON_SubDEdge* edge_to_insert
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Expert user tool to replace one edge with another in the face's edge array.
|
|
Parameters:
|
|
edge_to_remove - [in]
|
|
edge_to_insert - [in]
|
|
The inserted edge is assigned the same boundary orientation specified
|
|
in edgeptr_to_insert.
|
|
Remarks:
|
|
Does not modify the edge. The corresponding reference to this face must
|
|
be removed from the first edge and added to the second edge.
|
|
*/
|
|
bool ReplaceEdgeInArray(
|
|
unsigned int fei0,
|
|
ON_SubDEdge* edge_to_remove,
|
|
ON_SubDEdgePtr edgeptr_to_insert
|
|
);
|
|
|
|
const ON_SubDEdge* PrevEdge(
|
|
const ON_SubDEdge* edge
|
|
) const;
|
|
|
|
const ON_SubDEdge* NextEdge(
|
|
const ON_SubDEdge* edge
|
|
) const;
|
|
|
|
unsigned int PrevEdgeArrayIndex(
|
|
unsigned int edge_array_index
|
|
) const;
|
|
|
|
unsigned int NextEdgeArrayIndex(
|
|
unsigned int edge_array_index
|
|
) const;
|
|
|
|
/*
|
|
Description:
|
|
If the face is a quad, get the opposite corner vertex.
|
|
Parameters:
|
|
vertex - [in]
|
|
a vertex on this face.
|
|
Returns:
|
|
If the face is a quad and vertex is a vertex of the face, then
|
|
the vertex on the opposite corner is returned.
|
|
Otherwise, nullptr is returned.
|
|
*/
|
|
const ON_SubDVertex* QuadOppositeVertex(
|
|
const ON_SubDVertex* vertex
|
|
) const;
|
|
|
|
/*
|
|
Description:
|
|
If the face is a quad, get the opposite side edge.
|
|
Parameters:
|
|
edge - [in]
|
|
an edge on this face.
|
|
Returns:
|
|
If the face is a quad and edge is an edge of the face, then
|
|
the edge on the opposite side is returned.
|
|
Otherwise, nullptr is returned.
|
|
*/
|
|
const ON_SubDEdge* QuadOppositeEdge(
|
|
const ON_SubDEdge* edge
|
|
) const;
|
|
|
|
///*
|
|
//Parameters:
|
|
// subd_type - [in]
|
|
// bTestFaces - [in]
|
|
// If true, then false is returned if any neighboring face is not
|
|
// a quad (ccquad subdivision type) or tri (lwtri subdivsion type).
|
|
//*/
|
|
//bool IsOrdinary(
|
|
// ON_SubD::SubDType subd_type,
|
|
// bool bTestFaces
|
|
// ) const;
|
|
|
|
/*
|
|
Parameters:
|
|
subdivision_point_type - [in]
|
|
Selects subdivision algorithm. Must be either
|
|
ON_SubD::SubDType::TriLoopWarren or ON_SubD::SubDType::QuadCatmullClark.
|
|
bUseSavedSubdivisionPoint - [in]
|
|
If there is a saved subdivision point and bUseSavedSubdivisionPoint
|
|
is true, then the saved value is returned.
|
|
subdivision_point - [out]
|
|
The average of the face vertex locations.
|
|
Returns:
|
|
true if successful
|
|
*/
|
|
bool GetSubdivisionPoint(
|
|
ON_SubD::SubDType subdivision_point_type,
|
|
bool bUseSavedSubdivisionPoint,
|
|
double subdivision_point[3]
|
|
) const;
|
|
|
|
/*
|
|
Description:
|
|
Reverse the order and orientation of the edges that form
|
|
the boundary of this face.
|
|
*/
|
|
bool ReverseEdgeList();
|
|
|
|
/*
|
|
Description:
|
|
Get the bicubic b-spline control points for the limit surface.
|
|
The corresponding knots are uniform.
|
|
Parameters:
|
|
vertex - [in]
|
|
limit_surface_cv_stride0 - [int]
|
|
limit_surface_cv_stride1 - [out]
|
|
limit_surface_cv - [out]
|
|
control points for a cubic spline surface
|
|
CV[i][j][k] = limit_surface_cv[i*limit_bspline_cv_stride0 + j*limit_bspline_cv_stride1 + k]
|
|
0 <= i < 4, 0 <= j < 4, 0 <= k < 3
|
|
Returns:
|
|
true if successful
|
|
false if the limit surface for this face is not a cubic surface
|
|
Remarks:
|
|
The knots for the bicubic b-spline surface are uniform.
|
|
*/
|
|
bool GetQuadLimitSurface(
|
|
size_t limit_surface_cv_stride0,
|
|
size_t limit_surface_cv_stride1,
|
|
double* limit_surface_cv
|
|
) const;
|
|
|
|
bool GetQuadLimitSurface(
|
|
class ON_NurbsSurface& limit_surface
|
|
) const;
|
|
|
|
bool GetQuadLimitSurface(
|
|
class ON_BezierSurface& limit_surface
|
|
) const;
|
|
|
|
private:
|
|
friend class ON_SubDArchiveIdMap;
|
|
void CopyFrom(
|
|
const ON_SubDFace* src,
|
|
bool bCopyEdgeArray
|
|
);
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDVertexArray
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDVertexArray
|
|
{
|
|
public:
|
|
ON_SubDVertexArray(
|
|
const ON_SubD& subd
|
|
);
|
|
ON_SubDVertexArray() = default;
|
|
ON_SubDVertexArray(const ON_SubDVertexArray&) = default;
|
|
ON_SubDVertexArray& operator=(const ON_SubDVertexArray&) = default;
|
|
|
|
#if defined(ON_HAS_RVALUEREF)
|
|
// rvalue copy constructor
|
|
ON_SubDVertexArray(ON_SubDVertexArray&&) ON_NOEXCEPT;
|
|
|
|
// rvalue copy operator-=
|
|
ON_SubDVertexArray& operator=(ON_SubDVertexArray&&);
|
|
#endif
|
|
|
|
const ON_SubD& SubD() const
|
|
{
|
|
return m_subd;
|
|
}
|
|
|
|
unsigned int VertexCount() const
|
|
{
|
|
return m_vertex_count;
|
|
}
|
|
|
|
const class ON_SubDVertex* operator[](unsigned int i) const
|
|
{
|
|
return (i < m_vertex_count) ? m_a[i] : nullptr;
|
|
}
|
|
|
|
private:
|
|
ON_SubD m_subd;
|
|
const class ON_SubDVertex*const* m_a = nullptr;
|
|
unsigned int m_vertex_count = 0;
|
|
|
|
#pragma ON_PRAGMA_WARNING_PUSH
|
|
#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 )
|
|
// C4251: ... needs to have dll-interface to be used by clients of class ...
|
|
// m_sp is private and all code that manages m_sp is explicitly implemented in the DLL.
|
|
private:
|
|
std::shared_ptr< const class ON_SubDVertex* > m_sp;
|
|
#pragma ON_PRAGMA_WARNING_POP
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDEdgeArray
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDEdgeArray
|
|
{
|
|
public:
|
|
ON_SubDEdgeArray(
|
|
const ON_SubD& subd
|
|
);
|
|
ON_SubDEdgeArray() = default;
|
|
ON_SubDEdgeArray(const ON_SubDEdgeArray&) = default;
|
|
ON_SubDEdgeArray& operator=(const ON_SubDEdgeArray&) = default;
|
|
|
|
#if defined(ON_HAS_RVALUEREF)
|
|
// rvalue copy constructor
|
|
ON_SubDEdgeArray(ON_SubDEdgeArray&&) ON_NOEXCEPT;
|
|
|
|
// rvalue copy operator-=
|
|
ON_SubDEdgeArray& operator=(ON_SubDEdgeArray&&);
|
|
#endif
|
|
|
|
const ON_SubD& SubD() const
|
|
{
|
|
return m_subd;
|
|
}
|
|
|
|
unsigned int EdgeCount() const
|
|
{
|
|
return m_edge_count;
|
|
}
|
|
|
|
const class ON_SubDEdge* operator[](unsigned int i) const
|
|
{
|
|
return (i < m_edge_count) ? m_a[i] : nullptr;
|
|
}
|
|
|
|
private:
|
|
ON_SubD m_subd;
|
|
const class ON_SubDEdge*const* m_a = nullptr;
|
|
unsigned int m_edge_count = 0;
|
|
|
|
#pragma ON_PRAGMA_WARNING_PUSH
|
|
#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 )
|
|
// C4251: ... needs to have dll-interface to be used by clients of class ...
|
|
// m_sp is private and all code that manages m_sp is explicitly implemented in the DLL.
|
|
private:
|
|
std::shared_ptr< const class ON_SubDEdge* > m_sp;
|
|
#pragma ON_PRAGMA_WARNING_POP
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDFaceArray
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDFaceArray
|
|
{
|
|
public:
|
|
ON_SubDFaceArray(
|
|
const ON_SubD& subd
|
|
);
|
|
ON_SubDFaceArray() = default;
|
|
ON_SubDFaceArray(const ON_SubDFaceArray&) = default;
|
|
ON_SubDFaceArray& operator=(const ON_SubDFaceArray&) = default;
|
|
|
|
#if defined(ON_HAS_RVALUEREF)
|
|
// rvalue copy constructor
|
|
ON_SubDFaceArray(ON_SubDFaceArray&&) ON_NOEXCEPT;
|
|
|
|
// rvalue copy operator-=
|
|
ON_SubDFaceArray& operator=(ON_SubDFaceArray&&);
|
|
#endif
|
|
|
|
const ON_SubD& SubD() const
|
|
{
|
|
return m_subd;
|
|
}
|
|
|
|
unsigned int FaceCount() const
|
|
{
|
|
return m_face_count;
|
|
}
|
|
|
|
const class ON_SubDFace* operator[](unsigned int i) const
|
|
{
|
|
return (i < m_face_count) ? m_a[i] : nullptr;
|
|
}
|
|
|
|
private:
|
|
ON_SubD m_subd;
|
|
const class ON_SubDFace*const* m_a = nullptr;
|
|
unsigned int m_face_count = 0;
|
|
|
|
#pragma ON_PRAGMA_WARNING_PUSH
|
|
#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 )
|
|
// C4251: ... needs to have dll-interface to be used by clients of class ...
|
|
// m_sp is private and all code that manages m_sp is explicitly implemented in the DLL.
|
|
private:
|
|
std::shared_ptr< const class ON_SubDFace* > m_sp;
|
|
#pragma ON_PRAGMA_WARNING_POP
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDVertexIterator
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDVertexIterator
|
|
{
|
|
public:
|
|
// The ON_SubD member function
|
|
// ON_SubDVertexIterator ON_SubD::VertexIterator(subd_level_index)
|
|
// is the best way to get a vertex iterator.
|
|
ON_SubDVertexIterator(
|
|
const class ON_SubD& subd
|
|
);
|
|
ON_SubDVertexIterator(
|
|
const class ON_SubDRef& subd_ref
|
|
);
|
|
|
|
// Construct and interator that iterates over a single vertex.
|
|
ON_SubDVertexIterator(
|
|
const class ON_SubD& subd,
|
|
const class ON_SubDVertex& vertex
|
|
);
|
|
|
|
// Construct and interator that iterates over a single vertex.
|
|
ON_SubDVertexIterator(
|
|
const class ON_SubDRef& subd_ref,
|
|
const class ON_SubDVertex& vertex
|
|
);
|
|
|
|
// Construct and interator that iterates over the vertices of an edge.
|
|
ON_SubDVertexIterator(
|
|
const class ON_SubD& subd,
|
|
const class ON_SubDEdge& edge
|
|
);
|
|
|
|
// Construct and interator that iterates over the vertices of an edge.
|
|
ON_SubDVertexIterator(
|
|
const class ON_SubDRef& subd_ref,
|
|
const class ON_SubDEdge& edge
|
|
);
|
|
|
|
// Construct and interator that iterates over the vertices of a face.
|
|
ON_SubDVertexIterator(
|
|
const class ON_SubD& subd,
|
|
const class ON_SubDFace& face
|
|
);
|
|
|
|
// Construct and interator that iterates over the vertices of a face.
|
|
ON_SubDVertexIterator(
|
|
const class ON_SubDRef& subd_ref,
|
|
const class ON_SubDFace& face
|
|
);
|
|
|
|
ON_SubDVertexIterator() = default;
|
|
ON_SubDVertexIterator(const ON_SubDVertexIterator&) = default;
|
|
ON_SubDVertexIterator& operator=(const ON_SubDVertexIterator&) = default;
|
|
|
|
#if defined(ON_HAS_RVALUEREF)
|
|
// rvalue copy constructor
|
|
ON_SubDVertexIterator( ON_SubDVertexIterator&& ) ON_NOEXCEPT;
|
|
// rvalue assignment operator
|
|
ON_SubDVertexIterator& operator=( ON_SubDVertexIterator&& );
|
|
#endif
|
|
|
|
/*
|
|
Returns:
|
|
The subD object for this iterator.
|
|
*/
|
|
const class ON_SubD& SubD() const
|
|
{
|
|
return m_subd_ref.SubD();
|
|
}
|
|
|
|
const class ON_SubDRef& SubDRef() const
|
|
{
|
|
return m_subd_ref;
|
|
}
|
|
|
|
/*
|
|
Description:
|
|
Increment the iterator.
|
|
Returns:
|
|
Current vertex.
|
|
Remarks:
|
|
operator++ and NextVertex() behave differently.
|
|
*/
|
|
const class ON_SubDVertex* operator++()
|
|
{
|
|
const class ON_SubDVertex* v = m_v_current;
|
|
NextVertex();
|
|
return v;
|
|
}
|
|
|
|
/*
|
|
Return:
|
|
Number of vertices this iterator will iterate through.
|
|
*/
|
|
unsigned int VertexCount() const
|
|
{
|
|
return m_vertex_count;
|
|
}
|
|
|
|
/*
|
|
Return:
|
|
Interator index of the current vertex.
|
|
*/
|
|
unsigned int CurrentVertexIndex() const
|
|
{
|
|
return m_vertex_index;
|
|
}
|
|
|
|
/*
|
|
Description:
|
|
Set the iterator to the beginning of the vertex list.
|
|
Returns:
|
|
First vertex in the list.
|
|
*/
|
|
const class ON_SubDVertex* FirstVertex()
|
|
{
|
|
m_vertex_index = 0;
|
|
return (m_v_current = m_v_first);
|
|
}
|
|
|
|
/*
|
|
Description:
|
|
Increment the iterator.
|
|
Returns:
|
|
Next vertex.
|
|
Remarks:
|
|
operator++ and NextVertex() behave differently.
|
|
*/
|
|
const class ON_SubDVertex* NextVertex()
|
|
{
|
|
m_vertex_index++;
|
|
if (m_vertex_index < m_vertex_count)
|
|
{
|
|
if (0 == m_component_ptr.m_ptr)
|
|
{
|
|
if (nullptr != m_v_current)
|
|
m_v_current = m_v_current->m_next_vertex;
|
|
}
|
|
else
|
|
{
|
|
const ON_SubDEdge* edge = m_component_ptr.Edge();
|
|
if (nullptr != edge)
|
|
{
|
|
m_v_current = edge->Vertex(m_vertex_index);
|
|
}
|
|
else
|
|
{
|
|
const ON_SubDFace* face = m_component_ptr.Face();
|
|
if (nullptr != face)
|
|
m_v_current = face->Vertex(m_vertex_index);
|
|
else
|
|
m_v_current = nullptr;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_vertex_index = m_vertex_count;
|
|
m_v_current = nullptr;
|
|
}
|
|
return m_v_current;
|
|
}
|
|
|
|
/*
|
|
Returns:
|
|
Current vertex;
|
|
*/
|
|
const class ON_SubDVertex* CurrentVertex() const
|
|
{
|
|
return m_v_current;
|
|
}
|
|
|
|
/*
|
|
Description:
|
|
Set the iterator to the end of the vertex list.
|
|
Returns:
|
|
Last vertex in the list.
|
|
*/
|
|
const class ON_SubDVertex* LastVertex()
|
|
{
|
|
m_vertex_index = (m_vertex_count > 0) ? (m_vertex_count - 1) : 0;
|
|
return (m_v_current = m_v_last);
|
|
}
|
|
|
|
private:
|
|
void Internal_Init(
|
|
const ON_SubDRef& subd_ref,
|
|
unsigned int vertex_count,
|
|
const ON_SubDVertex* first,
|
|
const ON_SubDVertex* last,
|
|
ON_SubDComponentPtr component_ptr
|
|
);
|
|
ON_SubDRef m_subd_ref;
|
|
const ON_SubDVertex* m_v_first = nullptr;
|
|
const ON_SubDVertex* m_v_last = nullptr;
|
|
const ON_SubDVertex* m_v_current = nullptr;
|
|
unsigned int m_vertex_index = 0;
|
|
unsigned int m_vertex_count = 0;
|
|
ON_SubDComponentPtr m_component_ptr = ON_SubDComponentPtr::Null;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDEdgeIterator
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDEdgeIterator
|
|
{
|
|
public:
|
|
// The ON_SubD member function
|
|
// ON_SubDEdgeIterator ON_SubD::EdgeIterator()
|
|
// is the best way to get an edge iterator from an ON_SubD.
|
|
ON_SubDEdgeIterator(
|
|
const class ON_SubD& subd
|
|
);
|
|
|
|
// The ON_SubDRef member function
|
|
// ON_SubDEdgeIterator ON_SubDRef::EdgeIterator()
|
|
// is the best way to get an edge iterator from an ON_SubDRef.
|
|
ON_SubDEdgeIterator(
|
|
const class ON_SubDRef& subd_ref
|
|
);
|
|
|
|
// Construct and interator that iterates over a single edge.
|
|
ON_SubDEdgeIterator(
|
|
const class ON_SubD& subd,
|
|
const class ON_SubDEdge& edge
|
|
);
|
|
|
|
// Construct and interator that iterates over a single edge.
|
|
ON_SubDEdgeIterator(
|
|
const class ON_SubDRef& subd_ref,
|
|
const class ON_SubDEdge& edge
|
|
);
|
|
|
|
// Construct and interator that iterates over the edges of a vertex.
|
|
ON_SubDEdgeIterator(
|
|
const class ON_SubD& subd,
|
|
const class ON_SubDVertex& vertex
|
|
);
|
|
|
|
// Construct and interator that iterates over the edges of a vertex.
|
|
ON_SubDEdgeIterator(
|
|
const class ON_SubDRef& subd_ref,
|
|
const class ON_SubDVertex& vertex
|
|
);
|
|
|
|
// Construct and interator that iterates over the edges of a face.
|
|
ON_SubDEdgeIterator(
|
|
const class ON_SubD& subd,
|
|
const class ON_SubDFace& face
|
|
);
|
|
|
|
// Construct and interator that iterates over the edges of a face.
|
|
ON_SubDEdgeIterator(
|
|
const class ON_SubDRef& subd_ref,
|
|
const class ON_SubDFace& face
|
|
);
|
|
|
|
ON_SubDEdgeIterator() = default;
|
|
ON_SubDEdgeIterator(const ON_SubDEdgeIterator&) = default;
|
|
ON_SubDEdgeIterator& operator=(const ON_SubDEdgeIterator&) = default;
|
|
|
|
#if defined(ON_HAS_RVALUEREF)
|
|
// rvalue copy constructor
|
|
ON_SubDEdgeIterator( ON_SubDEdgeIterator&& ) ON_NOEXCEPT;
|
|
// rvalue assignment operator
|
|
ON_SubDEdgeIterator& operator=( ON_SubDEdgeIterator&& );
|
|
#endif
|
|
|
|
/*
|
|
Returns:
|
|
The subD object for this iterator.
|
|
*/
|
|
const class ON_SubD& SubD() const
|
|
{
|
|
return m_subd_ref.SubD();
|
|
}
|
|
|
|
const class ON_SubDRef& SubDRef() const
|
|
{
|
|
return m_subd_ref;
|
|
}
|
|
|
|
/*
|
|
Description:
|
|
Increment the iterator.
|
|
Returns:
|
|
Current edge.
|
|
Remarks:
|
|
operator++ and NextEdge() behave differently.
|
|
*/
|
|
const class ON_SubDEdge* operator++()
|
|
{
|
|
const class ON_SubDEdge* e = m_e_current;
|
|
NextEdge();
|
|
return e;
|
|
}
|
|
|
|
/*
|
|
Return:
|
|
Number of edges this iterator will iterate through.
|
|
*/
|
|
unsigned int EdgeCount() const
|
|
{
|
|
return m_edge_count;
|
|
}
|
|
|
|
/*
|
|
Return:
|
|
Interator index of the current edge.
|
|
*/
|
|
unsigned int CurrentEdgeIndex() const
|
|
{
|
|
return m_edge_index;
|
|
}
|
|
|
|
/*
|
|
Description:
|
|
Set the iterator to the beginning of the edge list.
|
|
Returns:
|
|
First edge in the list.
|
|
*/
|
|
const class ON_SubDEdge* FirstEdge()
|
|
{
|
|
m_edge_index = 0;
|
|
return m_e_current = m_e_first;
|
|
}
|
|
|
|
/*
|
|
Description:
|
|
Increment the iterator.
|
|
Returns:
|
|
Next edge.
|
|
Remarks:
|
|
operator++ and NextEdge() behave differently.
|
|
*/
|
|
const class ON_SubDEdge* NextEdge()
|
|
{
|
|
m_edge_index++;
|
|
if (m_edge_index < m_edge_count)
|
|
{
|
|
if (0 == m_component_ptr.m_ptr)
|
|
{
|
|
if (nullptr != m_e_current)
|
|
m_e_current = m_e_current->m_next_edge;
|
|
}
|
|
else
|
|
{
|
|
const ON_SubDVertex* vertex = m_component_ptr.Vertex();
|
|
if (nullptr != vertex)
|
|
{
|
|
m_e_current = vertex->Edge(m_edge_index);
|
|
}
|
|
else
|
|
{
|
|
const ON_SubDFace* face = m_component_ptr.Face();
|
|
if (nullptr != face)
|
|
m_e_current = face->Edge(m_edge_index);
|
|
else
|
|
m_e_current = nullptr;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_edge_index = m_edge_count;
|
|
m_e_current = nullptr;
|
|
}
|
|
return m_e_current;
|
|
}
|
|
|
|
/*
|
|
Returns:
|
|
Current edge;
|
|
*/
|
|
const class ON_SubDEdge* CurrentEdge() const
|
|
{
|
|
return m_e_current;
|
|
}
|
|
|
|
/*
|
|
Description:
|
|
Set the iterator to the end of the edge list.
|
|
Returns:
|
|
Last edge in the list.
|
|
*/
|
|
const class ON_SubDEdge* LastEdge()
|
|
{
|
|
m_edge_index = (m_edge_count > 0) ? (m_edge_count - 1) : 0;
|
|
return m_e_current = m_e_last;
|
|
}
|
|
|
|
private:
|
|
void Internal_Init(
|
|
const ON_SubDRef& subd_ref,
|
|
unsigned int edge_count,
|
|
const ON_SubDEdge* first,
|
|
const ON_SubDEdge* last,
|
|
ON_SubDComponentPtr component_ptr
|
|
);
|
|
ON_SubDRef m_subd_ref;
|
|
const ON_SubDEdge* m_e_first = nullptr;
|
|
const ON_SubDEdge* m_e_last = nullptr;
|
|
const ON_SubDEdge* m_e_current = nullptr;
|
|
unsigned int m_edge_index = 0;
|
|
unsigned int m_edge_count = 0;
|
|
ON_SubDComponentPtr m_component_ptr = ON_SubDComponentPtr::Null;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDFaceIterator
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDFaceIterator
|
|
{
|
|
public:
|
|
// The ON_SubD member function
|
|
// ON_SubDFaceIterator ON_SubD::FaceIterator()
|
|
// is the best way to get a face iterator from an ON_SubD.
|
|
ON_SubDFaceIterator(
|
|
const class ON_SubD& subd
|
|
);
|
|
|
|
// The ON_SubDRef member function
|
|
// ON_SubDFaceIterator ON_SubDRef::FaceIterator()
|
|
// is the best way to get a face iterator from an ON_SubDRef.
|
|
ON_SubDFaceIterator(
|
|
const class ON_SubDRef& subd_ref
|
|
);
|
|
|
|
// Construct and interator that iterates over the single face.
|
|
ON_SubDFaceIterator(
|
|
const class ON_SubD& subd,
|
|
const class ON_SubDFace& face
|
|
);
|
|
|
|
// Construct and interator that iterates over the single face.
|
|
ON_SubDFaceIterator(
|
|
const class ON_SubDRef& subd_ref,
|
|
const class ON_SubDFace& face
|
|
);
|
|
|
|
// Construct and interator that iterates over the faces of a vertex.
|
|
ON_SubDFaceIterator(
|
|
const class ON_SubD& subd,
|
|
const class ON_SubDVertex& vertex
|
|
);
|
|
|
|
// Construct and interator that iterates over the faces of a vertex.
|
|
ON_SubDFaceIterator(
|
|
const class ON_SubDRef& subd_ref,
|
|
const class ON_SubDVertex& vertex
|
|
);
|
|
|
|
// Construct and interator that iterates over the faces of an edge.
|
|
ON_SubDFaceIterator(
|
|
const class ON_SubD& subd,
|
|
const class ON_SubDEdge& edge
|
|
);
|
|
|
|
// Construct and interator that iterates over the faces of an edge.
|
|
ON_SubDFaceIterator(
|
|
const class ON_SubDRef& subd_ref,
|
|
const class ON_SubDEdge& edge
|
|
);
|
|
|
|
ON_SubDFaceIterator() = default;
|
|
ON_SubDFaceIterator(const ON_SubDFaceIterator&) = default;
|
|
ON_SubDFaceIterator& operator=(const ON_SubDFaceIterator&) = default;
|
|
|
|
#if defined(ON_HAS_RVALUEREF)
|
|
// rvalue copy constructor
|
|
ON_SubDFaceIterator( ON_SubDFaceIterator&& ) ON_NOEXCEPT;
|
|
// rvalue assignment operator
|
|
ON_SubDFaceIterator& operator=( ON_SubDFaceIterator&& );
|
|
#endif
|
|
|
|
/*
|
|
Returns:
|
|
The subD object for this iterator.
|
|
*/
|
|
const class ON_SubD& SubD() const
|
|
{
|
|
return m_subd_ref.SubD();
|
|
}
|
|
|
|
const class ON_SubDRef& SubDRef() const
|
|
{
|
|
return m_subd_ref;
|
|
}
|
|
|
|
/*
|
|
Description:
|
|
Returns the current face and increment the iterator.
|
|
Returns:
|
|
Current face.
|
|
Remarks:
|
|
operator++ and NextFace() behave differently.
|
|
*/
|
|
const class ON_SubDFace* operator++()
|
|
{
|
|
const class ON_SubDFace* f = m_face_current;
|
|
NextFace();
|
|
return f;
|
|
}
|
|
|
|
/*
|
|
Return:
|
|
Number of faces this iterator will iterate through.
|
|
*/
|
|
unsigned int FaceCount() const
|
|
{
|
|
return m_face_count;
|
|
}
|
|
|
|
/*
|
|
Return:
|
|
Interator index of the current face.
|
|
*/
|
|
unsigned int CurrentFaceIndex() const
|
|
{
|
|
return m_face_index;
|
|
}
|
|
|
|
|
|
/*
|
|
Description:
|
|
Set the iterator to the beginning of the face list.
|
|
Returns:
|
|
First face in the list.
|
|
*/
|
|
const class ON_SubDFace* FirstFace()
|
|
{
|
|
m_face_index = 0;
|
|
return (m_face_current = m_face_first);
|
|
}
|
|
|
|
/*
|
|
Description:
|
|
Returns the next face and incrments the iterator.
|
|
Returns:
|
|
Next face.
|
|
Remarks:
|
|
operator++ and NextFace() behave differently.
|
|
*/
|
|
const class ON_SubDFace* NextFace()
|
|
{
|
|
m_face_index++;
|
|
if (m_face_index < m_face_count)
|
|
{
|
|
if (0 == m_component_ptr.m_ptr)
|
|
{
|
|
if (nullptr != m_face_current)
|
|
m_face_current = m_face_current->m_next_face;
|
|
}
|
|
else
|
|
{
|
|
const ON_SubDVertex* vertex = m_component_ptr.Vertex();
|
|
if (nullptr != vertex)
|
|
{
|
|
m_face_current = vertex->Face(m_face_index);
|
|
}
|
|
else
|
|
{
|
|
const ON_SubDEdge* edge = m_component_ptr.Edge();
|
|
if (nullptr != edge)
|
|
m_face_current = edge->Face(m_face_index);
|
|
else
|
|
m_face_current = nullptr;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_face_index = m_face_count;
|
|
m_face_current = nullptr;
|
|
}
|
|
return m_face_current;
|
|
}
|
|
|
|
/*
|
|
Returns:
|
|
Current face;
|
|
*/
|
|
const class ON_SubDFace* CurrentFace() const
|
|
{
|
|
return m_face_current;
|
|
}
|
|
|
|
/*
|
|
Description:
|
|
Set the iterator to the end of the face list.
|
|
Returns:
|
|
Last face in the list.
|
|
*/
|
|
const class ON_SubDFace* LastFace()
|
|
{
|
|
m_face_index = (m_face_count > 0) ? (m_face_count - 1) : 0;
|
|
return (m_face_current = m_face_last);
|
|
}
|
|
|
|
unsigned int LimitSurfaceMeshFragmentCount(
|
|
ON_SubD::FacetType facet_type
|
|
) const;
|
|
|
|
private:
|
|
void Internal_Init(
|
|
const ON_SubDRef& subd_ref,
|
|
unsigned int face_count,
|
|
const ON_SubDFace* first,
|
|
const ON_SubDFace* last,
|
|
ON_SubDComponentPtr component_ptr
|
|
);
|
|
ON_SubDRef m_subd_ref;
|
|
const ON_SubDFace* m_face_first = nullptr;
|
|
const ON_SubDFace* m_face_last = nullptr;
|
|
const ON_SubDFace* m_face_current = nullptr;
|
|
unsigned int m_face_index = 0;
|
|
unsigned int m_face_count = 0;
|
|
ON_SubDComponentPtr m_component_ptr = ON_SubDComponentPtr::Null;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDComponentIterator
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDComponentIterator
|
|
{
|
|
public:
|
|
static const ON_SubDComponentIterator Empty;
|
|
|
|
// The ON_SubD member function
|
|
// ON_SubDComponentIterator ON_SubD::ComponentIterator(subd_level_index)
|
|
// is the best way to get a component iterator for a subd level.
|
|
ON_SubDComponentIterator(
|
|
const class ON_SubD& subd
|
|
);
|
|
ON_SubDComponentIterator(
|
|
const class ON_SubDRef& subd_ref
|
|
);
|
|
|
|
ON_SubDComponentIterator() = default;
|
|
ON_SubDComponentIterator(const ON_SubDComponentIterator&) = default;
|
|
ON_SubDComponentIterator& operator=(const ON_SubDComponentIterator&) = default;
|
|
|
|
#if defined(ON_HAS_RVALUEREF)
|
|
// rvalue copy constructor
|
|
ON_SubDComponentIterator( ON_SubDComponentIterator&& ) ON_NOEXCEPT;
|
|
// rvalue assignment operator
|
|
ON_SubDComponentIterator& operator=( ON_SubDComponentIterator&& );
|
|
#endif
|
|
|
|
/*
|
|
Returns:
|
|
The subD object for this iterator.
|
|
*/
|
|
const class ON_SubD& SubD() const
|
|
{
|
|
return m_subd_ref.SubD();
|
|
}
|
|
|
|
const class ON_SubDRef& SubDRef() const
|
|
{
|
|
return m_subd_ref;
|
|
}
|
|
|
|
/*
|
|
Returns:
|
|
The subD level for this iterator.
|
|
*/
|
|
unsigned int SubDLevel() const
|
|
{
|
|
return m_subd_level;
|
|
}
|
|
|
|
/*
|
|
Description:
|
|
Returns the current component and increment the iterator.
|
|
Returns:
|
|
Current component.
|
|
Remarks:
|
|
operator++ and NextComponent() behave differently.
|
|
*/
|
|
const class ON_SubDComponentPtr operator++()
|
|
{
|
|
const class ON_SubDComponentPtr cptr = m_cptr_current;
|
|
NextComponent();
|
|
return cptr;
|
|
}
|
|
|
|
/*
|
|
Description:
|
|
Set the iterator to the beginning of the component list.
|
|
Returns:
|
|
First component in the list.
|
|
*/
|
|
const class ON_SubDComponentPtr FirstComponent();
|
|
|
|
/*
|
|
Description:
|
|
Returns the next component and incrments the iterator.
|
|
Returns:
|
|
Next component.
|
|
Remarks:
|
|
operator++ and NextComponent() behave differently.
|
|
*/
|
|
const class ON_SubDComponentPtr NextComponent();
|
|
|
|
/*
|
|
Returns:
|
|
Current component;
|
|
*/
|
|
const class ON_SubDComponentPtr CurrentComponent() const
|
|
{
|
|
return m_cptr_current;
|
|
}
|
|
|
|
/*
|
|
Description:
|
|
Set the iterator to the end of the component list.
|
|
Returns:
|
|
Last component in the list.
|
|
*/
|
|
const class ON_SubDComponentPtr LastComponent();
|
|
|
|
private:
|
|
ON_SubDRef m_subd_ref;
|
|
unsigned int m_subd_level = 0;
|
|
const ON_SubDVertex* m_vertex_first = nullptr;
|
|
const ON_SubDVertex* m_vertex_last = nullptr;
|
|
const ON_SubDEdge* m_edge_first = nullptr;
|
|
const ON_SubDEdge* m_edge_last = nullptr;
|
|
const ON_SubDFace* m_face_first = nullptr;
|
|
const ON_SubDFace* m_face_last = nullptr;
|
|
ON_SubDComponentPtr m_cptr_current = ON_SubDComponentPtr::Null;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDSectorIterator
|
|
//
|
|
|
|
class ON_SUBD_CLASS ON_SubDSectorIterator
|
|
{
|
|
public:
|
|
static const ON_SubDSectorIterator Empty;
|
|
|
|
ON_SubDSectorIterator() = default;
|
|
~ON_SubDSectorIterator() = default;
|
|
ON_SubDSectorIterator(const ON_SubDSectorIterator&) = default;
|
|
ON_SubDSectorIterator& operator=(const ON_SubDSectorIterator&) = default;
|
|
|
|
/*
|
|
Parameters:
|
|
center_vertex - [in]
|
|
The vertex on initial_face that will be iterated around.
|
|
center_vertex->Face(0) is used to select the sector.
|
|
Returns:
|
|
If input is valid, a pointer to the center vertex is returned.
|
|
Otherwise, nullptr is returned.
|
|
*/
|
|
const ON_SubDVertex* Initialize(
|
|
const ON_SubDVertex* center_vertex
|
|
);
|
|
|
|
/*
|
|
Parameters:
|
|
initial_face - [in]
|
|
iterator_orientation - [in]
|
|
0: (more common)
|
|
"next" means counter-clockwise with respect to the orientation of initial_face
|
|
1: (less common)
|
|
"next" means clockwise with respect to the orientation of initial_face
|
|
center_vertex - [in]
|
|
The vertex on initial_face that will be iterated around.
|
|
Returns:
|
|
If input is valid, a pointer to the center vertex is returned.
|
|
Otherwise, nullptr is returned.
|
|
*/
|
|
const ON_SubDVertex* Initialize(
|
|
const ON_SubDFace* initial_face,
|
|
ON__UINT_PTR iterator_orientation,
|
|
const ON_SubDVertex* center_vertex
|
|
);
|
|
|
|
/*
|
|
Parameters:
|
|
initial_face - [in]
|
|
iterator_orientation - [in]
|
|
0: (more common)
|
|
"next" means counter-clockwise with respect to the orientation of initial_face
|
|
1: (less common)
|
|
"next" means clockwise with respect to the orientation of initial_face
|
|
face_vertex_index - [in]
|
|
initial_face->Vertex(face_vertex_index) is the center vertex
|
|
that will be iterated around.
|
|
Returns:
|
|
If input is valid, a pointer to the center vertex is returned.
|
|
Otherwise, nullptr is returned.
|
|
*/
|
|
const ON_SubDVertex* Initialize(
|
|
const ON_SubDFace* initial_face,
|
|
ON__UINT_PTR iterator_orientation,
|
|
unsigned int face_vertex_index
|
|
);
|
|
|
|
bool InitializeToCurrentFace();
|
|
|
|
void Initialize();
|
|
|
|
/*
|
|
Description:
|
|
The current ring index reports the total increment from the
|
|
start to the current state. It can be positive or negative.
|
|
*/
|
|
int CurrentRingIndex() const;
|
|
|
|
const ON_SubDVertex* CenterVertex() const;
|
|
|
|
const ON_SubDFace* InitialFace() const;
|
|
|
|
unsigned int InitialFaceCenterVertexIndex() const;
|
|
|
|
const ON_SubDFace* CurrentFace() const;
|
|
|
|
unsigned int CurrentFaceDirection() const;
|
|
|
|
ON_SubDFacePtr CurrentFacePtr() const;
|
|
|
|
unsigned int CurrentFaceCenterVertexIndex() const;
|
|
|
|
|
|
/*
|
|
Parameters:
|
|
face_side_index - [in]
|
|
0: Return the edge entering the center vertex.
|
|
1: Return the edge leaving the center vertex.
|
|
Returns:
|
|
The requested edge.
|
|
*/
|
|
ON_SubDEdgePtr CurrentEdgePtr(
|
|
unsigned int face_side_index
|
|
) const;
|
|
|
|
/*
|
|
Description:
|
|
|
|
CurrentEdge(1)
|
|
|
|
|
|
|
|
NextFace() | CurrentFace()
|
|
|
|
|
|
|
|
*------------- CurrentEdge(0)
|
|
PrevFace()
|
|
|
|
The asterisk (*) marks the center vertex.
|
|
The diagram is With respect to the initial iterator orientation.
|
|
|
|
Parameters:
|
|
face_side_index - [in]
|
|
CurrentEdge(0) = edge on the clockwise (PrevFace) side of the current face
|
|
CurrentEdge(1) = edge on the counterclockwise (NextFace) side of the current face
|
|
The directions "counterclockwise" and "clockwise" are with respect to the
|
|
initial iterator orientation.
|
|
Returns:
|
|
The requested edge or nullptr if the iterator is not initialized,
|
|
has terminated, or is not valid.
|
|
*/
|
|
const ON_SubDEdge* CurrentEdge(
|
|
unsigned int face_side_index
|
|
) const;
|
|
|
|
ON__UINT_PTR CurrentEdgeDirection(
|
|
unsigned int face_side_index
|
|
) const;
|
|
|
|
/*
|
|
Returns:
|
|
The vertex on CurrentEdge(face_side_index) that is opposite
|
|
the central vertex.
|
|
*/
|
|
const ON_SubDVertex* CurrentEdgeRingVertex(
|
|
unsigned int face_side_index
|
|
) const;
|
|
|
|
/*
|
|
Description:
|
|
Advance the "current" face to the "next" face in the ring
|
|
by "hopping across" CurrentEdge(1).
|
|
|
|
If the current face is not reversed (1 == CurrentFaceDirection),
|
|
then this rotation is counter-clockwise with respect to
|
|
the current face's orientation.
|
|
|
|
If the current face is reversed (1 == CurrentFaceDirection),
|
|
then this rotation is clockwise with respect to
|
|
the current face's orientation.
|
|
Parameters:
|
|
bStopAtCrease - [in]
|
|
If true and CurrentEdge(1) is tagged ON_SubD:EdgeTag::crease,
|
|
iteration terminates.
|
|
Returns:
|
|
When the input CurrentEdge(1) has exactly two faces and
|
|
is not tagged as a crease or bStopAtCrease is false, the
|
|
face on the other side of CurrentEdge(1) becomes the new
|
|
current face and a pointer to this face is returned.
|
|
Remarks:
|
|
Identical to calling IncrementFace(+1,bStopAtCrease);
|
|
*/
|
|
const ON_SubDFace* NextFace(
|
|
bool bStopAtCrease
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Advance the "current" face to the "previous" face in the ring
|
|
by "hopping across" CurrentEdge(0).
|
|
|
|
If the current face is not reversed (0 == CurrentFaceDirection),
|
|
then this rotation is clockwise with respect to
|
|
the current face's orientation.
|
|
|
|
If the current face is reversed (1 == CurrentFaceDirection),
|
|
then this rotation is counter-clockwise with respect to
|
|
the current face's orientation.
|
|
Parameters:
|
|
bStopAtCrease - [in]
|
|
If true and CurrentEdge(0) is tagged ON_SubD:EdgeTag::crease,
|
|
iteration terminates.
|
|
Returns:
|
|
When the input CurrentEdge(0) has exactly two faces and
|
|
is not tagged as a crease or bStopAtCrease is false, the
|
|
face on the other side of CurrentEdge(0) becomes the new
|
|
current face and a pointer to this face is returned.
|
|
In all other cases, nullptr is returned
|
|
Remarks:
|
|
Identical to calling IncrementFace(-1,bStopAtCrease);
|
|
*/
|
|
const ON_SubDFace* PrevFace(
|
|
bool bStopAtCrease
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Advance the "current" face by "hopping across" the edge
|
|
CurrentEdge((increment_direction>0) ? 1 : 0).
|
|
|
|
If the current face is not reversed (0 == CurrentFaceDirection),
|
|
then increment_direction>0 rotates counter-clockwise with respect to
|
|
the current face's orientation.
|
|
|
|
If the current face is reversed (1 == CurrentFaceDirection),
|
|
then increment_direction>0 rotates clockwise with respect to
|
|
the current face's orientation.
|
|
Parameters:
|
|
increment_direction - [in]
|
|
> 0 advance the "current" face to next face
|
|
<= 0 advance the "current" face to previous face
|
|
bStopAtCrease - [in]
|
|
If true and the input value of CurrentEdge((increment_direction>0) ? 1 : 0)
|
|
is tagged as ON_SubD:EdgeTag::crease, iteration terminates.
|
|
When iteration terminates at a crease,
|
|
CurrentFace() becomes nullptr
|
|
CurrentEdge((increment_direction>0) ? 1 : 0) becomes nullptr
|
|
CurrentEdge((increment_direction>0) ? 0 : 1) points at the crease
|
|
and nullptr returned.
|
|
Returns:
|
|
nullptr if iteration terminates.
|
|
*/
|
|
const ON_SubDFace* IncrementFace(
|
|
int increment_direction,
|
|
bool bStopAtCrease
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Increment the iterator until it reaches a face with
|
|
a crease
|
|
Parameters:
|
|
increment_direction - [in]
|
|
> 0 advance next until CurrentEdge(1) is a crease.
|
|
<= 0 advance previous until CurrentEdge(0) is a crease.
|
|
Returns:
|
|
nullptr - the sector has no creases.
|
|
not nullptr - incremented to a crease
|
|
*/
|
|
const ON_SubDFace* IncrementToCrease(
|
|
int increment_direction
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Reset iterator to initial face.
|
|
*/
|
|
const ON_SubDFace* FirstFace();
|
|
|
|
bool IsValid() const;
|
|
|
|
private:
|
|
const ON_SubDVertex* m_center_vertex = nullptr;
|
|
const ON_SubDFace* m_initial_face = nullptr;
|
|
const ON_SubDFace* m_current_face = nullptr;
|
|
|
|
// m_current_eptr[0].Edge() = "prev" side edge
|
|
// m_current_eptr[1].Edge() = "next" side edge
|
|
// When m_current_eptr[i].Edge() is not null,
|
|
// center vertex = m_current_eptr[i].Edge()->m_vertex[m_current_eptr[i].Direction()]
|
|
ON_SubDEdgePtr m_current_eptr[2]; // default = { ON_SubDEdgePtr::Null, ON_SubDEdgePtr::Null };
|
|
|
|
unsigned int m_initial_fvi = 0;
|
|
unsigned int m_current_fvi = 0;
|
|
unsigned int m_current_fei[2]; // default = { 0, 0 }; // "prev" and "next"
|
|
|
|
// m_initial_face_dir
|
|
// 0: "next" means clockwise with respect to the initial face's orientation.
|
|
// 1: "next" means counter-clockwise with respect to the initial face's orientation.
|
|
unsigned int m_initial_face_dir = 0;
|
|
|
|
// m_current_face_dir
|
|
// 0: "next" means clockwise with respect to the initial face's orientation.
|
|
// 1: "next" means counter-clockwise with respect to the initial face's orientation.
|
|
// When the subd faces around the center vertex are consistently oriented,
|
|
// m_current_face_dir is always equal to m_initial_face_dir.
|
|
unsigned int m_current_face_dir = 0;
|
|
|
|
int m_current_ring_index = 0;
|
|
};
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDFaceEdgeIterator
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDFaceEdgeIterator
|
|
{
|
|
public:
|
|
ON_SubDFaceEdgeIterator();
|
|
|
|
/*
|
|
Description:
|
|
Construct an iterator for going around the edges on a face.
|
|
Parameters:
|
|
face - [in]
|
|
first_edge - [in]
|
|
starting edge for the iterator or nullptr to start at face->Edge(0).
|
|
*/
|
|
ON_SubDFaceEdgeIterator(
|
|
const ON_SubDFace* face
|
|
);
|
|
|
|
ON_SubDFaceEdgeIterator(
|
|
const ON_SubDFace* face,
|
|
const ON_SubDEdge* first_edge
|
|
);
|
|
|
|
|
|
/*
|
|
Description:
|
|
Initialize an iterator for going around the edges on a face.
|
|
Parameters:
|
|
face - [in]
|
|
first_edge - [in]
|
|
starting edge for the iterator or nullptr to start at face->Edge(0).
|
|
*/
|
|
unsigned int Initialize(
|
|
const ON_SubDFace* face
|
|
);
|
|
|
|
unsigned int Initialize(
|
|
const ON_SubDFace* face,
|
|
const ON_SubDEdge* first_edge
|
|
);
|
|
|
|
unsigned int EdgeCount() const;
|
|
|
|
/*
|
|
Returns:
|
|
Resets the iterator and returns the first edge.
|
|
*/
|
|
const ON_SubDEdge* FirstEdge();
|
|
|
|
/*
|
|
Description:
|
|
Increments the iterator and returns the edge.
|
|
*/
|
|
const ON_SubDEdge* NextEdge();
|
|
|
|
/*
|
|
Description:
|
|
Decrements the iterator and returns the edge.
|
|
*/
|
|
const ON_SubDEdge* PrevEdge();
|
|
|
|
/*
|
|
Returns:
|
|
Current edge.
|
|
*/
|
|
const ON_SubDEdge* CurrentEdge() const;
|
|
|
|
unsigned int FirstEdgeIndex() const;
|
|
|
|
unsigned int CurrentEdgeIndex() const;
|
|
|
|
private:
|
|
const ON_SubDFace* m_face;
|
|
unsigned int m_edge_count;
|
|
unsigned int m_edge_index0;
|
|
unsigned int m_edge_index;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDFromMeshOptions
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDFromMeshOptions
|
|
{
|
|
public:
|
|
|
|
// Default construction is identical to ON_SubDFromMeshOptions::Smooth.
|
|
ON_SubDFromMeshOptions() = default;
|
|
~ON_SubDFromMeshOptions() = default;
|
|
ON_SubDFromMeshOptions(const ON_SubDFromMeshOptions&) = default;
|
|
ON_SubDFromMeshOptions& operator=(const ON_SubDFromMeshOptions&) = default;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Crease options
|
|
//
|
|
|
|
// No interior creases and no corners.
|
|
static const ON_SubDFromMeshOptions Smooth;
|
|
|
|
// Create an interior sub-D crease along coincident input mesh edges
|
|
// where the vertex normal directions at one end differ by at
|
|
// least 30 degrees.
|
|
static const ON_SubDFromMeshOptions InteriorCreaseAtMeshCrease;
|
|
|
|
// Create an interior sub-D crease along all coincident input mesh edges.
|
|
static const ON_SubDFromMeshOptions InteriorCreaseAtMeshEdge;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Custom interior crease options
|
|
//
|
|
#pragma region RH_C_SHARED_ENUM [SubD::InteriorCreaseOption] [Rhino.Geometry.SubD.InteriorCreaseOption] [nested:byte]
|
|
///<summary>
|
|
///Defines how interior creases are treated.
|
|
///</summary>
|
|
enum class InteriorCreaseOption : unsigned char
|
|
{
|
|
///<summary>The interior creases option is not defined.</summary>
|
|
Unset = 0,
|
|
|
|
///<summary>No interior creases.</summary>
|
|
None = 1,
|
|
|
|
///<summary>An interior subd crease will appear along coincident
|
|
///mesh edges where the angle between coindident vertex
|
|
///normals >= MinimumCreaseAngleRadians().</summary>
|
|
AtMeshCrease = 2,
|
|
|
|
///<summary>An interior subd crease will appear all coincident mesh edges.
|
|
///Input mesh vertex normals are ignored.</summary>
|
|
AtMeshEdge = 3
|
|
};
|
|
#pragma endregion
|
|
|
|
static ON_SubDFromMeshOptions::InteriorCreaseOption InteriorCreaseOptionFromUnsigned(
|
|
unsigned int interior_crease_option_as_unsigned
|
|
);
|
|
|
|
/*
|
|
Parameters:
|
|
interior_crease_option - [in]
|
|
*/
|
|
void SetInteriorCreaseOption(
|
|
ON_SubDFromMeshOptions::InteriorCreaseOption interior_crease_option
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
The interior crease option.
|
|
*/
|
|
ON_SubDFromMeshOptions::InteriorCreaseOption InteriorCreaseTest() const;
|
|
|
|
|
|
/*
|
|
Description:
|
|
When the interior crease option is
|
|
ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshCreases,
|
|
the value of MinimumCreaseAngleRadians() determines which
|
|
coincident input mesh edges generate sub-D creases.
|
|
|
|
If the input mesh has vertex normals, and the angle between
|
|
vertex normals is > MinimumCreaseAngleRadians() at an end
|
|
of a coincident input mesh edge, the the correspondeing sub-D
|
|
edge will be a crease.
|
|
|
|
Parameters:
|
|
minimum_crease_angle_radians - [in]
|
|
>= 0.0 and < ON_PI
|
|
*/
|
|
void SetMinimumCreaseAngleRadians(
|
|
double minimum_crease_angle_radians
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
When the interior crease option is
|
|
ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshCreases,
|
|
the value of MinimumCreaseAngleRadians() determines which
|
|
coincident input mesh edges generate sub-D creases.
|
|
|
|
If the input mesh has vertex normals, and the angle between
|
|
vertex normals is > MinimumCreaseAngleRadians() at an end
|
|
of a coincident input mesh edge, the the correspondeing sub-D
|
|
edge will be a crease.
|
|
Returns:
|
|
Current value of
|
|
*/
|
|
double MinimumCreaseAngleRadians() const;
|
|
|
|
/*
|
|
Description:
|
|
Copy all interior crease option settings from source_options to this.
|
|
Parameters:
|
|
source_options - [in]
|
|
Returns:
|
|
The currently selected interior crease option.
|
|
*/
|
|
ON_SubDFromMeshOptions::InteriorCreaseOption CopyInteriorCreaseTest(
|
|
ON_SubDFromMeshOptions source_options
|
|
);
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Convex corner options
|
|
//
|
|
|
|
// Look for convex corners at sub-D vertices with 2 edges
|
|
// that have an included angle <= 90 degrees.
|
|
static const ON_SubDFromMeshOptions ConvexCornerAtMeshCorner;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Custom convex corner options
|
|
//
|
|
#pragma region RH_C_SHARED_ENUM [SubD::ConvexCornerOption] [Rhino.Geometry.SubD.ConvexCornerOption] [nested:byte]
|
|
///<summary>
|
|
///Defines how convex corners are treated.
|
|
///</summary>
|
|
enum class ConvexCornerOption : unsigned char
|
|
{
|
|
///<summary>The option is not set.</summary>
|
|
Unset = 0,
|
|
|
|
///<summary>No convex coners.</summary>
|
|
None = 1,
|
|
|
|
///<summary>A convext subd corner will appear at input mesh/ boundary vertices
|
|
/// where the corner angle <= MaximumConvexCornerAngleRadians() and
|
|
/// the number of edges the end at the vertex is <= MaximumConvexCornerEdgeCount().
|
|
///</summary>
|
|
AtMeshCorner = 2
|
|
};
|
|
#pragma endregion
|
|
|
|
static ON_SubDFromMeshOptions::ConvexCornerOption ConvexCornerOptionFromUnsigned(
|
|
unsigned int convex_corner_option_as_unsigned
|
|
);
|
|
|
|
/*
|
|
Parameters:
|
|
convex_corner_option - [in]
|
|
*/
|
|
void SetConvexCornerOption(
|
|
ON_SubDFromMeshOptions::ConvexCornerOption convex_corner_option
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
The currently selected convex corner option.
|
|
*/
|
|
ON_SubDFromMeshOptions::ConvexCornerOption ConvexCornerTest() const;
|
|
|
|
/*
|
|
Description:
|
|
If ConvexCornerTest() ConvexCornerOption::at_mesh_corner, then an
|
|
input mesh boundary vertex becomes a sub-D corner when the number of
|
|
edges that end at the vertex is <= MaximumConvexCornerEdgeCount() edges
|
|
and the corner angle is <= MaximumConvexCornerAngleRadians().
|
|
Parameters:
|
|
maximum_convex_corner_edge_count - [in]
|
|
*/
|
|
void SetMaximumConvexCornerEdgeCount(
|
|
unsigned int maximum_convex_corner_edge_count
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
If ConvexCornerTest() ConvexCornerOption::at_mesh_corner, then an
|
|
input mesh boundary vertex becomes a sub-D corner when the number of
|
|
edges that end at the vertex is <= MaximumConvexCornerEdgeCount() edges
|
|
and the corner angle is <= MaximumConvexCornerAngleRadians().
|
|
Returns:
|
|
The maximum number of edges at a convex corner vertex.
|
|
*/
|
|
unsigned int MaximumConvexCornerEdgeCount() const;
|
|
|
|
/*
|
|
Description:
|
|
If ConvexCornerTest() ConvexCornerOption::at_mesh_corner, then an
|
|
input mesh boundary vertex becomes a sub-D corner when the number of
|
|
edges that end at the vertex is <= MaximumConvexCornerEdgeCount() edges
|
|
and the corner angle is <= MaximumConvexCornerAngleRadians().
|
|
Parameters:
|
|
maximum_convex_corner_angle_radians - [in]
|
|
> 0.0 and < ON_PI
|
|
*/
|
|
void SetMaximumConvexCornerAngleRadians(
|
|
double maximum_convex_corner_angle_radians
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
If ConvexCornerTest() ConvexCornerOption::at_mesh_corner, then an
|
|
input mesh boundary vertex becomes a sub-D corner when the number of
|
|
edges that end at the vertex is <= MaximumConvexCornerEdgeCount() edges
|
|
and the corner angle is <= MaximumConvexCornerAngleRadians().
|
|
Returns:
|
|
The maximum corner angle.
|
|
*/
|
|
double MaximumConvexCornerAngleRadians() const;
|
|
|
|
/*
|
|
Description:
|
|
Copy all convex corner option settings from source_options to this.
|
|
Parameters:
|
|
source_options - [in]
|
|
Returns:
|
|
The currently selected convex corner option.
|
|
*/
|
|
ON_SubDFromMeshOptions::ConvexCornerOption CopyConvexCornerTest(
|
|
ON_SubDFromMeshOptions source_parameters
|
|
);
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Sub-D type option
|
|
//
|
|
ON_SubD::SubDType SubDType() const;
|
|
|
|
void SetSubDType(
|
|
ON_SubD::SubDType subd_type
|
|
);
|
|
|
|
private:
|
|
ON_SubD::SubDType m_subd_type = ON_SubD::SubDType::Unset;
|
|
unsigned char m_reserved1 = 0;
|
|
unsigned short m_reserved2 = 0;
|
|
|
|
ON_SubDFromMeshOptions::InteriorCreaseOption m_interior_crease_option = ON_SubDFromMeshOptions::InteriorCreaseOption::None;
|
|
ON_SubDFromMeshOptions::ConvexCornerOption m_convex_corner_option = ON_SubDFromMeshOptions::ConvexCornerOption::None;
|
|
unsigned short m_maximum_convex_corner_edge_count = 2U;
|
|
|
|
double m_minimum_crease_angle_radians = ON_PI/6.0; // 30 degrees in radians
|
|
double m_maximum_convex_corner_angle_radians = 0.501*ON_PI; // 90 degrees (+ a smidge) in radians
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDComponentRef
|
|
//
|
|
// Used when an ON_SubD vertex, edge or face needs to be sent around as
|
|
// a piece of ON_Geometry.
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDComponentRef : public ON_Geometry
|
|
{
|
|
ON_OBJECT_DECLARE(ON_SubDComponentRef);
|
|
public:
|
|
static const ON_SubDComponentRef Empty;
|
|
|
|
ON_SubDComponentRef() = default;
|
|
~ON_SubDComponentRef() = default;
|
|
ON_SubDComponentRef(const ON_SubDComponentRef&) ON_NOEXCEPT;
|
|
ON_SubDComponentRef& operator=(const ON_SubDComponentRef&);
|
|
|
|
#if defined(ON_HAS_RVALUEREF)
|
|
// rvalue copy constructor
|
|
ON_SubDComponentRef( ON_SubDComponentRef&& ) ON_NOEXCEPT;
|
|
|
|
// The rvalue assignment operator calls ON_Object::operator=(ON_Object&&)
|
|
// which could throw exceptions. See the implementation of
|
|
// ON_Object::operator=(ON_Object&&) for details.
|
|
ON_SubDComponentRef& operator=( ON_SubDComponentRef&& );
|
|
#endif
|
|
|
|
/*
|
|
Parameters:
|
|
subd_ref - [in]
|
|
component_index - [in]
|
|
bLimitSurface - [in]
|
|
true - the reference is to the limit surface location
|
|
false - the reference is to the control net location
|
|
*/
|
|
static ON_SubDComponentRef Create(
|
|
const ON_SubDRef& subd_ref,
|
|
ON_COMPONENT_INDEX component_index,
|
|
ON_SubDComponentLocation component_location
|
|
);
|
|
|
|
static ON_SubDComponentRef Create(
|
|
const ON_SubDRef& subd_ref,
|
|
ON_SubDComponentPtr component_ptr,
|
|
ON_SubDComponentLocation component_location
|
|
);
|
|
|
|
static ON_SubDComponentRef Create(
|
|
const ON_SubDRef& subd_ref,
|
|
const class ON_SubDVertex* vertex,
|
|
ON_SubDComponentLocation component_location
|
|
);
|
|
|
|
static ON_SubDComponentRef Create(
|
|
const ON_SubDRef& subd_ref,
|
|
const class ON_SubDEdge* edge,
|
|
ON_SubDComponentLocation component_location
|
|
);
|
|
|
|
static ON_SubDComponentRef Create(
|
|
const ON_SubDRef& subd_ref,
|
|
const class ON_SubDFace* face,
|
|
ON_SubDComponentLocation component_location
|
|
);
|
|
|
|
void Clear();
|
|
|
|
ON_SubDRef SubDRef() const;
|
|
|
|
const class ON_SubD& SubD() const;
|
|
|
|
ON_COMPONENT_INDEX ComponentIndex() const override;
|
|
|
|
ON_SubDComponentPtr ComponentPtr() const;
|
|
|
|
ON_SubDComponentLocation ComponentLocation() const;
|
|
|
|
const class ON_SubDVertex* Vertex() const;
|
|
|
|
const class ON_SubDEdge* Edge() const;
|
|
|
|
const class ON_SubDFace* Face() const;
|
|
|
|
private:
|
|
ON_SubDRef m_subd_ref;
|
|
ON_SubDComponentPtr m_component_ptr = ON_SubDComponentPtr::Null;
|
|
ON_COMPONENT_INDEX m_component_index = ON_COMPONENT_INDEX::UnsetComponentIndex;
|
|
ON_SubDComponentLocation m_component_location = ON_SubDComponentLocation::Unset;
|
|
|
|
public:
|
|
// overrides of virtual ON_Object functions
|
|
bool IsValid( class ON_TextLog* text_log = nullptr ) const override;
|
|
void Dump( ON_TextLog& ) const override;
|
|
unsigned int SizeOf() const override;
|
|
ON::object_type ObjectType() const override;
|
|
|
|
// overrides of virtual ON_Geometry functions
|
|
int Dimension() const override;
|
|
|
|
// virtual ON_Geometry GetBBox override
|
|
bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDComponentPoint
|
|
//
|
|
// Used in selection tests to return a point and parameters on a component.
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDComponentPoint
|
|
{
|
|
public:
|
|
static const ON_SubDComponentPoint Unset;
|
|
|
|
ON_SubDComponentPoint() = default;
|
|
~ON_SubDComponentPoint() = default;
|
|
ON_SubDComponentPoint(const ON_SubDComponentPoint&) = default;
|
|
ON_SubDComponentPoint& operator=(const ON_SubDComponentPoint&) = default;
|
|
|
|
/*
|
|
Description:
|
|
Compares the m_component_ptr values. This function is useful for sorting
|
|
arrays of ON_SubDComponentPoint values remove duplicates.
|
|
*/
|
|
static int CompareComponentPtr(
|
|
const ON_SubDComponentPoint* a,
|
|
const ON_SubDComponentPoint* b
|
|
);
|
|
|
|
// m_component_ptr will be face, edge or vertex
|
|
ON_SubDComponentPtr m_component_ptr = ON_SubDComponentPtr::Null;
|
|
|
|
//// If the point is on a a face that does not have the ordinary number of
|
|
//// edges for the subdivision type, then m_face_corner_index identifies the
|
|
//// subfragment corner.
|
|
//unsigned int m_face_corner_index = ON_UNSET_UINT_INDEX;
|
|
|
|
//// If m_level_index is ON_UNSET_UINT_INDEX, the point is on the limit surface.
|
|
//// Otherwise the point is on the control net at the specified level.
|
|
//unsigned int m_level_index = ON_UNSET_UINT_INDEX;
|
|
|
|
ON_PickPoint m_pick_point = ON_PickPoint::Unset;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDMatrix
|
|
//
|
|
class ON_SUBD_CLASS ON_SubDMatrix
|
|
{
|
|
public:
|
|
ON_SubDMatrix() = default;
|
|
|
|
static const ON_SubDMatrix Empty;
|
|
|
|
/*
|
|
Description:
|
|
Precise evaluation of cos(a) and cos(a) where a = i/n pi.
|
|
These values are required for high qualitiy limit surface evaluation.
|
|
Parameters:
|
|
j - [in]
|
|
n - [in]
|
|
cos_theta - [out]
|
|
cos(j/n pi)
|
|
sin_theta - [out]
|
|
sin(j/n pi)
|
|
*/
|
|
static bool EvaluateCosAndSin(
|
|
unsigned int j,
|
|
unsigned int n,
|
|
double* cos_theta,
|
|
double* sin_theta
|
|
);
|
|
|
|
bool IsValid() const;
|
|
|
|
bool IsValidPointRing(
|
|
size_t point_ring_count,
|
|
size_t point_ring_stride,
|
|
const double* point_ring
|
|
) const;
|
|
|
|
bool EvaluateSubdivisionPoint(
|
|
unsigned int component_index,
|
|
size_t point_ring_count,
|
|
size_t point_ring_stride,
|
|
const double* point_ring,
|
|
double subd_point[3]
|
|
) const;
|
|
|
|
bool EvaluateLimitPoint(
|
|
size_t point_ring_count,
|
|
size_t point_ring_stride,
|
|
const double* point_ring,
|
|
double limit_point[3],
|
|
double limit_tangent1[3],
|
|
double limit_tangent2[3],
|
|
double limit_normal[3]
|
|
) const;
|
|
|
|
bool EvaluateLimitPoint(
|
|
size_t point_ring_count,
|
|
size_t point_ring_stride,
|
|
const double* point_ring,
|
|
class ON_SubDSectorLimitPoint& limit_point
|
|
) const;
|
|
|
|
/*
|
|
Description:
|
|
Get the subdivision matrix information for the case specified
|
|
by the input parameters. This information is retrieved from
|
|
a cache. In some cases, it will be calculated the first time
|
|
it is needed.
|
|
Parameters:
|
|
facet_type - [in]
|
|
vertex_tag - [in]
|
|
valence - [in]
|
|
The input parameters identify the subdivision case.
|
|
Remarks:
|
|
Every member function of ON_SubDMatrix, including this one
|
|
is thread safe.
|
|
*/
|
|
static const ON_SubDMatrix& FromCache(
|
|
ON_SubDSectorType sector_type
|
|
);
|
|
|
|
ON_SubDSectorType m_sector_type;
|
|
|
|
unsigned int m_R = 0; // the matrix m_S is m_R x m_R (m_R = m_sector_type.PointRingCount())
|
|
|
|
// The term "standard vertex ring points" is used below.
|
|
//
|
|
// If "C" is an interior vertex (m_vertex_tag is smooth or dart),
|
|
// (E[0], ...., E[N-1]) is a radially sorted list of its edges,
|
|
// (F[0], ..., F[N-1]) is a radially sorted list of its faces,
|
|
// and (P[0], ..., P[N-1]) is a list of the edge vertices opposite C,
|
|
// E0type = smooth for a smooth vertex and crease for a dart vertex,
|
|
// then C is "standard" if E[0] has type E0type, every other
|
|
// edge E[i] is smooth, every outer vertex/ P[i] is smooth, and every
|
|
// face F[i] has the stadard facet type (tri or quad) for the subdivision
|
|
// algorithm.
|
|
//
|
|
// If If "C" is a boundary vertex (m_vertex_tag is crease or corner), the conditions
|
|
// listed above are satisified except
|
|
// E[0] and E[N-1] are tagged as crease edges,
|
|
// P[0] and P[N-1] are tagged as crease vertices (NOT corners),
|
|
// and there are N-2 faces,
|
|
// then "C" is a standard boundary vertex.
|
|
//
|
|
// If the facet type is triangle and C is a standard interior or boundary vertex,
|
|
// then the "standard vertex ring" is the list of N+1 points
|
|
// (C, P[0], ...., P[N-1]).
|
|
//
|
|
// If the facet type is quad, and C is a standard interior vertex,
|
|
// then the "standard vertex ring" is the list of 2*N+1 points
|
|
// (C, P[0], Q[0], ...., P[N-1], Q[N-1]), where Q[I] is the average of the
|
|
// four corners of the quad F[i].
|
|
//
|
|
// If the facet type is quad, and C is a standard boundary vertex,
|
|
// then the "standard vertex ring" is the list of 2*N points
|
|
// (C, P[0], Q[0], ...., P[N-1]).
|
|
|
|
// m_S = R x R subdivision matrix
|
|
// If (vertexR[0], ..., vertexR[R-1]) is a list of standard vertex ring points,
|
|
// then then the location of the subdivided ring points
|
|
// (vertexR1[0], ..., vertexR1[R-1]) can be calculated from m_S.
|
|
// vertexR1[i] = m_S[i][0]*vertexR[0] + ... + m_S[i][R-1]*vertexR[R-1]
|
|
const double* const* m_S = nullptr;
|
|
|
|
// m_LP[] = limit point evaluation vector.
|
|
// The array m_LP[] has m_R elements.
|
|
// If (vertexR[0], ..., vertexR[R-1]) is a list of standard vertex ring points,
|
|
// then Limit point = m_LP[0]*vertexR[0] + ... + m_LP[R-1]*vertexR[R-1].
|
|
// m_LP is the eigenvector of Transpose(m_S) with eigenvalue = 1.
|
|
// Length(m_LP) = 1.
|
|
const double* m_LP = nullptr;
|
|
|
|
// m_L1 and m_L2 = tangent space evaluation vectors.
|
|
// The arrays m_L1[] and m_L2[] have m_R elements.
|
|
// If (vertexR[0], ..., vertexR[R-1]) is a list of standard vertex ring points,
|
|
// then the two vectors
|
|
// V1 = m_L1[0]*vertexR[0] + ... + m_L1[R-1]*vertexR[R-1].
|
|
// V2 = m_L2[0]*vertexR[0] + ... + m_L2[R-1]*vertexR[R-1].
|
|
// span the tangent plane and
|
|
// N = V1 x V2 is perpindicular to the limit tangent plane.
|
|
// In general and almost always in practice, V1 and V2 are not unit vectors
|
|
// and it is best to noramalize V1 and V2 before taking the cross product.
|
|
// m_L1 and m_L2 are subdominant eigenvectors of Transpose(m_S).
|
|
// When the subdominant eigenvalue has geometric multiplicity 2,
|
|
// m_L1 and m_L2 span the same space as m_E1 and m_E2.
|
|
// The values stored in m_L1 and m_L2 are chosen to provide accurate
|
|
// evaluation. In come common cases m_L1 and m_L2 are equal to m_E1 and m_E2,
|
|
// but not in all cases.
|
|
const double* m_L1 = nullptr;
|
|
const double* m_L2 = nullptr;
|
|
|
|
/*
|
|
Description:
|
|
Set the values in this ON_SubDMatrix so the information
|
|
can be used to evaluate the case identified by the input
|
|
parameters.
|
|
Parameters:
|
|
facet_type - [in]
|
|
vertex_tag - [in]
|
|
sector_edge_count - [in]
|
|
The input parameters identify the subdivision case.
|
|
Returns:
|
|
R > 0: Success. The matrix is R x R.
|
|
0: Failure.
|
|
*/
|
|
unsigned int SetFromSectorType(
|
|
ON_SubDSectorType sector_type
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
ON_UNSET_VALUE - serious error
|
|
>= 0:
|
|
Maximum value of numbers that should be zero in and ideal world.
|
|
When the matrices, eigenvalues and eigenvectors are correctly calculated,
|
|
this returned value is in the range from 1e-16 to 5e-13 as valence goes
|
|
from 3 to 100.
|
|
For valences < 100, if a value larger than 1.0e-12 occurs, there is a bug in the code.
|
|
*/
|
|
double TestMatrix() const;
|
|
|
|
/*
|
|
Description:
|
|
Run evaluation tests on this subdivision case.
|
|
Returns:
|
|
>= 0.0: Test passed. Maximum deviation found in any test is returned.
|
|
ON_UNSET_VALUE: Test failed.
|
|
*/
|
|
double TestEvaluation() const;
|
|
|
|
/*
|
|
Description:
|
|
Run evaluation tests on a range of subdivision cases.
|
|
Parameters:
|
|
sector_type - [in]
|
|
If ON_SubDSectorType::Empty, then all supported sector types types are tested.
|
|
minimum_sector_face_count - [in]
|
|
If 0, then testing begins at ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag)
|
|
when testing vertex_tag types
|
|
maximum_sector_face_count - [in]
|
|
If 0, then testing stops at ON_SubD::maximum_evaluation_valence.
|
|
text_log - [out]
|
|
If not nullptr, then a brief written report is printed for each test case.
|
|
Returns:
|
|
>= 0.0: Test passed. Maximum deviation found in any test is returned.
|
|
ON_UNSET_VALUE: Test failed.
|
|
*/
|
|
static double TestEvaluation(
|
|
ON_SubDSectorType sector_type,
|
|
unsigned int minimum_sector_face_count,
|
|
unsigned int maximum_sector_face_count,
|
|
ON_TextLog* text_log
|
|
);
|
|
|
|
double TestComponentRing(
|
|
size_t component_ring_count,
|
|
const ON_SubDComponentPtr* component_ring
|
|
) const;
|
|
|
|
/*
|
|
Description:
|
|
Test cached subdivision matrix on sector identified by sit.
|
|
Parameters:
|
|
subd_type - [in]
|
|
subd_recursion_count - [in]
|
|
number of times to subdivide
|
|
sit - [in]
|
|
vertex to subdivide
|
|
component_ring - [out]
|
|
subd_points - [out]
|
|
limit_point - [out]
|
|
limit_tangent0 - [out]
|
|
limit_tangent1 - [out]
|
|
limit_normal - [out]
|
|
*/
|
|
static double TestEvaluation(
|
|
ON_SubD::SubDType subd_type,
|
|
const unsigned int subd_recursion_count,
|
|
ON_SubDSectorIterator sit,
|
|
ON_SimpleArray<ON_SubDComponentPtr>& component_ring,
|
|
ON_SimpleArray< ON_3dPoint >& subd_points,
|
|
class ON_SubDSectorLimitPoint& limit_point
|
|
);
|
|
|
|
private:
|
|
unsigned int m__max_R = 0;
|
|
ON_Matrix m__S; // m_S matrix memory
|
|
ON_SimpleArray<double> m__buffer; // m_LP, m_L1, m_L2, m_E1, m_E2 memory
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubD_FixedSizeHeap
|
|
//
|
|
|
|
class ON_SUBD_CLASS ON_SubD_FixedSizeHeap
|
|
{
|
|
private:
|
|
static unsigned int m__sn_factory;
|
|
|
|
public:
|
|
// The serial number is used for debugging purposes.
|
|
const unsigned int m_sn = ++m__sn_factory;
|
|
|
|
public:
|
|
ON_SubD_FixedSizeHeap() = default;
|
|
~ON_SubD_FixedSizeHeap();
|
|
|
|
/*
|
|
Description:
|
|
Reserve enough room to accomodate a face
|
|
with one extraordinary vertex.
|
|
Parameters:
|
|
subd_type - [in]
|
|
extraordinary_valence - [in]
|
|
*/
|
|
bool ReserveSubDWorkspace(
|
|
ON_SubD::SubDType subd_type,
|
|
unsigned int extraordinary_valence
|
|
);
|
|
|
|
bool ReserveSubDWorkspace(
|
|
size_t vertex_capacity,
|
|
size_t edge_capacity,
|
|
size_t face_capacity,
|
|
size_t array_capacity
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Reset this ON_SubD_FixedSizeHeap so it can be used again.
|
|
*/
|
|
void Reset();
|
|
|
|
/*
|
|
Description:
|
|
Deallocate all reserved heap.
|
|
*/
|
|
void Destroy();
|
|
|
|
bool InUse() const;
|
|
|
|
ON_SubDVertex* AllocateVertex(
|
|
const double vertexP[3],
|
|
unsigned int edge_capacity,
|
|
unsigned int face_capacity
|
|
);
|
|
|
|
ON_SubDVertex* AllocateVertex(
|
|
const ON_SubDVertex* vertex0,
|
|
ON_SubD::SubDType subd_type,
|
|
bool bUseSavedSubdivisionPoint,
|
|
unsigned int edge_capacity,
|
|
unsigned int face_capacity
|
|
);
|
|
|
|
ON_SubDVertex* AllocateVertex(
|
|
const ON_SubDEdge* edge0,
|
|
ON_SubD::SubDType subd_type,
|
|
bool bUseSavedSubdivisionPoint,
|
|
unsigned int edge_capacity,
|
|
unsigned int face_capacity
|
|
);
|
|
|
|
ON_SubDVertex* AllocateVertex(
|
|
const ON_SubDFace* face0,
|
|
ON_SubD::SubDType subd_type,
|
|
bool bUseSavedSubdivisionPoint,
|
|
unsigned int edge_capacity,
|
|
unsigned int face_capacity
|
|
);
|
|
|
|
/*
|
|
Parameters:
|
|
v0 - [in]
|
|
v0_sector_weight - [in]
|
|
If v0 null or ON_SubD::VertexTag::Smooth == v0->m_vertex_tag, and v1 is null or tagged,
|
|
then m_sector_weight[0] is set to v0_sector_weight.
|
|
In all other cases the value of v0_sector_weight is ignored and m_sector_weight[0]
|
|
is set to ON_SubDSectorType::IgnoredSectorWeight.
|
|
v1 - [in]
|
|
v1_sector_weight - [in]
|
|
If v1 null or ON_SubD::VertexTag::Smooth == v1->m_vertex_tag, and v0 is null or tagged,
|
|
then m_sector_weight[1] is set to v1_sector_weight.
|
|
In all other cases the value of v1_sector_weight is ignored and m_sector_weight[1]
|
|
is set to ON_SubDSectorType::IgnoredSectorWeight.
|
|
Returns:
|
|
An edge.
|
|
The vertex parameter information is used to set the ON_SubDEdge.m_vertex[]
|
|
and ON_SubDEdge.m_sector_weight[] values.
|
|
If v0 and v1 are not null and both are tagged, then ON_SubDEdge.m_edge_tag is
|
|
set to ON_SubD::EdgeTag::Crease.
|
|
In all other cases, ON_SubDEdge.m_edge_tag is set to ON_SubD::EdgeTag::Smooth.
|
|
If v0 or v1 is not null, then ON_SubDEdge.m_level is set to the
|
|
maximum of v0->m_level or v1->m_level.
|
|
*/
|
|
ON_SubDEdge* AllocateEdge(
|
|
ON_SubDVertex* v0,
|
|
double v0_sector_weight,
|
|
ON_SubDVertex* v1,
|
|
double v1_sector_weight
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
A face with all field values zero (same values as ON_SubDEdge::Face), except ON_SubDFace.m_id.
|
|
*/
|
|
ON_SubDFace* AllocateFace(
|
|
unsigned int zero_face_id,
|
|
unsigned int parent_face_id
|
|
);
|
|
|
|
ON_SubDFace* AllocateQuad(
|
|
unsigned int zero_face_id,
|
|
unsigned int parent_face_id,
|
|
const ON_SubDEdgePtr eptrs[4]
|
|
);
|
|
|
|
ON_SubDFace* AllocateTri(
|
|
unsigned int zero_face_id,
|
|
unsigned int parent_face_id,
|
|
const ON_SubDEdgePtr eptrs[3]
|
|
);
|
|
|
|
/*
|
|
Parameters:
|
|
capacity - [in]
|
|
desired array size
|
|
bZeroMemory - [in]
|
|
If true, all array element values are zero.
|
|
If false, array element values are undefined.
|
|
Returns:
|
|
An array of capacity ON__UINT_PTR pointers.
|
|
*/
|
|
ON__UINT_PTR* AllocatePtrArray(
|
|
unsigned int capacity,
|
|
bool bZeroMemory
|
|
);
|
|
|
|
/*
|
|
Description:
|
|
Return the most recent array obtained from AllocatePtrArray().
|
|
so it can be reused.
|
|
Returns:
|
|
True:
|
|
Success.
|
|
False:
|
|
Failure. The array was not the most recent array obtained
|
|
from AllocatePtrArray().
|
|
*/
|
|
bool ReturnPtrArray(
|
|
unsigned int capacity,
|
|
void* p
|
|
);
|
|
|
|
private:
|
|
//unsigned int m_max_valence = 0;
|
|
|
|
ON_SubDVertex* m_v = nullptr;
|
|
unsigned int m_v_capacity = 0;
|
|
unsigned int m_v_index = 0;
|
|
|
|
ON_SubDEdge* m_e = nullptr;
|
|
unsigned int m_e_capacity = 0;
|
|
unsigned int m_e_index = 0;
|
|
|
|
ON_SubDFace* m_f = nullptr;
|
|
unsigned int m_f_capacity = 0;
|
|
unsigned int m_f_index = 0;
|
|
|
|
ON__UINT_PTR* m_p = nullptr;
|
|
unsigned int m_p_capacity = 0;
|
|
unsigned int m_p_index = 0;
|
|
|
|
private:
|
|
// copies not supported
|
|
ON_SubD_FixedSizeHeap(const ON_SubD_FixedSizeHeap&) = delete;
|
|
ON_SubD_FixedSizeHeap& operator=(const ON_SubD_FixedSizeHeap&) = delete;
|
|
};
|
|
|
|
#if defined(ON_COMPILING_OPENNURBS)
|
|
/*
|
|
The ON_SubDAsUserData class is used to attach a subd to it proxy mesh
|
|
when writing V6 files in commerical rhino.
|
|
*/
|
|
class ON_SubDMeshProxyUserData : public ON_UserData
|
|
{
|
|
public:
|
|
/*
|
|
Returns:
|
|
A pointer to a mesh that now manages subd.
|
|
*/
|
|
static ON_Mesh* MeshProxyFromSubD(
|
|
const ON_SubD* subd
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
A pointer to a subd and deletes mesh.
|
|
*/
|
|
static ON_SubD* SubDFromMeshProxy(
|
|
const ON_Mesh* mesh
|
|
);
|
|
|
|
/*
|
|
Returns:
|
|
A pointer to a subd and deletes mesh.
|
|
*/
|
|
static bool IsSubDMeshProxy(
|
|
const ON_Mesh* mesh
|
|
);
|
|
|
|
static const ON_SubDDisplayParameters MeshProxyDisplayParameters();
|
|
|
|
private:
|
|
ON_OBJECT_DECLARE(ON_SubDMeshProxyUserData);
|
|
|
|
public:
|
|
ON_SubDMeshProxyUserData();
|
|
~ON_SubDMeshProxyUserData();
|
|
ON_SubDMeshProxyUserData(const ON_SubDMeshProxyUserData&);
|
|
ON_SubDMeshProxyUserData& operator=(const ON_SubDMeshProxyUserData&);
|
|
|
|
private:
|
|
// ON_Object overrides
|
|
bool Write(ON_BinaryArchive& archive) const override;
|
|
bool Read(ON_BinaryArchive& archive) override;
|
|
bool IsValid( class ON_TextLog* text_log = nullptr ) const override;
|
|
|
|
bool ParentMeshValid() const;
|
|
|
|
private:
|
|
// ON_UserData overrides
|
|
bool GetDescription(ON_wString& description) override;
|
|
bool WriteToArchive(
|
|
const class ON_BinaryArchive& archive,
|
|
const class ON_Object* parent_object
|
|
) const override;
|
|
|
|
private:
|
|
// The subd
|
|
mutable ON_SubD* m_subd = nullptr;
|
|
|
|
private:
|
|
// information used to see if the parent mesh has been modified.
|
|
mutable unsigned int m_mesh_face_count = 0;
|
|
mutable unsigned int m_mesh_vertex_count = 0;
|
|
mutable ON_SHA1_Hash m_mesh_face_array_sha1 = ON_SHA1_Hash::EmptyContentHash;
|
|
mutable ON_SHA1_Hash m_mesh_vertex_array_sha1 = ON_SHA1_Hash::EmptyContentHash;
|
|
|
|
private:
|
|
void Internal_CopyFrom(const ON_SubDMeshProxyUserData& src);
|
|
void Internal_Destroy();
|
|
static const bool Internal_MeshHasFaces(const ON_Mesh* mesh);
|
|
static const ON_SHA1_Hash Internal_FaceSHA1(const ON_Mesh* mesh);
|
|
static const ON_SHA1_Hash Internal_VertexSHA1(const ON_Mesh* mesh);
|
|
};
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#endif
|