Files
opennurbs/opennurbs_pointcloud.cpp
Bozo The Builder 95dde7ec84 Sync changes from upstream repository
Co-authored-by: Andrew Le Bihan <andy@mcneel.com>
Co-authored-by: Dale Fugier <dale@mcneel.com>
Co-authored-by: Dale Lear <dalelear@mcneel.com>
Co-authored-by: David Eränen <deranen@gmail.com>
Co-authored-by: Greg Arden <greg@mcneel.com>
Co-authored-by: Jussi Aaltonen <jussi@mcneel.com>
Co-authored-by: Lowell <lowell@mcneel.com>
Co-authored-by: Mikko Oksanen <mikko@mcneel.com>
Co-authored-by: Pierre Cuvilliers <pierre@mcneel.com>
Co-authored-by: Steve Baer <steve@mcneel.com>
Co-authored-by: chuck <chuck@mcneel.com>
Co-authored-by: piac <giulio@mcneel.com>
Co-authored-by: wfcook <wfcook@mcneel.com>
2021-10-14 07:57:14 -07:00

686 lines
16 KiB
C++

/* $NoKeywords: $ */
/*
//
// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
// McNeel & Associates.
//
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
//
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
//
////////////////////////////////////////////////////////////////
*/
#include "opennurbs.h"
#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
ON_OBJECT_IMPLEMENT(ON_PointCloud, ON_Geometry, "2488F347-F8FA-11d3-BFEC-0010830122F0");
ON_3dPoint& ON_PointCloud::operator[](int i)
{
return m_P[i];
}
const ON_3dPoint& ON_PointCloud::operator[](int i) const
{
return m_P[i];
}
ON_3dPoint ON_PointCloud::Point( ON_COMPONENT_INDEX ci ) const
{
return (ON_COMPONENT_INDEX::pointcloud_point == ci.m_type && ci.m_index >= 0 && ci.m_index < m_P.Count() )
? m_P[ci.m_index]
: ON_3dPoint::UnsetPoint;
}
ON_PointCloud::ON_PointCloud() : m_flags(0)
{
m_hidden_count=0;
}
ON_PointCloud::ON_PointCloud( int capacity ) : m_P(capacity), m_flags(0)
{
m_hidden_count=0;
}
ON_PointCloud::ON_PointCloud( const ON_PointCloud& src )
{
*this = src;
}
ON_PointCloud& ON_PointCloud::operator=( const ON_PointCloud& src )
{
if ( this != &src ) {
Destroy();
ON_Geometry::operator=(src);
m_P = src.m_P;
m_H = src.m_H;
m_C = src.m_C;
m_V = src.m_V;
m_N = src.m_N;
m_hidden_count = src.m_hidden_count;
m_plane = src.m_plane;
m_bbox = src.m_bbox;
m_flags = src.m_flags;
}
return *this;
}
ON_PointCloud::ON_PointCloud(const ON_3dPoint* P0, int count) : m_P(count)
{
m_P.Create(3, false, count, 3, P0[0]);
}
ON_PointCloud::ON_PointCloud(const double* P0, int dim, bool is_rat, int count) : m_P(count)
{
m_P.Create(dim, is_rat, count, dim + is_rat, P0);
}
ON_PointCloud::~ON_PointCloud()
{
Destroy();
}
void ON_PointCloud::Destroy()
{
m_H.Destroy();
m_C.Destroy();
m_V.Destroy();
m_N.Destroy();
m_P.Destroy();
m_hidden_count=0;
m_flags = 0;
m_bbox.Destroy();
}
void ON_PointCloud::EmergencyDestroy()
{
m_P.EmergencyDestroy();
m_C.EmergencyDestroy();
m_V.EmergencyDestroy();
m_H.EmergencyDestroy();
m_N.EmergencyDestroy();
m_hidden_count=0;
m_flags = 0;
m_bbox.Destroy();
}
bool ON_PointCloud::IsValid( ON_TextLog* text_log ) const
{
return ( m_P.Count() > 0 ) ? true : false;
}
void ON_PointCloud::Dump( ON_TextLog& dump ) const
{
// Aug 23, 2016 Tim - Fix for RH-35351
// half_max is arbitrary, if you want more than 100 points
// change it to your liking. This is to prevent all of the
// points from displaying when you have a very large point
// cloud, like a billion. (see model on RH-35348)
const int half_max = 50;
int i;
const bool bHasNormals = HasPointNormals();
const bool bHasColors = HasPointColors();
const bool bHasHiddenPoints = (HiddenPointCount() > 0);
const int point_count = m_P.Count();
dump.Print("ON_PointCloud: %d points\n",point_count);
dump.PushIndent();
for ( i = 0; i < point_count; i++ )
{
if (i == half_max && 2 * half_max < point_count)
{
dump.Print("...\n");
i = point_count - half_max;
}
else
{
dump.Print("point[%2d]: ", i);
dump.Print(m_P[i]);
if (bHasNormals)
{
dump.Print(", normal = ");
dump.Print(m_N[i]);
}
if (bHasColors)
{
dump.Print(", color = ");
dump.PrintRGB(m_C[i]);
}
if (bHasHiddenPoints && m_H[i])
{
dump.Print(" (hidden)");
}
dump.Print("\n");
}
}
dump.PopIndent();
}
bool ON_PointCloud::Write( ON_BinaryArchive& file ) const
{
bool rc = file.Write3dmChunkVersion(1,2);
if (rc) rc = file.WriteArray( m_P );
if (rc) rc = file.WritePlane( m_plane );
if (rc) rc = file.WriteBoundingBox( m_bbox );
if (rc) rc = file.WriteInt( m_flags);
// added for 1.1 (7 December 2005)
if (rc) rc = file.WriteArray(m_N);
if (rc) rc = file.WriteArray(m_C);
// added for 1.2 (8 August 2016)
if (rc) rc = file.WriteArray(m_V);
return rc;
}
bool ON_PointCloud::Read( ON_BinaryArchive& file )
{
int major_version = 0;
int minor_version = 0;
bool rc = file.Read3dmChunkVersion(&major_version,&minor_version);
if (rc && major_version == 1 )
{
if (rc) rc = file.ReadArray( m_P );
if (rc) rc = file.ReadPlane( m_plane );
if (rc) rc = file.ReadBoundingBox( m_bbox );
if (rc) rc = file.ReadInt( &m_flags);
if (rc && minor_version >= 1 )
{
if (rc) rc = file.ReadArray( m_N );
if (rc) rc = file.ReadArray( m_C );
if (rc && minor_version >= 2)
{
rc = file.ReadArray( m_V );
}
}
}
return rc;
}
ON::object_type ON_PointCloud::ObjectType() const
{
return ON::pointset_object;
}
unsigned int ON_PointCloud::SizeOf() const
{
// Dale Lear fixed this function in April 2021.
// Before that all point clouds were reported to have
// ON_Geometry::SizeOf() bytes.
size_t sz = ON_Geometry::SizeOf();
sz += (sizeof(*this) - sizeof(ON_Geometry));
sz += (size_t)(m_P.SizeOfArray());
sz += (size_t)(m_N.SizeOfArray());
sz += (size_t)(m_C.SizeOfArray());
sz += (size_t)(m_V.SizeOfArray());
sz += (size_t)(m_H.SizeOfArray());
// Avoid overflowing 4 byte unsigned int
// and hope the consumer of this information is
// converting back to size_t before doing arithmetic.
// The low 4 bytes are zeroed so small additions won't
// overflow unsigned int.
return (sz > 0xFFFF0000U) ? 0xFFFF0000U : ((unsigned int)sz);
}
int ON_PointCloud::Dimension() const
{
return 3;
}
bool ON_PointCloud::GetBBox( // returns true if successful
double* boxmin, // minimum
double* boxmax, // maximum
bool bGrowBox // true means grow box
) const
{
if ( !m_bbox.IsValid() ) {
m_P.GetBBox( (double*)&m_bbox.m_min.x, (double*)&m_bbox.m_max.x, false );
}
bool rc = m_bbox.IsValid();
if (rc) {
if ( bGrowBox ) {
if ( boxmin ) {
if ( boxmin[0] > m_bbox.m_min.x ) boxmin[0] = m_bbox.m_min.x;
if ( boxmin[1] > m_bbox.m_min.y ) boxmin[1] = m_bbox.m_min.y;
if ( boxmin[2] > m_bbox.m_min.z ) boxmin[2] = m_bbox.m_min.z;
}
if ( boxmax ) {
if ( boxmax[0] < m_bbox.m_max.x ) boxmax[0] = m_bbox.m_max.x;
if ( boxmax[1] < m_bbox.m_max.y ) boxmax[1] = m_bbox.m_max.y;
if ( boxmax[2] < m_bbox.m_max.z ) boxmax[2] = m_bbox.m_max.z;
}
}
else {
if ( boxmin ) {
boxmin[0] = m_bbox.m_min.x;
boxmin[1] = m_bbox.m_min.y;
boxmin[2] = m_bbox.m_min.z;
}
if ( boxmax ) {
boxmax[0] = m_bbox.m_max.x;
boxmax[1] = m_bbox.m_max.y;
boxmax[2] = m_bbox.m_max.z;
}
}
}
return rc;
}
bool ON_PointCloud::Transform(
const ON_Xform& xform
)
{
TransformUserData(xform);
bool rc = m_P.Transform(xform);
if (rc && HasPlane() )
rc = m_plane.Transform(xform);
m_bbox.Destroy();
return rc;
}
bool ON_PointCloud::IsDeformable() const
{
return true;
}
bool ON_PointCloud::MakeDeformable()
{
return true;
}
bool ON_PointCloud::SwapCoordinates(
int i, int j // indices of coords to swap
)
{
bool rc = m_P.SwapCoordinates(i,j);
if ( rc && HasPlane() ) {
rc = m_plane.SwapCoordinates(i,j);
}
if ( rc && m_bbox.IsValid() ) {
rc = m_bbox.SwapCoordinates(i,j);
}
return rc;
}
int ON_PointCloud::PointCount() const
{
return m_P.Count();
}
void ON_PointCloud::AppendPoint( const ON_3dPoint& pt )
{
m_P.Append(pt);
}
void ON_PointCloud::InvalidateBoundingBox()
{
m_bbox.Destroy();
}
void ON_PointCloud::SetOrdered(bool b)
{
if ( b ) {
m_flags |= 1;
}
else {
m_flags &= 0xFFFFFFFE;
}
}
bool ON_PointCloud::IsOrdered() const
{
return (m_flags & 1) ? true : false;
}
bool ON_PointCloud::HasPlane() const
{
return ( m_flags&2) ? true : false;
}
void ON_PointCloud::SetPlane( const ON_Plane& plane )
{
m_plane = plane;
if ( m_plane.IsValid() ) {
m_flags |= 2;
}
else {
m_flags &= 0xFFFFFFFD;
}
}
const ON_Plane& ON_PointCloud::Plane()
{
return m_plane;
}
double ON_PointCloud::Height(int i)
{
return (m_P[i] - m_plane.origin)*m_plane.zaxis;
}
bool ON_GetClosestPointInPointList(
int point_count,
const ON_3dPoint* point_list,
ON_3dPoint P,
int* closest_point_index
)
{
bool rc = false;
if ( point_count>0 && 0 != point_list && closest_point_index )
{
double d = 1.0e300;
double d2 = 1.0e300;
double x,e;
int i;
int best_i = -1;
//const double* pl = &point_list[0].x;
for ( i = point_count; i--; point_list++ )
{
e = d2;
x = point_list->x - P.x;
e = x*x;
if ( e >= d2 ) continue;
x = point_list->y - P.y;
e += x*x;
if ( e >= d2 ) continue;
x = point_list->z - P.z;
e += x*x;
if ( e >= d2 ) continue;
d2 = (1.0+ON_SQRT_EPSILON)*e;
e = P.DistanceTo(*point_list);
if ( e < d )
{
d = e;
best_i = point_count-i-1;
}
}
if ( best_i >= 0 )
{
if ( closest_point_index )
*closest_point_index = best_i;
rc = true;
}
}
return rc;
}
bool ON_3dPointArray::GetClosestPoint(
ON_3dPoint P,
int* closest_point_index,
double maximum_distance
) const
{
int i;
bool rc = ON_GetClosestPointInPointList( m_count, m_a , P, &i );
if (rc)
{
if ( maximum_distance > 0.0 && P.DistanceTo(m_a[i]) > maximum_distance )
{
rc = false;
}
else if ( closest_point_index )
{
*closest_point_index = i;
}
}
return rc;
}
bool ON_PointCloud::HasPointColors() const
{
const unsigned int point_count = m_P.UnsignedCount();
return (point_count > 0 && point_count == m_C.UnsignedCount());
}
bool ON_PointCloud::HasPointValues() const
{
const unsigned int point_count = m_P.UnsignedCount();
return (point_count > 0 && point_count == m_V.UnsignedCount());
}
bool ON_PointCloud::HasPointNormals() const
{
const unsigned int point_count = m_P.UnsignedCount();
return (point_count > 0 && point_count == m_N.UnsignedCount());
}
bool ON_PointCloud::GetClosestPoint(
ON_3dPoint P,
int* closest_point_index,
double maximum_distance
) const
{
if ( maximum_distance > 0.0 && m_bbox.IsValid() )
{
// check bounding box
if ( m_bbox.MinimumDistanceTo(P) > maximum_distance )
return false;
}
return m_P.GetClosestPoint( P, closest_point_index, maximum_distance );
}
unsigned int ON_PointCloud::HiddenPointUnsignedCount() const
{
unsigned int point_count;
return ( m_hidden_count > 0
&& (point_count = m_P.UnsignedCount()) > 0
&& m_hidden_count <= point_count
&& m_H.UnsignedCount() == point_count
)
? m_hidden_count
: 0;
}
int ON_PointCloud::HiddenPointCount() const
{
return (int)HiddenPointUnsignedCount();
}
void ON_PointCloud::DestroyHiddenPointArray()
{
m_hidden_count = 0;
m_H.Destroy();
}
const bool* ON_PointCloud::HiddenPointArray() const
{
return (m_hidden_count > 0 && m_H.UnsignedCount() == m_P.UnsignedCount())
? m_H.Array()
: 0;
}
void ON_PointCloud::SetHiddenPointFlag( int point_index, bool bHidden )
{
const int point_count = m_P.Count();
if ( point_index >= 0 && point_index < point_count )
{
if ( bHidden )
{
if ( point_count != m_H.Count() )
{
m_H.SetCapacity(point_count);
m_H.SetCount(point_count);
m_H.Zero();
m_H[point_index] = true;
m_hidden_count = 1;
}
else if ( false == m_H[point_index] )
{
m_H[point_index] = true;
m_hidden_count++;
}
}
else
{
// show this vertex
if ( m_hidden_count > 0 && point_count == m_H.Count() )
{
if ( m_H[point_index] )
{
m_H[point_index] = false;
m_hidden_count--;
if ( 0 == m_hidden_count )
{
DestroyHiddenPointArray();
}
}
}
else if ( m_hidden_count > 0 || m_H.Capacity() > 0 )
{
// if m_H exists, it is bogus.
DestroyHiddenPointArray();
}
}
}
}
bool ON_PointCloud::PointIsHidden( int point_index ) const
{
int point_count;
return ( point_index >= 0
&& point_index < (point_count = m_P.Count())
&& m_H.Count() == point_count )
? m_H[point_index]
: false;
}
ON_PointCloud* ON_PointCloud::RandomSubsample(
const ON_PointCloud* source_point_cloud,
const unsigned int subsample_point_count,
ON_PointCloud* destination_point_cloud,
ON_ProgressReporter* progress_reporter,
ON_Terminator* terminator
)
{
if (
nullptr == source_point_cloud
|| subsample_point_count <= 0
|| subsample_point_count >= source_point_cloud->m_P.UnsignedCount()
)
return nullptr;
const unsigned int point_count = source_point_cloud->m_P.UnsignedCount();
const unsigned int points_to_remove = point_count - subsample_point_count;
if (points_to_remove <= 0)
return nullptr;
const bool bHaveNormals = point_count == source_point_cloud->m_N.UnsignedCount();
const bool bHaveColors = point_count == source_point_cloud->m_C.UnsignedCount();
const bool bHaveValues = point_count == source_point_cloud->m_V.UnsignedCount();
const bool bHaveHidden = point_count == source_point_cloud->m_H.UnsignedCount();
bool bAlloc = false;
if (destination_point_cloud)
{
if (source_point_cloud != destination_point_cloud)
{
destination_point_cloud->Destroy();
*destination_point_cloud = *source_point_cloud;
}
}
else
{
destination_point_cloud = new ON_PointCloud(*source_point_cloud);
bAlloc = true;
}
ON_RandomNumberGenerator gen;
gen.Seed();
unsigned int last_point_count = point_count;
for (unsigned int i = 0; i < points_to_remove; i++)
{
if (terminator && ON_Terminator::TerminationRequested(terminator))
{
if (source_point_cloud != destination_point_cloud)
destination_point_cloud->Destroy();
if (bAlloc)
delete destination_point_cloud;
return nullptr;
}
if (progress_reporter)
ON_ProgressReporter::ReportProgress(progress_reporter, i / (double)points_to_remove);
// For (min <= r < max): min + RandomNumber() % (max-min)
const int point_index = gen.RandomNumber() % last_point_count;
destination_point_cloud->m_P.Swap(point_index, last_point_count - 1);
if (bHaveNormals)
destination_point_cloud->m_N.Swap(point_index, last_point_count - 1);
if (bHaveColors)
destination_point_cloud->m_C.Swap(point_index, last_point_count - 1);
if (bHaveValues)
destination_point_cloud->m_V.Swap(point_index, last_point_count - 1);
if (bHaveHidden)
destination_point_cloud->m_H.Swap(point_index, last_point_count - 1);
last_point_count--;
if (last_point_count <= 0)
break;
}
if (progress_reporter)
ON_ProgressReporter::ReportProgress(progress_reporter, 1.0);
destination_point_cloud->m_P.SetCount(subsample_point_count);
destination_point_cloud->m_P.Shrink();
if (bHaveNormals)
{
destination_point_cloud->m_N.SetCount(subsample_point_count);
destination_point_cloud->m_N.Shrink();
}
if (bHaveColors)
{
destination_point_cloud->m_C.SetCount(subsample_point_count);
destination_point_cloud->m_C.Shrink();
}
if (bHaveValues)
{
destination_point_cloud->m_V.SetCount(subsample_point_count);
destination_point_cloud->m_V.Shrink();
}
if (bHaveHidden)
{
destination_point_cloud->m_H.SetCount(subsample_point_count);
destination_point_cloud->m_H.Shrink();
destination_point_cloud->m_hidden_count = 0;
for (unsigned int i = 0; i < destination_point_cloud->m_H.UnsignedCount(); i++)
{
if (destination_point_cloud->m_H[i])
destination_point_cloud->m_hidden_count++;
}
}
return destination_point_cloud;
}