mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-01 19:46:08 +08:00
1766 lines
41 KiB
C++
1766 lines
41 KiB
C++
#include "opennurbs.h"
|
|
|
|
#if !defined(ON_COMPILING_OPENNURBS)
|
|
// This check is included in all opennurbs source .c and .cpp files to insure
|
|
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
|
|
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined
|
|
// and the opennurbs .h files alter what is declared and how it is declared.
|
|
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
|
|
#endif
|
|
|
|
#include "opennurbs_subd_data.h"
|
|
|
|
/* $NoKeywords: $ */
|
|
/*
|
|
//
|
|
// Copyright (c) 1993-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 defined(OPENNURBS_SUBD_WIP)
|
|
|
|
static bool WriteDouble3(
|
|
const double x[3],
|
|
ON_BinaryArchive& archive
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (!archive.WriteDouble(3, x))
|
|
break;
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
static bool ReadDouble3(
|
|
ON_BinaryArchive& archive,
|
|
double x[3]
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (!archive.ReadDouble(3, x))
|
|
break;
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
static bool WriteBase(
|
|
const ON_SubDComponentBase* base,
|
|
ON_BinaryArchive& archive
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
unsigned int archive_id = base->ArchiveId();
|
|
unsigned int id = base->m_id;
|
|
unsigned short level = base->m_level;
|
|
if (!archive.WriteInt(archive_id))
|
|
break;
|
|
if (!archive.WriteInt(id))
|
|
break;
|
|
if (!archive.WriteShort(level))
|
|
break;
|
|
|
|
double P[3], V[3];
|
|
bool bHaveP = false;
|
|
bool bHaveV = false;
|
|
ON_SubD::SubDType subd_P_type = base->SavedSubdivisionPointType();
|
|
ON_SubD::SubDType subd_V_type = base->DisplacementType();
|
|
if (ON_SubD::SubDType::Unset != subd_P_type)
|
|
bHaveP = base->GetSavedSubdivisionPoint(subd_P_type, P);
|
|
|
|
if (ON_SubD::SubDType::Unset != subd_V_type)
|
|
bHaveV = base->GetSavedSubdivisionPoint(subd_V_type, V);
|
|
|
|
unsigned char cP = bHaveP ? ((unsigned char)subd_P_type) : 0U;
|
|
if (!archive.WriteChar(cP))
|
|
break;
|
|
if (0 != cP)
|
|
{
|
|
if (!WriteDouble3(P,archive))
|
|
break;
|
|
}
|
|
|
|
unsigned char cV = bHaveV ? ((unsigned char)subd_V_type) : 0U;
|
|
if (!archive.WriteChar(cV))
|
|
break;
|
|
if (0 != cV)
|
|
{
|
|
if (!WriteDouble3(V,archive))
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
|
|
static bool ReadBase(
|
|
ON_BinaryArchive& archive,
|
|
ON_SubDComponentBase& base
|
|
)
|
|
{
|
|
unsigned int archive_id = 0;
|
|
unsigned int id = 0;
|
|
unsigned short level = 0;
|
|
for (;;)
|
|
{
|
|
if (!archive.ReadInt(&archive_id))
|
|
break;
|
|
if (!archive.ReadInt(&id))
|
|
break;
|
|
if (!archive.ReadShort(&level))
|
|
break;
|
|
|
|
unsigned char cP = 0U;
|
|
unsigned char cV = 0U;
|
|
double P[3], V[3];
|
|
|
|
if (!archive.ReadChar(&cP))
|
|
break;
|
|
if (0 != cP)
|
|
{
|
|
if (!ReadDouble3(archive,P))
|
|
break;
|
|
}
|
|
|
|
if (!archive.ReadChar(&cV))
|
|
break;
|
|
if (0 != cV)
|
|
{
|
|
if (!ReadDouble3(archive,V))
|
|
break;
|
|
}
|
|
|
|
base.m_id = id;
|
|
base.SetArchiveId(archive_id);
|
|
base.m_level = level;
|
|
if ( 0 != cP )
|
|
base.SetSavedSubdivisionPoint(ON_SubD::SubDTypeFromUnsigned(cP),P);
|
|
if ( 0 != cV )
|
|
base.SetDisplacement(ON_SubD::SubDTypeFromUnsigned(cV),V);
|
|
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
|
|
static bool WriteArchiveIdAndFlags(
|
|
unsigned int archive_id,
|
|
ON__UINT_PTR ptr_flags,
|
|
ON_BinaryArchive& archive
|
|
)
|
|
{
|
|
if (!archive.WriteInt(archive_id))
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
unsigned char flags = (unsigned char)ON_SUBD_ELEMENT_FLAGS(ptr_flags);
|
|
if (!archive.WriteChar(flags))
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
static bool ReadArchiveIdAndFlagsIntoComponentPtr(
|
|
ON_BinaryArchive& archive,
|
|
ON__UINT_PTR& element_ptr
|
|
)
|
|
{
|
|
element_ptr = 0;
|
|
unsigned int archive_id = 0;
|
|
if (!archive.ReadInt(&archive_id))
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
unsigned char flags = 0;
|
|
if (!archive.ReadChar(&flags))
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
element_ptr = archive_id;
|
|
element_ptr *= (ON_SUBD_ELEMENT_FLAGS_MASK+1);
|
|
element_ptr += (flags & ON_SUBD_ELEMENT_FLAGS_MASK);
|
|
return true;
|
|
}
|
|
|
|
static bool WriteSavedLimitPointList(
|
|
unsigned int vertex_face_count,
|
|
ON_SubD::SubDType subd_type,
|
|
const ON_SubDSectorLimitPoint& limit_point,
|
|
ON_BinaryArchive& archive
|
|
)
|
|
{
|
|
unsigned int limit_point_count = 0;
|
|
const ON_SubDSectorLimitPoint* p;
|
|
|
|
if (ON_SubD::SubDType::Unset != subd_type)
|
|
{
|
|
for (p = &limit_point; nullptr != p && limit_point_count <= vertex_face_count; p = p->m_next_sector_limit_point)
|
|
{
|
|
if (!ON_IsValid(p->m_limitP[0]))
|
|
break;
|
|
if (limit_point_count > 0 && nullptr == p->m_sector_face)
|
|
break;
|
|
limit_point_count++;
|
|
}
|
|
if (limit_point_count > vertex_face_count || nullptr != p)
|
|
limit_point_count = 0;
|
|
|
|
if (limit_point_count > vertex_face_count)
|
|
limit_point_count = 0;
|
|
}
|
|
if ( 0 == limit_point_count )
|
|
subd_type = ON_SubD::SubDType::Unset;
|
|
|
|
for (;;)
|
|
{
|
|
unsigned char c = (unsigned char)subd_type;
|
|
if (!archive.WriteChar(c))
|
|
break;
|
|
|
|
if (0 == c)
|
|
return true;
|
|
|
|
if (!archive.WriteInt(limit_point_count))
|
|
break;
|
|
|
|
p = &limit_point;
|
|
for (unsigned int i = 0; i < limit_point_count; i++, p = p->m_next_sector_limit_point )
|
|
{
|
|
if (!WriteDouble3(limit_point.m_limitP, archive))
|
|
break;
|
|
if (!WriteDouble3(limit_point.m_limitT1, archive))
|
|
break;
|
|
if (!WriteDouble3(limit_point.m_limitT2, archive))
|
|
break;
|
|
if (!WriteDouble3(limit_point.m_limitN, archive))
|
|
break;
|
|
if (!WriteArchiveIdAndFlags(limit_point.m_sector_face ? limit_point.m_sector_face->ArchiveId() : 0, 0, archive))
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
static bool ReadSavedLimitPointList(
|
|
ON_BinaryArchive& archive,
|
|
unsigned int vertex_face_count,
|
|
ON_SubD::SubDType& limitP_type,
|
|
ON_SimpleArray< ON_SubDSectorLimitPoint > limit_points
|
|
)
|
|
{
|
|
limit_points.SetCount(0);
|
|
|
|
for (;;)
|
|
{
|
|
unsigned char c = 0;
|
|
if (!archive.ReadChar(&c))
|
|
break;
|
|
|
|
if ( 0 == c)
|
|
return true;
|
|
|
|
unsigned int limit_point_count = 0;
|
|
if (!archive.ReadInt(&limit_point_count))
|
|
break;
|
|
|
|
if ( 0 == limit_point_count )
|
|
break;
|
|
|
|
if (limit_point_count > vertex_face_count)
|
|
break;
|
|
|
|
limit_points.Reserve(limit_point_count);
|
|
|
|
for ( unsigned int i = 0; i < limit_point_count; i++ )
|
|
{
|
|
ON_SubDSectorLimitPoint limit_point = ON_SubDSectorLimitPoint::Unset;
|
|
if (!ReadDouble3(archive,limit_point.m_limitP))
|
|
break;
|
|
if (!ReadDouble3(archive,limit_point.m_limitT1))
|
|
break;
|
|
if (!ReadDouble3(archive,limit_point.m_limitT2))
|
|
break;
|
|
if (!ReadDouble3(archive,limit_point.m_limitN))
|
|
break;
|
|
ON_SubDFacePtr fptr = ON_SubDFacePtr::Null;
|
|
if (!ReadArchiveIdAndFlagsIntoComponentPtr(archive,fptr.m_ptr))
|
|
break;
|
|
limit_points.Append(limit_point);
|
|
}
|
|
|
|
if (limit_point_count != limit_points.UnsignedCount() )
|
|
break;
|
|
|
|
limitP_type = ON_SubD::SubDTypeFromUnsigned(c);
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
static bool WriteVertexList(
|
|
unsigned short vertex_count,
|
|
const ON_SubDVertex*const* vertex,
|
|
ON_BinaryArchive& archive
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
ON_SubDArchiveIdMap::ValidateArrayCounts(vertex_count,vertex_count,vertex,0,nullptr);
|
|
|
|
if (!archive.WriteShort(vertex_count))
|
|
break;
|
|
|
|
if ( 0 == vertex_count )
|
|
return true;
|
|
|
|
const ON__UINT_PTR ptr_flags = 0; // for future use
|
|
unsigned short i = 0;
|
|
for (i = 0; i < vertex_count; i++)
|
|
{
|
|
const ON_SubDVertex* v = vertex[i];
|
|
if (!WriteArchiveIdAndFlags((nullptr != v) ? v->ArchiveId() : 0, ptr_flags, archive))
|
|
break;
|
|
}
|
|
if ( i < vertex_count )
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
|
|
static bool ReadVertexList(
|
|
ON_BinaryArchive& archive,
|
|
unsigned short& vertex_count,
|
|
unsigned short vertex_capacity,
|
|
ON_SubDVertex* vertex[]
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
unsigned short archive_vertex_count = 0;
|
|
if (!archive.ReadShort(&archive_vertex_count))
|
|
break;
|
|
|
|
if (archive_vertex_count != vertex_count)
|
|
{
|
|
ON_ERROR("Archive vertex count != expected vertex count.");
|
|
if ( archive_vertex_count < vertex_count)
|
|
vertex_count = archive_vertex_count;
|
|
}
|
|
|
|
ON_SubDArchiveIdMap::ValidateArrayCounts(vertex_count,vertex_capacity,vertex,0,nullptr);
|
|
|
|
unsigned short i = 0;
|
|
for (i = 0; i < vertex_count; i++)
|
|
{
|
|
ON__UINT_PTR vptr = 0;
|
|
if (!ReadArchiveIdAndFlagsIntoComponentPtr(archive,vptr))
|
|
break;
|
|
vertex[i] = (ON_SubDVertex*)vptr;
|
|
}
|
|
if ( i < vertex_count )
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
|
|
static bool WriteEdgePtrList(
|
|
unsigned short edge_count,
|
|
unsigned short edgeN_capacity,
|
|
const ON_SubDEdgePtr* edgeN,
|
|
unsigned short edgeX_capacity,
|
|
const ON_SubDEdgePtr* edgeX,
|
|
ON_BinaryArchive& archive
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
ON_SubDArchiveIdMap::ValidateArrayCounts(edge_count,edgeN_capacity,edgeN,edgeX_capacity,edgeX);
|
|
|
|
if (!archive.WriteShort(edge_count))
|
|
break;
|
|
|
|
if ( 0 == edge_count )
|
|
return true;
|
|
|
|
const ON_SubDEdgePtr* eptr = edgeN;
|
|
unsigned short i = 0;
|
|
for (i = 0; i < edge_count; i++, eptr++)
|
|
{
|
|
if ( i == edgeN_capacity)
|
|
eptr = edgeX;
|
|
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr->m_ptr);
|
|
if (!WriteArchiveIdAndFlags((nullptr != edge) ? edge->ArchiveId() : 0,eptr->m_ptr,archive))
|
|
break;
|
|
}
|
|
if ( i < edge_count )
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
|
|
static bool ReadEdgePtrList(
|
|
ON_BinaryArchive& archive,
|
|
unsigned short& edge_count,
|
|
unsigned short edgeN_capacity,
|
|
ON_SubDEdgePtr* edgeN,
|
|
unsigned short edgeX_capacity,
|
|
ON_SubDEdgePtr* edgeX
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
unsigned short archive_edge_count = 0;
|
|
if (!archive.ReadShort(&archive_edge_count))
|
|
break;
|
|
|
|
if (archive_edge_count != edge_count)
|
|
{
|
|
ON_ERROR("Archive edge count != expected edge count.");
|
|
if ( archive_edge_count < edge_count)
|
|
edge_count = archive_edge_count;
|
|
}
|
|
|
|
ON_SubDArchiveIdMap::ValidateArrayCounts(edge_count,edgeN_capacity,edgeN,edgeX_capacity,edgeX);
|
|
|
|
|
|
ON_SubDEdgePtr* eptr = edgeN;
|
|
unsigned short i = 0;
|
|
for (i = 0; i < edge_count; i++, eptr++)
|
|
{
|
|
if ( i == edgeN_capacity)
|
|
eptr = edgeX;
|
|
if (!ReadArchiveIdAndFlagsIntoComponentPtr(archive,eptr->m_ptr))
|
|
break;
|
|
}
|
|
if ( i < edge_count )
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
|
|
static bool WriteFacePtrList(
|
|
unsigned short face_count,
|
|
size_t faceN_capacity,
|
|
const ON_SubDFacePtr* faceN,
|
|
unsigned short faceX_capacity,
|
|
const ON_SubDFacePtr* faceX,
|
|
ON_BinaryArchive& archive
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
ON_SubDArchiveIdMap::ValidateArrayCounts(face_count,faceN_capacity,faceN,faceX_capacity,faceX);
|
|
|
|
if (!archive.WriteShort(face_count))
|
|
break;
|
|
|
|
if ( 0 == face_count )
|
|
return true;
|
|
|
|
const ON_SubDFacePtr* fptr = faceN;
|
|
unsigned short i = 0;
|
|
for (i = 0; i < face_count; i++, fptr++)
|
|
{
|
|
if ( i == faceN_capacity)
|
|
fptr = faceX;
|
|
const ON_SubDFace* face = ON_SUBD_FACE_POINTER(fptr->m_ptr);
|
|
if (!WriteArchiveIdAndFlags((nullptr != face) ? face->ArchiveId() : 0,fptr->m_ptr,archive))
|
|
break;
|
|
}
|
|
if ( i < face_count )
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
|
|
static bool ReadFacePtrList(
|
|
ON_BinaryArchive& archive,
|
|
unsigned short& face_count,
|
|
unsigned short faceN_capacity,
|
|
ON_SubDFacePtr* faceN,
|
|
unsigned short faceX_capacity,
|
|
ON_SubDFacePtr* faceX
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
unsigned short archive_face_count = 0;
|
|
if (!archive.ReadShort(&archive_face_count))
|
|
break;
|
|
|
|
if (archive_face_count != face_count)
|
|
{
|
|
ON_ERROR("Archive face count != expected face count.");
|
|
if ( archive_face_count < face_count)
|
|
face_count = archive_face_count;
|
|
}
|
|
|
|
ON_SubDArchiveIdMap::ValidateArrayCounts(face_count,faceN_capacity,faceN,faceX_capacity,faceX);
|
|
|
|
ON_SubDFacePtr* fptr = faceN;
|
|
unsigned short i = 0;
|
|
for (i = 0; i < face_count; i++, fptr++)
|
|
{
|
|
if ( i == faceN_capacity)
|
|
fptr = faceX;
|
|
if (!ReadArchiveIdAndFlagsIntoComponentPtr(archive,fptr->m_ptr))
|
|
break;
|
|
}
|
|
if ( i < face_count )
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
static bool SkipReadingLaterAdditions(
|
|
ON_BinaryArchive& archive,
|
|
unsigned char skip_mark
|
|
)
|
|
{
|
|
if ( 0 == skip_mark)
|
|
return true;
|
|
|
|
if (1 == skip_mark)
|
|
{
|
|
// TODO ADD THIS // return archive.SkipReadingChunk(); //
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
// TODO ADD THIS return archive.SkipReadingBytes( skip_mark );
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
|
|
|
|
bool ON_SubDVertex::Write(
|
|
ON_BinaryArchive& archive
|
|
) const
|
|
{
|
|
for (;;)
|
|
{
|
|
if (!WriteBase(this,archive))
|
|
break;
|
|
if (!archive.WriteChar((unsigned char)m_vertex_tag))
|
|
break;
|
|
//if (!archive.WriteChar((unsigned char)m_vertex_edge_order))
|
|
// break;
|
|
//if (!archive.WriteChar((unsigned char)m_vertex_facet_type))
|
|
// break;
|
|
if (!WriteDouble3(m_P,archive))
|
|
break;
|
|
if (!archive.WriteShort(m_edge_count))
|
|
break;
|
|
if (!archive.WriteShort(m_face_count))
|
|
break;
|
|
if (!WriteSavedLimitPointList(m_face_count,SavedLimitPointType(),m_limit_point, archive))
|
|
break;
|
|
if (!WriteEdgePtrList(m_edge_count,m_edge_capacity,m_edges,0,nullptr, archive))
|
|
break;
|
|
if (!WriteFacePtrList(m_face_count,m_face_capacity,(const ON_SubDFacePtr*)m_faces,0,nullptr, archive))
|
|
break;
|
|
|
|
// mark end with a 0 byte
|
|
// If (when) new stuff is added, the value will be the number of bytes that are added
|
|
// or 1 if a chunk is added.
|
|
if (!archive.WriteChar((unsigned char)0U))
|
|
break;
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
bool ON_SubDVertex::Read(
|
|
class ON_BinaryArchive& archive,
|
|
class ON_SubD& subd,
|
|
class ON_SubDVertex*& vertex
|
|
)
|
|
{
|
|
vertex = nullptr;
|
|
|
|
for (;;)
|
|
{
|
|
ON_SubDimple* subdimple = const_cast<ON_SubDimple*>(subd.SubDimple());
|
|
if ( nullptr == subdimple)
|
|
break;
|
|
|
|
ON_SubDComponentBase base = ON_SubDComponentBase::Unset;
|
|
unsigned char vertex_tag = 0;
|
|
//unsigned char vertex_edge_order = 0;
|
|
//unsigned char vertex_facet_type = 0;
|
|
double P[3];
|
|
unsigned short edge_count = 0;
|
|
unsigned short face_count = 0;
|
|
|
|
ON_SubD::SubDType limitP_type = ON_SubD::SubDType::Unset;
|
|
ON_SimpleArray<ON_SubDSectorLimitPoint> limit_points;
|
|
|
|
if (!ReadBase(archive,base))
|
|
break;
|
|
if (!archive.ReadChar(&vertex_tag))
|
|
break;
|
|
//if (!archive.ReadChar(&vertex_edge_order))
|
|
// break;
|
|
//if (!archive.ReadChar(&vertex_facet_type))
|
|
// break;
|
|
if (!ReadDouble3(archive,P))
|
|
break;
|
|
if (!archive.ReadShort(&edge_count))
|
|
break;
|
|
if (!archive.ReadShort(&face_count))
|
|
break;
|
|
|
|
if (!ReadSavedLimitPointList(archive, face_count, limitP_type, limit_points))
|
|
break;
|
|
|
|
ON_SubDVertex* v = subdimple->AllocateVertex(
|
|
ON_SubD::VertexTagFromUnsigned(vertex_tag),
|
|
base.m_level,
|
|
P,
|
|
edge_count,
|
|
face_count
|
|
);
|
|
|
|
if ( nullptr == v )
|
|
break;
|
|
|
|
v->ON_SubDComponentBase::operator=(base);
|
|
|
|
//v->m_vertex_edge_order = ON_SubD::VertexEdgeOrderFromUnsigned(vertex_edge_order);
|
|
//v->m_vertex_facet_type = ON_SubD::VertexFacetTypeFromUnsigned(vertex_facet_type);
|
|
|
|
if (!ReadEdgePtrList(archive,edge_count,v->m_edge_capacity,v->m_edges,0,nullptr))
|
|
break;
|
|
v->m_edge_count = edge_count;
|
|
|
|
if (!ReadFacePtrList(archive,face_count,v->m_face_capacity,(ON_SubDFacePtr*)v->m_faces,0,nullptr))
|
|
break;
|
|
v->m_face_count = face_count;
|
|
|
|
|
|
unsigned char skip_mark = 0;
|
|
if (!archive.ReadChar(&skip_mark))
|
|
break;
|
|
|
|
if (!SkipReadingLaterAdditions(archive,skip_mark))
|
|
break;
|
|
|
|
if (ON_SubD::SubDType::Unset != limitP_type)
|
|
{
|
|
for (unsigned int i = 0; i < limit_points.UnsignedCount(); i++)
|
|
{
|
|
ON_SubDSectorLimitPoint limit_point = limit_points[i];
|
|
limit_point.m_next_sector_limit_point = (const ON_SubDSectorLimitPoint*)1U; // skips checks
|
|
if (false == v->SetSavedLimitPoint(limitP_type, limit_point))
|
|
{
|
|
v->ClearSavedLimitPoints();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
vertex = v;
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
|
|
bool ON_SubDEdge::Write(
|
|
ON_BinaryArchive& archive
|
|
) const
|
|
{
|
|
for (;;)
|
|
{
|
|
if (!WriteBase(this,archive))
|
|
break;
|
|
if (!archive.WriteChar((unsigned char)m_edge_tag))
|
|
break;
|
|
if (!archive.WriteShort(m_face_count))
|
|
break;
|
|
if (!archive.WriteDouble(2,m_sector_coefficient))
|
|
break;
|
|
if (!archive.WriteDouble(m_sharpness))
|
|
break;
|
|
if (!WriteVertexList(2, m_vertex, archive))
|
|
break;
|
|
if (!WriteFacePtrList(m_face_count,sizeof(m_face2)/sizeof(m_face2[0]),m_face2,m_facex_capacity,m_facex, archive))
|
|
break;
|
|
|
|
// mark end with a 0 byte
|
|
// If (when) new stuff is added, the value will be the number of bytes that are added
|
|
// or 1 if a chunk is added.
|
|
if (!archive.WriteChar((unsigned char)0U))
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
bool ON_SubDEdge::Read(
|
|
class ON_BinaryArchive& archive,
|
|
class ON_SubD& subd,
|
|
class ON_SubDEdge*& edge
|
|
)
|
|
{
|
|
edge = nullptr;
|
|
|
|
for (;;)
|
|
{
|
|
ON_SubDimple* subdimple = const_cast<ON_SubDimple*>(subd.SubDimple());
|
|
if ( nullptr == subdimple)
|
|
break;
|
|
|
|
ON_SubDComponentBase base = ON_SubDComponentBase::Unset;
|
|
unsigned char edge_tag = 0;
|
|
unsigned short face_count = 0;
|
|
double sector_weight[2] = { 0 };
|
|
double sharpness = 0.0;
|
|
|
|
if (!ReadBase(archive,base))
|
|
break;
|
|
if (!archive.ReadChar(&edge_tag))
|
|
break;
|
|
if (!archive.ReadShort(&face_count))
|
|
break;
|
|
if (!archive.ReadDouble(2,sector_weight))
|
|
break;
|
|
if (!archive.ReadDouble(&sharpness))
|
|
break;
|
|
|
|
ON_SubDVertex* v[2] = { 0 };
|
|
unsigned short vertex_count = 2;
|
|
if (!ReadVertexList(archive, vertex_count, 2, v))
|
|
break;
|
|
|
|
|
|
ON_SubDEdge* e = subdimple->AllocateEdge(
|
|
ON_SubD::EdgeTagFromUnsigned(edge_tag),
|
|
base.m_level,
|
|
face_count
|
|
);
|
|
|
|
if ( nullptr == e )
|
|
break;
|
|
|
|
e->ON_SubDComponentBase::operator=(base);
|
|
|
|
for ( unsigned short evi = 0; evi < 2 && evi < vertex_count; evi++ )
|
|
e->m_vertex[evi] = v[evi];
|
|
|
|
e->m_sector_coefficient[0] = sector_weight[0];
|
|
e->m_sector_coefficient[1] = sector_weight[1];
|
|
e->m_sharpness = sharpness;
|
|
|
|
if (!ReadFacePtrList(archive,face_count,sizeof(e->m_face2)/sizeof(e->m_face2[0]),e->m_face2,e->m_facex_capacity,e->m_facex))
|
|
break;
|
|
e->m_face_count = face_count;
|
|
|
|
unsigned char skip_mark = 0;
|
|
if (!archive.ReadChar(&skip_mark))
|
|
break;
|
|
|
|
if (!SkipReadingLaterAdditions(archive,skip_mark))
|
|
break;
|
|
|
|
edge = e;
|
|
|
|
return true;
|
|
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
bool ON_SubDFace::Write(
|
|
ON_BinaryArchive& archive
|
|
) const
|
|
{
|
|
for (;;)
|
|
{
|
|
if (!WriteBase(this,archive))
|
|
break;
|
|
if (!archive.WriteInt(m_zero_face_id))
|
|
break;
|
|
if (!archive.WriteInt(m_parent_face_id))
|
|
break;
|
|
if (!archive.WriteShort(m_edge_count))
|
|
break;
|
|
if (!WriteEdgePtrList(m_edge_count,sizeof(m_edge4)/sizeof(m_edge4[0]),m_edge4,m_edgex_capacity,m_edgex, archive))
|
|
break;
|
|
|
|
// mark end with a 0 byte
|
|
// If (when) new stuff is added, the value will be the number of bytes that are added
|
|
// or 1 if a chunk is added.
|
|
if (!archive.WriteChar((unsigned char)0U))
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
bool ON_SubDFace::Read(
|
|
class ON_BinaryArchive& archive,
|
|
class ON_SubD& subd,
|
|
class ON_SubDFace*& face
|
|
)
|
|
{
|
|
face = nullptr;
|
|
|
|
for (;;)
|
|
{
|
|
ON_SubDimple* subdimple = const_cast<ON_SubDimple*>(subd.SubDimple());
|
|
if ( nullptr == subdimple)
|
|
break;
|
|
|
|
ON_SubDComponentBase base = ON_SubDComponentBase::Unset;
|
|
unsigned int zero_face_id = 0;
|
|
unsigned int parent_face_id = 0;
|
|
unsigned short edge_count = 0;
|
|
|
|
if (!ReadBase(archive,base))
|
|
break;
|
|
if (!archive.ReadInt(&zero_face_id))
|
|
break;
|
|
if (!archive.ReadInt(&parent_face_id))
|
|
break;
|
|
if (!archive.ReadShort(&edge_count))
|
|
break;
|
|
|
|
ON_SubDFace* f = subdimple->AllocateFace(
|
|
base.m_level,
|
|
edge_count
|
|
);
|
|
|
|
if ( nullptr == f )
|
|
break;
|
|
|
|
f->ON_SubDComponentBase::operator=(base);
|
|
|
|
f->m_zero_face_id = zero_face_id;
|
|
f->m_parent_face_id = parent_face_id;
|
|
|
|
if (!ReadEdgePtrList(archive,edge_count,sizeof(f->m_edge4)/sizeof(f->m_edge4[0]),f->m_edge4,f->m_edgex_capacity,f->m_edgex))
|
|
break;
|
|
f->m_edge_count = edge_count;
|
|
|
|
unsigned char skip_mark = 0;
|
|
if (!archive.ReadChar(&skip_mark))
|
|
break;
|
|
|
|
if (!SkipReadingLaterAdditions(archive,skip_mark))
|
|
break;
|
|
|
|
face = f;
|
|
|
|
return true;
|
|
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
unsigned int ON_SubDLevel::SetArchiveId(
|
|
unsigned int archive_id_partition[4]
|
|
) const
|
|
{
|
|
unsigned int archive_id = 1;
|
|
//archive_id_partition[0] = 0;
|
|
//archive_id_partition[1] = 0;
|
|
//archive_id_partition[2] = 0;
|
|
//archive_id_partition[3] = 0;
|
|
|
|
archive_id_partition[0] = archive_id;
|
|
for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v; v = v->m_next_vertex)
|
|
{
|
|
v->SetArchiveId(archive_id++);
|
|
}
|
|
archive_id_partition[1] = archive_id;
|
|
for (const ON_SubDEdge* e = m_edge[0]; nullptr != e; e = e->m_next_edge)
|
|
{
|
|
e->SetArchiveId(archive_id++);
|
|
}
|
|
archive_id_partition[2] = archive_id;
|
|
for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face)
|
|
{
|
|
f->SetArchiveId(archive_id++);
|
|
}
|
|
archive_id_partition[3] = archive_id;
|
|
|
|
return archive_id-1;
|
|
}
|
|
|
|
|
|
void ON_SubDLevel::ClearArchiveId() const
|
|
{
|
|
for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v; v = v->m_next_vertex)
|
|
{
|
|
v->SetArchiveId(0);
|
|
}
|
|
for (const ON_SubDEdge* e = m_edge[0]; nullptr != e; e = e->m_next_edge)
|
|
{
|
|
e->SetArchiveId(0);
|
|
}
|
|
for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face)
|
|
{
|
|
f->SetArchiveId(0);
|
|
}
|
|
}
|
|
|
|
bool ON_SubDLevel::Write(
|
|
ON_BinaryArchive& archive
|
|
) const
|
|
{
|
|
if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1))
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
|
|
bool rc = false;
|
|
for (;;)
|
|
{
|
|
if (!archive.WriteShort((unsigned short)m_level_index))
|
|
break;
|
|
if (!archive.WriteChar((unsigned char)m_subdivision_type))
|
|
break;
|
|
if (!archive.WriteChar(m_ordinary_vertex_valence))
|
|
break;
|
|
if (!archive.WriteChar(m_ordinary_face_edge_count))
|
|
break;
|
|
ON_BoundingBox bbox = m_aggregates.m_bDirtyBoundingBox ? ON_BoundingBox::EmptyBoundingBox : m_aggregates.m_bbox;
|
|
if (!archive.WriteDouble(3,bbox[0]))
|
|
break;
|
|
if (!archive.WriteDouble(3,bbox[1]))
|
|
break;
|
|
|
|
|
|
unsigned int archive_id_partition[4] = { 0 };
|
|
SetArchiveId(archive_id_partition);
|
|
|
|
if (!archive.WriteInt(4,archive_id_partition))
|
|
break;
|
|
|
|
const ON_SubDVertex* v = nullptr;
|
|
const ON_SubDEdge* e = nullptr;
|
|
const ON_SubDFace* f = nullptr;
|
|
for (v = m_vertex[0]; nullptr != v; v = v->m_next_vertex)
|
|
{
|
|
if( !v->Write(archive) )
|
|
break;
|
|
}
|
|
if ( nullptr != v )
|
|
break;
|
|
|
|
for (e = m_edge[0]; nullptr != e; e = e->m_next_edge)
|
|
{
|
|
if( !e->Write(archive) )
|
|
break;
|
|
}
|
|
if ( nullptr != e )
|
|
break;
|
|
|
|
for (f = m_face[0]; nullptr != f; f = f->m_next_face)
|
|
{
|
|
if( !f->Write(archive) )
|
|
break;
|
|
}
|
|
if ( nullptr != f )
|
|
break;
|
|
|
|
// chunk 1.1 has meshes
|
|
unsigned char c = 0;
|
|
if (archive.Save3dmRenderMesh(ON::object_type::subd_object) || archive.Save3dmAnalysisMesh(ON::object_type::subd_object))
|
|
{
|
|
if (false == m_limit_mesh.IsEmpty())
|
|
{
|
|
c = 0;
|
|
// c = 1; TODO change to c = 1 when ON_SubDLimitMesh::Write()/Read() actually work
|
|
}
|
|
}
|
|
|
|
if (!archive.WriteChar(c))
|
|
break;
|
|
|
|
if (1 == c)
|
|
{
|
|
//if (!m_limit_mesh.Write(archive))
|
|
// break;
|
|
}
|
|
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
if (!archive.EndWrite3dmChunk())
|
|
rc = false;
|
|
ClearArchiveId();
|
|
|
|
if (rc)
|
|
return rc;
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
bool ON_SubDLevel::Read(
|
|
ON_BinaryArchive& archive,
|
|
class ON_SubDArchiveIdMap& element_list,
|
|
ON_SubD& subd
|
|
)
|
|
{
|
|
if ( false == element_list.Reset())
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
|
|
int major_version = 1;
|
|
int minor_version = 0;
|
|
if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version))
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
|
|
bool rc = false;
|
|
for (;;)
|
|
{
|
|
if ( 1 != major_version)
|
|
break;
|
|
|
|
unsigned short level_index = 0;
|
|
if (!archive.ReadShort(&level_index))
|
|
break;
|
|
m_level_index = level_index;
|
|
unsigned char c = 0;
|
|
if (!archive.ReadChar(&c))
|
|
break;
|
|
m_subdivision_type = ON_SubD::SubDTypeFromUnsigned(c);
|
|
if (!archive.ReadChar(&m_ordinary_vertex_valence))
|
|
break;
|
|
if (!archive.ReadChar(&m_ordinary_face_edge_count))
|
|
break;
|
|
ON_BoundingBox bbox;
|
|
if (!archive.ReadDouble(3,bbox[0]))
|
|
break;
|
|
if (!archive.ReadDouble(3,bbox[1]))
|
|
break;
|
|
if (bbox.IsValid())
|
|
{
|
|
m_aggregates.m_bDirtyBoundingBox = false;
|
|
m_aggregates.m_bbox = bbox;
|
|
}
|
|
else
|
|
{
|
|
m_aggregates.m_bDirtyBoundingBox = true;
|
|
}
|
|
|
|
if (!archive.ReadInt(4,element_list.m_archive_id_partition))
|
|
break;
|
|
|
|
unsigned int archive_id = 0;
|
|
|
|
for (archive_id = element_list.m_archive_id_partition[0]; archive_id < element_list.m_archive_id_partition[1]; archive_id++ )
|
|
{
|
|
ON_SubDVertex* v = nullptr;
|
|
if ( false == ON_SubDVertex::Read(archive, subd, v) )
|
|
break;
|
|
if ( nullptr == v )
|
|
break;
|
|
if (archive_id != v->ArchiveId())
|
|
break;
|
|
if ( !element_list.Add(v) )
|
|
break;
|
|
AddVertex(v);
|
|
}
|
|
if ( archive_id != element_list.m_archive_id_partition[1] )
|
|
break;
|
|
|
|
for (archive_id = element_list.m_archive_id_partition[1]; archive_id < element_list.m_archive_id_partition[2]; archive_id++ )
|
|
{
|
|
ON_SubDEdge* e = nullptr;
|
|
if ( false == ON_SubDEdge::Read(archive, subd, e) )
|
|
break;
|
|
if ( nullptr == e )
|
|
break;
|
|
if (archive_id != e->ArchiveId())
|
|
break;
|
|
if ( !element_list.Add(e) )
|
|
break;
|
|
AddEdge(e);
|
|
}
|
|
if ( archive_id != element_list.m_archive_id_partition[2] )
|
|
break;
|
|
|
|
for (archive_id = element_list.m_archive_id_partition[2]; archive_id < element_list.m_archive_id_partition[3]; archive_id++ )
|
|
{
|
|
ON_SubDFace* f = nullptr;
|
|
if ( false == ON_SubDFace::Read(archive, subd, f) )
|
|
break;
|
|
if ( nullptr == f )
|
|
break;
|
|
if (archive_id != f->ArchiveId())
|
|
break;
|
|
if ( !element_list.Add(f) )
|
|
break;
|
|
AddFace(f);
|
|
}
|
|
if ( archive_id != element_list.m_archive_id_partition[3] )
|
|
break;
|
|
|
|
if (archive_id != element_list.Count())
|
|
break;
|
|
|
|
// Convert archive_id references to runtime pointers.
|
|
archive_id = element_list.ConvertArchiveIdsToRuntimePointers();
|
|
if ( archive_id <= 0 )
|
|
break;
|
|
|
|
if (0 == minor_version )
|
|
break;
|
|
|
|
c = 0;
|
|
if (!archive.ReadChar(&c))
|
|
break;
|
|
|
|
if (1 == c)
|
|
{
|
|
//if (!m_limit_mesh.Read(archive))
|
|
// break;
|
|
}
|
|
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
ClearArchiveId();
|
|
|
|
if (!archive.EndRead3dmChunk())
|
|
rc = false;
|
|
|
|
if (rc)
|
|
return rc;
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
bool ON_SubDimple::Write(
|
|
ON_BinaryArchive& archive
|
|
) const
|
|
{
|
|
const_cast< ON_SubDHeap* >(&m_heap)->ClearArchiveId();
|
|
|
|
if ( !archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 0) )
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
bool rc = false;
|
|
for (;;)
|
|
{
|
|
unsigned int level_count = m_levels.UnsignedCount();
|
|
unsigned int level_index;
|
|
for (level_index = 0; level_index < level_count; level_index++)
|
|
{
|
|
if (nullptr == m_levels[level_index])
|
|
{
|
|
level_count = level_index;
|
|
break;
|
|
}
|
|
}
|
|
if (!archive.WriteInt(level_count))
|
|
break;
|
|
|
|
if (!archive.WriteInt(m_max_vertex_id))
|
|
break;
|
|
if (!archive.WriteInt(m_max_edge_id))
|
|
break;
|
|
if (!archive.WriteInt(m_max_face_id))
|
|
break;
|
|
|
|
// a global bounding box was saved before May 2015.
|
|
// Something has to be here so file IO is not broken.
|
|
if (!archive.WriteBoundingBox(ON_BoundingBox::EmptyBoundingBox))
|
|
break;
|
|
|
|
for (level_index = 0; level_index < level_count; level_index++)
|
|
{
|
|
if ( !m_levels[level_index]->Write(archive) )
|
|
break;
|
|
}
|
|
if (level_index < level_count)
|
|
break;
|
|
rc = true;
|
|
break;
|
|
}
|
|
if (!archive.EndWrite3dmChunk())
|
|
rc = false;
|
|
if (rc)
|
|
return true;
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
bool ON_SubDimple::Read(
|
|
ON_BinaryArchive& archive,
|
|
class ON_SubD& subd
|
|
)
|
|
{
|
|
m_heap.Clear();
|
|
int major_version = 0;
|
|
int minor_version = 0;
|
|
if ( !archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version) )
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
bool rc = false;
|
|
|
|
unsigned int max_vertex_id = 0;
|
|
unsigned int max_edge_id = 0;
|
|
unsigned int max_face_id = 0;
|
|
|
|
for (;;)
|
|
{
|
|
if (1 != major_version)
|
|
break;
|
|
|
|
unsigned int i;
|
|
if (!archive.ReadInt(&i))
|
|
break;
|
|
const unsigned int level_count = i;
|
|
|
|
if (!archive.ReadInt(&max_vertex_id))
|
|
break;
|
|
if (!archive.ReadInt(&max_edge_id))
|
|
break;
|
|
if (!archive.ReadInt(&max_face_id))
|
|
break;
|
|
|
|
|
|
ON_BoundingBox bbox_unsued_after_May_2015;
|
|
if (!archive.ReadBoundingBox(bbox_unsued_after_May_2015))
|
|
break;
|
|
|
|
ON_SubDArchiveIdMap element_list;
|
|
|
|
unsigned int level_index;
|
|
for (level_index = 0; level_index < level_count; level_index++)
|
|
{
|
|
ON_SubDLevel* level = SubDLevel(level_index,true);
|
|
if ( nullptr == level )
|
|
break;
|
|
if (false == level->Read(archive, element_list, subd ) )
|
|
break;
|
|
m_active_level = level;
|
|
}
|
|
|
|
|
|
const unsigned int heap_max_vertex_id = m_heap.MaximumVertexId();
|
|
const unsigned int heap_max_edge_id = m_heap.MaximumEdgeId();
|
|
const unsigned int heap_max_face_id = m_heap.MaximumFaceId();
|
|
if (m_max_vertex_id < heap_max_vertex_id)
|
|
m_max_vertex_id = heap_max_vertex_id;
|
|
if (m_max_edge_id < heap_max_edge_id)
|
|
m_max_edge_id = heap_max_edge_id;
|
|
if (m_max_face_id < heap_max_face_id)
|
|
m_max_face_id = heap_max_face_id;
|
|
|
|
if ( level_index != level_count)
|
|
break;
|
|
|
|
rc = true;
|
|
break;
|
|
}
|
|
if (!archive.EndRead3dmChunk())
|
|
rc = false;
|
|
|
|
// Heap id validation. Always an error if max_heap_..._id > m_max_..._id.
|
|
if (false == m_heap.IsValid())
|
|
{
|
|
ON_SUBD_ERROR("m_heap.IsValid() is false.");
|
|
m_heap.ResetId(); // breaks component id references, but this is a serious error.
|
|
}
|
|
const unsigned int max_heap_vertex_id = m_heap.MaximumVertexId();
|
|
const unsigned int max_heap_edge_id = m_heap.MaximumEdgeId();
|
|
const unsigned int max_heap_face_id = m_heap.MaximumFaceId();
|
|
if (m_max_vertex_id < max_heap_vertex_id)
|
|
{
|
|
ON_SUBD_ERROR("m_max_vertex_id is too small.");
|
|
m_max_vertex_id = max_heap_vertex_id;
|
|
}
|
|
if (m_max_edge_id < max_heap_edge_id)
|
|
{
|
|
ON_SUBD_ERROR("m_max_edge_id is too small.");
|
|
m_max_edge_id = max_heap_edge_id;
|
|
}
|
|
if (m_max_face_id < max_heap_face_id)
|
|
{
|
|
ON_SUBD_ERROR("m_max_face_id is too small.");
|
|
m_max_face_id = max_heap_face_id;
|
|
}
|
|
|
|
if (max_vertex_id > m_max_vertex_id)
|
|
m_max_vertex_id = max_vertex_id;
|
|
if (max_edge_id > m_max_edge_id)
|
|
m_max_edge_id = max_edge_id;
|
|
if (max_face_id > m_max_face_id)
|
|
m_max_face_id = max_face_id;
|
|
|
|
const unsigned int archive_version = archive.ArchiveOpenNURBSVersion();
|
|
const unsigned int bad_counts_cutoff_version = ON_VersionNumberConstruct(6, 12, 2018, 12, 12, 0);
|
|
if (archive_version > bad_counts_cutoff_version)
|
|
{
|
|
if( m_max_vertex_id != max_vertex_id
|
|
|| m_max_edge_id != max_edge_id
|
|
|| m_max_face_id != max_face_id
|
|
)
|
|
{
|
|
ON_SUBD_ERROR("Correct m_max_verrtex/edge/face_id differs from value saved in 3dm archive.");
|
|
}
|
|
}
|
|
|
|
if (rc)
|
|
return true;
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
//virtual
|
|
bool ON_SubD::Write(
|
|
ON_BinaryArchive& archive
|
|
) const // override
|
|
{
|
|
for (;;)
|
|
{
|
|
const ON_SubDimple* subdimple = SubDimple();
|
|
unsigned char c = (nullptr == subdimple) ? 0 : 1;
|
|
if (!archive.WriteChar(c))
|
|
break;
|
|
if (nullptr != subdimple)
|
|
{
|
|
if (!subdimple->Write(archive))
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
//virtual
|
|
bool ON_SubD::Read(
|
|
ON_BinaryArchive& archive
|
|
) // override
|
|
{
|
|
Destroy();
|
|
for (;;)
|
|
{
|
|
unsigned char c = 0;
|
|
if (!archive.ReadChar(&c))
|
|
break;
|
|
if (1 == c)
|
|
{
|
|
ON_SubDimple* subdimple = SubDimple(true);
|
|
if ( nullptr == subdimple)
|
|
break;
|
|
if (false == subdimple->Read(archive,*this))
|
|
{
|
|
Destroy();
|
|
break;
|
|
}
|
|
}
|
|
else if ( 0 != c )
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDMeshProxyUserData
|
|
//
|
|
|
|
ON_OBJECT_IMPLEMENT(ON_SubDMeshProxyUserData,ON_UserData,"2868B9CD-28AE-4EA7-8073-BD390B3E97C8");
|
|
|
|
const bool ON_SubDMeshProxyUserData::Internal_MeshHasFaces(const ON_Mesh* mesh)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (nullptr == mesh)
|
|
break;
|
|
if (mesh->m_F.UnsignedCount() <= 0)
|
|
break;
|
|
if (mesh->m_V.UnsignedCount() <= 2)
|
|
break;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const ON_SHA1_Hash ON_SubDMeshProxyUserData::Internal_FaceSHA1(const ON_Mesh* mesh)
|
|
{
|
|
if (false == ON_SubDMeshProxyUserData::Internal_MeshHasFaces(mesh))
|
|
return ON_SHA1_Hash::EmptyContentHash;
|
|
|
|
ON_SHA1 sha1;
|
|
const ON_MeshFace* f = mesh->m_F.Array();
|
|
sha1.AccumulateBytes(f, mesh->m_F.UnsignedCount() * sizeof(*f));
|
|
return sha1.Hash();
|
|
}
|
|
|
|
const ON_SHA1_Hash ON_SubDMeshProxyUserData::Internal_VertexSHA1(const ON_Mesh* mesh)
|
|
{
|
|
if (false == ON_SubDMeshProxyUserData::Internal_MeshHasFaces(mesh))
|
|
return ON_SHA1_Hash::EmptyContentHash;
|
|
|
|
ON_SHA1 sha1;
|
|
const ON_3fPoint* v = mesh->m_V.Array();
|
|
sha1.AccumulateBytes(v, mesh->m_V.UnsignedCount() * sizeof(*v));
|
|
return sha1.Hash();
|
|
}
|
|
|
|
|
|
void ON_SubDMeshProxyUserData::Internal_CopyFrom(const ON_SubDMeshProxyUserData& src)
|
|
{
|
|
if ( src.IsValid() )
|
|
{
|
|
m_subd = new ON_SubD(*src.m_subd);
|
|
m_mesh_face_count = src.m_mesh_face_count;
|
|
m_mesh_vertex_count = src.m_mesh_vertex_count;
|
|
m_mesh_face_array_sha1 = src.m_mesh_face_array_sha1;
|
|
m_mesh_vertex_array_sha1 = src.m_mesh_vertex_array_sha1;
|
|
}
|
|
}
|
|
|
|
void ON_SubDMeshProxyUserData::Internal_Destroy()
|
|
{
|
|
if (nullptr != m_subd)
|
|
{
|
|
delete m_subd;
|
|
m_subd = nullptr;
|
|
}
|
|
m_mesh_face_count = 0;
|
|
m_mesh_vertex_count = 0;
|
|
m_mesh_face_array_sha1 = ON_SHA1_Hash::EmptyContentHash;
|
|
m_mesh_vertex_array_sha1 = ON_SHA1_Hash::EmptyContentHash;
|
|
}
|
|
|
|
|
|
const ON_SubDDisplayParameters ON_SubDMeshProxyUserData::MeshProxyDisplayParameters()
|
|
{
|
|
return ON_SubDDisplayParameters::CreateFromDisplayDensity(4);
|
|
|
|
}
|
|
|
|
ON_Mesh* ON_SubDMeshProxyUserData::MeshProxyFromSubD(
|
|
const ON_SubD* subd
|
|
)
|
|
{
|
|
ON_Mesh* mesh = nullptr;
|
|
ON_SubD* subd_copy = nullptr;
|
|
|
|
for (;;)
|
|
{
|
|
if (nullptr == subd)
|
|
break;
|
|
subd_copy = new ON_SubD(*subd);
|
|
if (nullptr == subd_copy)
|
|
break;
|
|
mesh = subd_copy->GetControlNetMesh(nullptr);
|
|
if (false == ON_SubDMeshProxyUserData::Internal_MeshHasFaces(mesh))
|
|
break;
|
|
ON_SubDMeshProxyUserData* ud = new ON_SubDMeshProxyUserData();
|
|
ud->m_subd = subd_copy;
|
|
ud->m_mesh_face_count = mesh->FaceUnsignedCount();
|
|
ud->m_mesh_vertex_count = mesh->VertexUnsignedCount();
|
|
ud->m_mesh_face_array_sha1 = ON_SubDMeshProxyUserData::Internal_FaceSHA1(mesh);
|
|
ud->m_mesh_vertex_array_sha1 = ON_SubDMeshProxyUserData::Internal_VertexSHA1(mesh);
|
|
if (false == mesh->AttachUserData(ud))
|
|
{
|
|
ud->m_subd = nullptr;
|
|
delete ud;
|
|
break;
|
|
}
|
|
return mesh;
|
|
}
|
|
|
|
if (nullptr != mesh)
|
|
delete mesh;
|
|
|
|
if (nullptr != subd_copy)
|
|
delete subd_copy;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
ON_SubD* ON_SubDMeshProxyUserData::SubDFromMeshProxy(
|
|
const ON_Mesh* mesh
|
|
)
|
|
{
|
|
ON_SubD* subd = nullptr;
|
|
ON_SubDMeshProxyUserData* ud = nullptr;
|
|
|
|
for (;;)
|
|
{
|
|
if (nullptr == mesh)
|
|
break;
|
|
const ON_UUID udid = ON_CLASS_ID(ON_SubDMeshProxyUserData);
|
|
ud = ON_SubDMeshProxyUserData::Cast(mesh->GetUserData(udid));
|
|
if (nullptr == ud)
|
|
break;
|
|
if (false == ud->IsValid())
|
|
break;
|
|
if (false == ud->ParentMeshValid())
|
|
break;
|
|
subd = ud->m_subd;
|
|
ud->m_subd = nullptr;
|
|
}
|
|
|
|
if (nullptr != ud)
|
|
delete ud;
|
|
|
|
return subd;
|
|
}
|
|
|
|
|
|
bool ON_SubDMeshProxyUserData::IsSubDMeshProxy(
|
|
const ON_Mesh* mesh
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
ON_SubDMeshProxyUserData::ON_SubDMeshProxyUserData()
|
|
{
|
|
m_userdata_uuid = ON_CLASS_ID(ON_SubDMeshProxyUserData);
|
|
m_application_uuid = ON_opennurbs6_id; // opennurbs.dll reads/writes this userdata
|
|
// The id must be the version 4 id because
|
|
// V5 SaveAs V4 needs to work.
|
|
m_userdata_copycount = 1;
|
|
}
|
|
|
|
|
|
ON_SubDMeshProxyUserData::~ON_SubDMeshProxyUserData()
|
|
{
|
|
Internal_Destroy();
|
|
}
|
|
|
|
|
|
ON_SubDMeshProxyUserData::ON_SubDMeshProxyUserData(const ON_SubDMeshProxyUserData& src)
|
|
: ON_UserData(src)
|
|
{
|
|
Internal_CopyFrom(src);
|
|
}
|
|
|
|
|
|
ON_SubDMeshProxyUserData& ON_SubDMeshProxyUserData::operator=(const ON_SubDMeshProxyUserData& src)
|
|
{
|
|
if (this != &src)
|
|
{
|
|
Internal_Destroy();
|
|
Internal_CopyFrom(src);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool ON_SubDMeshProxyUserData::Write(ON_BinaryArchive& archive) const
|
|
{
|
|
const int chunk_version = 1;
|
|
if ( false == archive.BeginWrite3dmAnonymousChunk(chunk_version) )
|
|
return false;
|
|
|
|
bool rc = false;
|
|
|
|
for (;;)
|
|
{
|
|
const bool bIsValid = IsValid();
|
|
if (!archive.WriteBool(bIsValid))
|
|
break;
|
|
|
|
if (false == bIsValid)
|
|
{
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
if (!m_subd->Write(archive))
|
|
break;
|
|
if (!archive.WriteInt(m_mesh_face_count))
|
|
break;
|
|
if (!archive.WriteInt(m_mesh_vertex_count))
|
|
break;
|
|
if (!m_mesh_face_array_sha1.Write(archive))
|
|
break;
|
|
if (!m_mesh_vertex_array_sha1.Write(archive))
|
|
break;
|
|
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
if (!archive.EndWrite3dmChunk())
|
|
rc = false;
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_SubDMeshProxyUserData::Read(ON_BinaryArchive& archive)
|
|
{
|
|
Internal_Destroy();
|
|
|
|
int chunk_version = 0;
|
|
if ( false == archive.BeginRead3dmAnonymousChunk(&chunk_version) )
|
|
return false;
|
|
|
|
bool rc = false;
|
|
|
|
for (;;)
|
|
{
|
|
if (chunk_version <= 0)
|
|
break;
|
|
bool bIsValid = false;
|
|
|
|
if (!archive.ReadBool(&bIsValid))
|
|
break;
|
|
|
|
if (false == bIsValid)
|
|
{
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
m_subd = new ON_SubD();
|
|
if (!m_subd->Read(archive))
|
|
break;
|
|
if (!archive.ReadInt(&m_mesh_face_count))
|
|
break;
|
|
if (!archive.ReadInt(&m_mesh_vertex_count))
|
|
break;
|
|
if (!m_mesh_face_array_sha1.Read(archive))
|
|
break;
|
|
if (!m_mesh_vertex_array_sha1.Read(archive))
|
|
break;
|
|
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
if (!archive.EndRead3dmChunk())
|
|
rc = false;
|
|
|
|
if (!rc || !IsValid())
|
|
Internal_Destroy();
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool ON_SubDMeshProxyUserData::ParentMeshValid() const
|
|
{
|
|
for (;;)
|
|
{
|
|
if (!IsValid())
|
|
break;
|
|
|
|
const ON_Mesh* mesh = ON_Mesh::Cast(Owner());
|
|
if (false == ON_SubDMeshProxyUserData::Internal_MeshHasFaces(mesh))
|
|
break;
|
|
|
|
if (m_mesh_face_count != mesh->m_F.UnsignedCount())
|
|
break;
|
|
|
|
if (m_mesh_vertex_count != mesh->m_V.UnsignedCount())
|
|
break;
|
|
|
|
const ON_SHA1_Hash f_sha1 = ON_SubDMeshProxyUserData::Internal_FaceSHA1(mesh);
|
|
if (f_sha1 != m_mesh_face_array_sha1)
|
|
break;
|
|
|
|
const ON_SHA1_Hash v_sha1 = ON_SubDMeshProxyUserData::Internal_VertexSHA1(mesh);
|
|
if (v_sha1 != m_mesh_vertex_array_sha1)
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
|
|
m_mesh_face_count = 0;
|
|
m_mesh_vertex_count = 0;
|
|
m_mesh_face_array_sha1 = ON_SHA1_Hash::EmptyContentHash;
|
|
m_mesh_vertex_array_sha1 = ON_SHA1_Hash::EmptyContentHash;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ON_SubDMeshProxyUserData::IsValid(
|
|
class ON_TextLog* text_log
|
|
) const
|
|
{
|
|
for (;;)
|
|
{
|
|
if (nullptr == m_subd)
|
|
break;
|
|
if (m_mesh_face_count <= 0 )
|
|
break;
|
|
if (m_mesh_vertex_count <= 2 )
|
|
break;
|
|
if (ON_SHA1_Hash::EmptyContentHash == m_mesh_face_array_sha1)
|
|
break;
|
|
if (ON_SHA1_Hash::EmptyContentHash == m_mesh_vertex_array_sha1)
|
|
break;
|
|
if (false == m_userdata_xform.IsIdentity())
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool ON_SubDMeshProxyUserData::GetDescription(ON_wString& description)
|
|
{
|
|
if (IsValid())
|
|
description = L"SubD attached to a valid proxy mesh.";
|
|
else
|
|
description = L"SubD attached to an invalid proxy mesh.";
|
|
return true;
|
|
}
|
|
|
|
|
|
bool ON_SubDMeshProxyUserData::WriteToArchive(
|
|
const class ON_BinaryArchive& archive,
|
|
const class ON_Object* parent_object
|
|
) const
|
|
{
|
|
for (;;)
|
|
{
|
|
if (archive.Archive3dmVersion() < 60)
|
|
break;
|
|
if (false == IsValid())
|
|
return false;
|
|
if (false == ParentMeshValid())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
#endif
|