//return angle between two vectors (in degrees) //warning: vectors will be normalized by default double GetAngle_deg(CCVector3& AB, CCVector3& AC) { AB.normalize(); AC.normalize(); double dotprod = AB.dot(AC); if (dotprod<=-1.0) return 180.0; else if (dotprod>1.0) return 0.0; return 180.0*acos(dotprod)/M_PI; }
//return angle between two vectors (in degrees) //warning: vectors will be normalized by default static double GetAngle_deg(CCVector3 AB, CCVector3 AC) { AB.normalize(); AC.normalize(); double dotprod = AB.dot(AC); //clamp value (just in case) if (dotprod <= -1.0) dotprod = -1.0; else if (dotprod > 1.0) dotprod = 1.0; return acos(dotprod) * CC_RAD_TO_DEG; }
void CCMiscTools::ComputeBaseVectors(const CCVector3 &N, CCVector3& X, CCVector3& Y) { CCVector3 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! }
float ccFastMarchingForNormsDirection::computePropagationConfidence(DirectionCell* originCell, DirectionCell* destCell) const { //1) it depends on the angle between the current cell's orientation // and its neighbor's orientation (symmetric) //2) it depends on whether the neighbor's relative position is // compatible with the current cell orientation (symmetric) CCVector3 AB = destCell->C - originCell->C; AB.normalize(); float psOri = fabs(static_cast<float>(AB.dot(originCell->N))); //ideal: 90 degrees float psDest = fabs(static_cast<float>(AB.dot(destCell->N))); //ideal: 90 degrees float oriConfidence = (psOri + psDest)/2; //between 0 and 1 (ideal: 0) return 1.0f - oriConfidence; }
float FastMarchingForFacetExtraction::computeTCoefApprox(CCLib::FastMarching::Cell* originCell, CCLib::FastMarching::Cell* destCell) const { PlanarCell* oCell = static_cast<PlanarCell*>(originCell); PlanarCell* dCell = static_cast<PlanarCell*>(destCell); //compute the 'confidence' relatively to the neighbor cell //1) it depends on the angle between the current cell's orientation // and its neighbor's orientation (symmetric) //2) it depends on whether the neighbor's relative position is // compatible with the current cell orientation (symmetric) float orientationConfidence = 0; { CCVector3 AB = dCell->C - oCell->C; AB.normalize(); float psOri = fabs(static_cast<float>(AB.dot(oCell->N))); //ideal: 90 degrees float psDest = fabs(static_cast<float>(AB.dot(dCell->N))); //ideal: 90 degrees orientationConfidence = (psOri + psDest)/2; //between 0 and 1 (ideal: 0) } //add reprojection error into balance if (m_useRetroProjectionError && m_octree && oCell->N.norm2() != 0) { PointCoordinateType theLSQPlaneEquation[4]; theLSQPlaneEquation[0] = oCell->N.x; theLSQPlaneEquation[1] = oCell->N.y; theLSQPlaneEquation[2] = oCell->N.z; theLSQPlaneEquation[3] = oCell->C.dot(oCell->N); CCLib::ReferenceCloud Yk(m_octree->associatedCloud()); if (m_octree->getPointsInCell(oCell->cellCode,m_gridLevel,&Yk,true)) { ScalarType reprojError = CCLib::DistanceComputationTools::ComputeCloud2PlaneDistance(&Yk,theLSQPlaneEquation,m_errorMeasure); if (reprojError >= 0) return (1.0f-orientationConfidence) * static_cast<float>(reprojError); } } return (1.0f-orientationConfidence) /** oCell->planarError*/; }
void cc2DLabel::getLabelInfo3(LabelInfo3& info) const { info.cloud1 = info.cloud2 = info.cloud3 = 0; if (m_points.size() != 3) return; //1st point info.cloud1 = m_points[0].cloud; info.point1Index = m_points[0].index; const CCVector3* P1 = info.cloud1->getPointPersistentPtr(info.point1Index); //2nd point info.cloud2 = m_points[1].cloud; info.point2Index = m_points[1].index; const CCVector3* P2 = info.cloud2->getPointPersistentPtr(info.point2Index); //3rd point info.cloud3 = m_points[2].cloud; info.point3Index = m_points[2].index; const CCVector3* P3 = info.cloud3->getPointPersistentPtr(info.point3Index); //area CCVector3 P1P2 = *P2-*P1; CCVector3 P1P3 = *P3-*P1; CCVector3 P2P3 = *P3-*P2; CCVector3 N = P1P2.cross(P1P3); //N = ABxAC info.area = N.norm()/2; //normal N.normalize(); info.normal = N; //edges length info.edges.u[0] = P1P2.norm2d(); //edge 1-2 info.edges.u[1] = P2P3.norm2d(); //edge 2-3 info.edges.u[2] = P1P3.norm2d(); //edge 3-1 //angle info.angles.u[0] = GetAngle_deg(P1P2,P1P3); //angleAtP1 info.angles.u[1] = GetAngle_deg(P2P3,-P1P2); //angleAtP2 info.angles.u[2] = GetAngle_deg(-P1P3,-P2P3); //angleAtP3 (should be equal to 180-a1-a2!) }
void ccGLMatrix::getParameters(float& alpha, CCVector3& axis3D, CCVector3& t3D) const { float trace = R11 + R22 + R33; trace = 0.5f*(trace-1.0f); if (fabs(trace)<1.0) { alpha = acos(trace); if (alpha > (float)M_PI_2) alpha -= (float)M_PI; } else alpha = 0.0; axis3D.x = (float)(R32-R23); axis3D.y = (float)(R13-R31); axis3D.z = (float)(R21-R12); axis3D.normalize(); t3D.x = R14; t3D.y = R24; t3D.z = R34; }
void ccPolyline::drawMeOnly(CC_DRAW_CONTEXT& context) { //no picking enabled on polylines if (MACRO_DrawPointNames(context)) return; unsigned vertCount = size(); if (vertCount < 2) return; bool draw = false; if (MACRO_Draw3D(context)) { draw = !m_mode2D; } else if (m_mode2D) { bool drawFG = MACRO_Foreground(context); draw = ((drawFG && m_foreground) || (!drawFG && !m_foreground)); } if (draw) { //standard case: list names pushing bool pushName = MACRO_DrawEntityNames(context); if (pushName) glPushName(getUniqueIDForDisplay()); if (colorsShown()) ccGL::Color3v(m_rgbColor.rgb); //display polyline if (vertCount > 1) { if (m_width != 0) { glPushAttrib(GL_LINE_BIT); glLineWidth(static_cast<GLfloat>(m_width)); } //DGM: we do the 'GL_LINE_LOOP' manually as I have a strange bug //on one on my graphic card with this mode! //glBegin(m_isClosed ? GL_LINE_LOOP : GL_LINE_STRIP); glBegin(GL_LINE_STRIP); for (unsigned i=0; i<vertCount; ++i) { ccGL::Vertex3v(getPoint(i)->u); } if (m_isClosed) { ccGL::Vertex3v(getPoint(0)->u); } glEnd(); //display arrow if (m_showArrow && m_arrowIndex < vertCount && (m_arrowIndex > 0 || m_isClosed)) { const CCVector3* P0 = getPoint(m_arrowIndex == 0 ? vertCount-1 : m_arrowIndex-1); const CCVector3* P1 = getPoint(m_arrowIndex); //direction of the last polyline chunk CCVector3 u = *P1 - *P0; u.normalize(); if (m_mode2D) { u *= -m_arrowLength; static const PointCoordinateType s_defaultArrowAngle = static_cast<PointCoordinateType>(15.0 * CC_DEG_TO_RAD); static const PointCoordinateType cost = cos(s_defaultArrowAngle); static const PointCoordinateType sint = sin(s_defaultArrowAngle); CCVector3 A(cost * u.x - sint * u.y, sint * u.x + cost * u.y, 0); CCVector3 B(cost * u.x + sint * u.y, -sint * u.x + cost * u.y, 0); glBegin(GL_POLYGON); ccGL::Vertex3v((A+*P1).u); ccGL::Vertex3v((B+*P1).u); ccGL::Vertex3v(( *P1).u); glEnd(); } else { if (!c_unitArrow) { c_unitArrow = QSharedPointer<ccCone>(new ccCone(0.5,0.0,1.0)); c_unitArrow->showColors(true); c_unitArrow->showNormals(false); c_unitArrow->setVisible(true); c_unitArrow->setEnabled(true); } if (colorsShown()) c_unitArrow->setTempColor(m_rgbColor); else c_unitArrow->setTempColor(context.pointsDefaultCol); //build-up unit arrow own 'context' CC_DRAW_CONTEXT markerContext = context; markerContext.flags &= (~CC_DRAW_ENTITY_NAMES); //we must remove the 'push name flag' so that the sphere doesn't push its own! markerContext._win = 0; glMatrixMode(GL_MODELVIEW); glPushMatrix(); ccGL::Translate(P1->x,P1->y,P1->z); ccGLMatrix rotMat = ccGLMatrix::FromToRotation(CCVector3(0,0,1),u); glMultMatrixf(rotMat.inverse().data()); glScalef(m_arrowLength,m_arrowLength,m_arrowLength); ccGL::Translate(0.0,0.0,-0.5); c_unitArrow->draw(markerContext); glPopMatrix(); } } if (m_width != 0) { glPopAttrib(); } } //display vertices if (m_showVertices) { glPushAttrib(GL_POINT_BIT); glPointSize((GLfloat)m_vertMarkWidth); glBegin(GL_POINTS); for (unsigned i=0; i<vertCount; ++i) { ccGL::Vertex3v(getPoint(i)->u); } glEnd(); glPopAttrib(); } if (pushName) glPopName(); } }
void qRansacSD::doAction() { assert(m_app); if (!m_app) return; const ccHObject::Container& selectedEntities = m_app->getSelectedEntities(); size_t selNum = selectedEntities.size(); if (selNum!=1) { m_app->dispToConsole("Select only one cloud!",ccMainAppInterface::ERR_CONSOLE_MESSAGE); return; } ccHObject* ent = selectedEntities[0]; assert(ent); if (!ent || !ent->isA(CC_POINT_CLOUD)) { m_app->dispToConsole("Select a real point cloud!",ccMainAppInterface::ERR_CONSOLE_MESSAGE); return; } ccPointCloud* pc = static_cast<ccPointCloud*>(ent); //input cloud size_t count = (size_t)pc->size(); bool hasNorms = pc->hasNormals(); PointCoordinateType bbMin[3],bbMax[3]; pc->getBoundingBox(bbMin,bbMax); //Convert CC point cloud to RANSAC_SD type PointCloud cloud; { try { cloud.reserve(count); } catch(...) { m_app->dispToConsole("Not enough memory!",ccMainAppInterface::ERR_CONSOLE_MESSAGE); return; } //default point & normal Point Pt; Pt.normal[0] = 0.0; Pt.normal[1] = 0.0; Pt.normal[2] = 0.0; for (unsigned i=0; i<(unsigned)count; ++i) { const CCVector3* P = pc->getPoint(i); Pt.pos[0] = P->x; Pt.pos[1] = P->y; Pt.pos[2] = P->z; if (hasNorms) { const PointCoordinateType* N = pc->getPointNormal(i); Pt.normal[0] = N[0]; Pt.normal[1] = N[1]; Pt.normal[2] = N[2]; } cloud.push_back(Pt); } //manually set bounding box! Vec3f cbbMin,cbbMax; cbbMin[0] = bbMin[0]; cbbMin[1] = bbMin[1]; cbbMin[2] = bbMin[2]; cbbMax[0] = bbMax[0]; cbbMax[1] = bbMax[1]; cbbMax[2] = bbMax[2]; cloud.setBBox(cbbMin,cbbMax); } //cloud scale (useful for setting several parameters const float scale = cloud.getScale(); //init dialog with default values ccRansacSDDlg rsdDlg(m_app->getMainWindow()); rsdDlg.epsilonDoubleSpinBox->setValue(.01f * scale); // set distance threshold to .01f of bounding box width // NOTE: Internally the distance threshold is taken as 3 * ransacOptions.m_epsilon!!! rsdDlg.bitmapEpsilonDoubleSpinBox->setValue(.02f * scale); // set bitmap resolution to .02f of bounding box width // NOTE: This threshold is NOT multiplied internally! rsdDlg.supportPointsSpinBox->setValue(s_supportPoints); rsdDlg.normThreshDoubleSpinBox->setValue(s_normThresh); rsdDlg.probaDoubleSpinBox->setValue(s_proba); rsdDlg.planeCheckBox->setChecked(s_primEnabled[0]); rsdDlg.sphereCheckBox->setChecked(s_primEnabled[1]); rsdDlg.cylinderCheckBox->setChecked(s_primEnabled[2]); rsdDlg.coneCheckBox->setChecked(s_primEnabled[3]); rsdDlg.torusCheckBox->setChecked(s_primEnabled[4]); if (!rsdDlg.exec()) return; //for parameters persistence { s_supportPoints = rsdDlg.supportPointsSpinBox->value(); s_normThresh = rsdDlg.normThreshDoubleSpinBox->value(); s_proba = rsdDlg.probaDoubleSpinBox->value(); //consistency check { unsigned char primCount = 0; for (unsigned char k=0; k<5; ++k) primCount += (unsigned)s_primEnabled[k]; if (primCount==0) { m_app->dispToConsole("No primitive type selected!",ccMainAppInterface::ERR_CONSOLE_MESSAGE); return; } } s_primEnabled[0] = rsdDlg.planeCheckBox->isChecked(); s_primEnabled[1] = rsdDlg.sphereCheckBox->isChecked(); s_primEnabled[2] = rsdDlg.cylinderCheckBox->isChecked(); s_primEnabled[3] = rsdDlg.coneCheckBox->isChecked(); s_primEnabled[4] = rsdDlg.torusCheckBox->isChecked(); } //import parameters from dialog RansacShapeDetector::Options ransacOptions; { ransacOptions.m_epsilon = rsdDlg.epsilonDoubleSpinBox->value(); ransacOptions.m_bitmapEpsilon = rsdDlg.bitmapEpsilonDoubleSpinBox->value(); ransacOptions.m_normalThresh = rsdDlg.normThreshDoubleSpinBox->value(); ransacOptions.m_minSupport = rsdDlg.supportPointsSpinBox->value(); ransacOptions.m_probability = rsdDlg.probaDoubleSpinBox->value(); } //progress dialog ccProgressDialog progressCb(false,m_app->getMainWindow()); progressCb.setRange(0,0); if (!hasNorms) { progressCb.setInfo("Computing normals (please wait)"); progressCb.start(); QApplication::processEvents(); cloud.calcNormals(.01f * scale); if (pc->reserveTheNormsTable()) { for (size_t i=0; i<count; ++i) { CCVector3 N(cloud[i].normal); N.normalize(); pc->addNorm(N.u); } pc->showNormals(true); //currently selected entities appearance may have changed! pc->prepareDisplayForRefresh_recursive(); } else { m_app->dispToConsole("Not enough memory to compute normals!",ccMainAppInterface::ERR_CONSOLE_MESSAGE); return; } } // set which primitives are to be detected by adding the respective constructors RansacShapeDetector detector(ransacOptions); // the detector object if (rsdDlg.planeCheckBox->isChecked()) detector.Add(new PlanePrimitiveShapeConstructor()); if (rsdDlg.sphereCheckBox->isChecked()) detector.Add(new SpherePrimitiveShapeConstructor()); if (rsdDlg.cylinderCheckBox->isChecked()) detector.Add(new CylinderPrimitiveShapeConstructor()); if (rsdDlg.coneCheckBox->isChecked()) detector.Add(new ConePrimitiveShapeConstructor()); if (rsdDlg.torusCheckBox->isChecked()) detector.Add(new TorusPrimitiveShapeConstructor()); size_t remaining = count; typedef std::pair< MiscLib::RefCountPtr< PrimitiveShape >, size_t > DetectedShape; MiscLib::Vector< DetectedShape > shapes; // stores the detected shapes // run detection // returns number of unassigned points // the array shapes is filled with pointers to the detected shapes // the second element per shapes gives the number of points assigned to that primitive (the support) // the points belonging to the first shape (shapes[0]) have been sorted to the end of pc, // i.e. into the range [ pc.size() - shapes[0].second, pc.size() ) // the points of shape i are found in the range // [ pc.size() - \sum_{j=0..i} shapes[j].second, pc.size() - \sum_{j=0..i-1} shapes[j].second ) { progressCb.setInfo("Operation in progress"); progressCb.setMethodTitle("Ransac Shape Detection"); progressCb.start(); //run in a separate thread s_detector = &detector; s_shapes = &shapes; s_cloud = &cloud; QFuture<void> future = QtConcurrent::run(doDetection); unsigned progress = 0; while (!future.isFinished()) { #if defined(_WIN32) || defined(WIN32) ::Sleep(500); #else sleep(500); #endif progressCb.update(++progress); //Qtconcurrent::run can't be canceled! /*if (progressCb.isCancelRequested()) { future.cancel(); future.waitForFinished(); s_remainingPoints = count; break; } //*/ } remaining = s_remainingPoints; progressCb.stop(); QApplication::processEvents(); } //else //{ // remaining = detector.Detect(cloud, 0, cloud.size(), &shapes); //} #ifdef _DEBUG FILE* fp = fopen("RANS_SD_trace.txt","wt"); fprintf(fp,"[Options]\n"); fprintf(fp,"epsilon=%f\n",ransacOptions.m_epsilon); fprintf(fp,"bitmap epsilon=%f\n",ransacOptions.m_bitmapEpsilon); fprintf(fp,"normal thresh=%f\n",ransacOptions.m_normalThresh); fprintf(fp,"min support=%i\n",ransacOptions.m_minSupport); fprintf(fp,"probability=%f\n",ransacOptions.m_probability); fprintf(fp,"\n[Statistics]\n"); fprintf(fp,"input points=%i\n",count); fprintf(fp,"segmented=%i\n",count-remaining); fprintf(fp,"remaining=%i\n",remaining); if (shapes.size()>0) { fprintf(fp,"\n[Shapes]\n"); for (unsigned i=0; i<shapes.size(); ++i) { PrimitiveShape* shape = shapes[i].first; unsigned shapePointsCount = shapes[i].second; std::string desc; shape->Description(&desc); fprintf(fp,"#%i - %s - %i points\n",i+1,desc.c_str(),shapePointsCount); } } fclose(fp); #endif if (remaining == count) { m_app->dispToConsole("Segmentation failed...",ccMainAppInterface::ERR_CONSOLE_MESSAGE); return; } if (shapes.size() > 0) { ccHObject* group = 0; for (MiscLib::Vector<DetectedShape>::const_iterator it = shapes.begin(); it != shapes.end(); ++it) { const PrimitiveShape* shape = it->first; size_t shapePointsCount = it->second; //too many points?! if (shapePointsCount > count) { m_app->dispToConsole("Inconsistent result!",ccMainAppInterface::ERR_CONSOLE_MESSAGE); break; } std::string desc; shape->Description(&desc); //new cloud for sub-part ccPointCloud* pcShape = new ccPointCloud(desc.c_str()); //we fill cloud with sub-part points if (!pcShape->reserve((unsigned)shapePointsCount)) { m_app->dispToConsole("Not enough memory!",ccMainAppInterface::ERR_CONSOLE_MESSAGE); delete pcShape; break; } bool saveNormals = pcShape->reserveTheNormsTable(); for (size_t j=0; j<shapePointsCount; ++j) { pcShape->addPoint(CCVector3(cloud[count-1-j].pos)); if (saveNormals) pcShape->addNorm(cloud[count-1-j].normal); } //random color unsigned char col[3]= { (unsigned char)(255.0*(float)rand()/(float)RAND_MAX), (unsigned char)(255.0*(float)rand()/(float)RAND_MAX), 0 }; col[2]=255-(col[1]+col[2])/2; pcShape->setRGBColor(col); pcShape->showColors(true); pcShape->showNormals(saveNormals); pcShape->setVisible(true); //convert detected primitive into a CC primitive type ccGenericPrimitive* prim = 0; switch(shape->Identifier()) { case 0: //plane { const PlanePrimitiveShape* plane = static_cast<const PlanePrimitiveShape*>(shape); Vec3f G = plane->Internal().getPosition(); Vec3f N = plane->Internal().getNormal(); Vec3f X = plane->getXDim(); Vec3f Y = plane->getYDim(); //we look for real plane extents float minX,maxX,minY,maxY; for (size_t j=0; j<shapePointsCount; ++j) { std::pair<float,float> param; plane->Parameters(cloud[count-1-j].pos,¶m); if (j!=0) { if (minX<param.first) minX=param.first; else if (maxX>param.first) maxX=param.first; if (minY<param.second) minY=param.second; else if (maxY>param.second) maxY=param.second; } else { minX=maxX=param.first; minY=maxY=param.second; } } //we recenter plane (as it is not always the case!) float dX = maxX-minX; float dY = maxY-minY; G += X * (minX+dX*0.5); G += Y * (minY+dY*0.5); //we build matrix from these vecctors ccGLMatrix glMat(CCVector3(X.getValue()), CCVector3(Y.getValue()), CCVector3(N.getValue()), CCVector3(G.getValue())); //plane primitive prim = new ccPlane(dX,dY,&glMat); } break; case 1: //sphere { const SpherePrimitiveShape* sphere = static_cast<const SpherePrimitiveShape*>(shape); float radius = sphere->Internal().Radius(); Vec3f CC = sphere->Internal().Center(); pcShape->setName(QString("Sphere (r=%1)").arg(radius,0,'f')); //we build matrix from these vecctors ccGLMatrix glMat; glMat.setTranslation(CCVector3(CC.getValue())); //sphere primitive prim = new ccSphere(radius,&glMat); prim->setEnabled(false); } break; case 2: //cylinder { const CylinderPrimitiveShape* cyl = static_cast<const CylinderPrimitiveShape*>(shape); Vec3f G = cyl->Internal().AxisPosition(); Vec3f N = cyl->Internal().AxisDirection(); Vec3f X = cyl->Internal().AngularDirection(); Vec3f Y = N.cross(X); float r = cyl->Internal().Radius(); float hMin = cyl->MinHeight(); float hMax = cyl->MaxHeight(); float h = hMax-hMin; G += N * (hMin+h*0.5); pcShape->setName(QString("Cylinder (r=%1/h=%2)").arg(r,0,'f').arg(h,0,'f')); //we build matrix from these vecctors ccGLMatrix glMat(CCVector3(X.getValue()), CCVector3(Y.getValue()), CCVector3(N.getValue()), CCVector3(G.getValue())); //cylinder primitive prim = new ccCylinder(r,h,&glMat); prim->setEnabled(false); } break; case 3: //cone { const ConePrimitiveShape* cone = static_cast<const ConePrimitiveShape*>(shape); Vec3f CC = cone->Internal().Center(); Vec3f CA = cone->Internal().AxisDirection(); float alpha = cone->Internal().Angle(); //compute max height CCVector3 maxP(CC.getValue()); float maxHeight = 0; for (size_t j=0; j<shapePointsCount; ++j) { float h = cone->Internal().Height(cloud[count-1-j].pos); if (h>maxHeight) { maxHeight=h; maxP = CCVector3(cloud[count-1-j].pos); } } pcShape->setName(QString("Cone (alpha=%1/h=%2)").arg(alpha,0,'f').arg(maxHeight,0,'f')); float radius = tan(alpha)*maxHeight; CCVector3 Z = CCVector3(CA.getValue()); CCVector3 C = CCVector3(CC.getValue()); //cone apex //construct remaining of base Z.normalize(); CCVector3 X = maxP - (C + maxHeight * Z); X.normalize(); CCVector3 Y = Z * X; //we build matrix from these vecctors ccGLMatrix glMat(X,Y,Z,C+(maxHeight*0.5)*Z); //cone primitive prim = new ccCone(0,radius,maxHeight,0,0,&glMat); prim->setEnabled(false); } break; case 4: //torus { const TorusPrimitiveShape* torus = static_cast<const TorusPrimitiveShape*>(shape); if (torus->Internal().IsAppleShaped()) { m_app->dispToConsole("[qRansacSD] Apple-shaped torus are not handled by CloudCompare!",ccMainAppInterface::WRN_CONSOLE_MESSAGE); } else { Vec3f CC = torus->Internal().Center(); Vec3f CA = torus->Internal().AxisDirection(); float minRadius = torus->Internal().MinorRadius(); float maxRadius = torus->Internal().MajorRadius(); pcShape->setName(QString("Torus (r=%1/R=%2)").arg(minRadius,0,'f').arg(maxRadius,0,'f')); CCVector3 Z = CCVector3(CA.getValue()); CCVector3 C = CCVector3(CC.getValue()); //construct remaining of base CCVector3 X = Z.orthogonal(); CCVector3 Y = Z * X; //we build matrix from these vecctors ccGLMatrix glMat(X,Y,Z,C); //torus primitive prim = new ccTorus(maxRadius-minRadius,maxRadius+minRadius,M_PI*2.0,false,0,&glMat); prim->setEnabled(false); } } break; } //is there a primitive to add to part cloud? if (prim) { prim->applyGLTransformation_recursive(); pcShape->addChild(prim); prim->setDisplay(pcShape->getDisplay()); prim->setColor(col); prim->showColors(true); prim->setVisible(true); } if (!group) { group = new ccHObject(QString("Ransac Detected Shapes (%1)").arg(ent->getName())); m_app->addToDB(group,true,0,false); } group->addChild(pcShape); m_app->addToDB(pcShape,true,0,false); count -= shapePointsCount; QApplication::processEvents(); } if (group) { assert(group->getChildrenNumber()!=0); //we hide input cloud pc->setEnabled(false); m_app->dispToConsole("[qRansacSD] Input cloud has been automtically hidden!",ccMainAppInterface::WRN_CONSOLE_MESSAGE); //we add new group to DB/display group->setVisible(true); group->setDisplay_recursive(pc->getDisplay()); group->prepareDisplayForRefresh_recursive(); m_app->refreshAll(); } } }
QStringList cc2DLabel::getLabelContent(int precision) { QStringList body; switch(m_points.size()) { case 1: //point { //init title /*title = m_title; //automatically elide the title title = titleFontMetrics.elidedText(title,Qt::ElideRight,dx); //*/ //coordinates ccGenericPointCloud* cloud = m_points[0].cloud; const unsigned& pointIndex = m_points[0].index; const CCVector3* P = cloud->getPointPersistentPtr(pointIndex); QString coordStr = QString("P#%0: (%1;%2;%3)").arg(pointIndex).arg(P->x,0,'f',precision).arg(P->y,0,'f',precision).arg(P->z,0,'f',precision); body << coordStr; //normal if (cloud->hasNormals()) { const PointCoordinateType* N = cloud->getPointNormal(pointIndex); assert(N); QString normStr = QString("Normal: (%1;%2;%3)").arg(N[0],0,'f',precision).arg(N[1],0,'f',precision).arg(N[2],0,'f',precision); body << normStr; } //color if (cloud->hasColors()) { const colorType* C = cloud->getPointColor(pointIndex); assert(C); QString colorStr = QString("Color: (%1;%2;%3)").arg(C[0]).arg(C[1]).arg(C[2]); body << colorStr; } //scalar field if (cloud->hasDisplayedScalarField()) { ScalarType D = cloud->getPointScalarValue(pointIndex); QString sfStr = QString("Scalar: %1").arg(D,0,'f',precision); body << sfStr; } } break; case 2: //vector { //1st point ccGenericPointCloud* cloud1 = m_points[0].cloud; const unsigned& pointIndex1 = m_points[0].index; const CCVector3* P1 = cloud1->getPointPersistentPtr(pointIndex1); //2nd point ccGenericPointCloud* cloud2 = m_points[1].cloud; const unsigned& pointIndex2 = m_points[1].index; const CCVector3* P2 = cloud2->getPointPersistentPtr(pointIndex2); PointCoordinateType d = (*P1-*P2).norm(); QString distStr = QString("Distance = %1").arg(d,0,'f',precision); body << distStr; QString coordStr1 = QString("P#%0: (%1;%2;%3)").arg(pointIndex1).arg(P1->x,0,'f',precision).arg(P1->y,0,'f',precision).arg(P1->z,0,'f',precision); body << coordStr1; QString coordStr2 = QString("P#%0: (%1;%2;%3)").arg(pointIndex2).arg(P2->x,0,'f',precision).arg(P2->y,0,'f',precision).arg(P2->z,0,'f',precision); body << coordStr2; } break; case 3: //triangle/plane { //1st point ccGenericPointCloud* cloud1 = m_points[0].cloud; const unsigned& pointIndex1 = m_points[0].index; const CCVector3* P1 = cloud1->getPointPersistentPtr(pointIndex1); //2nd point ccGenericPointCloud* cloud2 = m_points[1].cloud; const unsigned& pointIndex2 = m_points[1].index; const CCVector3* P2 = cloud2->getPointPersistentPtr(pointIndex2); //3rd point ccGenericPointCloud* cloud3 = m_points[2].cloud; const unsigned& pointIndex3 = m_points[2].index; const CCVector3* P3 = cloud3->getPointPersistentPtr(pointIndex3); //area CCVector3 P1P2 = *P2-*P1; CCVector3 P1P3 = *P3-*P1; CCVector3 N = P1P2.cross(P1P3); //N=ABxAC PointCoordinateType area = N.norm()*(PointCoordinateType)0.5; QString areaStr = QString("Area = %1").arg(area,0,'f',precision); body << areaStr; //coordinates QString coordStr1 = QString("A#%0: (%1;%2;%3)").arg(pointIndex1).arg(P1->x,0,'f',precision).arg(P1->y,0,'f',precision).arg(P1->z,0,'f',precision); body << coordStr1; QString coordStr2 = QString("B#%0: (%1;%2;%3)").arg(pointIndex2).arg(P2->x,0,'f',precision).arg(P2->y,0,'f',precision).arg(P2->z,0,'f',precision); body << coordStr2; QString coordStr3 = QString("C#%0: (%1;%2;%3)").arg(pointIndex3).arg(P3->x,0,'f',precision).arg(P3->y,0,'f',precision).arg(P3->z,0,'f',precision); body << coordStr3; //normal N.normalize(); QString normStr = QString("Normal: (%1;%2;%3)").arg(N.x,0,'f',precision).arg(N.y,0,'f',precision).arg(N.z,0,'f',precision); body << normStr; //angle CCVector3 P2P3 = *P3-*P2; //negatives CCVector3 _P1P2 = -P1P2; CCVector3 _P1P3 = -P1P3; CCVector3 _P2P3 = -P2P3; double angleAtP1 = GetAngle_deg(P1P2,P1P3); double angleAtP2 = GetAngle_deg(P2P3,_P1P2); double angleAtP3 = GetAngle_deg(_P1P3,_P2P3); //should be equal to 180-a1-a2! QString angleStr = QString("Angles: A=%1 - B=%3 - C=%5 deg.").arg(angleAtP1,0,'f',precision).arg(angleAtP2,0,'f',precision).arg(angleAtP3,0,'f',precision); body << angleStr; } break; default: assert(false); break; } return body; }
bool ccCone::buildUp() { if (m_drawPrecision < MIN_DRAWING_PRECISION) return false; //invalid dimensions? if (m_height < ZERO_TOLERANCE || m_bottomRadius + m_topRadius < ZERO_TOLERANCE) return false; //topology bool singlePointBottom = (m_bottomRadius < ZERO_TOLERANCE); bool singlePointTop = (m_topRadius < ZERO_TOLERANCE); assert(!singlePointBottom || !singlePointTop); unsigned steps = m_drawPrecision; //vertices unsigned vertCount = 2; if (!singlePointBottom) vertCount += steps; if (!singlePointTop) vertCount += steps; //normals unsigned faceNormCounts = steps+2; //vertices unsigned facesCount = steps; if (!singlePointBottom) facesCount += steps; if (!singlePointTop) facesCount += steps; if (!singlePointBottom && !singlePointTop) facesCount += steps; //allocate (& clear) structures if (!init(vertCount,false,facesCount,faceNormCounts)) { ccLog::Error("[ccCone::buildUp] Not enough memory"); return false; } ccPointCloud* verts = vertices(); assert(verts); assert(m_triNormals); //2 first points: centers of the top & bottom surfaces CCVector3 bottomCenter = CCVector3(m_xOff,m_yOff,-m_height)/2; CCVector3 topCenter = CCVector3(-m_xOff,-m_yOff,m_height)/2; { //bottom center verts->addPoint(bottomCenter); CompressedNormType nIndex = ccNormalVectors::GetNormIndex(CCVector3(0,0,-1).u); m_triNormals->addElement(nIndex); //top center verts->addPoint(topCenter); nIndex = ccNormalVectors::GetNormIndex(CCVector3(0,0,1).u); m_triNormals->addElement(nIndex); } //then, angular sweep for top and/or bottom surfaces { PointCoordinateType angle_rad_step = static_cast<PointCoordinateType>(2.0*M_PI)/static_cast<PointCoordinateType>(steps); //bottom surface if (!singlePointBottom) { for (unsigned i=0; i<steps; ++i) { CCVector3 P(bottomCenter.x + cos(angle_rad_step*i)*m_bottomRadius, bottomCenter.y + sin(angle_rad_step*i)*m_bottomRadius, bottomCenter.z); verts->addPoint(P); } } //top surface if (!singlePointTop) { for (unsigned i=0; i<steps; ++i) { CCVector3 P(topCenter.x + cos(angle_rad_step*i)*m_topRadius, topCenter.y + sin(angle_rad_step*i)*m_topRadius, topCenter.z); verts->addPoint(P); } } //side normals { for (unsigned i=0; i<steps; ++i) { //slope CCVector3 u(-sin(angle_rad_step*i),cos(angle_rad_step*i),0); CCVector3 v(bottomCenter.x-topCenter.x + u.y*(m_bottomRadius-m_topRadius), bottomCenter.y-topCenter.y - u.x*(m_bottomRadius-m_topRadius), bottomCenter.z-topCenter.z); CCVector3 N = v.cross(u); N.normalize(); CompressedNormType nIndex = ccNormalVectors::GetNormIndex(N.u); m_triNormals->addElement(nIndex); } } } //mesh faces { assert(m_triVertIndexes); unsigned bottomIndex = 2; unsigned topIndex = 2+(singlePointBottom ? 0 : steps); //bottom surface if (!singlePointBottom) { for (unsigned i=0;i<steps;++i) { addTriangle(0,bottomIndex+(i+1)%steps,bottomIndex+i); addTriangleNormalIndexes(0,0,0); } } //top surface if (!singlePointTop) { for (unsigned i=0;i<steps;++i) { addTriangle(1,topIndex+i,topIndex+(i+1)%steps); addTriangleNormalIndexes(1,1,1); } } if (!singlePointBottom && !singlePointTop) { for (unsigned i=0;i<steps;++i) { unsigned iNext = (i+1)%steps; addTriangle(bottomIndex+i,bottomIndex+iNext,topIndex+i); addTriangleNormalIndexes(2+i,2+iNext,2+i); addTriangle(topIndex+i,bottomIndex+iNext,topIndex+iNext); addTriangleNormalIndexes(2+i,2+iNext,2+iNext); } } else if (!singlePointTop) { for (unsigned i=0;i<steps;++i) { unsigned iNext = (i+1)%steps; addTriangle(topIndex+i,0,topIndex+iNext); addTriangleNormalIndexes(2+i,2+iNext,2+iNext); //TODO: middle normal should be halfbetween?! } } else //if (!singlePointBottom) { for (unsigned i=0;i<steps;++i) { unsigned iNext = (i+1)%steps; addTriangle(bottomIndex+i,bottomIndex+iNext,1); addTriangleNormalIndexes(2+i,2+iNext,2+i); //TODO: last normal should be halfbetween?! } } } notifyGeometryUpdate(); showTriNorms(true); return true; }
virtual void add3dFace(const DL_3dFaceData& face) { //TODO: understand what this really is?! CCVector3 P[4]; for (unsigned i=0; i<4; ++i) { P[i] = CCVector3( static_cast<PointCoordinateType>(face.x[i]), static_cast<PointCoordinateType>(face.y[i]), static_cast<PointCoordinateType>(face.z[i]) ); } //create the 'faces' mesh if necessary if (!m_faces) { ccPointCloud* vertices = new ccPointCloud("vertices"); m_faces = new ccMesh(vertices); m_faces->setName("Faces"); m_faces->addChild(vertices); m_faces->setVisible(true); vertices->setEnabled(false); vertices->setLocked(true); m_root->addChild(m_faces); } ccPointCloud* vertices = dynamic_cast<ccPointCloud*>(m_faces->getAssociatedCloud()); if (!vertices) { assert(false); return; } int vertIndexes[4] = {-1, -1, -1, -1}; unsigned addedVertCount = 4; //check if the two last vertices are the same if (P[2].x == P[3].x && P[2].y == P[3].y && P[2].z == P[3].z) addedVertCount = 3; //current face color colorType col[3]; colorType* faceCol = 0; if (getCurrentColour(col)) faceCol = col; //look for already defined vertices unsigned vertCount = vertices->size(); if (vertCount) { //DGM TODO: could we be smarter? for (unsigned i=0; i<addedVertCount; ++i) { for (unsigned j=0; j<vertCount; ++j) { const CCVector3* Pj = vertices->getPoint(j); if (P[i].x == Pj->x && P[i].y == Pj->y && P[i].z == Pj->z) { bool useCurrentVertex = true; //We must also check that the color is the same (if any) if (faceCol || vertices->hasColors()) { const colorType* _faceCol = faceCol ? faceCol : ccColor::white; const colorType* _vertCol = vertices->hasColors() ? vertices->getPointColor(j) : ccColor::white; useCurrentVertex = (_faceCol[0] == _vertCol[0] && _faceCol[1] == _vertCol[1] && _faceCol[2] == _vertCol[2]); } if (useCurrentVertex) { vertIndexes[i] = static_cast<int>(j); break; } } } } } //now create new vertices unsigned createdVertCount = 0; { for (unsigned i=0; i<addedVertCount; ++i) if (vertIndexes[i] < 0) ++createdVertCount; } if (createdVertCount != 0) { //reserve memory for the new vertices if (!vertices->reserve(vertCount+createdVertCount)) { ccLog::Error("[DxfImporter] Not enough memory!"); return; } for (unsigned i=0; i<addedVertCount; ++i) { if (vertIndexes[i] < 0) { vertIndexes[i] = static_cast<int>(vertCount++); vertices->addPoint(P[i]); } } } //number of triangles to add unsigned addTriCount = (addedVertCount == 3 ? 1 : 2); //now add the corresponding face(s) if (!m_faces->reserve(m_faces->size() + addTriCount)) { ccLog::Error("[DxfImporter] Not enough memory!"); return; } m_faces->addTriangle(vertIndexes[0], vertIndexes[1], vertIndexes[2]); if (addedVertCount == 4) m_faces->addTriangle(vertIndexes[0], vertIndexes[2], vertIndexes[3]); //add per-triangle normals { //normals table NormsIndexesTableType* triNormsTable = m_faces->getTriNormsTable(); bool firstTime = false; if (!triNormsTable) { triNormsTable = new NormsIndexesTableType(); m_faces->setTriNormsTable(triNormsTable); m_faces->addChild(triNormsTable); firstTime = true; } //add 1 or 2 new entries unsigned triNormCount = triNormsTable->currentSize(); if (!triNormsTable->reserve(triNormsTable->currentSize() + addTriCount)) { ccLog::Error("[DxfImporter] Not enough memory!"); return; } CCVector3 N = (P[1]-P[0]).cross(P[2]-P[0]); N.normalize(); triNormsTable->addElement(ccNormalVectors::GetNormIndex(N.u)); if (addTriCount == 2) { N = (P[2]-P[0]).cross(P[3]-P[0]); N.normalize(); triNormsTable->addElement(ccNormalVectors::GetNormIndex(N.u)); } //per-triangle normals indexes if (firstTime) { if (!m_faces->reservePerTriangleNormalIndexes()) { ccLog::Error("[DxfImporter] Not enough memory!"); return; } m_faces->showNormals(true); } int n1 = static_cast<int>(triNormCount); m_faces->addTriangleNormalIndexes(n1, n1, n1); if (addTriCount == 2) { int n2 = static_cast<int>(triNormCount+1); m_faces->addTriangleNormalIndexes(n2, n2, n2); } } //and now for the color if (faceCol) { //RGB field already instantiated? if (vertices->hasColors()) { for (unsigned i=0; i<createdVertCount; ++i) vertices->addRGBColor(faceCol); } //otherwise, reserve memory and set all previous points to white by default else if (vertices->setRGBColor(ccColor::white)) { //then replace the last color(s) by the current one for (unsigned i=0; i<createdVertCount; ++i) vertices->setPointColor(vertCount-1-i,faceCol); m_faces->showColors(true); } } else if (vertices->hasColors()) { //add default color if none is defined! for (unsigned i=0; i<createdVertCount; ++i) vertices->addRGBColor(ccColor::white); } }
CC_FILE_ERROR MascaretFilter::saveToFile(ccHObject* entity, QString filename, SaveParameters& parameters) { if (!entity || filename.isEmpty()) return CC_FERR_BAD_ARGUMENT; //look for valid profiles std::vector<ccPolyline*> profiles; try { //get all polylines std::vector<ccPolyline*> candidates; if (entity->isA(CC_TYPES::POLY_LINE)) { candidates.push_back(static_cast<ccPolyline*>(entity)); } else if (entity->isA(CC_TYPES::HIERARCHY_OBJECT)) { for (unsigned i=0; i<entity->getChildrenNumber(); ++i) if (entity->getChild(i) && entity->getChild(i)->isA(CC_TYPES::POLY_LINE)) candidates.push_back(static_cast<ccPolyline*>(entity->getChild(i))); } //then keep the valid profiles only for (size_t i=0; i<candidates.size(); ++i) { ccPolyline* poly = candidates[i]; if ( !poly->hasMetaData(ccPolyline::MetaKeyUpDir()) || !poly->hasMetaData(ccPolyline::MetaKeyAbscissa()) || !poly->hasMetaData(ccPolyline::MetaKeyPrefixCenter()+".x") || !poly->hasMetaData(ccPolyline::MetaKeyPrefixCenter()+".y") || !poly->hasMetaData(ccPolyline::MetaKeyPrefixCenter()+".z") || !poly->hasMetaData(ccPolyline::MetaKeyPrefixDirection()+".x") || !poly->hasMetaData(ccPolyline::MetaKeyPrefixDirection()+".y") || !poly->hasMetaData(ccPolyline::MetaKeyPrefixDirection()+".z") ) { ccLog::Warning(QString("[Mascaret] Polyline '%1' is not a valid profile (missing meta-data)").arg(poly->getName())); break; } else { profiles.push_back(poly); } } } catch (const std::bad_alloc&) { return CC_FERR_NOT_ENOUGH_MEMORY; } if (profiles.empty()) return CC_FERR_NO_SAVE; //open ASCII file for writing QFile file(filename); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return CC_FERR_WRITING; QTextStream outFile(&file); outFile.setRealNumberPrecision(12); //ask some parameters SaveMascaretFileDlg smfDlg; if (!smfDlg.exec()) return CC_FERR_CANCELED_BY_USER; QString biefName = smfDlg.biefNameLineEdit->text(); QString type("T"); //B or T --> ask the user switch(smfDlg.typeComboBox->currentIndex()) { case 0: type = "B"; //bathy break; case 1: type = "T"; //topo break; default: assert(false); } //sanitize the 'bief' (reach) name biefName = MakeMascaretName(biefName); //sort the sections by their abscissa if (profiles.size() > 1) { for (size_t i=0; i<profiles.size()-1; ++i) { size_t smallestIndex = i; double smallestAbscissa = profiles[i]->getMetaData(ccPolyline::MetaKeyAbscissa()).toDouble(); for (size_t j=i+1; j<profiles.size(); ++j) { double a = profiles[j]->getMetaData(ccPolyline::MetaKeyAbscissa()).toDouble(); if (a < smallestAbscissa) { smallestAbscissa = a; smallestIndex = j; } } if (i != smallestIndex) { std::swap(profiles[i],profiles[smallestIndex]); } } } CC_FILE_ERROR result = CC_FERR_NO_SAVE; //for each profile for (size_t i=0; i<profiles.size(); ++i) { ccPolyline* poly = profiles[i]; unsigned vertCount = poly ? poly->size() : 0; if (vertCount < 2) { //invalid size ccLog::Warning(QString("[Mascaret] Polyline '%1' does not have enough vertices").arg(poly->getName())); continue; } //decode meta-data bool ok = true; int upDir = 2; double absc = 0.0; CCVector3d Cd(0,0,0); CCVector3d Ud(0,0,0); while (true) //fake loop for easy break { upDir = poly->getMetaData(ccPolyline::MetaKeyUpDir()).toInt(&ok); if (!ok) break; absc = poly->getMetaData(ccPolyline::MetaKeyAbscissa()).toDouble(&ok); if (!ok) break; Cd.x = poly->getMetaData(ccPolyline::MetaKeyPrefixCenter()+".x").toDouble(&ok); if (!ok) break; Cd.y = poly->getMetaData(ccPolyline::MetaKeyPrefixCenter()+".y").toDouble(&ok); if (!ok) break; Cd.z = poly->getMetaData(ccPolyline::MetaKeyPrefixCenter()+".z").toDouble(&ok); if (!ok) break; Ud.x = poly->getMetaData(ccPolyline::MetaKeyPrefixDirection()+".x").toDouble(&ok); if (!ok) break; Ud.y = poly->getMetaData(ccPolyline::MetaKeyPrefixDirection()+".y").toDouble(&ok); if (!ok) break; Ud.z = poly->getMetaData(ccPolyline::MetaKeyPrefixDirection()+".z").toDouble(&ok); break; } if (!ok) { ccLog::Warning(QString("[Mascaret] At least one of the meta-data entry of polyline '%1' is invalid?!").arg(poly->getName())); continue; } QString profileName = poly->getName(); profileName = MakeMascaretName(profileName); CCVector3 C = CCVector3::fromArray(Cd.u); CCVector3 U = CCVector3::fromArray(Ud.u); U.normalize(); //write header outFile << "PROFIL " << biefName << " " << profileName << " " << absc; #define SAVE_AS_GEO_MASCARET #ifdef SAVE_AS_GEO_MASCARET int xDir = upDir == 2 ? 0 : upDir+1; int yDir = xDir == 2 ? 0 : xDir+1; //for "geo"-mascaret, we add some more information: // - first point { const CCVector3* firstP = poly->getPoint(0); CCVector3d firstPg = poly->toGlobal3d(*firstP); outFile << " "; outFile << firstPg.u[xDir] << " " << firstPg.u[yDir]; } // - last point { const CCVector3* lastP = poly->getPoint(vertCount-1); CCVector3d lastPg = poly->toGlobal3d(*lastP); outFile << " "; outFile << lastPg.u[xDir] << " " << lastPg.u[yDir]; } // - profile/path intersection point { outFile << " AXE "; CCVector3d Cdg = poly->toGlobal3d(Cd); outFile << Cdg.u[xDir] << " " << Cdg.u[yDir]; } #endif outFile << endl; //check the abscissa values order (must be increasing!) bool inverted = false; { const CCVector3* P0 = poly->getPoint(0); //convert to 'local' coordinate system CCVector2 Q0; ToLocalAbscissa(*P0, C, U, upDir, Q0); const CCVector3* P1 = poly->getPoint(vertCount-1); //convert to 'local' coordinate system CCVector2 Q1; ToLocalAbscissa(*P1, C, U, upDir, Q1); inverted = (Q1.x < Q0.x); } for (unsigned j=0; j<vertCount; ++j) { const CCVector3* P = poly->getPoint(inverted ? vertCount-1-j : j); //convert to 'local' coordinate system CCVector2 Q; ToLocalAbscissa(*P, C, U, upDir, Q); outFile << Q.x << " " << Q.y << " " << type; #ifdef SAVE_AS_GEO_MASCARET { //for "geo"-mascaret, we add some more information: // - real coordinates of the point outFile << " "; CCVector3d Pg = poly->toGlobal3d(*P); outFile << Pg.u[xDir] << " " << Pg.u[yDir]; } #endif outFile << endl; } result = CC_FERR_NO_ERROR; } file.close(); return result; }