示例#1
0
//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;
}
示例#2
0
//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;
}
示例#3
0
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*/;
}
示例#6
0
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!)
}
示例#7
0
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;
}
示例#8
0
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();
	}
}
示例#9
0
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,&param);
                    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();
        }
    }
}
示例#10
0
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;
}
示例#11
0
文件: ccCone.cpp 项目: 3660628/trunk
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;
}
示例#12
0
文件: DxfFilter.cpp 项目: kod3r/trunk
	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);
		}
	}
示例#13
0
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;
}