void CCMiscTools::ComputeBaseVectors(const CCVector3d &N, CCVector3d& X, CCVector3d& Y) { CCVector3d Nunit = N; Nunit.normalize(); //we create a first vector orthogonal to the input one X = Nunit.orthogonal(); //X is also normalized //we deduce the orthogonal vector to the input one and X Y = N.cross(X); //Y.normalize(); //should already be normalized! }
ccGLMatrixd ccGLUtils::GenerateGLRotationMatrixFromVectors(const CCVector3d& sourceVec, const CCVector3d& destVec) { //we compute scalar prod between the two vectors double ps = sourceVec.dot(destVec); //we bound result (in case vecors are not exactly unit) if (ps > 1.0) ps = 1.0; else if (ps < -1.0) ps = -1.0; //we deduce angle from scalar prod double angle_deg = acos(ps) * CC_RAD_TO_DEG; //we compute rotation axis with scalar prod CCVector3d axis = sourceVec.cross(destVec); //we eventually compute the rotation matrix with axis and angle return GenerateGLRotationMatrixFromAxisAndAngle(axis, angle_deg); }
void ccPointListPickingDlg::exportToASCII(ExportFormat format) { if (!m_associatedCloud) return; //get all labels std::vector<cc2DLabel*> labels; unsigned count = getPickedPoints(labels); if (count == 0) return; QSettings settings; settings.beginGroup("PointListPickingDlg"); QString filename = settings.value("filename", "picking_list.txt").toString(); settings.endGroup(); filename = QFileDialog::getSaveFileName(this, "Export to ASCII", filename, AsciiFilter::GetFileFilter()); if (filename.isEmpty()) return; settings.beginGroup("PointListPickingDlg"); settings.setValue("filename", filename); settings.endGroup(); FILE* fp = fopen(qPrintable(filename),"wt"); if (!fp) { ccLog::Error(QString("Failed to open file '%1' for saving!").arg(filename)); return; } //if a global shift exists, ask the user if it should be applied CCVector3d shift = m_associatedCloud->getGlobalShift(); double scale = m_associatedCloud->getGlobalScale(); if (shift.norm2() != 0 || scale != 1.0) { if (QMessageBox::warning( this, "Apply global shift", "Do you want to apply global shift/scale to exported points?", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes ) == QMessageBox::No) { //reset shift shift = CCVector3d(0,0,0); scale = 1.0; } } //starting index int startIndex = startIndexSpinBox->value(); for (unsigned i=0; i<count; ++i) { assert(labels[i]->size() == 1); const cc2DLabel::PickedPoint& PP = labels[i]->getPoint(0); const CCVector3* P = PP.cloud->getPoint(PP.index); if (format == PLP_ASCII_EXPORT_IXYZ) fprintf(fp,"%i,",i+startIndex); fprintf(fp,"%.12f,%.12f,%.12f\n", static_cast<double>(P->x)/scale - shift.x, static_cast<double>(P->y)/scale - shift.y, static_cast<double>(P->z)/scale - shift.z); } fclose(fp); ccLog::Print(QString("[I/O] File '%1' saved successfully").arg(filename)); }
bool ccClipBox::move3D(const CCVector3d& uInput) { if (m_activeComponent == NONE || !m_box.isValid()) return false; CCVector3d u = uInput; //Arrows if (m_activeComponent >= X_MINUS_ARROW && m_activeComponent <= CROSS) { if (m_glTransEnabled) m_glTrans.inverse().applyRotation(u); switch(m_activeComponent) { case X_MINUS_ARROW: m_box.minCorner().x += static_cast<PointCoordinateType>(u.x); if (m_box.minCorner().x > m_box.maxCorner().x) m_box.minCorner().x = m_box.maxCorner().x; break; case X_PLUS_ARROW: m_box.maxCorner().x += static_cast<PointCoordinateType>(u.x); if (m_box.minCorner().x > m_box.maxCorner().x) m_box.maxCorner().x = m_box.minCorner().x; break; case Y_MINUS_ARROW: m_box.minCorner().y += static_cast<PointCoordinateType>(u.y); if (m_box.minCorner().y > m_box.maxCorner().y) m_box.minCorner().y = m_box.maxCorner().y; break; case Y_PLUS_ARROW: m_box.maxCorner().y += static_cast<PointCoordinateType>(u.y); if (m_box.minCorner().y > m_box.maxCorner().y) m_box.maxCorner().y = m_box.minCorner().y; break; case Z_MINUS_ARROW: m_box.minCorner().z += static_cast<PointCoordinateType>(u.z); if (m_box.minCorner().z > m_box.maxCorner().z) m_box.minCorner().z = m_box.maxCorner().z; break; case Z_PLUS_ARROW: m_box.maxCorner().z += static_cast<PointCoordinateType>(u.z); if (m_box.minCorner().z > m_box.maxCorner().z) m_box.maxCorner().z = m_box.minCorner().z; break; case CROSS: m_box += CCVector3::fromArray(u.u); break; default: assert(false); return false; } //send 'modified' signal emit boxModified(&m_box); } else if (m_activeComponent == SPHERE) { //handled by move2D! return false; } else if (m_activeComponent >= X_MINUS_TORUS && m_activeComponent <= Z_PLUS_TORUS) { //we guess the rotation order by comparing the current screen 'normal' //and the vector prod of u and the current rotation axis CCVector3d Rb(0,0,0); switch(m_activeComponent) { case X_MINUS_TORUS: Rb.x = -1; break; case X_PLUS_TORUS: Rb.x = 1; break; case Y_MINUS_TORUS: Rb.y = -1; break; case Y_PLUS_TORUS: Rb.y = 1; break; case Z_MINUS_TORUS: Rb.z = -1; break; case Z_PLUS_TORUS: Rb.z = 1; break; default: assert(false); return false; } CCVector3d R = Rb; if (m_glTransEnabled) m_glTrans.applyRotation(R); CCVector3d RxU = R.cross(u); //look for the most parallel dimension int minDim = 0; double maxDot = m_viewMatrix.getColumnAsVec3D(0).dot(RxU); for (int i=1; i<3; ++i) { double dot = m_viewMatrix.getColumnAsVec3D(i).dot(RxU); if (fabs(dot) > fabs(maxDot)) { maxDot = dot; minDim = i; } } //angle is proportional to absolute displacement double angle_rad = u.norm()/m_box.getDiagNorm() * M_PI; if (maxDot < 0.0) angle_rad = -angle_rad; ccGLMatrixd rotMat; rotMat.initFromParameters(angle_rad,Rb,CCVector3d(0,0,0)); CCVector3 C = m_box.getCenter(); ccGLMatrixd transMat; transMat.setTranslation(-C); transMat = rotMat * transMat; transMat.setTranslation(transMat.getTranslationAsVec3D() + CCVector3d::fromArray(C.u)); m_glTrans = m_glTrans * ccGLMatrix(transMat.inverse().data()); enableGLTransformation(true); } else { assert(false); return false; } update(); return true; }
CCLib::ReferenceCloud* qHPR::removeHiddenPoints(CCLib::GenericIndexedCloudPersist* theCloud, const CCVector3d& viewPoint, double fParam) { assert(theCloud); unsigned nbPoints = theCloud->size(); if (nbPoints == 0) return 0; //less than 4 points? no need for calculation, we return the whole cloud if (nbPoints < 4) { CCLib::ReferenceCloud* visiblePoints = new CCLib::ReferenceCloud(theCloud); if (!visiblePoints->addPointIndex(0,nbPoints)) //well even for less than 4 points we never know ;) { //not enough memory! delete visiblePoints; visiblePoints = 0; } return visiblePoints; } double maxRadius = 0; //convert point cloud to an array of double triplets (for qHull) coordT* pt_array = new coordT[(nbPoints+1)*3]; { coordT* _pt_array = pt_array; for (unsigned i=0; i<nbPoints; ++i) { CCVector3d P = CCVector3d::fromArray(theCloud->getPoint(i)->u) - viewPoint; *_pt_array++ = static_cast<coordT>(P.x); *_pt_array++ = static_cast<coordT>(P.y); *_pt_array++ = static_cast<coordT>(P.z); //we keep track of the highest 'radius' double r2 = P.norm2(); if (maxRadius < r2) maxRadius = r2; } //we add the view point (Cf. HPR) *_pt_array++ = 0; *_pt_array++ = 0; *_pt_array++ = 0; maxRadius = sqrt(maxRadius); } //apply spherical flipping { maxRadius *= pow(10.0,fParam) * 2; coordT* _pt_array = pt_array; for (unsigned i=0; i<nbPoints; ++i) { CCVector3d P = CCVector3d::fromArray(theCloud->getPoint(i)->u) - viewPoint; double r = (maxRadius/P.norm()) - 1.0; *_pt_array++ *= r; *_pt_array++ *= r; *_pt_array++ *= r; } } //array to flag points on the convex hull std::vector<bool> pointBelongsToCvxHull; static char qHullCommand[] = "qhull QJ Qci"; if (!qh_new_qhull(3,nbPoints+1,pt_array,False,qHullCommand,0,stderr)) { try { pointBelongsToCvxHull.resize(nbPoints+1,false); } catch (const std::bad_alloc&) { //not enough memory! delete[] pt_array; return 0; } vertexT *vertex = 0,**vertexp = 0; facetT *facet = 0; FORALLfacets { //if (!facet->simplicial) // error("convhulln: non-simplicial facet"); // should never happen with QJ setT* vertices = qh_facet3vertex(facet); FOREACHvertex_(vertices) { pointBelongsToCvxHull[qh_pointid(vertex->point)] = true; } qh_settempfree(&vertices); } } delete[] pt_array; pt_array = 0; qh_freeqhull(!qh_ALL); //free long memory int curlong, totlong; qh_memfreeshort (&curlong, &totlong); //free short memory and memory allocator if (!pointBelongsToCvxHull.empty()) { //compute the number of points belonging to the convex hull unsigned cvxHullSize = 0; { for (unsigned i=0; i<nbPoints; ++i) if (pointBelongsToCvxHull[i]) ++cvxHullSize; } CCLib::ReferenceCloud* visiblePoints = new CCLib::ReferenceCloud(theCloud); if (cvxHullSize!=0 && visiblePoints->reserve(cvxHullSize)) { for (unsigned i=0; i<nbPoints; ++i) if (pointBelongsToCvxHull[i]) visiblePoints->addPointIndex(i); //can't fail, see above return visiblePoints; } else //not enough memory { delete visiblePoints; visiblePoints = 0; } } return 0; }
bool GeometricalAnalysisTools::refineSphereLS( GenericIndexedCloudPersist* cloud, CCVector3& center, PointCoordinateType& radius, double minReltaiveCenterShift/*=1.0e-3*/) { if (!cloud || cloud->size() < 5) { //invalid input return false; } CCVector3d c = CCVector3d::fromArray(center.u); double r = radius; unsigned count = cloud->size(); //compute barycenter CCVector3d G(0,0,0); { for (unsigned i=0; i<count; ++i) { const CCVector3* P = cloud->getPoint(i); G += CCVector3d::fromArray(P->u); } G /= count; } static const unsigned MAX_ITERATIONS = 100; for (unsigned it=0; it<MAX_ITERATIONS; ++it) { // Compute average L, dL/da, dL/db, dL/dc. double meanNorm = 0.0; CCVector3d derivatives(0,0,0); unsigned realCount = 0; for (unsigned i=0; i<count; ++i) { const CCVector3* Pi = cloud->getPoint(i); CCVector3d Di = CCVector3d::fromArray(Pi->u) - c; double norm = Di.norm(); if (norm < ZERO_TOLERANCE) continue; meanNorm += norm; derivatives = Di/norm; ++realCount; } meanNorm /= count; derivatives /= count; //backup previous center CCVector3d c0 = c; //deduce new center c = G - derivatives * meanNorm; r = meanNorm; double shift = (c-c0).norm(); double relativeShift = shift/r; if (relativeShift < minReltaiveCenterShift) break; } return true; }