Exemple #1
0
//helper: computes a facet horizontal and vertical extensions
void ComputeFacetExtensions(CCVector3& N, ccPolyline* facetContour, double& horizExt, double& vertExt)
{
	//horizontal and vertical extensions
	horizExt = vertExt = 0;
	
	CCLib::GenericIndexedCloudPersist* vertCloud = facetContour->getAssociatedCloud();
	if (vertCloud)
	{
		//oriRotMat.applyRotation(N); //DGM: oriRotMat is only for display!
		//we assume that at this point the "up" direction is always (0,0,1)
		CCVector3 Xf(1,0,0), Yf(0,1,0);
		//we get the horizontal vector on the plane
		CCVector3 D = CCVector3(0,0,1).cross(N);
		if (D.norm2() > ZERO_TOLERANCE) //otherwise the facet is horizontal!
		{
			Yf = D;
			Yf.normalize();
			Xf = N.cross(Yf);
		}

		const CCVector3* G = CCLib::Neighbourhood(vertCloud).getGravityCenter();

		ccBBox box;
		for (unsigned i=0; i<vertCloud->size(); ++i)
		{
			const CCVector3 P = *(vertCloud->getPoint(i)) - *G;
			CCVector3 p( P.dot(Xf), P.dot(Yf), 0 );
			box.add(p);
		}

		vertExt = box.getDiagVec().x;
		horizExt = box.getDiagVec().y;
	}
}
Exemple #2
0
void TestShpFilter::readPolygonFile(const QString &filePath) const
{
	const unsigned expectedNumPoints = 14; // File has 15 points but as its a polygon, CC will keep the 14 first pts
	CCVector3d bbMin(-626146.0444521683, 5219675.646154184, 0);
	CCVector3d shift = ccGlobalShiftManager::BestShift(bbMin);
	bool shiftEnabled = true;

	ccHObject container;
	FileIOFilter::LoadParameters params;
	params.alwaysDisplayLoadDialog = false;
	params.shiftHandlingMode = ccGlobalShiftManager::Mode::NO_DIALOG;
	params.coordinatesShiftEnabled = &shiftEnabled;
	params.coordinatesShift = &shift;
	params.preserveShiftOnSave = true;
	ShpFilter filter;

	CC_FILE_ERROR error = filter.loadFile(filePath, container, params);
	QVERIFY(error == CC_FERR_NO_ERROR);

	QVERIFY(container.getChildrenNumber() == 1);
	ccHObject *item = container.getFirstChild();
	QVERIFY(item->getClassID() == CC_TYPES::POLY_LINE);

	auto *poly = static_cast<ccPolyline *>(item);
	QVERIFY(poly->size() == expectedNumPoints);
	QVERIFY(!poly->isScalarFieldEnabled());
	QVERIFY(!poly->is2DMode());
	QVERIFY(poly->isClosed());

	CCLib::GenericIndexedCloudPersist *vertices = poly->getAssociatedCloud();
	QVERIFY(vertices->size() == expectedNumPoints);

	std::array<double, 14> expectedXs{-626146.0444521683, -187004.53123683017, -59884.61951660062, 169316.43343351,
	                                  180872.78904444003, 300288.4636907161, 914701.3703384919, 752912.3917854726,
	                                  880032.303505702, 749060.273248496, 473633.79785466543, 375404.77516176086,
	                                  -212043.3017271784, -187004.53123683017};
	std::array<double, 14> expectedYs{6182705.280398346, 6409980.274079968, 6383015.444321131, 6488948.704087989,
	                                  6606438.319465778, 6650737.682641009, 6236634.939916019, 5878387.91597719,
	                                  5391094.921049644, 5271679.246403368, 5352573.735679878, 5219675.646154184,
	                                  5348721.617142901, 5789789.189626727};

	for (unsigned i(0); i < expectedNumPoints; ++i)
	{
		const CCVector3 *p = vertices->getPoint(i);
		QCOMPARE(p->x, static_cast<ScalarType >(expectedXs[i] + shift.x));
		QCOMPARE(p->y, static_cast<ScalarType >(expectedYs[i] + shift.y));
		QCOMPARE(p->z, 0.0);
	}
}
bool ccRegistrationTools::ICP(	ccHObject* data,
								ccHObject* model,
								ccGLMatrix& transMat,
								double &finalScale,
								double& finalRMS,
								unsigned& finalPointCount,
								double minRMSDecrease,
								unsigned maxIterationCount,
								unsigned randomSamplingLimit,
								bool removeFarthestPoints,
								CCLib::ICPRegistrationTools::CONVERGENCE_TYPE method,
								bool adjustScale,
								double finalOverlapRatio/*=1.0*/,
								bool useDataSFAsWeights/*=false*/,
								bool useModelSFAsWeights/*=false*/,
								int filters/*=CCLib::ICPRegistrationTools::SKIP_NONE*/,
								int maxThreadCount/*=0*/,
								QWidget* parent/*=0*/)
{
	//progress bar
	QScopedPointer<ccProgressDialog> progressDlg;
	if (parent)
	{
		progressDlg.reset(new ccProgressDialog(false, parent));
	}

	Garbage<CCLib::GenericIndexedCloudPersist> cloudGarbage;

	//if the 'model' entity is a mesh, we need to sample points on it
	CCLib::GenericIndexedCloudPersist* modelCloud = nullptr;
	ccGenericMesh* modelMesh = nullptr;
	if (model->isKindOf(CC_TYPES::MESH))
	{
		modelMesh = ccHObjectCaster::ToGenericMesh(model);
		modelCloud = modelMesh->getAssociatedCloud();
	}
	else
	{
		modelCloud = ccHObjectCaster::ToGenericPointCloud(model);
	}

	//if the 'data' entity is a mesh, we need to sample points on it
	CCLib::GenericIndexedCloudPersist* dataCloud = nullptr;
	if (data->isKindOf(CC_TYPES::MESH))
	{
		dataCloud = CCLib::MeshSamplingTools::samplePointsOnMesh(ccHObjectCaster::ToGenericMesh(data), s_defaultSampledPointsOnDataMesh, progressDlg.data());
		if (!dataCloud)
		{
			ccLog::Error("[ICP] Failed to sample points on 'data' mesh!");
			return false;
		}
		cloudGarbage.add(dataCloud);
	}
	else
	{
		dataCloud = ccHObjectCaster::ToGenericPointCloud(data);
	}

	//we activate a temporary scalar field for registration distances computation
	CCLib::ScalarField* dataDisplayedSF = nullptr;
	int oldDataSfIdx = -1, dataSfIdx = -1;

	//if the 'data' entity is a real ccPointCloud, we can even create a proper temporary SF for registration distances
	if (data->isA(CC_TYPES::POINT_CLOUD))
	{
		ccPointCloud* pc = static_cast<ccPointCloud*>(data);
		dataDisplayedSF = pc->getCurrentDisplayedScalarField();
		oldDataSfIdx = pc->getCurrentInScalarFieldIndex();
		dataSfIdx = pc->getScalarFieldIndexByName(REGISTRATION_DISTS_SF);
		if (dataSfIdx < 0)
			dataSfIdx = pc->addScalarField(REGISTRATION_DISTS_SF);
		if (dataSfIdx >= 0)
			pc->setCurrentScalarField(dataSfIdx);
		else
		{
			ccLog::Error("[ICP] Couldn't create temporary scalar field! Not enough memory?");
			return false;
		}
	}
	else
	{
		if (!dataCloud->enableScalarField())
		{
			ccLog::Error("[ICP] Couldn't create temporary scalar field! Not enough memory?");
			return false;
		}
	}

	//add a 'safety' margin to input ratio
	static double s_overlapMarginRatio = 0.2;
	finalOverlapRatio = std::max(finalOverlapRatio, 0.01); //1% minimum
	//do we need to reduce the input point cloud (so as to be close
	//to the theoretical number of overlapping points - but not too
	//low so as we are not registered yet ;)
	if (finalOverlapRatio < 1.0 - s_overlapMarginRatio)
	{
		//DGM we can now use 'approximate' distances as SAITO algorithm is exact (but with a coarse resolution)
		//level = 7 if < 1.000.000
		//level = 8 if < 10.000.000
		//level = 9 if > 10.000.000
		int gridLevel = static_cast<int>(floor(log10(static_cast<double>(std::max(dataCloud->size(), modelCloud->size()))))) + 2;
		    gridLevel = std::min(std::max(gridLevel, 7), 9);
		int result = -1;
		if (modelMesh)
		{
			CCLib::DistanceComputationTools::Cloud2MeshDistanceComputationParams c2mParams;
			c2mParams.octreeLevel = gridLevel;
			c2mParams.maxSearchDist = 0;
			c2mParams.useDistanceMap = true;
			c2mParams.signedDistances = false;
			c2mParams.flipNormals = false;
			c2mParams.multiThread = false;
			result = CCLib::DistanceComputationTools::computeCloud2MeshDistance(dataCloud, modelMesh, c2mParams, progressDlg.data());
		}
		else
		{
			result = CCLib::DistanceComputationTools::computeApproxCloud2CloudDistance(	dataCloud,
																						modelCloud,
																						gridLevel,
																						-1,
																						progressDlg.data());
		}

		if (result < 0)
		{
			ccLog::Error("Failed to determine the max (overlap) distance (not enough memory?)");
			return false;
		}

		//determine the max distance that (roughly) corresponds to the input overlap ratio
		ScalarType maxSearchDist = 0;
		{
			unsigned count = dataCloud->size();
			std::vector<ScalarType> distances;
			try
			{
				distances.resize(count);
			}
			catch (const std::bad_alloc&)
			{
				ccLog::Error("Not enough memory!");
				return false;
			}
			for (unsigned i=0; i<count; ++i)
			{
				distances[i] = dataCloud->getPointScalarValue(i);
			}
			
			ParallelSort(distances.begin(), distances.end());
			
			//now look for the max value at 'finalOverlapRatio+margin' percent
			maxSearchDist = distances[static_cast<unsigned>(std::max(1.0,count*(finalOverlapRatio+s_overlapMarginRatio)))-1];
		}

		//evntually select the points with distance below 'maxSearchDist'
		//(should roughly correspond to 'finalOverlapRatio + margin' percent)
		{
			CCLib::ReferenceCloud* refCloud = new CCLib::ReferenceCloud(dataCloud);
			cloudGarbage.add(refCloud);
			unsigned countBefore = dataCloud->size();
			unsigned baseIncrement = static_cast<unsigned>(std::max(100.0,countBefore*finalOverlapRatio*0.05));
			for (unsigned i=0; i<countBefore; ++i)
			{
				if (dataCloud->getPointScalarValue(i) <= maxSearchDist)
				{
					if (	refCloud->size() == refCloud->capacity()
						&&	!refCloud->reserve(refCloud->size() + baseIncrement) )
					{
						ccLog::Error("Not enough memory!");
						return false;
					}
					refCloud->addPointIndex(i);
				}
			}
			refCloud->resize(refCloud->size());
			dataCloud = refCloud;

			unsigned countAfter = dataCloud->size();
			double keptRatio = static_cast<double>(countAfter)/countBefore;
			ccLog::Print(QString("[ICP][Partial overlap] Selecting %1 points out of %2 (%3%) for registration").arg(countAfter).arg(countBefore).arg(static_cast<int>(100*keptRatio)));

			//update the relative 'final overlap' ratio
			finalOverlapRatio /= keptRatio;
		}
	}

	//weights
	CCLib::ScalarField* modelWeights = nullptr;
	CCLib::ScalarField* dataWeights = nullptr;
	{
		if (!modelMesh && useModelSFAsWeights)
		{
			if (modelCloud == dynamic_cast<CCLib::GenericIndexedCloudPersist*>(model) && model->isA(CC_TYPES::POINT_CLOUD))
			{
				ccPointCloud* pc = static_cast<ccPointCloud*>(model);
				modelWeights = pc->getCurrentDisplayedScalarField();
				if (!modelWeights)
					ccLog::Warning("[ICP] 'useDataSFAsWeights' is true but model has no displayed scalar field!");
			}
			else
			{
				ccLog::Warning("[ICP] 'useDataSFAsWeights' is true but only point cloud scalar fields can be used as weights!");
			}
		}

		if (useDataSFAsWeights)
		{
			if (!dataDisplayedSF)
			{
				if (dataCloud == ccHObjectCaster::ToPointCloud(data))
					ccLog::Warning("[ICP] 'useDataSFAsWeights' is true but data has no displayed scalar field!");
				else
					ccLog::Warning("[ICP] 'useDataSFAsWeights' is true but only point cloud scalar fields can be used as weights!");
			}
			else
			{
				dataWeights = dataDisplayedSF;
			}
		}
	}

	CCLib::ICPRegistrationTools::RESULT_TYPE result;
	CCLib::PointProjectionTools::Transformation transform;
	CCLib::ICPRegistrationTools::Parameters params;
	{
		params.convType = method;
		params.minRMSDecrease = minRMSDecrease;
		params.nbMaxIterations = maxIterationCount;
		params.adjustScale = adjustScale;
		params.filterOutFarthestPoints = removeFarthestPoints;
		params.samplingLimit = randomSamplingLimit;
		params.finalOverlapRatio = finalOverlapRatio;
		params.modelWeights = modelWeights;
		params.dataWeights = dataWeights;
		params.transformationFilters = filters;
		params.maxThreadCount = maxThreadCount;
	}

	result = CCLib::ICPRegistrationTools::Register(	modelCloud,
													modelMesh,
													dataCloud,
													params,
													transform,
													finalRMS,
													finalPointCount,
													static_cast<CCLib::GenericProgressCallback*>(progressDlg.data()));

	if (result >= CCLib::ICPRegistrationTools::ICP_ERROR)
	{
		ccLog::Error("Registration failed: an error occurred (code %i)",result);
	}
	else if (result == CCLib::ICPRegistrationTools::ICP_APPLY_TRANSFO)
	{
		transMat = FromCCLibMatrix<PointCoordinateType, float>(transform.R, transform.T, transform.s);
		finalScale = transform.s;
	}

	//remove temporary SF (if any)
	if (dataSfIdx >= 0)
	{
		assert(data->isA(CC_TYPES::POINT_CLOUD));
		ccPointCloud* pc = static_cast<ccPointCloud*>(data);
		pc->setCurrentScalarField(oldDataSfIdx);
		pc->deleteScalarField(dataSfIdx);
	}

	return (result < CCLib::ICPRegistrationTools::ICP_ERROR);
}
bool DistanceMapGenerationTool::ComputeSurfacesAndVolumes(	const QSharedPointer<Map>& map,
															ccPolyline* profile,
															Measures& surface,
															Measures& volume)
{
	if (!map || !profile)
		//invalid input!
		return false;

	CCLib::GenericIndexedCloudPersist* vertices = profile->getAssociatedCloud();
	unsigned vertexCount = vertices ? vertices->size() : 0;
	if (vertexCount < 2)
	{
		//invalid profile!
		return false;
	}

	const ccPointCloud* pcVertices = dynamic_cast<ccPointCloud*>(profile->getAssociatedCloud());
	if (!pcVertices)
		return false;

	//surface measures
	surface = Measures();
	//volume measures
	volume = Measures();

	//theoretical surface and volumes
	{
		double surfaceProd = 0.0;
		double volumeProd = 0.0;
		const double yMax = map->yMin + map->yStep * (double)map->ySteps;
		for (unsigned i=1; i<pcVertices->size(); ++i)
		{
			const CCVector3* P0 = pcVertices->getPoint(i-1);
			const CCVector3* P1 = pcVertices->getPoint(i);

			//polyline: X = radius, Y = height
			double r0 = P0->x;
			double y0 = P0->y;
			double r1 = P1->x;
			double y1 = P1->y;
			
			//without loss of generality ;)
			if (y0 > y1)
			{
				std::swap(y0,y1);
				std::swap(r0,r1);
			}

			//segment is totally outside the map?
			if (y1 < map->yMin || y0 > yMax)
			{
				//we skip it
				continue;
			}

			if (y0 < map->yMin)
			{
				//interpolate r0 @ map->yMin
				double alpha = (map->yMin - y0)/(y1 - y0);
				assert(alpha >= 0.0 && alpha <= 1.0);
				r0 = r0 + alpha * (r1 - r0);
				y0 = map->yMin;
			}
			else if (y1 > yMax)
			{
				//interpolate r1 @ map->yMax
				double alpha = (yMax - y0)/(y1 - y0);
				assert(alpha >= 0.0 && alpha <= 1.0);
				r1 = r0 + alpha * (r1 - r0);
				y1 = yMax;
			}

			//product for truncated cone surface (see http://en.wikipedia.org/wiki/Frustum)
			double segmentLength = sqrt((r1-r0)*(r1-r0) + (y1-y0)*(y1-y0));
			surfaceProd += (r0 + r1) * segmentLength;

			//product for truncated cone volume (see http://en.wikipedia.org/wiki/Frustum)
			volumeProd += (y1 - y0) * (r0*r0 + r1*r1 + r0*r1);
		}

		surface.theoretical = M_PI * surfaceProd;
		volume.theoretical  = M_PI / 3.0 * volumeProd;
	}

	int revolDim = GetPoylineRevolDim(profile);
	if (revolDim < 0)
		return false;

	//constant factors
	const double surfPart = map->xStep / 2.0;				//perimeter of a portion of circle of angle alpha = alpha * r (* height to get the external surface)
	const double volPart = map->yStep * map->xStep / 6.0;	//area of a portion of circle of angle alpha = alpha/2 * r^2 (* height to get the volume)

	const MapCell* cell = &map->at(0);
	//for each row
	for (unsigned j=0; j<map->ySteps; ++j)
	{
		//corresponding heights
		double height1 = map->yMin + (double)j * map->yStep;
		double height2 = height1 + map->yStep;
		double r_th1 = -1.0;
		double r_th2 = -1.0;
		
		//search nearest "segment" in polyline
		double height_middle = (height1 + height2)/2.0;
		for (unsigned k=1; k<vertexCount; ++k)
		{
			const CCVector3* A = vertices->getPoint(k-1);
			const CCVector3* B = vertices->getPoint(k);

			double alpha = (height_middle - A->y)/(B->y - A->y);
			if (alpha >= 0.0 && alpha <= 1.0)
			{
				r_th1 = A->x + (height1 - A->y)/(B->y - A->y) * (B->x - A->x);
				r_th2 = A->x + (height2 - A->y)/(B->y - A->y) * (B->x - A->x);
				break; //FIXME: we hope that there's only one segment facing this particular height?!
			}
		}

		if (r_th1 >= 0.0 /* && r_th2 >= 0.0*/)
		{
			//for each column
			for (unsigned i=0; i<map->xSteps; ++i, ++cell)
			{
				//deviation from theory
				double d = (cell->count != 0 ? cell->value : 0.0);

				//"real" radius
				double r1 = r_th1 + d;	//see ComputeRadialDist --> << double dist = radius - radius_th; >>
				double r2 = r_th2 + d;	//FIXME: works only if the "scalar field" used for map creation was radial distances!!!

				//surface of the element (truncated cone external face)
				{
					double s = sqrt((r2 - r1) * (r2 - r1) + map->yStep * map->yStep);
					double externalSurface = /*surfPart * */(r1 + r2) * s;
					surface.total += externalSurface;
					//dispatch in 'positive' and 'negative' surface
					if (d >= 0.0)
						surface.positive += externalSurface;
					else
						surface.negative += externalSurface;
				}

				//volume of the element
				{
					volume.total += /*volPart * */(r1*r1 + r2*r2 + r1*r2);
					//volume of the gain (or loss) of matter
					double diffVolume = /*volPart * */fabs(3.0*d * (r_th1 + r_th2 + d)); // = (r*r) * part - (r_th*r_th) * part = [(r_th+d)*(r_th+d)-r_th*r_th] * part
					if (d >= 0.0)
						volume.positive += diffVolume;
					else
						volume.negative += diffVolume;
				}
			}
		}
		else
		{
			cell += map->xSteps;
		}
	}

	//don't forget to mult. by constants
	surface.total *= surfPart;
	surface.positive *= surfPart;
	surface.negative *= surfPart;

	volume.total *= volPart;
	volume.positive *= volPart;
	volume.negative *= volPart;

	return true;
}
bool DistanceMapGenerationTool::ComputeRadialDist(	ccPointCloud* cloud,
													ccPolyline* profile,
													bool storeRadiiAsSF/*=false*/,
													ccMainAppInterface* app/*=0*/)
{
	//check input cloud and profile/polyline
	if (!cloud || !profile)
	{
		if (app)
			app->dispToConsole(QString("Internal error: invalid input parameters"),ccMainAppInterface::ERR_CONSOLE_MESSAGE);
		return false;
	}
	assert(cloud && profile);

	//number of vertices for the profile
	CCLib::GenericIndexedCloudPersist* vertices = profile->getAssociatedCloud();
	unsigned vertexCount = vertices->size();
	if (vertexCount < 2)
	{
		if (app)
			app->dispToConsole(QString("Invalid polyline (not enough vertices)"),ccMainAppInterface::ERR_CONSOLE_MESSAGE);
		return false;
	}

	//profile meta-data
	ProfileMetaData profileDesc;
	if (!GetPoylineMetaData(profile, profileDesc))
	{
		if (app)
			app->dispToConsole(QString("Invalid polyline (bad or missing meta-data)"),ccMainAppInterface::ERR_CONSOLE_MESSAGE);
		return false;
	}

	//reserve a new scalar field (or take the old one if it already exists)
	int sfIdx = cloud->getScalarFieldIndexByName(RADIAL_DIST_SF_NAME);
	if (sfIdx < 0)
		sfIdx = cloud->addScalarField(RADIAL_DIST_SF_NAME);
	if (sfIdx < 0)
	{
		if (app)
			app->dispToConsole(QString("Failed to allocate a new scalar field for computing distances! Try to free some memory ..."),ccMainAppInterface::ERR_CONSOLE_MESSAGE);
		return false;
	}
	ccScalarField* sf = static_cast<ccScalarField*>(cloud->getScalarField(sfIdx));
	unsigned pointCount = cloud->size();
	sf->resize(pointCount); //should always be ok
	assert(sf);

	ccScalarField* radiiSf = 0;
	if (storeRadiiAsSF)
	{
		int sfIdxRadii = cloud->getScalarFieldIndexByName(RADII_SF_NAME);
		if (sfIdxRadii < 0)
			sfIdxRadii = cloud->addScalarField(RADII_SF_NAME);
		if (sfIdxRadii < 0)
		{
			if (app)
				app->dispToConsole(QString("Failed to allocate a new scalar field for storing radii! You should try to free some memory ..."),ccMainAppInterface::WRN_CONSOLE_MESSAGE);
			//return false;
		}
		else
		{
			radiiSf = static_cast<ccScalarField*>(cloud->getScalarField(sfIdxRadii));
			radiiSf->resize(pointCount); //should always be ok
		}
	}

	bool success = true;

	//now compute the distance between the cloud and the (implicit) surface of revolution
	{
		ccGLMatrix cloudToSurface = profileDesc.computeProfileToSurfaceTrans();

		//we deduce the horizontal dimensions from the revolution axis
		const unsigned char dim1 = static_cast<unsigned char>(profileDesc.revolDim < 2 ? profileDesc.revolDim+1 : 0);
		const unsigned char dim2 = (dim1 < 2 ? dim1+1 : 0);

		ccProgressDialog dlg(true, app ? app->getMainWindow() : 0);
		dlg.setMethodTitle("Cloud to profile radial distance");
		dlg.setInfo(qPrintable(QString("Polyline: %1 vertices\nCloud: %2 points").arg(vertexCount).arg(pointCount)));
		dlg.start();
		CCLib::NormalizedProgress nProgress(static_cast<CCLib::GenericProgressCallback*>(&dlg),pointCount);

		for (unsigned i=0; i<pointCount; ++i)
		{
			const CCVector3* P = cloud->getPoint(i);

			//relative point position
			CCVector3 Prel = cloudToSurface * (*P);

			//deduce point height and radius (i.e. in profile 2D coordinate system)
			double height = Prel.u[profileDesc.revolDim] - profileDesc.heightShift;
			//TODO FIXME: we assume the surface of revolution is smooth!
			double radius = sqrt(Prel.u[dim1]*Prel.u[dim1] + Prel.u[dim2]*Prel.u[dim2]);

			if (radiiSf)
			{
				ScalarType radiusVal = static_cast<ScalarType>(radius);
				radiiSf->setValue(i,radiusVal);
			}

			//search nearest "segment" in polyline
			ScalarType minDist = NAN_VALUE;
			for (unsigned j=1; j<vertexCount; ++j)
			{
				const CCVector3* A = vertices->getPoint(j-1);
				const CCVector3* B = vertices->getPoint(j);

				double alpha = (height - A->y)/(B->y - A->y);
				if (alpha >= 0.0 && alpha <= 1.0)
				{
					//we deduce the right radius by linear interpolation
					double radius_th = A->x + alpha * (B->x - A->x);
					double dist = radius - radius_th;

					//we look at the closest segment (if the polyline is concave!)
					if (!CCLib::ScalarField::ValidValue(minDist) || dist*dist < minDist*minDist)
					{
						minDist = static_cast<ScalarType>(dist);
					}
				}
			}

			sf->setValue(i,minDist);

			if (!nProgress.oneStep())
			{
				//cancelled by user
				for (unsigned j=i; j<pointCount; ++j)
					sf->setValue(j,NAN_VALUE);

				success = false;
				break;
			}

			//TEST
			//*const_cast<CCVector3*>(P) = Prel;
		}
		
		//TEST
		//cloud->invalidateBoundingBox();
	}

	sf->computeMinAndMax();
	cloud->setCurrentDisplayedScalarField(sfIdx);
	cloud->showSF(true);

	return success;
}
ccPointCloud* DistanceMapGenerationTool::ConvertMapToCloud(	const QSharedPointer<Map>& map,
															ccPolyline* profile,
															double baseRadius/*=1.0*/,
															bool keepNaNPoints/*=true*/)
{
	if (!map || !profile)
		return 0;

	unsigned count = map->ySteps * map->xSteps;

	ccPointCloud* cloud = new ccPointCloud("map");
	ccScalarField* sf = new ccScalarField("values");
	if (!cloud->reserve(count) || !sf->reserve(count))
	{
		//not enough memory
		delete cloud;
		sf->release();
		return 0;
	}

	//number of vertices
	CCLib::GenericIndexedCloudPersist* polyVertices = profile->getAssociatedCloud();
	unsigned polyVertCount = polyVertices->size();
	if (polyVertCount < 2)
		return 0;

	//profile meta-data (we only need the height shift)
	PointCoordinateType heightShift = 0;
	GetPolylineHeightShift(profile, heightShift);

	const double xStep = baseRadius * (2.0*M_PI) / static_cast<double>(map->xSteps);

	const MapCell* cell = &map->at(0);
	for (unsigned j=0; j<map->ySteps; ++j)
	{
		CCVector3 P(0,static_cast<PointCoordinateType>(map->yMin + (static_cast<double>(j) + 0.5) * map->yStep),0);

		//for each column
		for (unsigned i=0; i<map->xSteps; ++i, ++cell)
		{
			if (keepNaNPoints || cell->count != 0)
			{
				P.x = static_cast<PointCoordinateType>(map->xMin + (static_cast<double>(i) + 0.5) * xStep);

				//search nearest "segment" in polyline
				for (unsigned k=1; k<polyVertCount; ++k)
				{
					const CCVector3* A = polyVertices->getPoint(k-1);
					const CCVector3* B = polyVertices->getPoint(k);

					double alpha = (P.y - heightShift - A->y)/(B->y - A->y);
					if (alpha >= 0.0 && alpha <= 1.0)
					{
						//we deduce the right radius by linear interpolation
						double radius_th = A->x + alpha * (B->x - A->x);
						//TODO: we take the first radius (even if there are other segments at
						//this particular height, because we can't guess which one is the 'right' one!
						P.z = static_cast<PointCoordinateType>(radius_th);
						break;
					}
				}

				cloud->addPoint(P);

				ScalarType val = cell->count ? static_cast<ScalarType>(cell->value) : NAN_VALUE;
				sf->addElement(val);
			}
		}
	}

	sf->computeMinAndMax();
	int sfIdx = cloud->addScalarField(sf);
	cloud->setCurrentDisplayedScalarField(sfIdx);
	cloud->showSF(true);
	cloud->resize(cloud->size()); //if we have skipped NaN values!

	return cloud;
}
ccMesh* DistanceMapGenerationTool::ConvertProfileToMesh(ccPolyline* profile,
														const ccGLMatrix& cloudToSurface,
														bool counterclockwise,
														unsigned angularSteps/*=36*/,
														QImage mapTexture/*=QImage()*/)
{
	if (!profile || angularSteps < 3)
	{
		return 0;
	}

	//profile vertices
	CCLib::GenericIndexedCloudPersist* profileVertices = profile->getAssociatedCloud();
	unsigned profVertCount = profileVertices->size();
	if (profVertCount < 2)
	{
		return 0;
	}

	//profile meta-data
	ProfileMetaData profileDesc;
	if (!GetPoylineMetaData(profile, profileDesc))
	{
		assert(false);
		return 0;
	}

	unsigned char Z = static_cast<unsigned char>(profileDesc.revolDim);
	//we deduce the 2 other ('horizontal') dimensions
	const unsigned char X = (Z < 2 ? Z+1 : 0);
	const unsigned char Y = (X < 2 ? X+1 : 0);

	unsigned meshVertCount = profVertCount * angularSteps;
	unsigned meshFaceCount = (profVertCount-1) * angularSteps * 2;
	ccPointCloud* cloud = new ccPointCloud("vertices");
	ccMesh* mesh = new ccMesh(cloud);
	if (!cloud->reserve(meshVertCount) || !mesh->reserve(meshFaceCount))
	{
		//not enough memory
		delete cloud;
		delete mesh;
		return 0;
	}

	ccGLMatrix surfaceToCloud = cloudToSurface.inverse();

	//create vertices
	{
		double cwSign = (counterclockwise ? -1.0 : 1.0);
		for (unsigned j=0; j<angularSteps; ++j)
		{
			double angle_rad = static_cast<double>(j)/angularSteps * (2*M_PI);

			CCVector3d N(sin(angle_rad) * cwSign,
						 cos(angle_rad),
						 0);

			for (unsigned i=0; i<profVertCount; ++i)
			{
				const CCVector3* P = profileVertices->getPoint(i);
				double radius = static_cast<double>(P->x);

				CCVector3 Pxyz;
				Pxyz.u[X] = static_cast<PointCoordinateType>(radius * N.x);
				Pxyz.u[Y] = static_cast<PointCoordinateType>(radius * N.y);
				Pxyz.u[Z] = P->y + profileDesc.heightShift;

				surfaceToCloud.apply(Pxyz);

				cloud->addPoint(Pxyz);
			}
		}
		mesh->addChild(cloud);
	}

	PointCoordinateType h0 = profileVertices->getPoint(0)->y;
	PointCoordinateType dH = profileVertices->getPoint(profVertCount-1)->y - h0;
	bool invertedHeight = (dH < 0);

	//create facets
	{
		for (unsigned j=0; j<angularSteps; ++j)
		{
			unsigned nextJ = ((j+1) % angularSteps);
			for (unsigned i=0; i+1<profVertCount; ++i)
			{
				unsigned vertA = j*profVertCount+i;
				unsigned vertB = nextJ*profVertCount+i;
				unsigned vertC = vertB+1;
				unsigned vertD = vertA+1;

				if (invertedHeight)
				{
					mesh->addTriangle(vertB,vertC,vertD);
					mesh->addTriangle(vertB,vertD,vertA);
				}
				else
				{
					mesh->addTriangle(vertB,vertD,vertC);
					mesh->addTriangle(vertB,vertA,vertD);
				}
			}
		}
	}

	//do we have a texture as well?
	if (!mapTexture.isNull())
	{
		//texture coordinates
		TextureCoordsContainer* texCoords = new TextureCoordsContainer();
		mesh->addChild(texCoords);
		if (!texCoords->reserve(meshVertCount+profVertCount)) //we add a column for correct wrapping!
		{
			//not enough memory to finish the job!
			return mesh;
		}

		//create default texture coordinates
		for (unsigned j=0; j<=angularSteps; ++j)
		{
			float T[2] = {static_cast<float>(j)/static_cast<float>(angularSteps), 0.0f};
			for (unsigned i=0; i<profVertCount; ++i)
			{
				T[1] = (profileVertices->getPoint(i)->y - h0) / dH;
				if (invertedHeight)
					T[1] = 1.0f - T[1];
				texCoords->addElement(T);
			}
		}

		if (!mesh->reservePerTriangleTexCoordIndexes())
		{
			//not enough memory to finish the job!
			return mesh;
		}
		
		//set texture indexes
		{
			for (unsigned j=0; j<angularSteps; ++j)
			{
				unsigned nextJ = ((j+1)/*% angularSteps*/);
				for (unsigned i=0; i+1<profVertCount; ++i)
				{
					unsigned vertA = j*profVertCount+i;
					unsigned vertB = nextJ*profVertCount+i;
					unsigned vertC = vertB+1;
					unsigned vertD = vertA+1;

					if (invertedHeight)
					{
						mesh->addTriangleTexCoordIndexes(vertB,vertC,vertD);
						mesh->addTriangleTexCoordIndexes(vertB,vertD,vertA);
					}
					else
					{
						mesh->addTriangleTexCoordIndexes(vertB,vertD,vertC);
						mesh->addTriangleTexCoordIndexes(vertB,vertA,vertD);
					}
				}
			}
		}
	
		//set material indexes
		if (!mesh->reservePerTriangleMtlIndexes())
		{
			//not enough memory to finish the job!
			mesh->removeChild(texCoords);
			mesh->removePerTriangleTexCoordIndexes();
			return mesh;
		}
		for (unsigned i=0; i<meshFaceCount; ++i)
		{
			mesh->addTriangleMtlIndex(0);
		}

		//set material
		{
			ccMaterial::Shared material(new ccMaterial("texture"));
			material->setTexture(mapTexture, QString(), false);

			ccMaterialSet* materialSet = new ccMaterialSet();
			materialSet->addMaterial(material);

			mesh->setMaterialSet(materialSet);
		}

		mesh->setTexCoordinatesTable(texCoords);
		mesh->showMaterials(true);
		mesh->setVisible(true);
		cloud->setVisible(false);
	}

	return mesh;
}
void ccGraphicalSegmentationTool::doExportSegmentationPolyline()
{
	MainWindow* mainWindow = MainWindow::TheInstance();
	if (mainWindow && m_segmentationPoly)
	{
		QMessageBox messageBox(0);
		
		messageBox.setWindowTitle("Choose export type");
		messageBox.setText("Export polyline in:\n - 2D (with coordinates relative to the screen)\n - 3D (with coordinates relative to the segmented entities)");
		QPushButton* button2D = new QPushButton("2D");
		QPushButton* button3D = new QPushButton("3D");
		messageBox.addButton(button2D,QMessageBox::AcceptRole);
		messageBox.addButton(button3D,QMessageBox::AcceptRole);
		messageBox.addButton(QMessageBox::Cancel);
		messageBox.setDefaultButton(button3D);
		messageBox.exec();
		if (messageBox.clickedButton() == messageBox.button(QMessageBox::Cancel))
		{
			//process cancelled by user
			return;
		}

		ccPolyline* poly = new ccPolyline(*m_segmentationPoly);

		//if we export the polyline in 3D, we must project its vertices
		bool mode2D = (messageBox.clickedButton() == button2D);
		if (!mode2D)
		{
			//get current display parameters
			const double* MM = m_associatedWin->getModelViewMatd(); //viewMat
			const double* MP = m_associatedWin->getProjectionMatd(); //projMat
			const GLdouble half_w = static_cast<GLdouble>(m_associatedWin->width())/2;
			const GLdouble half_h = static_cast<GLdouble>(m_associatedWin->height())/2;
			int VP[4];
			m_associatedWin->getViewportArray(VP);

			//project the 2D polyline in 3D
			CCLib::GenericIndexedCloudPersist* vertices = poly->getAssociatedCloud();
			ccPointCloud* verticesPC = dynamic_cast<ccPointCloud*>(vertices);
			if (verticesPC)
			{
				for (unsigned i=0; i<vertices->size(); ++i)
				{
					CCVector3* Pscreen = const_cast<CCVector3*>(verticesPC->getPoint(i));
					GLdouble xp,yp,zp;
					gluUnProject(half_w+Pscreen->x,half_h+Pscreen->y,0/*Pscreen->z*/,MM,MP,VP,&xp,&yp,&zp);
					Pscreen->x = static_cast<PointCoordinateType>(xp);
					Pscreen->y = static_cast<PointCoordinateType>(yp);
					Pscreen->z = static_cast<PointCoordinateType>(zp);
				}
				verticesPC->invalidateBoundingBox();
			}
			else
			{
				assert(false);
				ccLog::Warning("[Segmentation] Failed to convert 2D polyline to 3D! (internal inconsistency)");
				mode2D = false;
			}
		}
		
		poly->setName(QString("Segmentation polyline #%1").arg(++s_polylineExportCount));
		poly->setEnabled(false); //we don't want it to appear while the segmentation mode is enabled! (anyway it's 2D only...)
		poly->set2DMode(mode2D);
		poly->setColor(ccColor::yellow); //we use a different color so as to differentiate them from the active polyline!

		mainWindow->addToDB(poly);
		ccLog::Print(QString("[Segmentation] Polyline exported (%1 vertices)").arg(poly->size()));
	}
}
void ccGraphicalSegmentationTool::doActionUseExistingPolyline()
{
	MainWindow* mainWindow = MainWindow::TheInstance();
	if (mainWindow)
	{
		ccHObject* root = mainWindow->dbRootObject();
		ccHObject::Container polylines;
		if (root)
		{
			root->filterChildren(polylines,true,CC_TYPES::POLY_LINE);
		}

		if (!polylines.empty())
		{
			ccEntityPickerDlg epDlg(polylines,0,this);
			if (!epDlg.exec())
				return;

			int index = epDlg.getSelectedIndex();
			assert(index >= 0 && index < static_cast<int>(polylines.size()));
			assert(polylines[index]->isA(CC_TYPES::POLY_LINE));
			ccPolyline* poly = static_cast<ccPolyline*>(polylines[index]);
			CCLib::GenericIndexedCloudPersist* vertices = poly->getAssociatedCloud();
			bool mode3D = !poly->is2DMode();

			//viewing parameters (for conversion from 3D to 2D)
			const double* MM = m_associatedWin->getModelViewMatd(); //viewMat
			const double* MP = m_associatedWin->getProjectionMatd(); //projMat
			const GLdouble half_w = static_cast<GLdouble>(m_associatedWin->width())/2;
			const GLdouble half_h = static_cast<GLdouble>(m_associatedWin->height())/2;
			int VP[4];
			m_associatedWin->getViewportArray(VP);

			//force polygonal selection mode
			doSetPolylineSelection();
			m_segmentationPoly->clear();
			m_polyVertices->clear();

			//duplicate polyline 'a minima' (only points and indexes + closed state)
			if (	m_polyVertices->reserve(vertices->size())
				&&	m_segmentationPoly->reserve(poly->size()))
			{
				for (unsigned i=0; i<vertices->size(); ++i)
				{
					CCVector3 P = *vertices->getPoint(i);
					if (mode3D)
					{
						GLdouble xp,yp,zp;
						gluProject(P.x,P.y,P.z,MM,MP,VP,&xp,&yp,&zp);

						P.x = static_cast<PointCoordinateType>(xp-half_w);
						P.y = static_cast<PointCoordinateType>(yp-half_h);
						P.z = 0;
					}
					m_polyVertices->addPoint(P);
				}
				for (unsigned j=0; j<poly->size(); ++j)
					m_segmentationPoly->addPointIndex(poly->getPointGlobalIndex(j));
				
				m_segmentationPoly->setClosed(poly->isClosed());
				if (m_segmentationPoly->isClosed())
				{
					//stop
					m_state &= (~RUNNING);
				}

				if (m_associatedWin)
					m_associatedWin->updateGL();
			}
			else
			{
				ccLog::Error("Not enough memory!");
			}
		}
		else
		{
			ccLog::Error("No polyline in DB!");
		}
	}
}
Exemple #10
0
void qFacets::exportFacets()
{
	assert(m_app);
	if (!m_app)
		return;

	//disclaimer accepted?
	if (!ShowDisclaimer(m_app))
		return;

	//Retrive selected facets
	FacetSet facets;
	getFacetsInCurrentSelection(facets);

	if (facets.empty())
	{
		m_app->dispToConsole("Couldn't find any facet in the current selection!",ccMainAppInterface::ERR_CONSOLE_MESSAGE);
		return;
	}
	assert(!facets.empty());

	FacetsExportDlg fDlg(FacetsExportDlg::SHAPE_FILE_IO,m_app->getMainWindow());

	//persistent settings (default export path)
	QSettings settings;
	settings.beginGroup("qFacets");
	QString facetsSavePath = settings.value("exportPath",QApplication::applicationDirPath()).toString();
	fDlg.destinationPathLineEdit->setText(facetsSavePath + QString("/facets.shp"));

	if (!fDlg.exec())
		return;

	QString filename = fDlg.destinationPathLineEdit->text();

	//save current export path to persistent settings
	settings.setValue("exportPath",QFileInfo(filename).absolutePath());

	if (QFile(filename).exists())
	{
		//if the file already exists, ask for confirmation!
		if (QMessageBox::warning(m_app->getMainWindow(),"File already exists!","File already exists! Are you sure you want to overwrite it?",QMessageBox::Yes,QMessageBox::No) == QMessageBox::No)
			return;
	}

	//fields (shapefile) - WARNING names must not have more than 10 chars!
	ShpFilter::IntegerField  facetIndex("index");
	ShpFilter::DoubleField   facetSurface("surface");
	ShpFilter::DoubleField   facetRMS("rms");
	ShpFilter::IntegerField  facetDipDir("dip_dir");
	ShpFilter::IntegerField  facetDip("dip");
	ShpFilter::IntegerField  familyIndex("family_ind");
	ShpFilter::IntegerField  subfamilyIndex("subfam_ind");
	ShpFilter::DoubleField3D facetNormal("normal");
	ShpFilter::DoubleField3D facetBarycenter("center");
	ShpFilter::DoubleField   horizExtension("horiz_ext");
	ShpFilter::DoubleField   vertExtension("vert_ext");
	ShpFilter::DoubleField   surfaceExtension("surf_ext");

	size_t facetCount = facets.size();
	assert(facetCount != 0);
	try
	{
		facetIndex.values.reserve(facetCount);
		facetSurface.values.reserve(facetCount);
		facetRMS.values.reserve(facetCount);
		facetDipDir.values.reserve(facetCount);
		facetDip.values.reserve(facetCount);
		familyIndex.values.reserve(facetCount);
		subfamilyIndex.values.reserve(facetCount);
		facetNormal.values.reserve(facetCount);
		facetBarycenter.values.reserve(facetCount);
		horizExtension.values.reserve(facetCount);
		vertExtension.values.reserve(facetCount);
		surfaceExtension.values.reserve(facetCount);
	}
	catch (const std::bad_alloc&)
	{
		m_app->dispToConsole("Not enough memory!",ccMainAppInterface::ERR_CONSOLE_MESSAGE);
		return;
	}

	ccHObject toSave("facets");

	//depending on the 'main orientation', the job is more or less easy ;)
	bool useNativeOrientation = fDlg.nativeOriRadioButton->isChecked();
	bool useGlobalOrientation = fDlg.verticalOriRadioButton->isChecked();
	bool useCustomOrientation = fDlg.customOriRadioButton->isChecked();

	//Default base
	CCVector3 X(1,0,0), Y(0,1,0), Z(0,0,1);

	//'vertical' orientation (potentially specified by the user)
	if (!useNativeOrientation)
	{
		if (useCustomOrientation)
		{
			Z = CCVector3(	static_cast<PointCoordinateType>(fDlg.nXLineEdit->text().toDouble()),
							static_cast<PointCoordinateType>(fDlg.nYLineEdit->text().toDouble()),
							static_cast<PointCoordinateType>(fDlg.nZLineEdit->text().toDouble()) );
			Z.normalize();
		}
		else if (useGlobalOrientation)
		{
			//we compute the mean orientation (weighted by each facet's surface)
			CCVector3d Nsum(0,0,0);
			for (FacetSet::iterator it = facets.begin(); it != facets.end(); ++it)
			{
				double surf = (*it)->getSurface();
				CCVector3 N = (*it)->getNormal();
				Nsum.x += static_cast<double>(N.x) * surf;
				Nsum.y += static_cast<double>(N.y) * surf;
				Nsum.z += static_cast<double>(N.z) * surf;
			}
			Nsum.normalize();

			Z = CCVector3(	static_cast<PointCoordinateType>(Nsum.x),
							static_cast<PointCoordinateType>(Nsum.y),
							static_cast<PointCoordinateType>(Nsum.z) );
		}

		//update X & Y
		CCVector3 D = Z.cross(CCVector3(0,0,1));
		if (D.norm2() > ZERO_TOLERANCE) //otherwise the vertical dir hasn't changed!
		{
			X = -D;
			X.normalize();
			Y = Z.cross(X);
		}
	}

	//we compute the mean center (weighted by each facet's surface)
	CCVector3 C(0,0,0);
	{
		double weightSum = 0;
		for (FacetSet::iterator it = facets.begin(); it != facets.end(); ++it)
		{
			double surf = (*it)->getSurface();
			CCVector3 Ci = (*it)->getCenter();
			C += Ci * static_cast<PointCoordinateType>(surf);
			weightSum += surf;
		}
		if (weightSum)
			C /= static_cast<PointCoordinateType>(weightSum);
	}

	//determine the 'global' orientation matrix
	ccGLMatrix oriRotMat;
	oriRotMat.toIdentity();
	if (!useNativeOrientation)
	{
		oriRotMat.getColumn(0)[0] = static_cast<float>(X.x);
		oriRotMat.getColumn(0)[1] = static_cast<float>(X.y);
		oriRotMat.getColumn(0)[2] = static_cast<float>(X.z);
		oriRotMat.getColumn(1)[0] = static_cast<float>(Y.x);
		oriRotMat.getColumn(1)[1] = static_cast<float>(Y.y);
		oriRotMat.getColumn(1)[2] = static_cast<float>(Y.z);
		oriRotMat.getColumn(2)[0] = static_cast<float>(Z.x);
		oriRotMat.getColumn(2)[1] = static_cast<float>(Z.y);
		oriRotMat.getColumn(2)[2] = static_cast<float>(Z.z);
		oriRotMat.invert();

		ccGLMatrix transMat;
		transMat.setTranslation(-C);
		oriRotMat = oriRotMat * transMat;
		oriRotMat.setTranslation(oriRotMat.getTranslationAsVec3D() + C);
	}

	//for each facet
	for (FacetSet::iterator it=facets.begin(); it!=facets.end(); ++it)
	{
		ccFacet* facet = *it;
		ccPolyline* poly = facet->getContour();

		//if necessary, we create a (temporary) new facet
		if (!useNativeOrientation)
		{
			CCLib::GenericIndexedCloudPersist* vertices = poly->getAssociatedCloud();
			if (!vertices || vertices->size() < 3)
				continue;

			//create (temporary) new polyline
			ccPolyline* newPoly = new ccPolyline(*poly);
			ccPointCloud* pc = (newPoly ? dynamic_cast<ccPointCloud*>(newPoly->getAssociatedCloud()) : 0);
			if (pc)
			{
				pc->applyGLTransformation_recursive(&oriRotMat);
			}
			else
			{
				m_app->dispToConsole(QString("Failed to change the orientation of polyline '%1'! (not enough memory)").arg(poly->getName()),ccMainAppInterface::WRN_CONSOLE_MESSAGE);
				continue;
			}

			newPoly->set2DMode(true);
			poly = newPoly;
		}

		toSave.addChild(poly, useNativeOrientation ? ccHObject::DP_NONE : ccHObject::DP_PARENT_OF_OTHER);

		//save associated meta-data as 'shapefile' fields
		{
			//main parameters
			FacetMetaData data;
			GetFacetMetaData(facet, data);

			//horizontal and vertical extensions
			double horizExt = 0, vertExt = 0;
			ComputeFacetExtensions(data.normal,poly,horizExt,vertExt);

			facetIndex.values.push_back(data.facetIndex);
			facetSurface.values.push_back(data.surface);
			facetRMS.values.push_back(data.rms);
			facetDipDir.values.push_back(data.dipDir_deg);
			facetDip.values.push_back(data.dip_deg);
			familyIndex.values.push_back(data.familyIndex);
			subfamilyIndex.values.push_back(data.subfamilyIndex);
			facetNormal.values.push_back(CCVector3d(data.normal.x,data.normal.y,data.normal.z));
			facetBarycenter.values.push_back(CCVector3d(data.center.x,data.center.y,data.center.z));
			vertExtension.values.push_back(vertExt);
			horizExtension.values.push_back(horizExt);
			surfaceExtension.values.push_back(horizExt*vertExt);
		}
	}

	//save entities
	if (toSave.getChildrenNumber())
	{
		std::vector<ShpFilter::GenericField*> fields;
		fields.push_back(&facetIndex);
		fields.push_back(&facetBarycenter);
		fields.push_back(&facetNormal);
		fields.push_back(&facetRMS);
		fields.push_back(&horizExtension);
		fields.push_back(&vertExtension);
		fields.push_back(&surfaceExtension);
		fields.push_back(&facetSurface);
		fields.push_back(&facetDipDir);
		fields.push_back(&facetDip);
		fields.push_back(&familyIndex);
		fields.push_back(&subfamilyIndex);
		ShpFilter filter;
		filter.treatClosedPolylinesAsPolygons(true);
		ShpFilter::SaveParameters params;
		params.alwaysDisplaySaveDialog = false;
		if (filter.saveToFile(&toSave,fields,filename,params) == CC_FERR_NO_ERROR)
		{
			m_app->dispToConsole(QString("[qFacets] File '%1' successfully saved").arg(filename),ccMainAppInterface::STD_CONSOLE_MESSAGE);
		}
		else
		{
			m_app->dispToConsole(QString("[qFacets] Failed to save file '%1'!").arg(filename),ccMainAppInterface::WRN_CONSOLE_MESSAGE);
		}
	}
}
Exemple #11
0
CC_FILE_ERROR SavePolyline(ccPolyline* poly, QFile& file, int32_t& bytesWritten, ESRI_SHAPE_TYPE outputShapeType)
{
	bytesWritten = 0;

	if (!poly)
	{
		assert(false);
		return CC_FERR_BAD_ENTITY_TYPE;
	}

	CCLib::GenericIndexedCloudPersist* vertices = poly->getAssociatedCloud();
	if (!vertices)
		return CC_FERR_BAD_ENTITY_TYPE;

	int32_t realNumPoints = poly->size();
	if (realNumPoints < 3)
		return CC_FERR_BAD_ENTITY_TYPE;

	bool is2D = poly->is2DMode();
	bool isClosed = poly->isClosed();

	ccBBox box = poly->getBB();
	assert(box.isValid());

	//Shape Type
	{
		//Byte 0: Shape Type
		int32_t shapeTypeInt = qToLittleEndian<int32_t>(outputShapeType);
		file.write((const char*)&shapeTypeInt,4);
		bytesWritten += 4;
	}

	//Byte 4: Box
	{
		double xMin = qToLittleEndian<double>(box.minCorner().x);
		double xMax = qToLittleEndian<double>(box.maxCorner().x);
		double yMin = qToLittleEndian<double>(box.minCorner().y);
		double yMax = qToLittleEndian<double>(box.maxCorner().y);
		//The Bounding Box for the PolyLine stored in the order Xmin, Ymin, Xmax, Ymax
		/*Byte  4*/file.write((const char*)&xMin,8);
		/*Byte 12*/file.write((const char*)&yMin,8);
		/*Byte 20*/file.write((const char*)&xMax,8);
		/*Byte 28*/file.write((const char*)&yMax,8);
		bytesWritten += 32;
	}

	//Byte 36: NumParts (The number of parts in the PolyLine)
	{
		int32_t numParts = qToLittleEndian<int32_t>(1);
		file.write((const char*)&numParts,4);
		bytesWritten += 4;
	}

	//Byte 40: NumPoints (The total number of points for all parts)
	int32_t numPoints = realNumPoints;
	if (isClosed)
		numPoints++;
	{
		int32_t numPointsLE = qToLittleEndian<int32_t>(numPoints);
		file.write((const char*)&numPointsLE,4);
		bytesWritten += 4;
	}

	//Byte 44: Parts (An array of length NumParts)
	{
		//for each part, the index of its first point in the points array
		int32_t startIndex = qToLittleEndian<int32_t>(0);
		file.write((const char*)&startIndex,4);
		bytesWritten += 4;
	}

	//for polygons we must list the vertices in the right order:
	//"The neighborhood to the right of an observer walking along
	//the ring in vertex order is the inside of the polygon"
	bool inverseOrder = false;
	if (outputShapeType == SHP_POLYGON || outputShapeType == SHP_POLYGON_Z)
	{
		assert(isClosed);
		assert(numPoints > 2);
		//get bounding box
		ccBBox box = poly->getBB();
		assert(box.isValid());
		//get the two largest (ordered) dimensions (dim1, dim2)
		CCVector3 diag = box.getDiagVec();
		unsigned char minDim = diag.y < diag.x ? 1 : 0;
		if (diag.z < diag.u[minDim])
			minDim = 2;
		unsigned char dim1 = ((minDim+1) % 3);
		unsigned char dim2 = ((minDim+2) % 3);

		if (diag.u[dim1] > 0) //if the polyline is flat, no need to bother ;)
		{
			//look for the top-left-most point in this 'plane'
			int32_t leftMostPointIndex = 0;
			{
				const CCVector3* leftMostPoint = vertices->getPoint(0);
				for (int32_t i=1; i<realNumPoints; ++i)
				{
					const CCVector3* P = vertices->getPoint(i);
					if (P->u[dim1] < leftMostPoint->u[dim1] || (P->u[dim1] == leftMostPoint->u[dim1] && P->u[dim2] < leftMostPoint->u[dim2]))
					{
						leftMostPoint = P;
						leftMostPointIndex = i;
					}
				}
			}

			//we simply compare the angles betwween the two edges that have the top-left-most vertex in common
			{
				const CCVector3* B = vertices->getPoint(leftMostPointIndex > 0 ? leftMostPointIndex-1 : realNumPoints-1);
				const CCVector3* P = vertices->getPoint(leftMostPointIndex);
				const CCVector3* A = vertices->getPoint(leftMostPointIndex+1 < realNumPoints ? leftMostPointIndex+1 : 0);
			
				CCVector3 PA = *A-*P;
				CCVector3 PB = *B-*P;
				PointCoordinateType anglePA = atan2(PA.u[dim2],PA.u[dim1]); //forward
				PointCoordinateType anglePB = atan2(PB.u[dim2],PB.u[dim1]); //backward
				//angles should all be in [-PI/2;0]
				if (anglePA < anglePB)
					inverseOrder = true;
			}
		}
	}

	//Points (An array of length NumPoints)
	{
		for (int32_t i=0; i<numPoints; ++i)
		{
			int32_t ii = (inverseOrder ? numPoints-1-i : i);
			const CCVector3* P = vertices->getPoint(ii % realNumPoints); //warning: handle loop if polyline is closed

			double x = qToLittleEndian<double>(P->x);
			double y = qToLittleEndian<double>(P->y);
			/*Byte 0*/file.write((const char*)&x,8);
			/*Byte 8*/file.write((const char*)&y,8);
			bytesWritten += 16;
		}
	}

	//3D polylines
	if (!is2D)
	{
		//Z boundaries
		{
			double zMin = qToLittleEndian<double>(box.minCorner().z);
			double zMax = qToLittleEndian<double>(box.maxCorner().z);
			file.write((const char*)&zMin,8);
			file.write((const char*)&zMax,8);
			bytesWritten += 16;
		}

		//Z coordinates (for each part - just one here)
		{
			for (int32_t i=0; i<numPoints; ++i)
			{
				int32_t ii = (inverseOrder ? numPoints-1-i : i);
				const CCVector3* P = vertices->getPoint(ii % realNumPoints); //warning: handle loop if polyline is closed
				double z = qToLittleEndian<double>(P->z);
				file.write((const char*)&z,8);
				bytesWritten += 8;
			}
		}

		//M boundaries
		bool hasSF = vertices->isScalarFieldEnabled();
		{
			double mMin = ESRI_NO_DATA;
			double mMax = ESRI_NO_DATA;
			if (hasSF)
			{
				for (int32_t i=0; i<realNumPoints; ++i)
				{
					ScalarType scalar = vertices->getPointScalarValue(i);
					if (i != 0)
					{
						if (mMin > scalar)
							mMin = static_cast<double>(scalar);
						else if (mMax < scalar)
							mMax = static_cast<double>(scalar);
					}
					else
					{
						mMin = mMax = static_cast<double>(scalar);
					}
				}
			}
			mMin = qToLittleEndian<double>(mMin);
			mMax = qToLittleEndian<double>(mMax);
			file.write((const char*)&mMin,8);
			file.write((const char*)&mMax,8);
			bytesWritten += 16;
		}

		//M values (for each part - just one here)
		{
			double scalar = qToLittleEndian<double>(ESRI_NO_DATA);
			for (int32_t i=0; i<numPoints; ++i)
			{
				if (hasSF)
				{
					scalar = static_cast<double>(vertices->getPointScalarValue(i % realNumPoints)); //warning: handle loop if polyline is closed
					scalar = qToLittleEndian<double>(scalar);
				}
				file.write((const char*)&scalar,8);
				bytesWritten += 8;
			}
		}
	}

	return CC_FERR_NO_ERROR;
}
Exemple #12
0
bool ccRegistrationTools::ICP(	ccHObject* data,
								ccHObject* model,
								ccGLMatrix& transMat,
								double &finalScale,
								double& finalError,
								double minErrorDecrease,
								unsigned maxIterationCount,
								unsigned randomSamplingLimit,
								bool removeFarthestPoints,
								ConvergenceMethod method,
								bool adjustScale,
								bool useDataSFAsWeights/*=false*/,
								bool useModelSFAsWeights/*=false*/,
								QWidget* parent/*=0*/)
{
    //progress bar
    ccProgressDialog pDlg(false,parent);

    //if the 'model' entity is a mesh, we need to sample points on it
    CCLib::GenericIndexedCloudPersist* modelCloud = 0;
    if (model->isKindOf(CC_TYPES::MESH))
    {
        modelCloud = CCLib::MeshSamplingTools::samplePointsOnMesh(ccHObjectCaster::ToGenericMesh(model),s_defaultSampledPointsOnModelMesh,&pDlg);
        if (!modelCloud)
        {
            ccLog::Error("[ICP] Failed to sample points on 'model' mesh!");
            return false;
        }
    }
    else
    {
        modelCloud = ccHObjectCaster::ToGenericPointCloud(model);
    }

    //if the 'data' entity is a mesh, we need to sample points on it
    CCLib::GenericIndexedCloudPersist* dataCloud = 0;
    if (data->isKindOf(CC_TYPES::MESH))
    {
        dataCloud = CCLib::MeshSamplingTools::samplePointsOnMesh(ccHObjectCaster::ToGenericMesh(data),s_defaultSampledPointsOnDataMesh,&pDlg);
        if (!dataCloud)
        {
            ccLog::Error("[ICP] Failed to sample points on 'data' mesh!");
            return false;
        }
    }
    else
    {
        dataCloud = ccHObjectCaster::ToGenericPointCloud(data);
    }

    //we activate a temporary scalar field for registration distances computation
	CCLib::ScalarField* dataDisplayedSF = 0;
    int oldDataSfIdx=-1, dataSfIdx=-1;

    //if the 'data' entity is a real ccPointCloud, we can even create a temporary SF for registration distances
    if (data->isA(CC_TYPES::POINT_CLOUD))
    {
        ccPointCloud* pc = static_cast<ccPointCloud*>(data);
		dataDisplayedSF = pc->getCurrentDisplayedScalarField();
        oldDataSfIdx = pc->getCurrentInScalarFieldIndex();
        dataSfIdx = pc->getScalarFieldIndexByName(REGISTRATION_DISTS_SF);
        if (dataSfIdx < 0)
            dataSfIdx = pc->addScalarField(REGISTRATION_DISTS_SF);
        if (dataSfIdx >= 0)
            pc->setCurrentScalarField(dataSfIdx);
        else
            ccLog::Warning("[ICP] Couldn't create temporary scalar field! Not enough memory?");
    }
    else
    {
        dataCloud->enableScalarField();
    }

    //parameters
    CCLib::PointProjectionTools::Transformation transform;

	CCLib::ScalarField* modelWeights = 0;
	if (useModelSFAsWeights)
	{
		if (modelCloud == dynamic_cast<CCLib::GenericIndexedCloudPersist*>(model) && model->isA(CC_TYPES::POINT_CLOUD))
		{
			ccPointCloud* pc = static_cast<ccPointCloud*>(model);
			modelWeights = pc->getCurrentDisplayedScalarField();
			if (!modelWeights)
				ccLog::Warning("[ICP] 'useDataSFAsWeights' is true but model has no displayed scalar field!");
		}
		else
		{
			ccLog::Warning("[ICP] 'useDataSFAsWeights' is true but only point clouds scalar fields can be used as weights!");
		}
	}

	CCLib::ScalarField* dataWeights = 0;
	if (useDataSFAsWeights)
	{
		if (!dataDisplayedSF)
		{
			if (dataCloud == (CCLib::GenericIndexedCloudPersist*)data && data->isA(CC_TYPES::POINT_CLOUD))
				ccLog::Warning("[ICP] 'useDataSFAsWeights' is true but data has no displayed scalar field!");
			else
				ccLog::Warning("[ICP] 'useDataSFAsWeights' is true but inly point clouds scalar fields can be used as weights!");
		}
		else
			dataWeights = dataDisplayedSF;

	}

	CCLib::ICPRegistrationTools::CC_ICP_RESULT result;

	result = CCLib::ICPRegistrationTools::RegisterClouds(	modelCloud,
															dataCloud,
															transform,
															method,
															minErrorDecrease,
															maxIterationCount,
															finalError,
															adjustScale,
															static_cast<CCLib::GenericProgressCallback*>(&pDlg),
															removeFarthestPoints,
															randomSamplingLimit,
															modelWeights,
															dataWeights);

    if (result >= CCLib::ICPRegistrationTools::ICP_ERROR)
    {
        ccLog::Error("Registration failed: an error occurred (code %i)",result);
    }
    else if (result == CCLib::ICPRegistrationTools::ICP_APPLY_TRANSFO)
    {
        transMat = ccGLMatrix(transform.R, transform.T, transform.s);
		finalScale = transform.s;
    }

    //if we had to sample points an the data mesh
    if (!data->isKindOf(CC_TYPES::POINT_CLOUD))
    {
        delete dataCloud;
		dataCloud = 0;
    }
    else
    {
        if (data->isA(CC_TYPES::POINT_CLOUD))
        {
            ccPointCloud* pc = static_cast<ccPointCloud*>(data);
            pc->setCurrentScalarField(oldDataSfIdx);
            if (dataSfIdx >= 0)
                pc->deleteScalarField(dataSfIdx);
            dataSfIdx=-1;
        }
    }

    //if we had to sample points an the model mesh
    if (!model->isKindOf(CC_TYPES::POINT_CLOUD))
	{
        delete modelCloud;
		modelCloud = 0;
	}

	return (result < CCLib::ICPRegistrationTools::ICP_ERROR);
}
void ccGraphicalSegmentationTool::doExportSegmentationPolyline()
{
	MainWindow* mainWindow = MainWindow::TheInstance();
	if (mainWindow && m_segmentationPoly)
	{
		bool mode2D = false;
#ifdef ALLOW_2D_OR_3D_EXPORT
		QMessageBox messageBox(0);
		messageBox.setWindowTitle("Choose export type");
		messageBox.setText("Export polyline in:\n - 2D (with coordinates relative to the screen)\n - 3D (with coordinates relative to the segmented entities)");
		QPushButton* button2D = new QPushButton("2D");
		QPushButton* button3D = new QPushButton("3D");
		messageBox.addButton(button2D,QMessageBox::AcceptRole);
		messageBox.addButton(button3D,QMessageBox::AcceptRole);
		messageBox.addButton(QMessageBox::Cancel);
		messageBox.setDefaultButton(button3D);
		messageBox.exec();
		if (messageBox.clickedButton() == messageBox.button(QMessageBox::Cancel))
		{
			//process cancelled by user
			return;
		}
		mode2D = (messageBox.clickedButton() == button2D);
#endif

		ccPolyline* poly = new ccPolyline(*m_segmentationPoly);

		//if the polyline is 2D and we export the polyline in 3D, we must project its vertices
		if (!mode2D)
		{
			//get current display parameters
			ccGLCameraParameters camera;
			m_associatedWin->getGLCameraParameters(camera);
			const double half_w = camera.viewport[2] / 2.0;
			const double half_h = camera.viewport[3] / 2.0;

			//project the 2D polyline in 3D
			CCLib::GenericIndexedCloudPersist* vertices = poly->getAssociatedCloud();
			ccPointCloud* verticesPC = dynamic_cast<ccPointCloud*>(vertices);
			if (verticesPC)
			{
				for (unsigned i=0; i<vertices->size(); ++i)
				{
					CCVector3* Pscreen = const_cast<CCVector3*>(verticesPC->getPoint(i));
					CCVector3d Pd(half_w + Pscreen->x, half_h + Pscreen->y, 0/*Pscreen->z*/);
					CCVector3d Q3D;
					camera.unproject(Pd, Q3D);
					*Pscreen = CCVector3::fromArray(Q3D.u);
				}
				verticesPC->invalidateBoundingBox();
			}
			else
			{
				assert(false);
				ccLog::Warning("[Segmentation] Failed to convert 2D polyline to 3D! (internal inconsistency)");
				mode2D = false;
			}
		}
		
		QString polyName = QString("Segmentation polyline #%1").arg(++s_polylineExportCount);
		poly->setName(polyName);
		poly->setEnabled(false); //we don't want it to appear while the segmentation mode is enabled! (anyway it's 2D only...)
		poly->set2DMode(mode2D);
		poly->setColor(ccColor::yellow); //we use a different color so as to differentiate them from the active polyline!

		//save associated viewport
		cc2DViewportObject* viewportObject = new cc2DViewportObject(polyName + QString(" viewport"));
		viewportObject->setParameters(m_associatedWin->getViewportParameters());
		viewportObject->setDisplay(m_associatedWin);
		poly->addChild(viewportObject);

		mainWindow->addToDB(poly,false,false,false);
		ccLog::Print(QString("[Segmentation] Polyline exported (%1 vertices)").arg(poly->size()));
	}
}
void ccGraphicalSegmentationTool::doActionUseExistingPolyline()
{
	MainWindow* mainWindow = MainWindow::TheInstance();
	if (mainWindow)
	{
		ccHObject* root = mainWindow->dbRootObject();
		ccHObject::Container polylines;
		if (root)
		{
			root->filterChildren(polylines,true,CC_TYPES::POLY_LINE);
		}

		if (!polylines.empty())
		{
			ccEntityPickerDlg epDlg(polylines,false,0,this);
			if (!epDlg.exec())
				return;

			int index = epDlg.getSelectedIndex();
			assert(index >= 0 && index < static_cast<int>(polylines.size()));
			assert(polylines[index]->isA(CC_TYPES::POLY_LINE));
			ccPolyline* poly = static_cast<ccPolyline*>(polylines[index]);

			//look for an asociated viewport
			ccHObject::Container viewports;
			if (poly->filterChildren(viewports,false,CC_TYPES::VIEWPORT_2D_OBJECT,true) == 1)
			{
				//shall we apply this viewport?
				if (QMessageBox::question(	m_associatedWin ? m_associatedWin->asWidget() : 0,
											"Associated viewport",
											"The selected polyline has an associated viewport: do you want to apply it?",
											QMessageBox::Yes,
											QMessageBox::No) == QMessageBox::Yes)
				{
					m_associatedWin->setViewportParameters(static_cast<cc2DViewportObject*>(viewports.front())->getParameters());
					m_associatedWin->redraw(false);
				}
			}

			CCLib::GenericIndexedCloudPersist* vertices = poly->getAssociatedCloud();
			bool mode3D = !poly->is2DMode();

			//viewing parameters (for conversion from 3D to 2D)
			ccGLCameraParameters camera;
			m_associatedWin->getGLCameraParameters(camera);
			const double half_w = camera.viewport[2] / 2.0;
			const double half_h = camera.viewport[3] / 2.0;

			//force polygonal selection mode
			doSetPolylineSelection();
			m_segmentationPoly->clear();
			m_polyVertices->clear();
			allowPolylineExport(false);

			//duplicate polyline 'a minima' (only points and indexes + closed state)
			if (	m_polyVertices->reserve(vertices->size())
				&&	m_segmentationPoly->reserve(poly->size()))
			{
				for (unsigned i=0; i<vertices->size(); ++i)
				{
					CCVector3 P = *vertices->getPoint(i);
					if (mode3D)
					{
						CCVector3d Q2D;
						camera.project(P, Q2D);

						P.x = static_cast<PointCoordinateType>(Q2D.x-half_w);
						P.y = static_cast<PointCoordinateType>(Q2D.y-half_h);
						P.z = 0;
					}
					m_polyVertices->addPoint(P);
				}
				for (unsigned j=0; j<poly->size(); ++j)
				{
					m_segmentationPoly->addPointIndex(poly->getPointGlobalIndex(j));
				}
				
				m_segmentationPoly->setClosed(poly->isClosed());
				if (m_segmentationPoly->isClosed())
				{
					//stop
					m_state &= (~RUNNING);
					allowPolylineExport(m_segmentationPoly->size() > 1);
				}

				if (m_associatedWin)
					m_associatedWin->redraw(true, false);
			}
			else
			{
				ccLog::Error("Not enough memory!");
			}
		}
		else
		{
			ccLog::Error("No polyline in DB!");
		}
	}
}