#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"

#if defined(OPENNURBS_SUBD_WIP)

/////////////////////////////////////////////////////////////////////////////////////////
//
// ON_SubDLimitMeshFragment
// 

unsigned int ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(
  unsigned int display_density
  )
{
  if (display_density <= ON_SubDLimitMesh::MaximumDisplayDensity)
    return (1 << display_density);
  
  return ON_SUBD_RETURN_ERROR(0);
}

unsigned int ON_SubDLimitMeshFragment::DisplayDensityFromSideSegmentCount(
  unsigned int side_segment_count
  )
{
  unsigned int display_density;
  for (display_density = 0; display_density < ON_SubDLimitMesh::MaximumDisplayDensity; display_density++)
  {
    if ( 1U << display_density >= side_segment_count )
      break;
  }

  if ( 1U << display_density == side_segment_count ) 
    return display_density;

  return ON_SUBD_RETURN_ERROR(display_density);
}

unsigned int ON_SubDLimitMeshFragment::PointCountFromDisplayDensity(
  ON_SubD::FacetType facet_type,
  unsigned int mesh_density
  )
{
  unsigned int count = ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(mesh_density);
  if ( 0 == count)
    return 0;
  if ( ON_SubD::FacetType::Quad == facet_type )
    return (count + 1)*(count+1);
  
  if ( ON_SubD::FacetType::Tri == facet_type )
    return ((count+1)*(count+2))/2;

  return ON_SUBD_RETURN_ERROR(0);
}

unsigned int ON_SubDLimitMeshFragment::FaceCountFromDisplayDensity(
  unsigned int mesh_density
  )
{
  unsigned int count = ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(mesh_density);
  return count*count; // same for quads or tris
}


/////////////////////////////////////////////////////////////////////////////////////////
//
// ON_SubDManagedLimitMeshFragment
// 

ON_SubDManagedLimitMeshFragment::ON_SubDManagedLimitMeshFragment() ON_NOEXCEPT
{
  memset(this, 0, sizeof(*this));
}

ON_SubDManagedLimitMeshFragment::~ON_SubDManagedLimitMeshFragment() ON_NOEXCEPT
{
  Destroy();
}

ON_SubDManagedLimitMeshFragment::ON_SubDManagedLimitMeshFragment(const ON_SubDManagedLimitMeshFragment& src) ON_NOEXCEPT
{
  Clear();
  CopyHelper(src);
}

ON_SubDManagedLimitMeshFragment& ON_SubDManagedLimitMeshFragment::operator=(const ON_SubDManagedLimitMeshFragment& src) ON_NOEXCEPT
{
  if (this != &src)
  {
    Clear();
    CopyHelper(src);
  }
  return *this;
}

#if defined(ON_HAS_RVALUEREF)
// rvalue copy constructor
ON_SubDManagedLimitMeshFragment::ON_SubDManagedLimitMeshFragment( ON_SubDManagedLimitMeshFragment&& src ) ON_NOEXCEPT
{
  memcpy(this, &src, sizeof(*this));
  src.m_storage = nullptr;
  src.m_storage_capacity = 0;
}

// rvalue assignment operator
ON_SubDManagedLimitMeshFragment& ON_SubDManagedLimitMeshFragment::operator=( ON_SubDManagedLimitMeshFragment&& src ) ON_NOEXCEPT
{
  if (this != &src)
  {
    Destroy();
    memcpy(this, &src, sizeof(*this));
    src.m_storage = nullptr;
    src.m_storage_capacity = 0;
  }
  return *this;
}

#endif

bool ON_SubDManagedLimitMeshFragment::ReserveCapacity(
  ON_SubD::FacetType facet_type,
  unsigned int mesh_density
  ) ON_NOEXCEPT
{
  Clear();

  unsigned int fragment_side_count = ON_SubDManagedLimitMeshFragment::SideSegmentCountFromDisplayDensity(mesh_density);
  if ( 0 == fragment_side_count )
    return true;

  // Sanity check
  if ( fragment_side_count > 0xFFU )
    return ON_SUBD_RETURN_ERROR(false);

  const unsigned int short_capacity_sanity_check = 0xFFFFU;

  const unsigned int P_capacity = ON_SubDManagedLimitMeshFragment::PointCountFromDisplayDensity(facet_type,mesh_density);
  if ( P_capacity >= short_capacity_sanity_check )
    return ON_SUBD_RETURN_ERROR(false);

  const unsigned int F_capacity = ON_SubDManagedLimitMeshFragment::FaceCountFromDisplayDensity(mesh_density);
  if ( F_capacity >= short_capacity_sanity_check)
    return ON_SUBD_RETURN_ERROR(false);

  const size_t P_stride = 3;
  const size_t N_stride = 3;

  size_t storage_capacity = (P_stride + N_stride)*P_capacity;
  if (storage_capacity > m_storage_capacity || nullptr == m_storage)
  {
    if (m_storage_capacity > 0 && nullptr != m_storage)
    {
      delete[] m_storage;
      m_storage = nullptr;
    }

    m_storage = new(std::nothrow) double[storage_capacity];
    if (nullptr == m_storage)
      return ON_SUBD_RETURN_ERROR(false);
    m_storage_capacity = storage_capacity;
  }

  m_P_capacity = (unsigned short)P_capacity;

  m_P_stride = P_stride;
  m_N_stride = N_stride;

  m_P = m_storage;
  m_N = m_P + (3*P_capacity);

  m_grid = ON_SubDLimitMeshFragmentGrid::Facets(facet_type,fragment_side_count,0U);
  if ( nullptr == m_grid.m_F)
    return ON_SUBD_RETURN_ERROR(false);

  return true;
}

void ON_SubDManagedLimitMeshFragment::Clear() ON_NOEXCEPT
{
  memset(this, 0, sizeof(ON_SubDLimitMeshFragment));
}

void ON_SubDManagedLimitMeshFragment::Destroy() ON_NOEXCEPT
{
  double* p = const_cast<double*>(m_P);
  memset(this, 0, sizeof(*this));
  if ( nullptr != p)
    delete[] p;
}

void ON_SubDManagedLimitMeshFragment::CopyHelper(const ON_SubDLimitMeshFragment& src)
{
  unsigned short P_count = (nullptr != src.m_P && src.m_P_stride >= 3) ? src.m_P_count : 0U;
  unsigned short N_count = (nullptr != src.m_N && src.m_N_stride >= 3) ? src.m_P_count : 0U;

  const size_t P_stride = 3;
  const size_t N_stride = 3;

  size_t storage_capacity = P_stride*P_count + N_stride*N_count;
  if (storage_capacity > 0)
  {
    double* p = new(std::nothrow) double[storage_capacity];
    if (nullptr == p)
    {
      ON_SubDIncrementErrorCount();
      return;
    }
    m_storage = p;
    m_storage_capacity = storage_capacity;
    if (P_count > 0)
    {
      m_P = p;
      m_P_count = P_count;
      m_P_stride = P_stride;
      double* p1 = p + (m_P_stride*P_count);
      const double* srcP = src.m_P;
      while (p < p1)
      {
        p[0] = srcP[0];
        p[1] = srcP[1];
        p[2] = srcP[2];
        p += P_stride;
        srcP += src.m_P_stride;
      }
    }
    if (N_count > 0)
    {
      m_N = p;
      m_P_count = N_count; // correct m_P_count counts both m_P and m_N.
      m_N_stride = N_stride;
      double* p1 = p + (m_N_stride*N_count);
      const double* srcN = src.m_N;
      while (p < p1)
      {
        p[0] = srcN[0];
        p[1] = srcN[1];
        p[2] = srcN[2];
        p += N_stride;
        srcN += src.m_N_stride;
      }
    }

    m_grid = src.m_grid;
  }
}

ON_SubDManagedLimitMeshFragment ON_SubDManagedLimitMeshFragment::Create(const ON_SubDLimitMeshFragment& src) ON_NOEXCEPT
{
  ON_SubDManagedLimitMeshFragment mf;
  mf.CopyHelper(src);
  return mf;
}


/////////////////////////////////////////////////////////////////////////////////////////
//
// ON_SubDLimitMeshFragmentGrid
// 

unsigned int ON_SubDLimitMeshFragmentGrid::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
  )
{
  if ( false == ON_SubDLimitMeshFragment::SideSegmentCountIsValid(side_segment_count) )
    return ON_SUBD_RETURN_ERROR(0);
  
  if ( side_segment_count <= 1 )
    level_of_detail = 0;
  else if (level_of_detail > 0)
  {
    if (level_of_detail >= 32 || 1U << level_of_detail > side_segment_count)
    {
      level_of_detail = 1; 
      while ( 2*level_of_detail < side_segment_count )
        level_of_detail*= 2;
    }
  }


  const unsigned int side_point_count = (side_segment_count+1);
  const unsigned int P_di = (1 << level_of_detail);
  const unsigned int P_dj = P_di*side_point_count;

  const unsigned int side_quad_count = side_segment_count / P_di;

  if (quad_capacity > 0 || nullptr != quads)
  {
    if ( quad_stride < 4 )
      return ON_SUBD_RETURN_ERROR(0);
    if (side_quad_count*side_quad_count > quad_capacity)
      return ON_SUBD_RETURN_ERROR(0);

    unsigned int* fvi = quads;
    for (unsigned int n = 0; n < side_quad_count; n++)
    {
      unsigned int vi0 = n*P_dj;
      unsigned int vi1 = vi0 + P_dj;
      for (const unsigned int* fvi1 = fvi + quad_stride*side_quad_count; fvi < fvi1; fvi += quad_stride)
      {
        fvi[0] = vi0;
        fvi[3] = vi1;
        vi0 += P_di;
        vi1 += P_di;
        fvi[1] = vi0;
        fvi[2] = vi1;
      }
    }
  }

  if (side_capacity > 0 || nullptr != sides)
  {
    if ( side_stride < 1 )
      return ON_SUBD_RETURN_ERROR(0);
    if (side_capacity < 4*side_quad_count +1 )
      return ON_SUBD_RETURN_ERROR(0);
    
    unsigned int vi = 0;

    for (unsigned int* sides1 = sides + side_quad_count; sides < sides1; sides += side_stride)
    {
      *sides = vi;
      vi += P_di;
    }

    for (unsigned int* sides1 = sides + side_quad_count; sides < sides1; sides += side_stride)
    {
      *sides = vi;
      vi += P_dj;
    }
    
    for (unsigned int* sides1 = sides + side_quad_count; sides < sides1; sides += side_stride)
    {
      *sides = vi;
      vi -= P_di;
    }
    
    for (unsigned int* sides1 = sides + side_quad_count; sides < sides1; sides += side_stride)
    {
      *sides = vi;
      vi -= P_dj;
    }

    *sides = 0;
  }

  return side_quad_count*side_quad_count;
}

bool ON_SubDLimitMeshFragment::SideSegmentCountIsValid(
  unsigned int side_segment_count
  )
{
  if (side_segment_count > 0 && side_segment_count <= ON_SubDLimitMeshFragment::MaximumSideSegmentCount)
  {
    for (unsigned int n = 1; n <= side_segment_count; n *= 2)
    {
      if (n == side_segment_count)
        return true;
    }
  }

  return ON_SUBD_RETURN_ERROR(false);
}

unsigned int ON_SubDLimitMeshFragment::SidePointCountFromSideCount(
  unsigned int side_segment_count
  )
{
  return ON_SubDLimitMeshFragment::SideSegmentCountIsValid(side_segment_count) ? (side_segment_count+1) : 0U;
}

unsigned int ON_SubDLimitMeshFragment::QuadGridPointCountFromSideCount(
  unsigned int side_segment_count
  )
{
  unsigned int side_point_count = ON_SubDLimitMeshFragment::SidePointCountFromSideCount(side_segment_count);
  return side_point_count*side_point_count;
}

unsigned int ON_SubDLimitMeshFragment::QuadGridQuadCountFromSideCount(
  unsigned int side_segment_count
  )
{
  return ON_SubDLimitMeshFragment::SideSegmentCountIsValid(side_segment_count) ? (side_segment_count*side_segment_count) : 0U;
}


const class ON_SubDEdge* ON_SubDLimitMeshFragment::Edge(
  unsigned int grid_side_index
  ) const
{
  return EdgePtr(grid_side_index).Edge();
}

const class ON_SubDEdgePtr ON_SubDLimitMeshFragment::EdgePtr(
  unsigned int grid_side_index
  ) const
{
  const int grid_side_count = 4; // will be 3 for tri grid
  if (nullptr != m_face && grid_side_index < grid_side_count)
  {
    unsigned short fei = m_face_vertex_index[grid_side_index];
    if (fei < m_face->m_edge_count)
      return m_face->EdgePtr(fei);
    grid_side_index = (grid_side_index+grid_side_count-1) % grid_side_count;
    fei = m_face_vertex_index[grid_side_index];
    if (fei < m_face->m_edge_count)
      return m_face->EdgePtr(fei);
  }
  return ON_SubDEdgePtr::Null;
}

const class ON_SubDVertex* ON_SubDLimitMeshFragment::Vertex(
  unsigned int grid_corner_index
  ) const
{
  ON_SubDEdgePtr eptr = EdgePtr(grid_corner_index);
  const ON_SubDEdge* edge = eptr.Edge();
  if ( nullptr != edge )
    return edge->m_vertex[eptr.EdgeDirection()];
  return nullptr;
}

ON_3dPoint ON_SubDLimitMeshFragment::CornerPoint(
  unsigned int grid_corner_index
  ) const
{
  if ( grid_corner_index >= 4 || nullptr == m_P || m_P_stride <= 0 || nullptr == m_grid.m_S )
    return ON_3dPoint::UnsetPoint;
  
  //unsigned int i = grid_corner_index*m_grid.m_side_segment_count + 1;
  unsigned int i = grid_corner_index*m_grid.m_side_segment_count;

  return ON_3dPoint(m_P + (i*m_P_stride));
}



const class ON_SubDVertexPtr ON_SubDLimitMeshFragment::VertexPtr(
  unsigned int grid_corner_index
  ) const
{
  return ON_SubDVertexPtr::Create(Vertex(grid_corner_index));
}

ON_ComponentStatus ON_SubDLimitMeshFragment::Status() const
{
  return (nullptr == m_face) ? ON_ComponentStatus::NoneSet : m_face->m_status;
}


bool ON_SubDLimitMeshFragment::IsSubFragment() const
{
  return ( nullptr != m_face && m_face_vertex_index[0] < ON_SubDFace::MaximumEdgeCount );
}


bool ON_SubDLimitMeshFragment::IsCompleteFragment() const
{
  return ( 
    nullptr != m_face 
    && m_face_vertex_index[0] < ON_SubDFace::MaximumEdgeCount 
    && m_face_vertex_index[1] < ON_SubDFace::MaximumEdgeCount 
    && m_face_vertex_index[2] < ON_SubDFace::MaximumEdgeCount 
    );
}

unsigned int ON_SubDLimitMeshFragmentGrid::SideSegmentCount() const
{
  unsigned int side_segment_count = 1;
  while( side_segment_count*side_segment_count < m_F_count)
    side_segment_count *= 2;
  return (side_segment_count*side_segment_count == m_F_count) ? side_segment_count : 0;
}

unsigned int ON_SubDLimitMeshFragmentGrid::GridId() const
{
  for (;;)
  {
    if (nullptr != m_F)
      break;

    // m_F_count = 2^(2n)
    for (unsigned int id = 0; id <= 16; id += 2)
    {
      if ((1U << id) == m_F_count)
      {
        id /= 2; // id = "n"
        unsigned int lod = (m_F_level_of_detail > id) ? id : (unsigned int)m_F_level_of_detail;
        id = 32*id + 2*lod;
        if ( ON_SubD::FacetType::Tri == m_F_type)
          id += 1;
        else if ( ON_SubD::FacetType::Quad != m_F_type)
          break;
        return id;
      }
    }
  }
  
  return 0;
}

unsigned int ON_SubDLimitMeshFragmentGrid::GridFacetSideCount() const
{
  switch (m_F_type)
  {
  case ON_SubD::FacetType::Tri:
    return 3;
  case ON_SubD::FacetType::Quad:
    return 4;
  }
  return 0;
}

bool ON_SubDLimitMeshFragmentGrid::GetGridParameters(
  unsigned int grid_point_index,
  double grid_parameters[2]
  ) const
{
  for (;;)
  {
    const unsigned int side_segment_count = SideSegmentCount();
    if ( 0 == side_segment_count )
      break;
    const unsigned int grid_side_point_count = side_segment_count + 1;
    if (grid_point_index >= grid_side_point_count*grid_side_point_count)
      break;
    unsigned int i = grid_point_index % grid_side_point_count;
    unsigned int j = grid_point_index / grid_side_point_count;
    if ( 0 == i )
      grid_parameters[0] = 0.0;
    else if ( i == grid_side_point_count)
      grid_parameters[0] = 1.0;
    else
      grid_parameters[0] = i*1.0 / ((double)grid_side_point_count);
    if ( 0 == j )
      grid_parameters[1] = 0.0;
    else if ( j == grid_side_point_count)
      grid_parameters[1] = 1.0;
    else
      grid_parameters[1] = j*1.0 / ((double)grid_side_point_count);
    return true;
  }
  grid_parameters[0] = ON_UNSET_VALUE;
  grid_parameters[1] = ON_UNSET_VALUE;
  return false;
}

ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Facets(
  ON_SubD::FacetType facet_type,
  unsigned int side_segment_count,
  unsigned int level_of_detail
  )
{
  if (ON_SubD::FacetType::Quad == facet_type)
    return ON_SubDLimitMeshFragmentGrid::Quads(side_segment_count, level_of_detail);
  
  if (ON_SubD::FacetType::Tri == facet_type)
    return ON_SubDLimitMeshFragmentGrid::Quads(side_segment_count, level_of_detail);
  
  return ON_SubDLimitMeshFragmentGrid::Empty;
}

ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Tris(
  unsigned int side_segment_count,
  unsigned int level_of_detail
  )
{
  return ON_SUBD_RETURN_ERROR(ON_SubDLimitMeshFragmentGrid::Empty);
}

ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Quads(
  unsigned int side_segment_count,
  unsigned int level_of_detail
  )
{
  static const ON_SubDLimitMeshFragmentGrid* grid_cache[9] = { 0 }; // side_segment_count = 1,2,4,8,16,32,64,128,256

  const unsigned int grid_cache_index = ON_SubDLimitMeshFragment::DisplayDensityFromSideSegmentCount(side_segment_count);
  if ( side_segment_count != ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(grid_cache_index) )
    return ON_SUBD_RETURN_ERROR(ON_SubDLimitMeshFragmentGrid::Empty);

  const ON_SubDLimitMeshFragmentGrid* fragment_grid = grid_cache[grid_cache_index];
  if (nullptr != fragment_grid)
  {
    while ( fragment_grid->m_F_level_of_detail < level_of_detail && nullptr != fragment_grid->m_next_level_of_detail)
      fragment_grid = fragment_grid->m_next_level_of_detail;
    return *fragment_grid;
  }

#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE)
  // The code below is thread safe and constructs the ON_SubDLimitMeshFragmentGrids
  // that are resources shared by all ON_SubDLimitMeshFragments.

  static ON_SleepLock lock;
  const bool bReturnLock = lock.GetLock(50,ON_SleepLock::OneMinute,true);
#endif


  // The ON_SubDLimitMeshFragmentGrid classes created below are created one time as needed
  // and used millions of times after that. These are app workspace allocations
  // and not memory leaks. Once a grid exists, it is saved in grid_cache[] and returned
  // above the next zillion times it is required.
  ON_MemoryAllocationTracking disable_tracking(false);

  // make the requested grid
  unsigned int quad_capacity = 0;
  unsigned int side_segment_capacity = 0;
  unsigned int grid_count = 0;
  unsigned int grid_cache_index0 = grid_cache_index;
  unsigned int grid_cache_index1 = grid_cache_index;
  if (grid_cache_index <= 4)
  {
    // make all the common small grids
    grid_cache_index0 = 0;
    grid_cache_index1 = 4;
  }
  for (unsigned int i = grid_cache_index0; i <= grid_cache_index1; i++)
  {
    // allocate all levels of detail for each segment side count
    unsigned int s1 = (1U << i);
    for (unsigned int s2 = s1; s2 > 0; s2 /= 2)
    {
      quad_capacity += ON_SubDLimitMeshFragment::QuadGridQuadCountFromSideCount(s2);
      side_segment_capacity += 4*s2 + 1;
      grid_count++;
    }
  }            


  const unsigned int vdex_capacity = (quad_capacity*4 + side_segment_capacity);
  size_t sz1 = grid_count*sizeof(ON_SubDLimitMeshFragmentGrid);
  size_t sz2 = vdex_capacity*sizeof(unsigned int);
  if (0 != sz2 % sizeof(ON_SubDLimitMeshFragmentGrid))
    sz2 = (1 + sz2/sizeof(ON_SubDLimitMeshFragmentGrid))*sizeof(ON_SubDLimitMeshFragmentGrid);
  ON_SubDLimitMeshFragmentGrid* grids = new (std::nothrow) ON_SubDLimitMeshFragmentGrid[(sz1 + sz2)/sizeof(ON_SubDLimitMeshFragmentGrid)];

  ON_SubDLimitMeshFragmentGrid grid = ON_SubDLimitMeshFragmentGrid::Empty;
  grid.m_F_type = ON_SubD::FacetType::Quad;
  grid.m_F_stride = 4;
  unsigned int* vdex0 = (unsigned int*)(grids + grid_count);
  unsigned int* vdex1 = vdex0 + vdex_capacity;
  unsigned int* vdex = vdex0;

  for (unsigned int i = grid_cache_index0; i <= grid_cache_index1; i++)
  {
    const unsigned int s1 = (1U << i);
    ON_SubDLimitMeshFragmentGrid* first_lod = grids;
    ON_SubDLimitMeshFragmentGrid* prev_lod = nullptr;
    grid.m_F_level_of_detail = 0;
    for (unsigned int s2 = s1; s2 > 0; s2 /= 2, grids++)
    {
      const unsigned int grid_F_capacity = ON_SubDLimitMeshFragment::QuadGridQuadCountFromSideCount(s2);
      const unsigned int grid_S_capacity = 4*s2 + 1;

      grid.m_side_segment_count = (unsigned char)s2;
      grid.m_F_count = (unsigned short)grid_F_capacity;
      grid.m_F = vdex;
      vdex += 4*grid_F_capacity;
      grid.m_S = vdex;
      vdex += grid_S_capacity;

      if (vdex > vdex1)
      {
        ON_SubDIncrementErrorCount();
        break;
      }

      ON_SubDLimitMeshFragmentGrid::SetQuads(
        s1, // top level side_segment_count
        grid.m_F_level_of_detail,
        grid_F_capacity,
        grid.m_F_stride,
        const_cast<unsigned int*>(grid.m_F),
        grid_S_capacity,
        1U,
        const_cast<unsigned int*>(grid.m_S)
        );

      *grids = grid;
      if ( nullptr != prev_lod )
      {
        grids->m_prev_level_of_detail = prev_lod;
        prev_lod->m_next_level_of_detail = grids;
      }
      prev_lod = grids;

      grid.m_F += (grid.m_F_count*grid.m_F_stride);
      grid.m_F_level_of_detail++;
    }

    // Do not initialize grid_cache[i] until entire linked list is ready to be used.
    // This way if the lock is stolen for some unforseen reason, we risk leaking memory
    // but we will not crash.
    grid_cache[i] = first_lod;
  }           

#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE)
  if ( bReturnLock )
    lock.ReturnLock();
#endif

  if (vdex != vdex1)
  {
    ON_SubDIncrementErrorCount();
  }

  fragment_grid = grid_cache[grid_cache_index];
  if (nullptr != fragment_grid)
  {
    while ( fragment_grid->m_F_level_of_detail < level_of_detail && nullptr != fragment_grid->m_next_level_of_detail)
      fragment_grid = fragment_grid->m_next_level_of_detail;
    return *fragment_grid;
  }

  return ON_SUBD_RETURN_ERROR(ON_SubDLimitMeshFragmentGrid::Empty);
}

//////////////////////////////////////////////////////////////////////////
//
// ON_SubDLimitMesh
//

void ON_SubDLimitMeshImpl::ClearFragmentFacePointers()
{
  // When the ON_SubDimple that manages the faces referenced by
  // fragment->m_face is deleted, fragment->m_face must be set to zero.
  if (nullptr != m_first_fragment && nullptr != m_first_fragment->m_face)
  {
    for (auto fragment = m_first_fragment; nullptr != fragment; fragment = fragment->m_next_fragment)
      fragment->m_face = nullptr;
  }
}
//
//bool ON_SubDLimitMeshImpl::ReserveCapacityx(
//  const class ON_SubD& subd,
//  ON_SubDDisplayParameters limit_mesh_parameters
//  )
//{
//  const unsigned int level_index = limit_mesh_parameters.m_level_index;
//
//  unsigned int subd_fragment_count = subd.LimitSurfaceMeshFragmentCount(level_index);
//  if ( subd_fragment_count < 1 )
//    return ON_SUBD_RETURN_ERROR(false);
//
//  ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subd.LevelSubDType(level_index));
//
//  return ReserveCapacity(
//    subd_fragment_count,
//    facet_type,
//    limit_mesh_parameters.m_display_density,
//    level_index);
//}

bool ON_SubDLimitMeshImpl::ReserveCapacity(
  unsigned int subd_fragment_count,
  ON_SubD::FacetType facet_type,
  unsigned int display_density
  )
{
  ClearTree();

  m_display_density = 0;
  m_facet_type = ON_SubD::FacetType::Unset;
  m_fragment_count = 0;
  m_fragment_point_count = 0;
  m_first_fragment = nullptr;

  if ( display_density > ON_SubDLimitMesh::MaximumDisplayDensity)
    return ON_SUBD_RETURN_ERROR(false);

  unsigned int fragment_point_count = ON_SubDLimitMeshFragment::PointCountFromDisplayDensity(facet_type, display_density);
  if ( subd_fragment_count < 1 )
    return ON_SUBD_RETURN_ERROR(false);

  size_t sizeof_point_and_normals = 6*fragment_point_count*sizeof(double);
  size_t sizeof_fragment = sizeof(ON_SubDLimitMeshFragment);
  if (0 != sizeof_fragment % sizeof(double))
  {
    sizeof_fragment = (1 + sizeof_fragment / sizeof(double))*sizeof(double);
  }

  if( false == m_fsp.Create(sizeof_fragment + sizeof_point_and_normals,subd_fragment_count,0))
    return ON_SUBD_RETURN_ERROR(false);

  m_display_density = display_density;
  m_facet_type = facet_type;
  m_fragment_point_count = fragment_point_count;

  return true;
}

ON_SubDLimitMeshFragment* ON_SubDLimitMeshImpl::CopyCallbackFragment(
    const ON_SubDLimitMeshFragment* callback_fragment
    )
{
  if ( nullptr == callback_fragment)
    return ON_SUBD_RETURN_ERROR(nullptr);
  if ( 0 == callback_fragment->m_P_count )
    return ON_SUBD_RETURN_ERROR(nullptr);
  if ( callback_fragment->m_P_count > m_fragment_point_count )
    return ON_SUBD_RETURN_ERROR(nullptr);
  if ( nullptr == callback_fragment->m_P || nullptr == callback_fragment->m_N )
    return ON_SUBD_RETURN_ERROR(nullptr);

  double* P = (double*)m_fsp.AllocateDirtyElement();
  if ( nullptr == P)
    return ON_SUBD_RETURN_ERROR(nullptr);

  double* N = P + 3*m_fragment_point_count;
  ON_SubDLimitMeshFragment* fragment = (ON_SubDLimitMeshFragment*)(N + 3*m_fragment_point_count);
  
  *fragment = *callback_fragment;

  const size_t fragment_stride = 3;
  fragment->m_P_capacity = (unsigned short)m_fragment_point_count;
  fragment->m_P_stride = fragment_stride;
  fragment->m_P = P;
  fragment->m_N_stride = fragment_stride;
  fragment->m_N = N;


  size_t srcP_stride = callback_fragment->m_P_stride;
  const double* srcP = callback_fragment->m_P;
  const double* srcP1 = srcP + srcP_stride*callback_fragment->m_P_count;
  while ( srcP < srcP1 )
  {
    P[0] = srcP[0];
    P[1] = srcP[1];
    P[2] = srcP[2];
    P += fragment_stride;
    srcP += srcP_stride;
  }

  srcP_stride = callback_fragment->m_N_stride;
  srcP = callback_fragment->m_N;
  srcP1 = srcP + srcP_stride*callback_fragment->m_P_count;
  while ( srcP < srcP1 )
  {
    N[0] = srcP[0];
    N[1] = srcP[1];
    N[2] = srcP[2];
    N += fragment_stride;
    srcP += srcP_stride;
  }

  m_limit_mesh_content_serial_number = ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber();

  return fragment;
}

bool ON_SubDLimitMeshImpl::AddFinishedFragment(
  ON_SubDLimitMeshFragment* finished_fragment
  )
{
  if ( nullptr == finished_fragment)
    return ON_SUBD_RETURN_ERROR(false);
  if ( 0 == finished_fragment->m_P_count )
    return ON_SUBD_RETURN_ERROR(false);
  if ( finished_fragment->m_P_count > m_fragment_point_count )
    return ON_SUBD_RETURN_ERROR(false);
  if ( nullptr == finished_fragment->m_P || nullptr == finished_fragment->m_N )
    return ON_SUBD_RETURN_ERROR(false);

  m_fragment_count++;

  if (nullptr == m_first_fragment)
  {
    m_first_fragment = finished_fragment;
    m_last_fragment = finished_fragment;
    m_bbox = finished_fragment->m_bbox;
  }
  else
  {
    m_last_fragment->m_next_fragment = finished_fragment;
    finished_fragment->m_prev_fragment = m_last_fragment;
    m_last_fragment = finished_fragment;
    m_bbox.Union(finished_fragment->m_bbox);
  }
  
  m_limit_mesh_content_serial_number = ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber();

  return true;
}

unsigned int ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber()
{
  static unsigned int serial_number = 0;
  serial_number++;
  return serial_number;
}

ON_SubDLimitMeshImpl::ON_SubDLimitMeshImpl()
  : m_limit_mesh_content_serial_number(ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber())
{}

ON_SubDLimitMeshImpl::ON_SubDLimitMeshImpl(
  const class ON_SubDLimitMeshImpl& src
  )
  : m_limit_mesh_content_serial_number(ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber())
{
  if ( nullptr != src.m_first_fragment && ReserveCapacity((unsigned int)src.m_fsp.ActiveElementCount(), src.m_facet_type, src.m_display_density ) )
  {
    for (const ON_SubDLimitMeshFragment* src_fragment = src.m_first_fragment; nullptr != src_fragment; src_fragment = src_fragment->m_next_fragment)
    {
      ON_SubDLimitMeshFragment* fragment = CopyCallbackFragment(src_fragment);
      AddFinishedFragment(fragment);
    }
  }
}

void ON_SubDLimitMeshImpl::ClearTree()
{
  if (nullptr != m_fragment_tree)
  {
    delete m_fragment_tree;
    m_fragment_tree = nullptr;
  }
}

const ON_RTree& ON_SubDLimitMeshImpl::FragmentTree() const
{
  if (nullptr != m_fragment_tree && nullptr != m_first_fragment)
  {
    double Pbox[2][3];
    ON_RTree* fragment_tree = new ON_RTree();
    for (const ON_SubDLimitMeshFragment* fragment = m_first_fragment; nullptr != fragment; fragment = fragment->m_next_fragment)
    {
      const double* P = fragment->m_P;
      const size_t P_stride = fragment->m_P_stride;
      if (nullptr == P || P_stride < 3 || fragment->m_P_count <= 0)
        continue;
      const double* P1 = P + P_stride*fragment->m_P_count;
      Pbox[0][0] = Pbox[1][0] = P[0];
      Pbox[0][1] = Pbox[1][1] = P[1];
      Pbox[0][2] = Pbox[1][2] = P[2];
      for (P += P_stride; P < P1; P += P_stride)
      {
        if (P[0] < Pbox[0][0])
          Pbox[0][0] = P[0];
        else if (P[0] > Pbox[1][0])
          Pbox[1][0] = P[0];
        if (P[1] < Pbox[0][1])
          Pbox[0][1] = P[1];
        else if (P[1] > Pbox[1][1])
          Pbox[1][1] = P[1];
        if (P[2] < Pbox[0][2])
          Pbox[0][2] = P[2];
        else if (P[2] > Pbox[1][2])
          Pbox[1][2] = P[2];
        fragment_tree->Insert(Pbox[0], Pbox[1], (void*)fragment);
      }
    }
    const_cast< ON_SubDLimitMeshImpl* >(this)->m_fragment_tree = fragment_tree;
  }
  return (nullptr == m_fragment_tree ) ? ON_RTree::Empty : *m_fragment_tree;
}

bool ON_SubDLimitMeshImpl::GetTightBoundingBox(
  ON_BoundingBox& bbox,
  bool bGrowBox,
  const ON_Xform* xform
  ) const
{
  ON_BoundingBox local_bbox = ON_BoundingBox::EmptyBoundingBox;
  if (nullptr != xform && xform->IsIdentity())
    xform = nullptr;

  if (nullptr == xform)
  {
    local_bbox = m_bbox;
  }
  else
  {
    for (const ON_SubDLimitMeshFragment* fragment = m_first_fragment; nullptr != fragment; fragment = fragment->m_next_fragment)
    {
      ON_GetPointListBoundingBox(3, 0, fragment->m_P_count, (int)fragment->m_P_stride, fragment->m_P, local_bbox, fragment != m_first_fragment, xform);
    }
  }

  if (bGrowBox && bbox.IsValid())
    bbox.Union(local_bbox);
  else
    bbox = local_bbox;

  return true;
}

class /*DO NOT EXPORT*/ON_SubDLimitMeshImpl_CallbackContext
{
public:
#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE)
  bool m_bUseMultipleThreads = false; // If true, callback uses the lock
  ON_SleepLock m_lock;
#endif
  ON_SubDLimitMeshImpl* m_impl = nullptr;
  ON_SimpleArray< ON_SubDLimitMeshFragment* > m_face_mesh_fragments;

  static bool FragmentCallbackFunction(
    ON__UINT_PTR impl_context_as_void,
    const class ON_SubDLimitMeshFragment* fragment
    );

  /*
  Description:
    compares the face id and then the fragment index.
  Parameters:
    a - [in]
    b - [in]
      The caller insures that a, b, a->m_face and b->m_face are not nullptr.
  */
  static int CompareFragmentIndex(
    ON_SubDLimitMeshFragment*const* a,
    ON_SubDLimitMeshFragment*const* b
    );
  
  /*
  Description:
    Once all the mesh fragments for a face are delivered and sorted,
    this function is called to make coincident vertices have identical
    locations and normals.

  Parameters:
     count - [in]
       Number of elements in mesh_fragments[].  This should be the same
       number as sub_fragments[i].m_face_fragment_count.
     mesh_fragments - [in]
       Every element in the array should have the same ON_SubDLimitMeshFragment.m_face->m_id
  */
  static bool ProcessSortedFragments(
    unsigned int count,
    ON_SubDLimitMeshFragment** mesh_fragments
    );

  static bool CoincidentSideCopy(
    ON_SubDLimitMeshFragment* prev_fragment,
    ON_SubDLimitMeshFragment* fragment
    );
};

bool ON_SubDLimitMeshImpl_CallbackContext::FragmentCallbackFunction(
  ON__UINT_PTR callback_context_as_void,
  const class ON_SubDLimitMeshFragment* callback_fragment
  )
{
  bool bContinueMeshCalculation = true;

  ON_SubDLimitMeshImpl_CallbackContext* context = (ON_SubDLimitMeshImpl_CallbackContext*)callback_context_as_void;

#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE)
  bool bReleaseLock = false;
  if (context->m_bUseMultipleThreads)
  {
    if (false == context->m_lock.GetLock(0, ON_SleepLock::OneSecond))
    {
      // return true to keep going, but something is really hogging the lock
      return ON_SUBD_RETURN_ERROR(bContinueMeshCalculation);
    }
  }
#endif

  for (;;)
  {
    ON_SubDLimitMeshFragment* impl_managed_fragment = context->m_impl->CopyCallbackFragment(callback_fragment);
    if (nullptr == impl_managed_fragment)
    {
      ON_SubDIncrementErrorCount();
      bContinueMeshCalculation = false; // terminate meshing
      break;
    }

    const unsigned int face_fragment_count = impl_managed_fragment->m_face_fragment_count;

    const unsigned int face_id 
      = (nullptr == impl_managed_fragment->m_face)
      ? 0
      : impl_managed_fragment->m_face->m_id;


    if ( 0 == face_id || ON_UNSET_UINT_INDEX == face_id
      || face_fragment_count <= 1
      || impl_managed_fragment->m_face_fragment_index >= face_fragment_count
      )
    {
      // simple case where no additional processing is reqired
      context->m_impl->AddFinishedFragment(impl_managed_fragment);
      break;
    }


    unsigned int count = context->m_face_mesh_fragments.UnsignedCount();

    if (count + 1 < face_fragment_count)
    {
      // waiting on more fragments
      context->m_face_mesh_fragments.Append(impl_managed_fragment);
      break;
    }

    ON_SubDLimitMeshFragment** mesh_fragments = context->m_face_mesh_fragments.Array();
    unsigned int delivered_mesh_fragment_count = 0;
    unsigned int i0 = ON_UNSET_UINT_INDEX;
    for (unsigned int i = 0; i < count; i++)
    {
      if ( face_id != mesh_fragments[i]->m_face->m_id)
        continue;
      if ( 0 == delivered_mesh_fragment_count++)
        i0 = i;
      if ( delivered_mesh_fragment_count + 1 == face_fragment_count )
        break;
    }

    if (delivered_mesh_fragment_count + 1 < face_fragment_count)
    {
      // waiting on more fragments
      context->m_face_mesh_fragments.Append(impl_managed_fragment);
      break;
    }

    // copy the fragments we need to process to local storage I can release the lock.
    ON_SimpleArray< ON_SubDLimitMeshFragment* > local_mesh_fragments(face_fragment_count);
    if (delivered_mesh_fragment_count == count)
    {
      local_mesh_fragments.Append(count,mesh_fragments);
      context->m_face_mesh_fragments.SetCount(0);
    }
    else
    {
      local_mesh_fragments.Append(mesh_fragments[i0]);
      unsigned int count1 = 0;
      for (unsigned int i = i0+1; i < count; i++)
      {
        if (face_id != mesh_fragments[i]->m_face->m_id)
        {
          mesh_fragments[count1++] = mesh_fragments[i];
          continue;
        }
        local_mesh_fragments.Append(mesh_fragments[i]);
      }
      context->m_face_mesh_fragments.SetCount(count1);
    }

#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE)
    if (bReleaseLock)
    {
      // don't keep lock during processing
      context->m_lock.ReturnLock();
      bReleaseLock = false;
    }
#endif

    local_mesh_fragments.Append(impl_managed_fragment);
    local_mesh_fragments.QuickSort(ON_SubDLimitMeshImpl_CallbackContext::CompareFragmentIndex);
    count = local_mesh_fragments.UnsignedCount();
    mesh_fragments = local_mesh_fragments.Array();

    if ( count == face_fragment_count)
      ON_SubDLimitMeshImpl_CallbackContext::ProcessSortedFragments(count,mesh_fragments);
    else
    {
      // there is a bug in the code above
      ON_SubDIncrementErrorCount();
    }
    
#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE)
    // If needed, get a lock before adding the processed fragments to
    // the linked list.
    if (context->m_bUseMultipleThreads)
    {
      if (false == context->m_lock.GetLock(0, ON_SleepLock::OneSecond))
      {
        // return true to keep going, but something is really hogging the lock
        return ON_SUBD_RETURN_ERROR(bContinueMeshCalculation);
      }
    }
#endif

    for ( unsigned int i = 0; i < count; i++ )
      context->m_impl->AddFinishedFragment(local_mesh_fragments[i]);

    break;
  }

#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE)
  if (bReleaseLock)
    context->m_lock.ReturnLock();
#endif

  return bContinueMeshCalculation;
}


int ON_SubDLimitMeshImpl_CallbackContext::CompareFragmentIndex(
    ON_SubDLimitMeshFragment*const* a,
    ON_SubDLimitMeshFragment*const* b
    )
{
  if ( a == b )
    return 0;

  // caller insures no nulls
  //if ( nullptr == a )
  //  return 1;
  //if ( nullptr == b )
  //  return -1;

  //unsigned int a_id = ( nullptr == (*a)->m_face ) ? ON_UNSET_UINT_INDEX : (*a)->m_face->m_id;
  //unsigned int b_id = ( nullptr == (*b)->m_face ) ? ON_UNSET_UINT_INDEX : (*b)->m_face->m_id;
  //if ( a_id < b_id )
  //  return -1;
  //if ( a_id > b_id )
  //  return 1;

  unsigned int a_index = (*a)->m_face_fragment_index;
  unsigned int b_index = (*b)->m_face_fragment_index;
  if ( a_index < b_index )
    return -1;
  if ( a_index > b_index )
    return 1;

  // identical values should never appear in separate entries.
  return ON_SUBD_RETURN_ERROR(0);
}


bool ON_SubDLimitMeshImpl_CallbackContext::CoincidentSideCopy(
  ON_SubDLimitMeshFragment* prev_fragment,
  ON_SubDLimitMeshFragment* fragment
  )
{
  if ( prev_fragment->m_grid.m_side_segment_count != fragment->m_grid.m_side_segment_count)
    return ON_SUBD_RETURN_ERROR(false);

  //const unsigned int side_segment_count = fragment->m_grid.m_side_segment_count;
  //const unsigned int* S0 = prev_fragment->m_grid.m_S + 4*side_segment_count;
  //const unsigned int* S1 = fragment->m_grid.m_S;

  //const double* P0 = prev_fragment->m_P;
  //double* P1 = fragment->m_P;
  //const double* N0 = prev_fragment->m_N;
  //double* N1 = fragment->m_N;

  // const double* src;
  // double* dst;


  //for (const unsigned int* S1max = S1 + side_segment_count; S1 < S1max; S1++, S0--)
  //{
  //  src = P0 + P0_stride * *S0;
  //  dst = P1 + P1_stride * *S1;
  //  
  //  d = fabs(src[0] - dst[0]) + fabs(src[1] - dst[1]) + fabs(src[2] - dst[2]);
  //  if (!(d <= 1e-8))
  //    return ON_SUBD_RETURN_ERROR(false);

  //  *dst++ = *src++;
  //  *dst++ = *src++;
  //  *dst = *src;

  //  src = N0 + N0_stride * *S0;
  //  dst = N1 + N1_stride * *S1;

  //  d = fabs(src[0] - dst[0]) + fabs(src[1] - dst[1]) + fabs(src[2] - dst[2]);
  //  if (!(d <= 0.01))
  //    return ON_SUBD_RETURN_ERROR(false);

  //  *dst++ = *src++;
  //  *dst++ = *src++;
  //  *dst = *src;
  //}

  const size_t side_point_count = 1U + (unsigned int)(fragment->m_grid.m_side_segment_count);

  const double* P0 = prev_fragment->m_P;
  const double* N0 = prev_fragment->m_N;
  const size_t P0_stride = side_point_count*prev_fragment->m_P_stride;
  const size_t N0_stride = side_point_count*prev_fragment->m_N_stride;

  double* P1 = fragment->m_P;
  double* N1 = fragment->m_N;
  const size_t P1_stride = fragment->m_P_stride;
  const size_t N1_stride = fragment->m_N_stride;

  double* P1max = P1 + side_point_count*P1_stride;
  while ( P1 < P1max )
  {
    double d = fabs(P0[0] - P1[0]) + fabs(P0[1] - P1[1]) + fabs(P0[2] - P1[2]);
    if (!(d <= 1e-8))
      return ON_SUBD_RETURN_ERROR(false);

    P1[0] = P0[0];
    P1[1] = P0[1];
    P1[2] = P0[2];
    P0 += P0_stride;
    P1 += P1_stride;

    d = fabs(N0[0] - N1[0]) + fabs(N0[1] - N1[1]) + fabs(N0[2] - N1[2]);
    if (!(d <= 0.01))
      return ON_SUBD_RETURN_ERROR(false);

    N1[0] = N0[0];
    N1[1] = N0[1];
    N1[2] = N0[2];
    N0 += N0_stride;
    N1 += N1_stride;
  }

  return true;
}


bool ON_SubDLimitMeshImpl_CallbackContext::ProcessSortedFragments(
  unsigned int count,
  ON_SubDLimitMeshFragment** mesh_fragments
  )
{
  if ( count < 2 || nullptr == mesh_fragments)
    return ON_SUBD_RETURN_ERROR(false);
  if ( nullptr == mesh_fragments[0] 
    || nullptr == mesh_fragments[0]->m_face
    || nullptr == mesh_fragments[0]->m_P
    || nullptr == mesh_fragments[0]->m_N
    || 0 != mesh_fragments[0]->m_face_fragment_index
    || count != mesh_fragments[0]->m_face_fragment_count
    )
    return ON_SUBD_RETURN_ERROR(false);

  const unsigned int face_id = mesh_fragments[0]->m_face->m_id;
  const unsigned int grid_F_count = mesh_fragments[0]->m_grid.m_F_count;
  const unsigned int grid_side_count = mesh_fragments[0]->m_grid.m_side_segment_count;
  if ( 0 == face_id || ON_UNSET_UINT_INDEX == face_id )
    return ON_SUBD_RETURN_ERROR(false);
  if ( 0 == grid_F_count || grid_side_count*grid_side_count != grid_F_count )
    return ON_SUBD_RETURN_ERROR(false);

  if ( nullptr == mesh_fragments[count-1] 
    || nullptr == mesh_fragments[count-1]->m_face
    || nullptr == mesh_fragments[count-1]->m_P
    || nullptr == mesh_fragments[count-1]->m_N
    || (count-1) != mesh_fragments[count-1]->m_face_fragment_index
    || count != mesh_fragments[count-1]->m_face_fragment_count
    || face_id != mesh_fragments[count-1]->m_face->m_id
    || grid_F_count != mesh_fragments[count-1]->m_grid.m_F_count
    || grid_side_count != mesh_fragments[count-1]->m_grid.m_side_segment_count
    )
    return ON_SUBD_RETURN_ERROR(false);


  ON_SubDLimitMeshFragment* fragment = mesh_fragments[count-1];

  for (unsigned int i = 0; i < count; i++)
  {
    ON_SubDLimitMeshFragment* prev_fragment = fragment;
    fragment = mesh_fragments[i];
    if (i > 0)
    {
      if (nullptr == fragment
        || nullptr == fragment->m_face
        || face_id != fragment->m_face->m_id
        || grid_F_count != fragment->m_grid.m_F_count
        || grid_side_count != fragment->m_grid.m_side_segment_count
        || i != fragment->m_face_fragment_index
        || count != fragment->m_face_fragment_count
        || nullptr == fragment->m_P
        || nullptr == fragment->m_N
        )
        return ON_SUBD_RETURN_ERROR(false);
    }

    if ( false == ON_SubDLimitMeshImpl_CallbackContext::CoincidentSideCopy(prev_fragment,fragment) )
      return ON_SUBD_RETURN_ERROR(false);
  }
  return true;
}

#if defined(ON_HAS_RVALUEREF)
ON_SubDLimitMesh::ON_SubDLimitMesh( ON_SubDLimitMesh&& src) ON_NOEXCEPT
  : m_impl_sp(std::move(src.m_impl_sp))
{}

ON_SubDLimitMesh& ON_SubDLimitMesh::operator=(ON_SubDLimitMesh&& src)
{
  if (this != &src)
  {
    m_impl_sp.reset();
    m_impl_sp = std::move(src.m_impl_sp);
  }
  return *this;
}
#endif

ON_SubDDisplayParameters ON_SubDLimitMesh::DisplayParameters() const
{
  const ON_SubDLimitMeshImpl* impl = m_impl_sp.get();
  if (nullptr != impl)
  {
    ON_SubDDisplayParameters dp;
    dp.m_display_density = impl->m_display_density;
    return dp;
  }
  return ON_SubDDisplayParameters::Empty;
}

unsigned int ON_SubDLimitMesh::DisplayDensity() const
{
  const ON_SubDLimitMeshImpl* impl = m_impl_sp.get();
  return (nullptr != impl)
    ? impl->m_display_density
    : 0;
}

unsigned int ON_SubDLimitMesh::FragmentCount() const
{
  const ON_SubDLimitMeshImpl* impl = m_impl_sp.get();
  return (nullptr != impl)
    ? impl->m_fragment_count
    : 0;
}

const ON_SubDLimitMeshFragment* ON_SubDLimitMesh::FirstFragment() const
{
  const ON_SubDLimitMeshImpl* impl = m_impl_sp.get();
  return (nullptr != impl)
    ? impl->m_first_fragment
    : nullptr;
}
  

bool ON_SubDLimitMesh::Update(
  bool bShareUpdate
  )
{
  return false;
}

bool ON_SubDLimitMesh::IsEmpty() const
{
  const ON_SubDLimitMeshImpl* impl = m_impl_sp.get();
  return (nullptr == impl || 0 == impl->m_fragment_count );
}

const ON_RTree& ON_SubDLimitMesh::FragmentTree() const
{
  const ON_SubDLimitMeshImpl* impl = m_impl_sp.get();
  if (nullptr != impl)
    return impl->FragmentTree();
  return ON_RTree::Empty;
}

ON_BoundingBox ON_SubDLimitMesh::BoundingBox() const
{
  const ON_SubDLimitMeshImpl* impl = m_impl_sp.get();
  if (nullptr != impl)
    return impl->m_bbox;
  return ON_BoundingBox::EmptyBoundingBox;
}

ON_SubD ON_SubDLimitMesh::SubD() const
{
  const ON_SubDLimitMeshImpl* impl = m_impl_sp.get();
  if ( nullptr == impl )
    return ON_SubD::Empty;
  ON_SubD subd;
  subd.ShareDimple(*impl);
  return subd;
}

ON_SubDRef ON_SubDLimitMesh::SubDRef() const
{
  const ON_SubDLimitMeshImpl* impl = m_impl_sp.get();
  if ( nullptr == impl )
    return ON_SubDRef::Empty;
  ON_SubDRef subd_ref;
  ON_SubD& subd = subd_ref.NewSubD();
  subd.ShareDimple(*impl);
  return subd_ref;
}

bool ON_SubDLimitMesh::GetTightBoundingBox(
  ON_BoundingBox& bbox,
  bool bGrowBox,
  const ON_Xform* xform
  ) const
{
  const ON_SubDLimitMeshImpl* impl = m_impl_sp.get();
  if (nullptr != impl)
    return impl->GetTightBoundingBox(bbox,bGrowBox,xform);
  return (bGrowBox && bbox.IsValid());
}

ON_SubD::FacetType ON_SubDLimitMesh::GridType() const
{
  const ON_SubDLimitMeshImpl* impl = m_impl_sp.get();
  if (nullptr != impl)
    return impl->m_facet_type;
  return ON_SubD::FacetType::Unset;
}

void ON_SubDLimitMesh::Clear()
{
  m_impl_sp.reset();
}

void ON_SubDLimitMesh::ClearTree()
{
  ON_SubDLimitMeshImpl* impl = m_impl_sp.get();
  if (nullptr != impl)
    impl->ClearTree();
}

unsigned int ON_SubDLimitMesh::ContentSerialNumber() const
{
  ON_SubDLimitMeshImpl* impl = m_impl_sp.get();
  return (nullptr != impl) ? impl->m_limit_mesh_content_serial_number : 0;
}

ON_SubDLimitMesh* ON_SubDLimitMesh::Create(
  const ON_SubD& subd,
  const class ON_SubDDisplayParameters& limit_mesh_parameters,
  ON_SubDLimitMesh* destination_mesh
  )
{
  ON_SubDFaceIterator fit = subd.FaceIterator();
  return ON_SubDLimitMesh::Create(fit,limit_mesh_parameters,destination_mesh);
}

ON_SubDLimitMesh& ON_SubDLimitMesh::CopyFrom(
  const ON_SubDLimitMesh& src
  )
{
  if (this != &src)
  {
    m_impl_sp.reset();
    const ON_SubDLimitMeshImpl* src_impl = src.m_impl_sp.get();
    if (nullptr != src_impl)
    {
      ON_SubDLimitMeshImpl* impl = new ON_SubDLimitMeshImpl(*src_impl);
      std::shared_ptr< ON_SubDLimitMeshImpl > new_impl_sp(impl);
      m_impl_sp.swap(new_impl_sp);
    }
  }
  return *this;
}

void ON_SubDLimitMesh::Swap(
  ON_SubDLimitMesh& a,
  ON_SubDLimitMesh& b
  )
{
  if (&a == &ON_SubDLimitMesh::Empty || &b == &ON_SubDLimitMesh::Empty)
  {
    ON_SubDIncrementErrorCount();
  }
  else
  {
    std::swap(a.m_impl_sp, b.m_impl_sp);
  }
}

ON_SubDLimitMesh* ON_SubDLimitMesh::Create(
    ON_SubDFaceIterator fit,
    const class ON_SubDDisplayParameters& limit_mesh_parameters,
    ON_SubDLimitMesh* destination_mesh
    )
{
  ON_SubDLimitMeshImpl* impl = 0;
  std::shared_ptr< ON_SubDLimitMeshImpl > impl_sp;

  if (nullptr != destination_mesh)
  {
    destination_mesh->Clear();
    std::shared_ptr< ON_SubDLimitMeshImpl > dest_sp = destination_mesh->m_impl_sp;
    if (1 == dest_sp.use_count())
    {
      impl_sp.swap(dest_sp);
      impl = impl_sp.get();
    }
  }

  if ( limit_mesh_parameters.m_display_density > ON_SubDLimitMesh::MaximumDisplayDensity )
    return ON_SUBD_RETURN_ERROR(nullptr);

  const ON_SubD& subd = fit.SubD();

  ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subd.ActiveLevelSubDType());
  
  const unsigned int subd_fragment_count = fit.LimitSurfaceMeshFragmentCount(facet_type);
  if ( 0 == subd_fragment_count )
    return ON_SUBD_RETURN_ERROR(nullptr);

  if (nullptr == impl)
  {
    impl = new(std::nothrow) ON_SubDLimitMeshImpl();
    if ( nullptr == impl)
      return ON_SUBD_RETURN_ERROR(nullptr);
    std::shared_ptr< ON_SubDLimitMeshImpl > new_impl_sp(impl);
    impl_sp.swap(new_impl_sp);
  }

  if (false == impl->ReserveCapacity(subd_fragment_count,facet_type,limit_mesh_parameters.m_display_density))
    return ON_SUBD_RETURN_ERROR(nullptr);

  ON_SubDLimitMeshImpl_CallbackContext callback_context;

#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE)
  callback_context.m_bUseMultipleThreads = limit_mesh_parameters.m_bUseMultipleThreads;
#endif

  callback_context.m_impl = impl_sp.get();
  subd.GetLimitSurfaceMeshInFragments(
    limit_mesh_parameters,
    (ON__UINT_PTR)&callback_context,
    ON_SubDLimitMeshImpl_CallbackContext::FragmentCallbackFunction
    );

  if (ON_Terminator::TerminationRequested(limit_mesh_parameters.m_terminator))
  {
    return nullptr; // not an error - outside intervention canceled meshing
  }

  if (impl->m_fragment_count < 1)
    return ON_SUBD_RETURN_ERROR(nullptr);
  
  ON_SubDLimitMesh* limit_mesh 
    = (nullptr != destination_mesh)
    ? destination_mesh
    : new ON_SubDLimitMesh();


  // The next three lines are conceptually the same as
  //   impl_sp->m_subdimple_sp = fit.SubD().m_subdimple_sp;
  // The three line approach is required because ON_SubD::m_subdimple_sp is  private.
  {
    ON_SubD tmp_sub;
    tmp_sub.ShareDimple(fit.SubD()); // tmp_sub.m_subdimple_sp = fit.SubD().m_subdimple_sp (increments fit.SubD().m_subdimple_sp ref count)
    // NOTE: 
    //  There are at least std::shared_ptr references to the ON_SubDimple (fit and tmp_subd),
    //  This insures the std::weak_ptr on impl_sp will be valid.
    tmp_sub.SwapDimple(*impl_sp);  // swap impl_sp->m_subdimple_wp and tmp_sub.m_subdimple_sp
  }

  // Let limit_mesh manage the contents
  limit_mesh->m_impl_sp.swap(impl_sp);

  // return the new mesh
  return limit_mesh;
}


ON_SubDDisplayParameters ON_SubDDisplayParameters::CreateFromDisplayDensity(
  unsigned int display_density
  )
{
  if ( display_density > ON_SubDLimitMesh::MaximumDisplayDensity )
    return ON_SUBD_RETURN_ERROR(ON_SubDDisplayParameters::Empty);

  ON_SubDDisplayParameters limit_mesh_parameters;
  limit_mesh_parameters.m_display_density = display_density;
  return limit_mesh_parameters;
}

#endif
