ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvQuadric.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "ecvQuadric.h"
9 
10 // CV_CORE_LIB
12 
13 // CV_DB_LIB
14 #include "ecvMaterialSet.h"
15 #include "ecvNormalVectors.h"
16 #include "ecvPointCloud.h"
17 
18 // system
19 #include <string.h>
20 
22  CCVector2 maxCorner,
23  const PointCoordinateType eq[6],
24  const Tuple3ub* dims /*=0*/,
25  const ccGLMatrix* transMat /*=0*/,
26  QString name /*=QString("Quadric")*/,
27  unsigned precision /*=DEFAULT_DRAWING_PRECISION*/)
28  : ccGenericPrimitive(name, transMat),
29  m_minCorner(minCorner),
30  m_maxCorner(maxCorner),
31  m_dims(0, 1, 2),
32  m_minZ(0),
33  m_maxZ(0) {
34  memcpy(m_eq, eq, sizeof(PointCoordinateType) * 6);
35 
36  if (dims) {
37  m_dims = *dims;
38  }
39 
40  setDrawingPrecision(std::max<unsigned>(
41  precision, MIN_DRAWING_PRECISION)); // automatically calls
42  // updateRepresentation
43 }
44 
45 ccQuadric::ccQuadric(QString name /*=QString("Plane")*/)
47  m_minCorner(0, 0),
48  m_maxCorner(0, 0),
49  m_dims(0, 1, 2),
50  m_minZ(0),
51  m_maxZ(0) {}
52 
54  if (m_drawPrecision < MIN_DRAWING_PRECISION) return false;
55 
56  unsigned vertCount = m_drawPrecision * m_drawPrecision;
57  unsigned triCount = (m_drawPrecision - 1) * (m_drawPrecision - 1) * 2;
58  if (!init(vertCount, true, triCount, 0)) {
59  CVLog::Error("[ccQuadric::buildUp] Not enough memory");
60  return false;
61  }
62 
63  ccPointCloud* verts = vertices();
64  assert(verts);
65  assert(verts->hasNormals());
66 
67  CCVector2 areaSize = m_maxCorner - m_minCorner;
68  PointCoordinateType stepX =
69  areaSize.x / static_cast<PointCoordinateType>(m_drawPrecision - 1);
70  PointCoordinateType stepY =
71  areaSize.y / static_cast<PointCoordinateType>(m_drawPrecision - 1);
72 
73  for (unsigned x = 0; x < m_drawPrecision; ++x) {
74  CCVector3 P(m_minCorner.x + stepX * x, 0, 0);
75  for (unsigned y = 0; y < m_drawPrecision; ++y) {
76  P.y = m_minCorner.y + stepY * y;
77 
78  P.z = m_eq[0] + m_eq[1] * P.x + m_eq[2] * P.y +
79  m_eq[3] * P.x * P.x + m_eq[4] * P.x * P.y +
80  m_eq[5] * P.y * P.y;
81 
82  // compute the min and max heights of the quadric!
83  if (x != 0 || y != 0) {
84  if (m_minZ > P.z)
85  m_minZ = P.z;
86  else if (m_maxZ < P.z)
87  m_maxZ = P.z;
88  } else {
89  m_minZ = m_maxZ = P.z;
90  }
91 
92  verts->addPoint(P);
93 
94  // normal --> TODO: is there a simple way to deduce it from the
95  // equation? CCVector3 N; N.x = m_eq[1] + 2*m_eq[3]*P.x +
96  // m_eq[4]*P.y; N.y = m_eq[2] + 2*m_eq[5]*P.y + m_eq[4]*P.x; N.z =
97  // -PC_ONE; N.normalize(); verts->addNorm(N);
98 
99  if (x != 0 && y != 0) {
100  unsigned iA = (x - 1) * m_drawPrecision + y - 1;
101  unsigned iB = iA + 1;
102  unsigned iC = iA + m_drawPrecision;
103  unsigned iD = iB + m_drawPrecision;
104 
105  addTriangle(iA, iC, iB);
106  addTriangle(iB, iC, iD);
107  }
108  }
109  }
110 
111  computeNormals(true);
112 
113  return true;
114 }
115 
119  m_drawPrecision));
120 }
121 
123  double* rms /*=0*/) {
124  // number of points
125  unsigned count = cloud->size();
128  QString("[ccQuadric::fitTo] Not enough points in input cloud "
129  "to fit a quadric! (%1 at the very least are required)")
131  return nullptr;
132  }
133 
134  // project the points on a 2D plane
135  CCVector3 G, X, Y, N;
136  {
137  cloudViewer::Neighbourhood Yk(cloud);
138 
139  // plane equation
140  const PointCoordinateType* theLSPlane = Yk.getLSPlane();
141  if (!theLSPlane) {
143  "[ccQuadric::Fit] Not enough points to fit a quadric!");
144  return nullptr;
145  }
146 
147  assert(Yk.getGravityCenter());
148  G = *Yk.getGravityCenter();
149 
150  // local base
151  N = CCVector3(theLSPlane);
152  assert(Yk.getLSPlaneX() && Yk.getLSPlaneY());
153  X = *Yk.getLSPlaneX(); // main direction
154  Y = *Yk.getLSPlaneY(); // secondary direction
155  }
156 
157  // project the points in a temporary cloud
158  ccPointCloud tempCloud("temporary");
159  if (!tempCloud.reserve(count)) {
160  CVLog::Warning("[ccQuadric::Fit] Not enough memory!");
161  return nullptr;
162  }
163 
164  cloud->placeIteratorAtBeginning();
165  for (unsigned k = 0; k < count; ++k) {
166  // projection into local 2D plane ref.
167  CCVector3 P = *(cloud->getNextPoint()) - G;
168 
169  tempCloud.addPoint(CCVector3(P.dot(X), P.dot(Y), P.dot(N)));
170  }
171 
172  cloudViewer::Neighbourhood Zk(&tempCloud);
173  {
174  // set exact values for gravity center and plane equation
175  //(just to be sure and to avoid re-computing them)
176  Zk.setGravityCenter(CCVector3(0, 0, 0));
177  PointCoordinateType perfectEq[4] = {0, 0, 1, 0};
178  Zk.setLSPlane(perfectEq, CCVector3(1, 0, 0), CCVector3(0, 1, 0),
179  CCVector3(0, 0, 1));
180  }
181 
182  Tuple3ub dims;
183  const PointCoordinateType* eq = Zk.getQuadric(&dims);
184  if (!eq) {
185  CVLog::Warning("[ccQuadric::Fit] Failed to fit a quadric!");
186  return nullptr;
187  }
188 
189  // we recenter the quadric object
190  ccGLMatrix glMat(X, Y, N, G);
191 
192  ccBBox bb = tempCloud.getOwnBB();
193  CCVector2 minXY(bb.minCorner().x, bb.minCorner().y);
194  CCVector2 maxXY(bb.maxCorner().x, bb.maxCorner().y);
195 
196  ccQuadric* quadric = new ccQuadric(minXY, maxXY, eq, &dims, &glMat);
197 
198  quadric->setMetaData(QString("Equation"),
199  QVariant(quadric->getEquationString()));
200 
201  // compute rms if necessary
202  if (rms) {
203  const unsigned char dX = dims.x;
204  const unsigned char dY = dims.y;
205  // const unsigned char dZ = dims.z;
206 
207  *rms = 0;
208 
209  for (unsigned k = 0; k < count; ++k) {
210  // projection into local 2D plane ref.
211  const CCVector3* P = tempCloud.getPoint(k);
212 
214  eq[0] + eq[1] * P->u[dX] + eq[2] * P->u[dY] +
215  eq[3] * P->u[dX] * P->u[dX] + eq[4] * P->u[dX] * P->u[dY] +
216  eq[5] * P->u[dY] * P->u[dY];
217  *rms += (z - P->z) * (z - P->z);
218  }
219 
220  if (count) {
221  *rms = sqrt(*rms / count);
222  quadric->setMetaData(QString("RMS"), QVariant(*rms));
223  }
224  }
225 
226  return quadric;
227 }
228 
230  CCVector3& Q) const {
231  // back project into quadric coordinate system
232  Q = P;
234 
235  const unsigned char dX = m_dims.x;
236  const unsigned char dY = m_dims.y;
237  const unsigned char dZ = m_dims.z;
238 
239  PointCoordinateType originalZ = Q.u[dZ];
240  Q.u[dZ] = m_eq[0] + m_eq[1] * Q.u[dX] + m_eq[2] * Q.u[dY] +
241  m_eq[3] * Q.u[dX] * Q.u[dX] + m_eq[4] * Q.u[dX] * Q.u[dY] +
242  m_eq[5] * Q.u[dY] * Q.u[dY];
243 
245 
246  return originalZ - Q.u[dZ];
247 }
248 
250  const unsigned char dX = m_dims.x;
251  const unsigned char dY = m_dims.y;
252  const unsigned char dZ = m_dims.z;
253  static const char dimChars[3] = {'x', 'y', 'z'};
254 
255  QString equationStr = QString("%1 = %2 + %3 * %4")
256  .arg(dimChars[dZ])
257  .arg(m_eq[0])
258  .arg(m_eq[1])
259  .arg(dimChars[dX]);
260  equationStr += QString(" + %1 * %2 + %3 * %4^2")
261  .arg(m_eq[2])
262  .arg(dimChars[dY])
263  .arg(m_eq[3])
264  .arg(dimChars[dX]);
265  equationStr += QString(" + %1 * %2*%3 + %4 * %5^2")
266  .arg(m_eq[4])
267  .arg(dimChars[dX])
268  .arg(dimChars[dY])
269  .arg(m_eq[5])
270  .arg(dimChars[dY]);
271 
272  return equationStr;
273 }
274 
275 bool ccQuadric::toFile_MeOnly(QFile& out, short dataVersion) const {
276  assert(out.isOpen() && (out.openMode() & QIODevice::WriteOnly));
277  if (dataVersion < 35) {
278  assert(false);
279  return false;
280  }
281 
282  if (!ccGenericPrimitive::toFile_MeOnly(out, dataVersion)) return false;
283 
284  // parameters (dataVersion>=35)
285  QDataStream outStream(&out);
286  outStream << m_minCorner.x;
287  outStream << m_minCorner.y;
288  outStream << m_maxCorner.x;
289  outStream << m_maxCorner.y;
290 
291  for (unsigned i = 0; i < 6; ++i) outStream << m_eq[i];
292 
293  return true;
294 }
295 
297  return std::max(static_cast<short>(35),
299 }
300 
302  short dataVersion,
303  int flags,
304  LoadedIDMap& oldToNewIDMap) {
305  if (!ccGenericPrimitive::fromFile_MeOnly(in, dataVersion, flags,
306  oldToNewIDMap))
307  return false;
308 
309  // parameters (dataVersion>=35)
310  QDataStream inStream(&in);
312  1);
314  1);
316  1);
318  1);
319 
320  for (unsigned i = 0; i < 6; ++i)
322  1);
323 
324  return true;
325 }
326 
328  trans = m_transformation;
331 }
constexpr unsigned CV_LOCAL_MODEL_MIN_SIZE[]
Min number of points to compute local models (see CV_LOCAL_MODEL_TYPES)
Definition: CVConst.h:129
@ QUADRIC
Definition: CVConst.h:125
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
Definition: CVGeom.h:798
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
std::string name
int count
void * X
Definition: SmallVector.cpp:45
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
Type y
Definition: CVGeom.h:137
Type u[3]
Definition: CVGeom.h:139
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:36
Type y
Definition: CVGeom.h:36
Type dot(const Vector3Tpl &v) const
Dot product.
Definition: CVGeom.h:408
Bounding box structure.
Definition: ecvBBox.h:25
ccGLMatrixTpl< T > inverse() const
Returns inverse transformation.
void apply(float vec[3]) const
Applies transformation to a 3D vector (in place) - float version.
Float version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:19
ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
Generic primitive interface.
bool init(unsigned vertCount, bool vertNormals, unsigned faceCount, unsigned faceNormCount)
Inits internal structures.
virtual bool setDrawingPrecision(unsigned steps)
Sets drawing precision.
ccGenericPrimitive * finishCloneJob(ccGenericPrimitive *primitive) const
Finished 'clone' job (vertices color, etc.)
ccPointCloud * vertices()
Returns vertices.
unsigned m_drawPrecision
Drawing precision (for primitives that support this feature)
static const int MIN_DRAWING_PRECISION
Minimum drawing precision.
bool toFile_MeOnly(QFile &out, short dataVersion) const override
Save own object data.
bool fromFile_MeOnly(QFile &in, short dataVersion, int flags, LoadedIDMap &oldToNewIDMap) override
Loads own object data.
ccGLMatrix m_transformation
Associated transformation (applied to vertices)
short minimumFileVersion_MeOnly() const override
void addTriangle(unsigned i1, unsigned i2, unsigned i3)
Adds a triangle to the mesh.
Definition: ecvMesh.cpp:2428
bool computeNormals(bool perVertex)
Computes normals.
Definition: ecvMesh.cpp:242
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
void setMetaData(const QString &key, const QVariant &data)
Sets a meta-data element.
Definition: ecvObject.cpp:216
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool hasNormals() const override
Returns whether normals are enabled or not.
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
Quadric (primitive)
Definition: ecvQuadric.h:16
CCVector2 m_maxCorner
Max corner.
Definition: ecvQuadric.h:104
short minimumFileVersion_MeOnly() const override
Definition: ecvQuadric.cpp:296
PointCoordinateType m_minZ
Min height.
Definition: ecvQuadric.h:113
CCVector2 m_minCorner
Min corner.
Definition: ecvQuadric.h:102
ccQuadric(CCVector2 minCorner, CCVector2 maxCorner, const PointCoordinateType eq[6], const Tuple3ub *dims=0, const ccGLMatrix *transMat=0, QString name=QString("Quadric"), unsigned precision=DEFAULT_DRAWING_PRECISION)
Default constructor.
Definition: ecvQuadric.cpp:21
bool fromFile_MeOnly(QFile &in, short dataVersion, int flags, LoadedIDMap &oldToNewIDMap) override
Loads own object data.
Definition: ecvQuadric.cpp:301
Tuple3ub m_dims
Dimension indexes.
Definition: ecvQuadric.h:110
QString getEquationString() const
Returns the quadric equation coefficients as a string.
Definition: ecvQuadric.cpp:249
PointCoordinateType m_eq[6]
Equation coefficients.
Definition: ecvQuadric.h:107
bool toFile_MeOnly(QFile &out, short dataVersion) const override
Save own object data.
Definition: ecvQuadric.cpp:275
PointCoordinateType m_maxZ
Max height.
Definition: ecvQuadric.h:115
static ccQuadric * Fit(cloudViewer::GenericIndexedCloudPersist *cloud, double *rms)
Fits a quadric primitive on a cloud.
Definition: ecvQuadric.cpp:122
virtual ccGenericPrimitive * clone() const override
Clones primitive.
Definition: ecvQuadric.cpp:116
PointCoordinateType projectOnQuadric(const CCVector3 &P, CCVector3 &Q) const
Projects a 3D point in the quadric coordinate system.
Definition: ecvQuadric.cpp:229
virtual ccBBox getOwnFitBB(ccGLMatrix &trans) override
Returns best-fit bounding-box (if available)
Definition: ecvQuadric.cpp:327
virtual bool buildUp() override
Builds primitive.
Definition: ecvQuadric.cpp:53
QMultiMap< unsigned, unsigned > LoadedIDMap
Map of loaded unique IDs (old ID --> new ID)
static void CoordsFromDataStream(QDataStream &stream, int flags, PointCoordinateType *out, unsigned count=1)
const Vector3Tpl< T > & maxCorner() const
Returns max corner (const)
Definition: BoundingBox.h:156
const Vector3Tpl< T > & minCorner() const
Returns min corner (const)
Definition: BoundingBox.h:154
virtual void placeIteratorAtBeginning()=0
Sets the cloud iterator at the beginning.
virtual const CCVector3 * getNextPoint()=0
Returns the next point (relatively to the global iterator position)
virtual unsigned size() const =0
Returns the number of points.
A generic 3D point cloud with index-based and presistent access to points.
const PointCoordinateType * getQuadric(Tuple3ub *dims=nullptr)
Returns the best interpolating 2.5D quadric.
const CCVector3 * getLSPlaneY()
Returns best interpolating plane (Least-square) 'Y' base vector.
const PointCoordinateType * getLSPlane()
Returns best interpolating plane equation (Least-square)
const CCVector3 * getGravityCenter()
Returns gravity center.
void setLSPlane(const PointCoordinateType eq[4], const CCVector3 &X, const CCVector3 &Y, const CCVector3 &N)
Sets the best interpolating plane equation (Least-square)
const CCVector3 * getLSPlaneX()
Returns best interpolating plane (Least-square) 'X' base vector.
void setGravityCenter(const CCVector3 &G)
Sets gravity center.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
const CCVector3 * getPoint(unsigned index) const override
normal_z y
normal_z x
normal_z z