Example #1
0
bool cc2Point5DimEditor::RasterGrid::fillWith(	ccGenericPointCloud* cloud,
												unsigned char projectionDimension,
												cc2Point5DimEditor::ProjectionType projectionType,
												bool interpolateEmptyCells,
												cc2Point5DimEditor::ProjectionType sfInterpolation/*=INVALID_PROJECTION_TYPE*/,
												ccProgressDialog* progressDialog/*=0*/)
{
	if (!cloud)
	{
		assert(false);
		return false;
	}

	//current parameters
	unsigned gridTotalSize = width * height;
	
	//vertical dimension
	const unsigned char Z = projectionDimension;
	assert(Z >= 0 && Z <= 2);
	const unsigned char X = Z == 2 ? 0 : Z +1;
	const unsigned char Y = X == 2 ? 0 : X +1;

	//do we need to interpolate scalar fields?
	ccPointCloud* pc = cloud->isA(CC_TYPES::POINT_CLOUD) ? static_cast<ccPointCloud*>(cloud) : 0;
	bool interpolateSF = (sfInterpolation != INVALID_PROJECTION_TYPE);
	interpolateSF &= (pc && pc->hasScalarFields());
	if (interpolateSF)
	{
		unsigned sfCount = pc->getNumberOfScalarFields();

		bool memoryError = false;
		size_t previousCount = scalarFields.size();
		if (sfCount > previousCount)
		{
			try
			{
				scalarFields.resize(sfCount,0);
			}
			catch (const std::bad_alloc&)
			{
				//not enough memory
				memoryError = true;
			}
		}

		for (size_t i=previousCount; i<sfCount; ++i)
		{
			assert(scalarFields[i] == 0);
			scalarFields[i] = new double[gridTotalSize];
			if (!scalarFields[i])
			{
				//not enough memory
				memoryError = true;
				break;
			}
		}

		if (memoryError)
		{
			ccLog::Warning(QString("[Rasterize] Failed to allocate memory for scalar fields!"));
		}
	}

	//filling the grid
	unsigned pointCount = cloud->size();

	double gridMaxX = gridStep * width;
	double gridMaxY = gridStep * height;

	if (progressDialog)
	{
		progressDialog->setMethodTitle("Grid generation");
		progressDialog->setInfo(qPrintable(QString("Points: %1\nCells: %2 x %3").arg(pointCount).arg(width).arg(height)));
		progressDialog->start();
		progressDialog->show();
		QCoreApplication::processEvents();
	}
	CCLib::NormalizedProgress nProgress(progressDialog,pointCount);

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

		CCVector3d relativePos = CCVector3d::fromArray(P->u) - minCorner;
		int i = static_cast<int>(relativePos.u[X]/gridStep);
		int j = static_cast<int>(relativePos.u[Y]/gridStep);

		//specific case: if we fall exactly on the max corner of the grid box
		if (i == static_cast<int>(width) && relativePos.u[X] == gridMaxX)
			--i;
		if (j == static_cast<int>(height) && relativePos.u[Y] == gridMaxY)
			--j;

		//we skip points outside the box!
		if (	i < 0 || i >= static_cast<int>(width)
			||	j < 0 || j >= static_cast<int>(height) )
			continue;

		assert(i >= 0 && j >= 0);

		RasterCell* aCell = data[j]+i;
		unsigned& pointsInCell = aCell->nbPoints;
		if (pointsInCell)
		{
			if (P->u[Z] < aCell->minHeight)
			{
				aCell->minHeight = P->u[Z];
				if (projectionType == PROJ_MINIMUM_VALUE)
					aCell->pointIndex = n;
			}
			else if (P->u[Z] > aCell->maxHeight)
			{
				aCell->maxHeight = P->u[Z];
				if (projectionType == PROJ_MAXIMUM_VALUE)
					aCell->pointIndex = n;
			}
		}
		else
		{
			aCell->minHeight = aCell->maxHeight = P->u[Z];
			aCell->pointIndex = n;
		}
		// Sum the points heights
		aCell->avgHeight += P->u[Z];
		aCell->stdDevHeight += static_cast<double>(P->u[Z])*P->u[Z];

		//scalar fields
		if (interpolateSF)
		{
			int pos = j*static_cast<int>(width)+i; //pos in 2D SF grid(s)
			assert(pos < static_cast<int>(gridTotalSize));
			for (size_t k=0; k<scalarFields.size(); ++k)
			{
				if (scalarFields[k])
				{
					CCLib::ScalarField* sf = pc->getScalarField(static_cast<unsigned>(k));
					assert(sf);
					ScalarType sfValue = sf->getValue(n);
					ScalarType formerValue = static_cast<ScalarType>(scalarFields[k][pos]);

					if (pointsInCell && ccScalarField::ValidValue(formerValue))
					{
						if (ccScalarField::ValidValue(sfValue))
						{
							switch (sfInterpolation)
							{
							case PROJ_MINIMUM_VALUE:
								// keep the minimum value
								scalarFields[k][pos] = std::min<double>(formerValue,sfValue);
								break;
							case PROJ_AVERAGE_VALUE:
								//we sum all values (we will divide them later)
								scalarFields[k][pos] += sfValue;
								break;
							case PROJ_MAXIMUM_VALUE:
								// keep the maximum value
								scalarFields[k][pos] = std::max<double>(formerValue,sfValue);
								break;
							default:
								assert(false);
								break;
							}
						}
					}
					else
					{
						//for the first (vaild) point, we simply have to store its SF value (in any case)
						scalarFields[k][pos] = sfValue;
					}
				}
			}
		}

		pointsInCell++;

		if (!nProgress.oneStep())
		{
			//process cancelled by user
			return false;
		}
	}

	//update SF grids for 'average' cases
	if (sfInterpolation == PROJ_AVERAGE_VALUE)
	{
		for (size_t k=0; k<scalarFields.size(); ++k)
		{
			if (scalarFields[k])
			{
				double* _gridSF = scalarFields[k];
				for (unsigned j=0;j<height;++j)
				{
					RasterCell* cell = data[j];
					for (unsigned i=0; i<width; ++i,++cell,++_gridSF)
					{
						if (cell->nbPoints > 1)
						{
							ScalarType s = static_cast<ScalarType>(*_gridSF);
							if (ccScalarField::ValidValue(s)) //valid SF value
							{
								*_gridSF /= cell->nbPoints;
							}
						}
					}
				}
			}
		}
	}

	//update the main grid (average height and std.dev. computation + current 'height' value)
	{
		for (unsigned j=0; j<height; ++j)
		{
			RasterCell* cell = data[j];
			for (unsigned i=0; i<width; ++i,++cell)
			{
				if (cell->nbPoints > 1)
				{
					cell->avgHeight /= cell->nbPoints;
					cell->stdDevHeight = sqrt(fabs(cell->stdDevHeight/cell->nbPoints - cell->avgHeight*cell->avgHeight));
				}
				else
				{
					cell->stdDevHeight = 0;
				}

				if (cell->nbPoints != 0)
				{
					//set the right 'height' value
					switch (projectionType)
					{
					case PROJ_MINIMUM_VALUE:
						cell->h = cell->minHeight;
						break;
					case PROJ_AVERAGE_VALUE:
						cell->h = cell->avgHeight;
						break;
					case PROJ_MAXIMUM_VALUE:
						cell->h = cell->maxHeight;
						break;
					default:
						assert(false);
						break;
					}
				}
			}
		}
	}

	//compute the number of non empty cells
	nonEmptyCellCount = 0;
	{
		for (unsigned i=0; i<height; ++i)
			for (unsigned j=0; j<width; ++j)
				if (data[i][j].nbPoints)
					++nonEmptyCellCount;
	}

	//specific case: interpolate the empty cells
	if (interpolateEmptyCells)
	{
		std::vector<CCVector2> the2DPoints;
		if (nonEmptyCellCount < 3)
		{
			ccLog::Warning("[Rasterize] Not enough non-empty cells to interpolate!");
		}
		else if (nonEmptyCellCount < width * height) //otherwise it's useless!
		{
			try
			{
				the2DPoints.resize(nonEmptyCellCount);
			}
			catch (const std::bad_alloc&)
			{
				//out of memory
				ccLog::Warning("[Rasterize] Not enough memory to interpolate empty cells!");
			}
		}

		//fill 2D vector with non-empty cell indexes
		if (!the2DPoints.empty())
		{
			unsigned index = 0;
			for (unsigned j=0; j<height; ++j)
			{
				const RasterCell* cell = data[j];
				for (unsigned i=0; i<width; ++i, ++cell)
				{
					if (cell->nbPoints)
					{
						//we only use the non-empty cells to interpolate
						the2DPoints[index++] = CCVector2(static_cast<PointCoordinateType>(i),static_cast<PointCoordinateType>(j));
					}
				}
			}
			assert(index == nonEmptyCellCount);

			//mesh the '2D' points
			CCLib::Delaunay2dMesh delaunayMesh;
			char errorStr[1024];
			if (delaunayMesh.buildMesh(the2DPoints,0,errorStr))
			{
				unsigned triNum = delaunayMesh.size();
				//now we are going to 'project' all triangles on the grid
				delaunayMesh.placeIteratorAtBegining();
				for (unsigned k=0; k<triNum; ++k)
				{
					const CCLib::VerticesIndexes* tsi = delaunayMesh.getNextTriangleVertIndexes();
					//get the triangle bounding box (in grid coordinates)
					int P[3][2];
					int xMin = 0, yMin = 0, xMax = 0, yMax = 0;
					{
						for (unsigned j=0; j<3; ++j)
						{
							const CCVector2& P2D = the2DPoints[tsi->i[j]];
							P[j][0] = static_cast<int>(P2D.x);
							P[j][1] = static_cast<int>(P2D.y);
						}
						xMin = std::min(std::min(P[0][0],P[1][0]),P[2][0]);
						yMin = std::min(std::min(P[0][1],P[1][1]),P[2][1]);
						xMax = std::max(std::max(P[0][0],P[1][0]),P[2][0]);
						yMax = std::max(std::max(P[0][1],P[1][1]),P[2][1]);
					}
					//now scan the cells
					{
						//pre-computation for barycentric coordinates
						const double& valA = data[ P[0][1] ][ P[0][0] ].h;
						const double& valB = data[ P[1][1] ][ P[1][0] ].h;
						const double& valC = data[ P[2][1] ][ P[2][0] ].h;

						int det = (P[1][1]-P[2][1])*(P[0][0]-P[2][0]) + (P[2][0]-P[1][0])*(P[0][1]-P[2][1]);

						for (int j=yMin; j<=yMax; ++j)
						{
							RasterCell* cell = data[static_cast<unsigned>(j)];

							for (int i=xMin; i<=xMax; ++i)
							{
								//if the cell is empty
								if (!cell[i].nbPoints)
								{
									//we test if it's included or not in the current triangle
									//Point Inclusion in Polygon Test (inspired from W. Randolph Franklin - WRF)
									bool inside = false;
									for (int ti=0; ti<3; ++ti)
									{
										const int* P1 = P[ti];
										const int* P2 = P[(ti+1)%3];
										if ((P2[1] <= j &&j < P1[1]) || (P1[1] <= j && j < P2[1]))
										{
											int t = (i-P2[0])*(P1[1]-P2[1])-(P1[0]-P2[0])*(j-P2[1]);
											if (P1[1] < P2[1])
												t = -t;
											if (t < 0)
												inside = !inside;
										}
									}
									//can we interpolate?
									if (inside)
									{
										double l1 = static_cast<double>((P[1][1]-P[2][1])*(i-P[2][0])+(P[2][0]-P[1][0])*(j-P[2][1]))/det;
										double l2 = static_cast<double>((P[2][1]-P[0][1])*(i-P[2][0])+(P[0][0]-P[2][0])*(j-P[2][1]))/det;
										double l3 = 1.0-l1-l2;

										cell[i].h = l1 * valA + l2 * valB + l3 * valC;
										assert(cell[i].h == cell[i].h);

										//interpolate SFs as well!
										for (size_t sfIndex=0; sfIndex<scalarFields.size(); ++sfIndex)
										{
											if (scalarFields[sfIndex])
											{
												double* gridSF = scalarFields[sfIndex];
												const double& sfValA = gridSF[ P[0][0] + P[0][1]*width ];
												const double& sfValB = gridSF[ P[1][0] + P[1][1]*width ];
												const double& sfValC = gridSF[ P[2][0] + P[2][1]*width ];
												gridSF[i + j*width] = l1 * sfValA + l2 * sfValB + l3 * sfValC;
											}
										}
									}
								}
							}
						}
					}
				}
			}
			else
			{
				ccLog::Warning(QString("[Rasterize] Empty cells interpolation failed: Triangle lib. said '%1'").arg(errorStr));
			}
		}
	}

	//computation of the average and extreme height values in the grid
	{
		minHeight = 0;
		maxHeight = 0;
		meanHeight = 0;
		validCellCount = 0;

		for (unsigned i=0; i<height; ++i)
		{
			for (unsigned j=0; j<width; ++j)
			{
				if (data[i][j].h == data[i][j].h) //valid height
				{
					double h = data[i][j].h;

					if (validCellCount)
					{
						if (h < minHeight)
							minHeight = h;
						else if (h > maxHeight)
							maxHeight = h;
						
						meanHeight += h;
					}
					else
					{
						//first valid cell
						meanHeight = minHeight = maxHeight = h;
					}
					++validCellCount;
				}
			}
		}
		meanHeight /= validCellCount;
	}

	setValid(true);

	return true;
}
QSharedPointer<DistanceMapGenerationTool::Map> DistanceMapGenerationTool::CreateMap(ccPointCloud* cloud,
																					ccScalarField* sf,
																					const ccGLMatrix& cloudToSurface,
																					unsigned char revolutionAxisDim,
																					double xStep_rad,
																					double yStep,
																					double yMin,
																					double yMax,
																					bool spherical,
																					bool counterclockwise,
																					FillStrategyType fillStrategy,
																					EmptyCellFillOption emptyCellfillOption,
																					ccMainAppInterface* app/*=0*/)
{
	assert(cloud && sf);
	if (!cloud || !sf)
	{
		if (app)
			app->dispToConsole(QString("[DistanceMapGenerationTool] Internal error: invalid input structures!"),ccMainAppInterface::ERR_CONSOLE_MESSAGE);
		return QSharedPointer<Map>(0);
	}

	//invalid parameters?
	if (xStep_rad <= 0.0 || yStep <= 0.0 || yMax <= yMin || revolutionAxisDim > 2)
	{
		if (app)
			app->dispToConsole(QString("[DistanceMapGenerationTool] Internal error: invalid grid parameters!"),ccMainAppInterface::ERR_CONSOLE_MESSAGE);
		return QSharedPointer<Map>(0);
	}

	unsigned count = cloud->size();
	if (count == 0)
	{
		if (app)
			app->dispToConsole(QString("[DistanceMapGenerationTool] Cloud is empty! Nothing to do!"),ccMainAppInterface::ERR_CONSOLE_MESSAGE);
		return QSharedPointer<Map>(0);
	}

	//revolution axis
	const unsigned char Z = revolutionAxisDim;
	//we deduce the 2 other ('horizontal') dimensions from the revolution axis
	const unsigned char X = (Z < 2 ? Z+1 : 0);
	const unsigned char Y = (X < 2 ? X+1 : 0);

	//grid dimensions
	unsigned xSteps = 0;
	{
		if (xStep_rad > 0)
			xSteps = static_cast<unsigned>(ceil((2 * M_PI) / xStep_rad));
		if (xSteps == 0)
		{
			if (app)
				app->dispToConsole(QString("[DistanceMapGenerationTool] Invalid longitude step/boundaries! Can't generate a proper map!"),ccMainAppInterface::ERR_CONSOLE_MESSAGE);
			return QSharedPointer<Map>(0);
		}
	}

	unsigned ySteps = 0;
	{
		if (yStep > 0)
			ySteps = static_cast<unsigned>(ceil((yMax - yMin) / yStep));
		if (ySteps == 0)
		{
			if (app)
				app->dispToConsole(QString("[DistanceMapGenerationTool] Invalid latitude step/boundaries! Can't generate a proper map!"),ccMainAppInterface::ERR_CONSOLE_MESSAGE);
			return QSharedPointer<Map>(0);
		}
	}

	unsigned cellCount = xSteps * ySteps;
	if (app)
		app->dispToConsole(QString("[DistanceMapGenerationTool] Projected map size: %1 x %2 (%3 cells)").arg(xSteps).arg(ySteps).arg(cellCount),ccMainAppInterface::STD_CONSOLE_MESSAGE);

	//reserve memory for the output matrix
	QSharedPointer<Map> grid(new Map);
	try
	{
		grid->resize(cellCount);
	}
	catch (const std::bad_alloc&)
	{
		if (app)
			app->dispToConsole(QString("[DistanceMapGenerationTool] Not enough memory!"),ccMainAppInterface::ERR_CONSOLE_MESSAGE);
		return QSharedPointer<Map>(0);
	}

	//update grid info ("for the records")
	grid->xSteps = xSteps;
	grid->xMin = 0.0;
	grid->xMax = 2.0 * M_PI;
	grid->xStep = xStep_rad;
	grid->ySteps = ySteps;
	grid->yMin = yMin;
	grid->yMax = yMax;
	grid->yStep = yStep;

	//motion direction
	grid->counterclockwise = counterclockwise;
	double ccw = (counterclockwise ? -1.0 : 1.0);

	for (unsigned n=0; n<count; ++n)
	{
		//we skip invalid values
		const ScalarType& val = sf->getValue(n);
		if (!CCLib::ScalarField::ValidValue(val))
			continue;

		const CCVector3* P = cloud->getPoint(n);
		CCVector3 relativePos = cloudToSurface * (*P);

		//convert to cylindrical or spherical coordinates
		double x = ccw * atan2(relativePos.u[X],relativePos.u[Y]); //longitude
		if (x < 0.0)
			x += 2.0 * M_PI;
		
		double y = 0.0;
		if (spherical)
		{
			y = ComputeLatitude_rad(relativePos.u[X],relativePos.u[Y],relativePos.u[Z]); //latitude between 0 and pi/2
		}
		else
		{
			y = relativePos.u[Z]; //height
		}

		int i = static_cast<int>((x-grid->xMin)/grid->xStep);
		int j = static_cast<int>((y-grid->yMin)/grid->yStep);

		//if we fall exactly on the max corner of the grid box
		if (i == static_cast<int>(grid->xSteps))
			--i;
		if (j == static_cast<int>(grid->ySteps))
			--j;

		//we skip points outside the box!
		if (	i < 0 || i >= static_cast<int>(grid->xSteps)
			||	j < 0 || j >= static_cast<int>(grid->ySteps) )
		{
			continue;
		}
		assert(i >= 0 && j >= 0);

		MapCell& cell = (*grid)[j*static_cast<int>(grid->xSteps)+i];
		if (cell.count) //if there's already values projected in this cell
		{
			switch (fillStrategy)
			{
			case FILL_STRAT_MIN_DIST:
				// Set the minimum SF value
				if (val < cell.value)
					cell.value = val;
				break;
			case FILL_STRAT_AVG_DIST:
				// Sum the values
				cell.value += static_cast<double>(val);
				break;
			case FILL_STRAT_MAX_DIST:
				// Set the maximum SF value
				if (val > cell.value)
					cell.value = val;
				break;
			default:
				assert(false);
				break;
			}
		}
		else
		{
			//for the first point, we simply have to store its associated value (whatever the case)
			cell.value = val;
		}
		++cell.count;

		//if (progressCb)
		//	progressCb->update(30.0 * (float)n / (float)cloud->size());
	}

	//we need to finish the average values computation
	if (fillStrategy == FILL_STRAT_AVG_DIST)
	{
		MapCell* cell = &grid->at(0);
		for (unsigned i=0; i<cellCount; ++i, ++cell)
			if (cell->count > 1)
				cell->value /= (double)cell->count;
	}

	//fill empty cells with zero?
	if (emptyCellfillOption == FILL_WITH_ZERO)
	{
		MapCell* cell = &grid->at(0);
		for (unsigned i=0; i<cellCount; ++i, ++cell)
		{
			if (cell->count == 0)
			{
				cell->value = 0.0;
				cell->count = 1;
			}
		}
	}
	else if (emptyCellfillOption == FILL_INTERPOLATE)
	{
		//convert the non-empty cells to a 2D point cloud
		unsigned fillCount = 0;
		{
			MapCell* cell = &grid->at(0);
			for (unsigned i=0; i<cellCount; ++i, ++cell)
				if (cell->count != 0)
					++fillCount;
		}

		//do we really need to interpolate empty grid cells?
		if (fillCount)
		{
			std::vector<CCVector2> the2DPoints;
			try
			{
				the2DPoints.reserve(fillCount);
			}
			catch (...)
			{
				//out of memory
				if (app)
					app->dispToConsole(QString("[DistanceMapGenerationTool] Not enough memory to interpolate!"),ccMainAppInterface::ERR_CONSOLE_MESSAGE);
			}

			if (the2DPoints.capacity() == fillCount)
			{
				//fill 2D vector with non-empty cell indexes
				{
					const MapCell* cell = &grid->at(0);
					for (unsigned j=0; j<grid->ySteps; ++j)
						for (unsigned i=0; i<grid->xSteps; ++i, ++cell)
							if (cell->count)
								the2DPoints.push_back(CCVector2(static_cast<PointCoordinateType>(i),static_cast<PointCoordinateType>(j)));
				}

				//mesh the '2D' points
				CCLib::Delaunay2dMesh* dm = new CCLib::Delaunay2dMesh();
				char errorStr[1024];
				if (!dm->buildMesh(the2DPoints,0,errorStr))
				{
					if (app)
						app->dispToConsole(QString("[DistanceMapGenerationTool] Interpolation failed: Triangle lib. said '%1'").arg(errorStr),ccMainAppInterface::ERR_CONSOLE_MESSAGE);
				}
				else
				{
					unsigned triNum = dm->size();
					MapCell* cells = &grid->at(0);
					//now we are going to 'project' all triangles on the grid
					dm->placeIteratorAtBegining();
					for (unsigned k=0; k<triNum; ++k)
					{
						const CCLib::VerticesIndexes* tsi = dm->getNextTriangleVertIndexes();
						//get the triangle bounding box (in grid coordinates)
						int P[3][2];
						int xMin=0,yMin=0,xMax=0,yMax=0;
						{
							for (unsigned j=0; j<3; ++j)
							{
								const CCVector2& P2D = the2DPoints[tsi->i[j]];
								P[j][0] = static_cast<int>(P2D.x);
								P[j][1] = static_cast<int>(P2D.y);
							}
							xMin = std::min(std::min(P[0][0],P[1][0]),P[2][0]);
							yMin = std::min(std::min(P[0][1],P[1][1]),P[2][1]);
							xMax = std::max(std::max(P[0][0],P[1][0]),P[2][0]);
							yMax = std::max(std::max(P[0][1],P[1][1]),P[2][1]);
						}
						//now scan the cells
						{
							//pre-computation for barycentric coordinates
							const double& valA = cells[P[0][0] + P[0][1] * grid->xSteps].value;
							const double& valB = cells[P[1][0] + P[1][1] * grid->xSteps].value;
							const double& valC = cells[P[2][0] + P[2][1] * grid->xSteps].value;
							int det = (P[1][1]-P[2][1])*(P[0][0]-P[2][0]) + (P[2][0]-P[1][0])*(P[0][1]-P[2][1]);

							for (int j=yMin; j<=yMax; ++j)
							{
								MapCell* cell = cells + static_cast<unsigned>(j)*grid->xSteps;

								for (int i=xMin; i<=xMax; ++i)
								{
									//if the cell is empty
									if (!cell[i].count)
									{
										//we test if it's included or not in the current triangle
										//Point Inclusion in Polygon Test (inspired from W. Randolph Franklin - WRF)
										bool inside = false;
										for (int ti=0; ti<3; ++ti)
										{
											const int* P1 = P[ti];
											const int* P2 = P[(ti+1)%3];
											if ((P2[1] <= j &&j < P1[1]) || (P1[1] <= j && j < P2[1]))
											{
												int t = (i-P2[0])*(P1[1]-P2[1])-(P1[0]-P2[0])*(j-P2[1]);
												if (P1[1] < P2[1])
													t = -t;
												if (t < 0)
													inside = !inside;
											}
										}
										//can we interpolate?
										if (inside)
										{
											double l1 = static_cast<double>((P[1][1]-P[2][1])*(i-P[2][0])+(P[2][0]-P[1][0])*(j-P[2][1]))/det;
											double l2 = static_cast<double>((P[2][1]-P[0][1])*(i-P[2][0])+(P[0][0]-P[2][0])*(j-P[2][1]))/det;
											double l3 = 1.0-l1-l2;

											cell[i].count = 1;
											cell[i].value = l1 * valA + l2 * valB + l3 * valC;
										}
									}
								}
							}
						}
					}
				}

				delete dm;
				dm = 0;
			}
		}
	}

	//update min and max values
	{
		const MapCell* cell = &grid->at(0);
		grid->minVal = grid->maxVal = cell->value;
		++cell;
		for (unsigned i=1; i<cellCount; ++i, ++cell)
		{
			if (cell->value < grid->minVal)
				grid->minVal = cell->value;
			else if (cell->value > grid->maxVal)
				grid->maxVal = cell->value;
		}
	}

	//end of process
	return grid;
}
Example #3
0
bool ccExtru::buildUp()
{
	unsigned count = static_cast<unsigned>(m_profile.size());
	if (count < 3)
		return false;

	CCLib::Delaunay2dMesh mesh;

	//DGM: we check that last vertex is different from the first one!
	//(yes it happens ;)
	if (m_profile.back().x == m_profile.front().x &&  m_profile.back().y == m_profile.front().y)
		--count;

	char errorStr[1024];
	if (!mesh.buildMesh(m_profile,count,errorStr))
	{
		ccLog::Warning(QString("[ccPlane::buildUp] Profile triangulation failed (CClib said: '%1'").arg(errorStr));
		return false;
	}

	unsigned numberOfTriangles = mesh.size();
	int* triIndexes = mesh.getTriangleVertIndexesArray();

	if (numberOfTriangles == 0)
		return false;

	//vertices
	unsigned vertCount = 2*count;
	//faces
	unsigned faceCount = 2*numberOfTriangles+2*count;
	//faces normals
	unsigned faceNormCount = 2+count;

	if (!init(vertCount,false,faceCount,faceNormCount))
	{
		ccLog::Error("[ccPlane::buildUp] Not enough memory");
		return false;
	}

	ccPointCloud* verts = vertices();
	assert(verts);
	assert(m_triNormals);

	//bottom & top faces normals
	m_triNormals->addElement(ccNormalVectors::GetNormIndex(CCVector3(0.0,0.0,-1.0).u));
	m_triNormals->addElement(ccNormalVectors::GetNormIndex(CCVector3(0.0,0.0,1.0).u));

	//add profile vertices & normals
	for (unsigned i=0;i<count;++i)
	{
		const CCVector2& P = m_profile[i];
		verts->addPoint(CCVector3(P.x,P.y,0));
		verts->addPoint(CCVector3(P.x,P.y,m_height));

		const CCVector2& PNext = m_profile[(i+1)%count];
		CCVector2 N(-(PNext.y-P.y),PNext.x-P.x);
		N.normalize();
		m_triNormals->addElement(ccNormalVectors::GetNormIndex(CCVector3(N.x,N.y,0.0).u));
	}

	//add faces
	{
		//side faces
		{
			const int* _triIndexes = triIndexes;
			for (unsigned i=0;i<numberOfTriangles;++i,_triIndexes+=3)
			{
				addTriangle(_triIndexes[0]*2,_triIndexes[2]*2,_triIndexes[1]*2);
				addTriangleNormalIndexes(0,0,0);
				addTriangle(_triIndexes[0]*2+1,_triIndexes[1]*2+1,_triIndexes[2]*2+1);
				addTriangleNormalIndexes(1,1,1);
			}
		}

		//thickness
		{
			for (unsigned i=0;i<count;++i)
			{
				unsigned iNext = ((i+1)%count);
				addTriangle(i*2,i*2+1,iNext*2);
				addTriangleNormalIndexes(2+i,2+i,2+i);
				addTriangle(iNext*2,i*2+1,iNext*2+1);
				addTriangleNormalIndexes(2+i,2+i,2+i);
			}
		}
	}

	return true;
}
Example #4
0
bool ccFacet::createInternalRepresentation(	CCLib::GenericIndexedCloudPersist* points,
											const PointCoordinateType* planeEquation/*=0*/)
{
	assert(points);
	if (!points)
		return false;
	unsigned ptsCount = points->size();
	if (ptsCount < 3)
		return false;

	CCLib::Neighbourhood Yk(points);

	//get corresponding plane
	if (!planeEquation)
	{
		planeEquation = Yk.getLSPlane();
		if (!planeEquation)
		{
			ccLog::Warning("[ccFacet::createInternalRepresentation] Failed to compute the LS plane passing through the input points!");
			return false;
		}
	}
	memcpy(m_planeEquation, planeEquation, sizeof(PointCoordinateType) * 4);

	//we project the input points on a plane
	std::vector<CCLib::PointProjectionTools::IndexedCCVector2> points2D;
	CCVector3 X, Y; //local base
	if (!Yk.projectPointsOn2DPlane<CCLib::PointProjectionTools::IndexedCCVector2>(points2D, nullptr, &m_center, &X, &Y))
	{
		ccLog::Error("[ccFacet::createInternalRepresentation] Not enough memory!");
		return false;
	}

	//compute resulting RMS
	m_rms = CCLib::DistanceComputationTools::computeCloud2PlaneDistanceRMS(points, m_planeEquation);
	
	//update the points indexes (not done by Neighbourhood::projectPointsOn2DPlane)
	{
		for (unsigned i = 0; i < ptsCount; ++i)
		{
			points2D[i].index = i;
		}
	}

	//try to get the points on the convex/concave hull to build the contour and the polygon
	{
		std::list<CCLib::PointProjectionTools::IndexedCCVector2*> hullPoints;
		if (!CCLib::PointProjectionTools::extractConcaveHull2D(	points2D,
																hullPoints,
																m_maxEdgeLength*m_maxEdgeLength))
		{
			ccLog::Error("[ccFacet::createInternalRepresentation] Failed to compute the convex hull of the input points!");
		}

		unsigned hullPtsCount = static_cast<unsigned>(hullPoints.size());

		//create vertices
		m_contourVertices = new ccPointCloud();
		{
			if (!m_contourVertices->reserve(hullPtsCount))
			{
				delete m_contourVertices;
				m_contourVertices = nullptr;
				ccLog::Error("[ccFacet::createInternalRepresentation] Not enough memory!");
				return false;
			}
			
			//projection on the LS plane (in 3D)
			for (std::list<CCLib::PointProjectionTools::IndexedCCVector2*>::const_iterator it = hullPoints.begin(); it != hullPoints.end(); ++it)
			{
				m_contourVertices->addPoint(m_center + X*(*it)->x + Y*(*it)->y);
			}
			m_contourVertices->setName(DEFAULT_CONTOUR_POINTS_NAME);
			m_contourVertices->setLocked(true);
			m_contourVertices->setEnabled(false);
			addChild(m_contourVertices);
		}

		//we create the corresponding (3D) polyline
		{
			m_contourPolyline = new ccPolyline(m_contourVertices);
			if (m_contourPolyline->reserve(hullPtsCount))
			{
				m_contourPolyline->addPointIndex(0, hullPtsCount);
				m_contourPolyline->setClosed(true);
				m_contourPolyline->setVisible(true);
				m_contourPolyline->setLocked(true);
				m_contourPolyline->setName(DEFAULT_CONTOUR_NAME);
				m_contourVertices->addChild(m_contourPolyline);
				m_contourVertices->setEnabled(true);
				m_contourVertices->setVisible(false);
			}
			else
			{
				delete m_contourPolyline;
				m_contourPolyline = nullptr;
				ccLog::Warning("[ccFacet::createInternalRepresentation] Not enough memory to create the contour polyline!");
			}
		}

		//we create the corresponding (2D) mesh
		std::vector<CCVector2> hullPointsVector;
		try
		{
			hullPointsVector.reserve(hullPoints.size());
			for (std::list<CCLib::PointProjectionTools::IndexedCCVector2*>::const_iterator it = hullPoints.begin(); it != hullPoints.end(); ++it)
			{
				hullPointsVector.push_back(**it);
			}
		}
		catch (...)
		{
			ccLog::Warning("[ccFacet::createInternalRepresentation] Not enough memory to create the contour mesh!");
		}

		//if we have computed a concave hull, we must remove triangles falling outside!
		bool removePointsOutsideHull = (m_maxEdgeLength > 0);

		if (!hullPointsVector.empty() && CCLib::Delaunay2dMesh::Available())
		{
			//compute the facet surface
			CCLib::Delaunay2dMesh dm;
			char errorStr[1024];
			if (dm.buildMesh(hullPointsVector, 0, errorStr))
			{
				if (removePointsOutsideHull)
					dm.removeOuterTriangles(hullPointsVector, hullPointsVector);
				unsigned triCount = dm.size();
				assert(triCount != 0);

				m_polygonMesh = new ccMesh(m_contourVertices);
				if (m_polygonMesh->reserve(triCount))
				{
					//import faces
					for (unsigned i = 0; i < triCount; ++i)
					{
						const CCLib::VerticesIndexes* tsi = dm.getTriangleVertIndexes(i);
						m_polygonMesh->addTriangle(tsi->i1, tsi->i2, tsi->i3);
					}
					m_polygonMesh->setVisible(true);
					m_polygonMesh->enableStippling(true);

					//unique normal for facets
					if (m_polygonMesh->reservePerTriangleNormalIndexes())
					{
						NormsIndexesTableType* normsTable = new NormsIndexesTableType();
						normsTable->reserve(1);
						CCVector3 N(m_planeEquation);
						normsTable->addElement(ccNormalVectors::GetNormIndex(N.u));
						m_polygonMesh->setTriNormsTable(normsTable);
						for (unsigned i = 0; i < triCount; ++i)
							m_polygonMesh->addTriangleNormalIndexes(0, 0, 0); //all triangles will have the same normal!
						m_polygonMesh->showNormals(true);
						m_polygonMesh->setLocked(true);
						m_polygonMesh->setName(DEFAULT_POLYGON_MESH_NAME);
						m_contourVertices->addChild(m_polygonMesh);
						m_contourVertices->setEnabled(true);
						m_contourVertices->setVisible(false);
					}
					else
					{
						ccLog::Warning("[ccFacet::createInternalRepresentation] Not enough memory to create the polygon mesh's normals!");
					}

					//update facet surface
					m_surface = CCLib::MeshSamplingTools::computeMeshArea(m_polygonMesh);
				}
				else
				{
					delete m_polygonMesh;
					m_polygonMesh = nullptr;
					ccLog::Warning("[ccFacet::createInternalRepresentation] Not enough memory to create the polygon mesh!");
				}
			}
			else
			{
				ccLog::Warning(QString("[ccFacet::createInternalRepresentation] Failed to create the polygon mesh (third party lib. said '%1'").arg(errorStr));
			}
		}
	}

	return true;
}