std::vector<float> getComboItemAsStdFloatVector(ComboItemDescriptor desc, const ccPointCloud* cloud) { int n = cloud->size(); std::vector<float> v; v.resize(n); v.reserve(n); if (desc.type == ComboItemDescriptor::COORDINATE) { CCVector3 point; for (int i = 0; i < n; i++) { cloud->getPoint(i, point); v[i] = point[desc.index_in_cloud]; } } else if (desc.type == ComboItemDescriptor::SCALAR) { CCLib::ScalarField * field = cloud->getScalarField(desc.index_in_cloud); for (int i = 0; i < n; i++) v[i] = field->getValue(i); } return v; }
void copyScalarFields(const ccPointCloud *inCloud, ccPointCloud *outCloud, pcl::PointIndicesPtr &in2outMapping, bool overwrite = true) { int n_in = inCloud->size(); int n_out = outCloud->size(); assert(in2outMapping->indices.size() == outCloud->size()); int n_scalars = inCloud->getNumberOfScalarFields(); for (int i = 0; i < n_scalars; ++i) { CCLib::ScalarField * field = inCloud->getScalarField(i); const char * name = field->getName(); //we need to verify no scalar field with the same name exists in the output cloud int id = outCloud->getScalarFieldIndexByName(name); ccScalarField * new_field = new ccScalarField; //resize the scalar field to the outcloud size new_field->reserve(outCloud->size()); new_field->setName(name); if (id >= 0) //a scalar field with the same name exists { if (overwrite) outCloud->deleteScalarField(id); else break; } //now perform point to point copy for (unsigned int j = 0; j < outCloud->size(); ++j) { new_field->setValue(j, field->getValue(in2outMapping->indices.at(j))); } //recompute stats new_field->computeMinAndMax(); ccScalarField * casted_field = static_cast<ccScalarField *> (new_field); casted_field->computeMinAndMax(); //now put back the scalar field to the outCloud if (id < 0) outCloud->addScalarField(casted_field); } }
static int scalar_cb(p_ply_argument argument) { CCLib::ScalarField* sf = 0; ply_get_argument_user_data(argument, (void**)(&sf), NULL); p_ply_element element; long instance_index; ply_get_argument_element(argument, &element, &instance_index); ScalarType scal = static_cast<ScalarType>(ply_get_argument_value(argument)); sf->setValue(instance_index,scal); if ((++s_totalScalarCount % PROCESS_EVENTS_FREQ) == 0) QCoreApplication::processEvents(); return 1; }
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; }
CC_FILE_ERROR LASFilter::saveToFile(ccHObject* entity, const char* filename) { if (!entity || !filename) return CC_FERR_BAD_ARGUMENT; ccHObject::Container clouds; if (entity->isKindOf(CC_POINT_CLOUD)) clouds.push_back(entity); else entity->filterChildren(clouds, true, CC_POINT_CLOUD); if (clouds.empty()) { ccConsole::Error("No point cloud in input selection!"); return CC_FERR_BAD_ENTITY_TYPE; } else if (clouds.size()>1) { ccConsole::Error("Can't save more than one cloud per LAS file!"); return CC_FERR_BAD_ENTITY_TYPE; } //the cloud to save ccGenericPointCloud* theCloud = static_cast<ccGenericPointCloud*>(clouds[0]); unsigned numberOfPoints = theCloud->size(); if (numberOfPoints==0) { ccConsole::Error("Cloud is empty!"); return CC_FERR_BAD_ENTITY_TYPE; } //colors bool hasColor = theCloud->hasColors(); //additional fields (as scalar fields) CCLib::ScalarField* classifSF = 0; CCLib::ScalarField* intensitySF = 0; CCLib::ScalarField* timeSF = 0; CCLib::ScalarField* returnNumberSF = 0; if (theCloud->isA(CC_POINT_CLOUD)) { ccPointCloud* pc = static_cast<ccPointCloud*>(theCloud); //Classification { int sfIdx = pc->getScalarFieldIndexByName(CC_LAS_CLASSIFICATION_FIELD_NAME); if (sfIdx>=0) { classifSF = pc->getScalarField(sfIdx); assert(classifSF); if ((int)classifSF->getMin()<0 || (int)classifSF->getMax()>255) //outbounds unsigned char? { ccConsole::Warning("[LASFilter] Found a 'Classification' scalar field, but its values outbound LAS specifications (0-255)..."); classifSF = 0; } } } //Classification end //intensity (as a scalar field) { int sfIdx = pc->getScalarFieldIndexByName(CC_SCAN_INTENSITY_FIELD_NAME); if (sfIdx>=0) { intensitySF = pc->getScalarField(sfIdx); assert(intensitySF); if ((int)intensitySF->getMin()<0 || (int)intensitySF->getMax()>65535) //outbounds unsigned short? { ccConsole::Warning("[LASFilter] Found a 'Intensity' scalar field, but its values outbound LAS specifications (0-65535)..."); intensitySF = 0; } } } //Intensity end //Time (as a scalar field) { int sfIdx = pc->getScalarFieldIndexByName(CC_SCAN_TIME_FIELD_NAME); if (sfIdx>=0) { timeSF = pc->getScalarField(sfIdx); assert(timeSF); } } //Time end //Return number (as a scalar field) { int sfIdx = pc->getScalarFieldIndexByName(CC_SCAN_RETURN_INDEX_FIELD_NAME); if (sfIdx>=0) { returnNumberSF = pc->getScalarField(sfIdx); assert(returnNumberSF); if ((int)returnNumberSF->getMin()<0 || (int)returnNumberSF->getMax()>7) //outbounds 3 bits? { ccConsole::Warning("[LASFilter] Found a 'Return number' scalar field, but its values outbound LAS specifications (0-7)..."); returnNumberSF = 0; } } } //Return number end } //open binary file for writing std::ofstream ofs; ofs.open(filename, std::ios::out | std::ios::binary); if (ofs.fail()) return CC_FERR_WRITING; const double* shift = theCloud->getOriginalShift(); liblas::Writer* writer = 0; try { liblas::Header header; //LAZ support based on extension! if (QFileInfo(filename).suffix().toUpper() == "LAZ") { header.SetCompressed(true); } //header.SetDataFormatId(liblas::ePointFormat3); ccBBox bBox = theCloud->getBB(); if (bBox.isValid()) { header.SetMin(-shift[0]+(double)bBox.minCorner().x,-shift[1]+(double)bBox.minCorner().y,-shift[2]+(double)bBox.minCorner().z); header.SetMax(-shift[0]+(double)bBox.maxCorner().x,-shift[1]+(double)bBox.maxCorner().y,-shift[2]+(double)bBox.maxCorner().z); CCVector3 diag = bBox.getDiagVec(); //Set offset & scale, as points will be stored as boost::int32_t values (between 0 and 4294967296) //int_value = (double_value-offset)/scale header.SetOffset(-shift[0]+(double)bBox.minCorner().x,-shift[1]+(double)bBox.minCorner().y,-shift[2]+(double)bBox.minCorner().z); header.SetScale(1.0e-9*std::max<double>(diag.x,ZERO_TOLERANCE), //result must fit in 32bits?! 1.0e-9*std::max<double>(diag.y,ZERO_TOLERANCE), 1.0e-9*std::max<double>(diag.z,ZERO_TOLERANCE)); } header.SetPointRecordsCount(numberOfPoints); //header.SetDataFormatId(Header::ePointFormat1); writer = new liblas::Writer(ofs, header); } catch (...) { return CC_FERR_WRITING; } //progress dialog ccProgressDialog pdlg(true); //cancel available CCLib::NormalizedProgress nprogress(&pdlg,numberOfPoints); pdlg.setMethodTitle("Save LAS file"); char buffer[256]; sprintf(buffer,"Points: %i",numberOfPoints); pdlg.setInfo(buffer); pdlg.start(); //liblas::Point point(boost::shared_ptr<liblas::Header>(new liblas::Header(writer->GetHeader()))); liblas::Point point(&writer->GetHeader()); for (unsigned i=0; i<numberOfPoints; i++) { const CCVector3* P = theCloud->getPoint(i); { double x=-shift[0]+(double)P->x; double y=-shift[1]+(double)P->y; double z=-shift[2]+(double)P->z; point.SetCoordinates(x, y, z); } if (hasColor) { const colorType* rgb = theCloud->getPointColor(i); point.SetColor(liblas::Color(rgb[0]<<8,rgb[1]<<8,rgb[2]<<8)); //DGM: LAS colors are stored on 16 bits! } if (classifSF) { liblas::Classification classif; classif.SetClass((boost::uint32_t)classifSF->getValue(i)); point.SetClassification(classif); } if (intensitySF) { point.SetIntensity((boost::uint16_t)intensitySF->getValue(i)); } if (timeSF) { point.SetTime((double)timeSF->getValue(i)); } if (returnNumberSF) { point.SetReturnNumber((boost::uint16_t)returnNumberSF->getValue(i)); point.SetNumberOfReturns((boost::uint16_t)returnNumberSF->getMax()); } writer->WritePoint(point); if (!nprogress.oneStep()) break; } delete writer; //ofs.close(); return CC_FERR_NO_ERROR; }
CC_FILE_ERROR PVFilter::loadFile(QString filename, ccHObject& container, LoadParameters& parameters) { //opening file QFile in(filename); if (!in.open(QIODevice::ReadOnly)) return CC_FERR_READING; //we deduce the points number from the file size qint64 fileSize = in.size(); qint64 singlePointSize = 4*sizeof(float); //check that size is ok if (fileSize == 0) return CC_FERR_NO_LOAD; if ((fileSize % singlePointSize) != 0) return CC_FERR_MALFORMED_FILE; unsigned numberOfPoints = static_cast<unsigned>(fileSize / singlePointSize); //progress dialog ccProgressDialog pdlg(true); //cancel available CCLib::NormalizedProgress nprogress(&pdlg,numberOfPoints); pdlg.setMethodTitle("Open PV file"); pdlg.setInfo(qPrintable(QString("Points: %1").arg(numberOfPoints))); pdlg.start(); ccPointCloud* loadedCloud = 0; //if the file is too big, it will be chuncked in multiple parts unsigned chunkIndex = 0; unsigned fileChunkPos = 0; unsigned fileChunkSize = 0; //number of points read for the current cloud part unsigned pointsRead = 0; CC_FILE_ERROR result = CC_FERR_NO_ERROR; for (unsigned i=0;i<numberOfPoints;i++) { //if we reach the max. cloud size limit, we cerate a new chunk if (pointsRead == fileChunkPos+fileChunkSize) { if (loadedCloud) { int sfIdx = loadedCloud->getCurrentInScalarFieldIndex(); if (sfIdx>=0) { CCLib::ScalarField* sf = loadedCloud->getScalarField(sfIdx); sf->computeMinAndMax(); loadedCloud->setCurrentDisplayedScalarField(sfIdx); loadedCloud->showSF(true); } container.addChild(loadedCloud); } fileChunkPos = pointsRead; fileChunkSize = std::min<unsigned>(numberOfPoints-pointsRead,CC_MAX_NUMBER_OF_POINTS_PER_CLOUD); loadedCloud = new ccPointCloud(QString("unnamed - Cloud #%1").arg(++chunkIndex)); if (!loadedCloud || !loadedCloud->reserveThePointsTable(fileChunkSize) || !loadedCloud->enableScalarField()) { result = CC_FERR_NOT_ENOUGH_MEMORY; if (loadedCloud) delete loadedCloud; loadedCloud=0; break; } } //we read the 3 coordinates of the point float rBuff[3]; if (in.read((char*)rBuff,3*sizeof(float))>=0) { //conversion to CCVector3 CCVector3 P((PointCoordinateType)rBuff[0], (PointCoordinateType)rBuff[1], (PointCoordinateType)rBuff[2]); loadedCloud->addPoint(P); } else { result = CC_FERR_READING; break; } //then the scalar value if (in.read((char*)rBuff,sizeof(float))>=0) { loadedCloud->setPointScalarValue(pointsRead,(ScalarType)rBuff[0]); } else { //add fake scalar value for consistency then break loadedCloud->setPointScalarValue(pointsRead,0); result = CC_FERR_READING; break; } ++pointsRead; if (!nprogress.oneStep()) { result = CC_FERR_CANCELED_BY_USER; break; } } in.close(); if (loadedCloud) { if (loadedCloud->size() < loadedCloud->capacity()) loadedCloud->resize(loadedCloud->size()); int sfIdx = loadedCloud->getCurrentInScalarFieldIndex(); if (sfIdx>=0) { CCLib::ScalarField* sf = loadedCloud->getScalarField(sfIdx); sf->computeMinAndMax(); loadedCloud->setCurrentDisplayedScalarField(sfIdx); loadedCloud->showSF(true); } container.addChild(loadedCloud); } return result; }
CC_FILE_ERROR PlyFilter::loadFile(const char* filename, ccHObject& container, bool alwaysDisplayLoadDialog/*=true*/, bool* coordinatesShiftEnabled/*=0*/, double* coordinatesShift/*=0*/) { //reset statics! s_triCount = 0; s_unsupportedPolygonType = false; s_scalarCount=0; s_IntensityCount=0; s_ColorCount=0; s_NormalCount=0; s_PointCount=0; s_PointDataCorrupted=false; s_AlwaysDisplayLoadDialog=alwaysDisplayLoadDialog; s_ShiftApplyAll=false; s_ShiftAlreadyEnabled = (coordinatesShiftEnabled && *coordinatesShiftEnabled && coordinatesShift); if (s_ShiftAlreadyEnabled) memcpy(s_Pshift,coordinatesShift,sizeof(double)*3); else memset(s_Pshift,0,sizeof(double)*3); /****************/ /*** Header ***/ /****************/ //open a PLY file for reading p_ply ply = ply_open(filename,NULL, 0, NULL); if (!ply) return CC_FERR_READING; ccConsole::PrintDebug("[PLY] Opening file '%s' ...",filename); if (!ply_read_header(ply)) { ply_close(ply); return CC_FERR_WRONG_FILE_TYPE; } //storage mode: little/big endian e_ply_storage_mode storage_mode; get_plystorage_mode(ply,&storage_mode); /******************/ /*** Comments ***/ /******************/ //display comments const char* lastComment = NULL; while ((lastComment = ply_get_next_comment(ply, lastComment))) ccConsole::Print("[PLY][Comment] %s",lastComment); /*******************************/ /*** Elements & properties ***/ /*******************************/ //Point-based elements (points, colors, normals, etc.) std::vector<plyElement> pointElements; //Mesh-based elements (vertices, etc.) std::vector<plyElement> meshElements; //Point-based element properties (coordinates, color components, etc.) std::vector<plyProperty> stdProperties; //Mesh-based element properties (vertex indexes, etc.) std::vector<plyProperty> listProperties; unsigned i=0; //last read element plyElement lastElement; lastElement.elem = 0; while ((lastElement.elem = ply_get_next_element(ply, lastElement.elem))) { //we get next element info ply_get_element_info(lastElement.elem, &lastElement.elementName, &lastElement.elementInstances); if (lastElement.elementInstances == 0) { ccConsole::Warning("[PLY] Element '%s' was ignored as it has 0 instance!",lastElement.elementName); continue; } lastElement.properties.clear(); lastElement.propertiesCount=0; lastElement.isList=false; //printf("Element: %s\n",lastElement.elementName); //last read property plyProperty lastProperty; lastProperty.prop = 0; lastProperty.elemIndex = 0; while ((lastProperty.prop = ply_get_next_property(lastElement.elem,lastProperty.prop))) { //we get next property info ply_get_property_info(lastProperty.prop, &lastProperty.propName, &lastProperty.type, &lastProperty.length_type, &lastProperty.value_type); //printf("\tProperty: %s (%s)\n",lastProperty.propName,e_ply_type_names[lastProperty.type]); if (lastProperty.type == 16) //PLY_LIST lastElement.isList = true; lastElement.properties.push_back(lastProperty); ++lastElement.propertiesCount; } //if we have a "mesh-like" element if (lastElement.isList) { //we store its properties in 'listProperties' for (i=0;i<lastElement.properties.size();++i) { plyProperty& prop = lastElement.properties[i]; prop.elemIndex = meshElements.size(); //we only keep track of lists (we can't handle per triangle scalars) if (prop.type == 16) listProperties.push_back(prop); else { ccConsole::Warning("[PLY] Unhandled property: [%s:%s] (%s)", lastElement.elementName, prop.propName, e_ply_type_names[prop.type]); } } meshElements.push_back(lastElement); } else //else if we have a "point-like" element { //we store its properties in 'stdProperties' for (i=0;i<lastElement.properties.size();++i) { plyProperty& prop = lastElement.properties[i]; prop.elemIndex = pointElements.size(); stdProperties.push_back(prop); } pointElements.push_back(lastElement); } } //We need some points at least! if (pointElements.empty()) { ply_close(ply); return CC_FERR_NO_LOAD; } /**********************/ /*** Objects info ***/ /**********************/ const char* lastObjInfo = NULL; while ((lastObjInfo = ply_get_next_obj_info(ply, lastObjInfo))) ccConsole::Print("[PLY][Info] %s",lastObjInfo); /****************/ /*** Dialog ***/ /****************/ //properties indexes (0=unassigned) static const unsigned nStdProp=11; int stdPropIndexes[nStdProp]={0,0,0,0,0,0,0,0,0,0,0}; int& xIndex = stdPropIndexes[0]; int& yIndex = stdPropIndexes[1]; int& zIndex = stdPropIndexes[2]; int& nxIndex = stdPropIndexes[3]; int& nyIndex = stdPropIndexes[4]; int& nzIndex = stdPropIndexes[5]; int& rIndex = stdPropIndexes[6]; int& gIndex = stdPropIndexes[7]; int& bIndex = stdPropIndexes[8]; int& iIndex = stdPropIndexes[9]; int& sfIndex = stdPropIndexes[10]; static const unsigned nListProp=1; int listPropIndexes[nListProp]={0}; int& facesIndex = listPropIndexes[0]; //Combo box items for standard properties (coordinates, color components, etc.) QStringList stdPropsText; stdPropsText << QString("None"); for (i=1; i<=stdProperties.size(); ++i) { plyProperty& pp = stdProperties[i-1]; QString itemText = QString("%1 - %2 [%3]").arg(pointElements[pp.elemIndex].elementName).arg(pp.propName).arg(e_ply_type_names[pp.type]); assert(pp.type!=16); //we don't want any PLY_LIST here stdPropsText << itemText; QString elementName = QString(pointElements[pp.elemIndex].elementName).toUpper(); QString propName = QString(pp.propName).toUpper(); if (nxIndex == 0 && (propName.contains("NX") || (elementName.contains("NORM") && propName.endsWith("X")))) nxIndex = i; else if (nyIndex == 0 && (propName.contains("NY") || (elementName.contains("NORM") && propName.endsWith("Y")))) nyIndex = i; else if (nzIndex == 0 && (propName.contains("NZ") || (elementName.contains("NORM") && propName.endsWith("Z")))) nzIndex = i; else if (rIndex == 0 && (propName.contains("RED") || (elementName.contains("COL") && propName.endsWith("R")))) rIndex = i; else if (gIndex == 0 && (propName.contains("GREEN") || (elementName.contains("COL") && propName.endsWith("G")))) gIndex = i; else if (bIndex == 0 && (propName.contains("BLUE") || (elementName.contains("COL") && propName.endsWith("B")))) bIndex = i; else if (iIndex == 0 && (propName.contains("INTENSITY") || propName.contains("GRAY") || propName.contains("GREY") || (elementName.contains("COL") && propName.endsWith("I")))) iIndex = i; else if (elementName.contains("VERT") || elementName.contains("POINT")) { if (sfIndex == 0 && propName.contains("SCAL")) sfIndex = i; else if (xIndex == 0 && propName.endsWith("X")) xIndex = i; else if (yIndex == 0 && propName.endsWith("Y")) yIndex = i; else if (zIndex == 0 && propName.endsWith("Z")) zIndex = i; } else if (sfIndex == 0 && (propName.contains("SCAL") || propName.contains("VAL"))) sfIndex = i; } //Combo box items for list properties (vertex indexes, etc.) QStringList listPropsText; listPropsText << QString("None"); for (i=0; i<listProperties.size(); ++i) { plyProperty& pp = listProperties[i]; QString itemText = QString("%0 - %1 [%2]").arg(meshElements[pp.elemIndex].elementName).arg(pp.propName).arg(e_ply_type_names[pp.type]); assert(pp.type==16); //we only want PLY_LIST here listPropsText << itemText; QString elementName = QString(meshElements[pp.elemIndex].elementName).toUpper(); QString propName = QString(pp.propName).toUpper(); if (facesIndex == 0 && (elementName.contains("FACE") || elementName.contains("TRI")) && propName.contains("IND")) facesIndex = i+1; } //combo-box max visible items int stdPropsCount = stdPropsText.count(); int listPropsCount = listPropsText.count(); //we need at least 2 coordinates! if (stdPropsCount<2) { return CC_FERR_BAD_ENTITY_TYPE; } else if (stdPropsCount<4 && !alwaysDisplayLoadDialog) { //brute force heuristic xIndex = 1; yIndex = 2; zIndex = (stdPropsCount>3 ? 3 : 0); facesIndex = (listPropsCount>1 ? 1 : 0); } else { //we count all assigned properties int assignedStdProperties = 0; for (i=0;i<nStdProp;++i) if (stdPropIndexes[i]>0) ++assignedStdProperties; int assignedListProperties = 0; for (i=0;i<nListProp;++i) if (listPropIndexes[i]>0) ++assignedListProperties; if (alwaysDisplayLoadDialog || stdPropsCount > assignedStdProperties+1 || //+1 because of the first item in the combo box ('none') listPropsCount > assignedListProperties+1) //+1 because of the first item in the combo box ('none') { PlyOpenDlg pod/*(MainWindow::TheInstance())*/; pod.plyTypeEdit->setText(e_ply_storage_mode_names[storage_mode]); pod.elementsEdit->setText(QString::number(pointElements.size())); pod.propertiesEdit->setText(QString::number(listProperties.size()+stdProperties.size())); //we fill every combo box pod.xComboBox->addItems(stdPropsText); pod.xComboBox->setCurrentIndex(xIndex); pod.xComboBox->setMaxVisibleItems(stdPropsCount); pod.yComboBox->addItems(stdPropsText); pod.yComboBox->setCurrentIndex(yIndex); pod.yComboBox->setMaxVisibleItems(stdPropsCount); pod.zComboBox->addItems(stdPropsText); pod.zComboBox->setCurrentIndex(zIndex); pod.zComboBox->setMaxVisibleItems(stdPropsCount); pod.rComboBox->addItems(stdPropsText); pod.rComboBox->setCurrentIndex(rIndex); pod.rComboBox->setMaxVisibleItems(stdPropsCount); pod.gComboBox->addItems(stdPropsText); pod.gComboBox->setCurrentIndex(gIndex); pod.gComboBox->setMaxVisibleItems(stdPropsCount); pod.bComboBox->addItems(stdPropsText); pod.bComboBox->setCurrentIndex(bIndex); pod.bComboBox->setMaxVisibleItems(stdPropsCount); pod.iComboBox->addItems(stdPropsText); pod.iComboBox->setCurrentIndex(iIndex); pod.iComboBox->setMaxVisibleItems(stdPropsCount); pod.sfComboBox->addItems(stdPropsText); pod.sfComboBox->setCurrentIndex(sfIndex); pod.sfComboBox->setMaxVisibleItems(stdPropsCount); pod.nxComboBox->addItems(stdPropsText); pod.nxComboBox->setCurrentIndex(nxIndex); pod.nxComboBox->setMaxVisibleItems(stdPropsCount); pod.nyComboBox->addItems(stdPropsText); pod.nyComboBox->setCurrentIndex(nyIndex); pod.nyComboBox->setMaxVisibleItems(stdPropsCount); pod.nzComboBox->addItems(stdPropsText); pod.nzComboBox->setCurrentIndex(nzIndex); pod.nzComboBox->setMaxVisibleItems(stdPropsCount); pod.facesComboBox->addItems(listPropsText); pod.facesComboBox->setCurrentIndex(facesIndex); pod.facesComboBox->setMaxVisibleItems(listPropsCount); //We execute dialog if (!pod.exec()) { ply_close(ply); return CC_FERR_CANCELED_BY_USER; } //Force events processing (to hide dialog) QCoreApplication::processEvents(); xIndex = pod.xComboBox->currentIndex(); yIndex = pod.yComboBox->currentIndex(); zIndex = pod.zComboBox->currentIndex(); nxIndex = pod.nxComboBox->currentIndex(); nyIndex = pod.nyComboBox->currentIndex(); nzIndex = pod.nzComboBox->currentIndex(); rIndex = pod.rComboBox->currentIndex(); gIndex = pod.gComboBox->currentIndex(); bIndex = pod.bComboBox->currentIndex(); iIndex = pod.iComboBox->currentIndex(); facesIndex = pod.facesComboBox->currentIndex(); sfIndex = pod.sfComboBox->currentIndex(); } } /*************************/ /*** Callbacks setup ***/ /*************************/ //Main point cloud ccPointCloud* cloud = new ccPointCloud("unnamed - Cloud"); /* POINTS (X,Y,Z) */ unsigned numberOfPoints=0; assert(xIndex != yIndex && xIndex != zIndex && yIndex != zIndex); //POINTS (X) if (xIndex>0) { long flags = ELEM_POS_0; //X coordinate if (xIndex > yIndex && xIndex > zIndex) flags |= ELEM_EOL; plyProperty& pp = stdProperties[xIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, vertex_cb, cloud, flags); numberOfPoints = pointElements[pp.elemIndex].elementInstances; } //POINTS (Y) if (yIndex>0) { long flags = ELEM_POS_1; //Y coordinate if (yIndex > xIndex && yIndex > zIndex) flags |= ELEM_EOL; plyProperty& pp = stdProperties[yIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, vertex_cb, cloud, flags); if (numberOfPoints > 0) { if ((long)numberOfPoints != pointElements[pp.elemIndex].elementInstances) { ccConsole::Warning("[PLY] Bad/uncompatible assignation of point properties!"); delete cloud; ply_close(ply); return CC_FERR_BAD_ENTITY_TYPE; } } else numberOfPoints = pointElements[pp.elemIndex].elementInstances; } //POINTS (Z) if (zIndex>0) { long flags = ELEM_POS_2; //Z coordinate if (zIndex > xIndex && zIndex > yIndex) flags |= ELEM_EOL; plyProperty& pp = stdProperties[zIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, vertex_cb, cloud, flags); if (numberOfPoints > 0) { if ((long)numberOfPoints != pointElements[pp.elemIndex].elementInstances) { ccConsole::Warning("[PLY] Bad/uncompatible assignation of point properties!"); delete cloud; ply_close(ply); return CC_FERR_BAD_ENTITY_TYPE; } } else numberOfPoints = pointElements[pp.elemIndex].elementInstances; } if (numberOfPoints == 0 || !cloud->reserveThePointsTable(numberOfPoints)) { delete cloud; ply_close(ply); return CC_FERR_NOT_ENOUGH_MEMORY; } /* NORMALS (X,Y,Z) */ unsigned numberOfNormals=0; assert(nxIndex == 0 || (nxIndex != nyIndex && nxIndex != nzIndex)); assert(nyIndex == 0 || (nyIndex != nxIndex && nyIndex != nzIndex)); assert(nzIndex == 0 || (nzIndex != nxIndex && nzIndex != nyIndex)); //NORMALS (X) if (nxIndex>0) { long flags = ELEM_POS_0; //Nx if (nxIndex > nyIndex && nxIndex > nzIndex) flags |= ELEM_EOL; plyProperty& pp = stdProperties[nxIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, normal_cb, cloud, flags); numberOfNormals = pointElements[pp.elemIndex].elementInstances; } //NORMALS (Y) if (nyIndex>0) { long flags = ELEM_POS_1; //Ny if (nyIndex > nxIndex && nyIndex > nzIndex) flags |= ELEM_EOL; plyProperty& pp = stdProperties[nyIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, normal_cb, cloud, flags); numberOfNormals = ccMax(numberOfNormals, (unsigned)pointElements[pp.elemIndex].elementInstances); } //NORMALS (Z) if (nzIndex>0) { long flags = ELEM_POS_2; //Nz if (nzIndex > nxIndex && nzIndex > nyIndex) flags |= ELEM_EOL; plyProperty& pp = stdProperties[nzIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, normal_cb, cloud, flags); numberOfNormals = ccMax(numberOfNormals, (unsigned)pointElements[pp.elemIndex].elementInstances); } //We check that the number of normals corresponds to the number of points if (numberOfNormals > 0) { if (numberOfPoints != numberOfNormals) { ccConsole::Warning("[PLY] The number of normals doesn't match the number of points!"); delete cloud; ply_close(ply); return CC_FERR_BAD_ENTITY_TYPE; } if (!cloud->reserveTheNormsTable()) { delete cloud; ply_close(ply); return CC_FERR_NOT_ENOUGH_MEMORY; } cloud->showNormals(true); } /* COLORS (R,G,B) */ unsigned numberOfColors=0; assert(rIndex == 0 || (rIndex != gIndex && rIndex != bIndex)); assert(gIndex == 0 || (gIndex != rIndex && gIndex != bIndex)); assert(bIndex == 0 || (bIndex != rIndex && bIndex != gIndex)); if (rIndex>0) { long flags = ELEM_POS_0; //R if (rIndex > gIndex && rIndex > bIndex) flags |= ELEM_EOL; plyProperty& pp = stdProperties[rIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, rgb_cb, cloud, flags); numberOfColors = pointElements[pp.elemIndex].elementInstances; } if (gIndex>0) { long flags = ELEM_POS_1; //G if (gIndex > rIndex && gIndex > bIndex) flags |= ELEM_EOL; plyProperty& pp = stdProperties[gIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, rgb_cb, cloud, flags); numberOfColors = ccMax(numberOfColors, (unsigned)pointElements[pp.elemIndex].elementInstances); } if (bIndex>0) { long flags = ELEM_POS_2; //B if (bIndex > rIndex && bIndex > gIndex) flags |= ELEM_EOL; plyProperty& pp = stdProperties[bIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, rgb_cb, cloud, flags); numberOfColors = ccMax(numberOfColors, (unsigned)pointElements[pp.elemIndex].elementInstances); } /* Intensity (I) */ //INTENSITE (G) if (iIndex>0) { if (numberOfColors>0) { ccConsole::Error("Can't import colors AND intensity (intensities will be ignored)!"); ccConsole::Warning("[PLY] intensities will be ignored"); } else { plyProperty pp = stdProperties[iIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, grey_cb, cloud, 0); numberOfColors = pointElements[pp.elemIndex].elementInstances; } } //We check that the number of colors corresponds to the number of points if (numberOfColors > 0) { if (numberOfPoints != numberOfColors) { ccConsole::Warning("The number of colors doesn't match the number of points!"); delete cloud; ply_close(ply); return CC_FERR_BAD_ENTITY_TYPE; } if (!cloud->reserveTheRGBTable()) { delete cloud; ply_close(ply); return CC_FERR_NOT_ENOUGH_MEMORY; } cloud->showColors(true); } /* SCALAR FIELD (SF) */ unsigned numberOfScalars=0; if (sfIndex>0) { plyProperty& pp = stdProperties[sfIndex-1]; numberOfScalars = pointElements[pp.elemIndex].elementInstances; //does the number of scalars matches the number of points? if (numberOfPoints != numberOfScalars) { ccConsole::Error("The number of scalars doesn't match the number of points (they will be ignored)!"); ccConsole::Warning("[PLY] Scalar field ignored!"); numberOfScalars = 0; } else if (!cloud->enableScalarField()) { ccConsole::Error("Not enough memory to load scalar field (they will be ignored)!"); ccConsole::Warning("[PLY] Scalar field ignored!"); numberOfScalars = 0; } else { CCLib::ScalarField* sf = cloud->getCurrentInScalarField(); if (sf) { QString qPropName(pp.propName); if (qPropName.startsWith("scalar_") && qPropName.length()>7) { //remove the 'scalar_' prefix added when saving SF with CC! qPropName = qPropName.mid(7).replace('_',' '); sf->setName(qPrintable(qPropName)); } else { sf->setName(pp.propName); } } ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, scalar_cb, cloud, 1); } cloud->showSF(true); } /* MESH FACETS (TRI) */ ccMesh* mesh = 0; unsigned numberOfFacets=0; if (facesIndex>0) { plyProperty& pp = listProperties[facesIndex-1]; assert(pp.type==16); //we only accept PLY_LIST here! mesh = new ccMesh(cloud); numberOfFacets = meshElements[pp.elemIndex].elementInstances; if (!mesh->reserve(numberOfFacets)) { ccConsole::Error("Not enough memory to load facets (they will be ignored)!"); ccConsole::Warning("[PLY] Mesh ignored!"); delete mesh; mesh = 0; numberOfFacets = 0; } else { ply_set_read_cb(ply, meshElements[pp.elemIndex].elementName, pp.propName, face_cb, mesh, 0); } } QProgressDialog progressDlg(QString("Loading in progress..."),0,0,0,0,Qt::Popup); progressDlg.setMinimumDuration(0); progressDlg.setModal(true); progressDlg.show(); QApplication::processEvents(); int success = ply_read(ply); progressDlg.close(); ply_close(ply); if (success<1) { if (mesh) delete mesh; delete cloud; return CC_FERR_READING; } //we check mesh if (mesh && mesh->size()==0) { if (s_unsupportedPolygonType) ccConsole::Error("Mesh is not triangular! (unsupported)"); else ccConsole::Error("Mesh is empty!"); delete mesh; mesh=0; } //we save coordinates shift information if (s_ShiftApplyAll && coordinatesShiftEnabled && coordinatesShift) { *coordinatesShiftEnabled = true; coordinatesShift[0] = s_Pshift[0]; coordinatesShift[1] = s_Pshift[1]; coordinatesShift[2] = s_Pshift[2]; } //we update scalar field CCLib::ScalarField* sf = cloud->getCurrentInScalarField(); if (sf) { sf->setPositive(!s_negSF); sf->computeMinAndMax(); int sfIdx = cloud->getCurrentInScalarFieldIndex(); cloud->setCurrentDisplayedScalarField(sfIdx); cloud->showSF(sfIdx>=0); } if (mesh) { assert(s_triCount > 0); //check number of loaded facets against 'theoretical' number if (s_triCount<numberOfFacets) { mesh->resize(s_triCount); ccConsole::Warning("[PLY] Missing vertex indexes!"); } //check that vertex indices start at 0 unsigned minVertIndex=numberOfPoints,maxVertIndex=0; for (unsigned i=0;i<s_triCount;++i) { const CCLib::TriangleSummitsIndexes* tri = mesh->getTriangleIndexes(i); if (tri->i1 < minVertIndex) minVertIndex = tri->i1; else if (tri->i1 > maxVertIndex) maxVertIndex = tri->i1; if (tri->i2 < minVertIndex) minVertIndex = tri->i2; else if (tri->i2 > maxVertIndex) maxVertIndex = tri->i2; if (tri->i3 < minVertIndex) minVertIndex = tri->i3; else if (tri->i3 > maxVertIndex) maxVertIndex = tri->i3; } if (maxVertIndex>=numberOfPoints) { if (maxVertIndex == numberOfPoints && minVertIndex > 0) { ccLog::Warning("[PLY] Vertex indices seem to be shifted (+1)! We will try to 'unshift' indices (otherwise file is corrupted...)"); for (unsigned i=0;i<s_triCount;++i) { CCLib::TriangleSummitsIndexes* tri = mesh->getTriangleIndexes(i); --tri->i1; --tri->i2; --tri->i3; } } else //file is definitely corrupted! { ccLog::Warning("[PLY] Invalid vertex indices!"); delete mesh; delete cloud; return CC_FERR_MALFORMED_FILE; } } mesh->addChild(cloud); cloud->setEnabled(false); cloud->setName("Vertices"); //cloud->setLocked(true); //DGM: no need to lock it as it is only used by one mesh! if (cloud->hasColors()) mesh->showColors(true); if (cloud->hasDisplayedScalarField()) mesh->showSF(true); if (cloud->hasNormals()) mesh->showNormals(true); else mesh->computeNormals(); container.addChild(mesh); } else { container.addChild(cloud); } return CC_FERR_NO_ERROR; }
CC_FILE_ERROR VTKFilter::saveToFile(ccHObject* entity, QString filename, SaveParameters& parameters) { if (!entity || filename.isEmpty()) return CC_FERR_BAD_ARGUMENT; //look for either a cloud or a mesh ccMesh* mesh = ccHObjectCaster::ToMesh(entity); unsigned triCount = 0; ccGenericPointCloud* vertices = 0; if (mesh) { //input entity is a mesh triCount = mesh->size(); if (triCount == 0) { ccLog::Warning("[VTK] Input mesh has no triangle?!"); return CC_FERR_NO_SAVE; } vertices = mesh->getAssociatedCloud(); } else { //no mesh? maybe the input entity is a cloud? vertices = ccHObjectCaster::ToGenericPointCloud(entity); } //in any case, we must have a valid 'vertices' entity now if (!vertices) { ccLog::Warning("[VTK] No point cloud nor mesh in input selection!"); return CC_FERR_BAD_ENTITY_TYPE; } unsigned ptsCount = vertices->size(); if (!ptsCount) { ccLog::Warning("[VTK] No point/vertex to save?!"); 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(sizeof(PointCoordinateType) == 4 ? 8 : 12); //write header outFile << "# vtk DataFile Version 3.0" << endl; outFile << "vtk output" << endl; outFile << "ASCII" << endl; outFile << "DATASET " << (mesh ? "POLYDATA" : "UNSTRUCTURED_GRID") << endl; //data type QString floatType = (sizeof(PointCoordinateType) == 4 ? "float" : "double"); /*** what shall we save now? ***/ // write the points { outFile << "POINTS " << ptsCount << " " << floatType << endl; for (unsigned i=0; i<ptsCount; ++i) { const CCVector3* P = vertices->getPoint(i); CCVector3d Pglobal = vertices->toGlobal3d<PointCoordinateType>(*P); outFile << Pglobal.x << " " << Pglobal.y << " " << Pglobal.z << endl; } } // write triangles if (mesh) { outFile << "POLYGONS " << triCount << " " << 4*triCount << endl; mesh->placeIteratorAtBegining(); for (unsigned i=0; i<triCount; ++i) { const CCLib::VerticesIndexes* tsi = mesh->getNextTriangleVertIndexes(); //DGM: getNextTriangleVertIndexes is faster for mesh groups! outFile << "3 " << tsi->i1 << " " << tsi->i2 << " " << tsi->i3 << endl; } } else { // write cell data outFile << "CELLS " << ptsCount << " " << 2*ptsCount << endl; for (unsigned i=0; i<ptsCount; ++i) outFile << "1 " << i << endl; outFile << "CELL_TYPES " << ptsCount << endl; for (unsigned i=0; i<ptsCount; ++i) outFile << "1 " << endl; } outFile << "POINT_DATA " << ptsCount << endl; // write normals if (vertices->hasNormals()) { outFile << "NORMALS Normals "<< floatType << endl; for (unsigned i=0; i<ptsCount; ++i) { const CCVector3& N = vertices->getPointNormal(i); outFile << N.x << " " << N.y << " " << N.z << endl; } } // write colors if (vertices->hasColors()) { outFile << "COLOR_SCALARS RGB 3" << endl; for (unsigned i=0; i<ptsCount; ++i) { const colorType* C = vertices->getPointColor(i); outFile << static_cast<float>(C[0])/ccColor::MAX << " " << static_cast<float>(C[1])/ccColor::MAX << " " << static_cast<float>(C[2])/ccColor::MAX << endl; } } // write scalar field(s)? if (vertices->isA(CC_TYPES::POINT_CLOUD)) { ccPointCloud* pointCloud = static_cast<ccPointCloud*>(vertices); unsigned sfCount = pointCloud->getNumberOfScalarFields(); for (unsigned i=0;i<sfCount;++i) { CCLib::ScalarField* sf = pointCloud->getScalarField(i); outFile << "SCALARS " << QString(sf->getName()).replace(" ","_") << (sizeof(ScalarType)==4 ? " float" : " double") << " 1" << endl; outFile << "LOOKUP_TABLE default" << endl; for (unsigned j=0;j<ptsCount; ++j) outFile << sf->getValue(j) << endl; } } else //virtual point cloud, we only have access to its currently displayed scalar field { if (vertices->hasScalarFields()) { outFile << "SCALARS ScalarField" << (sizeof(ScalarType)==4 ? " float" : " double") << " 1" << endl; outFile << "LOOKUP_TABLE default" << endl; for (unsigned j=0;j<ptsCount; ++j) outFile << vertices->getPointDisplayedDistance(j) << endl; } } file.close(); return CC_FERR_NO_ERROR; }
CC_FILE_ERROR VTKFilter::saveToFile(ccHObject* entity, const char* filename) { if (!entity || !filename) return CC_FERR_BAD_ARGUMENT; //look for either a cloud or a mesh ccHObject::Container clouds,meshes; if (entity->isA(CC_TYPES::POINT_CLOUD)) clouds.push_back(entity); else if (entity->isKindOf(CC_TYPES::MESH)) meshes.push_back(entity); else //group? { for (unsigned i=0; i<entity->getChildrenNumber(); ++i) { ccHObject* child = entity->getChild(i); if (child->isKindOf(CC_TYPES::POINT_CLOUD)) clouds.push_back(child); else if (child->isKindOf(CC_TYPES::MESH)) meshes.push_back(child); } } if (clouds.empty() && meshes.empty()) { ccLog::Error("No point cloud nor mesh in input selection!"); return CC_FERR_BAD_ENTITY_TYPE; } else if (clouds.size()+meshes.size()>1) { ccLog::Error("Can't save more than one entity per VTK file!"); return CC_FERR_BAD_ENTITY_TYPE; } //the cloud to save ccGenericPointCloud* vertices = 0; ccMesh* mesh = 0; unsigned triCount = 0; if (!clouds.empty()) //1 cloud, no mesh { vertices = ccHObjectCaster::ToGenericPointCloud(clouds[0]); } else //1 mesh, with vertices as cloud { mesh = static_cast<ccMesh*>(meshes[0]); triCount = mesh->size(); if (triCount == 0) { ccLog::Error("Mesh has no triangle?!"); return CC_FERR_NO_SAVE; } vertices = mesh->getAssociatedCloud(); } assert(vertices); unsigned ptsCount = vertices->size(); if (!ptsCount) { ccLog::Error("No point/vertex to save?!"); 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(sizeof(PointCoordinateType) == 4 ? 8 : 12); //write header outFile << "# vtk DataFile Version 3.0" << endl; outFile << "vtk output" << endl; outFile << "ASCII" << endl; outFile << "DATASET " << (mesh ? "POLYDATA" : "UNSTRUCTURED_GRID") << endl; //data type QString floatType = (sizeof(PointCoordinateType) == 4 ? "float" : "double"); /*** what shall we save now? ***/ // write the points { outFile << "POINTS " << ptsCount << " " << floatType << endl; for (unsigned i=0; i<ptsCount; ++i) { const CCVector3* P = vertices->getPoint(i); CCVector3d Pglobal = vertices->toGlobal3d<PointCoordinateType>(*P); outFile << Pglobal.x << " " << Pglobal.y << " " << Pglobal.z << endl; } } // write triangles if (mesh) { outFile << "POLYGONS " << triCount << " " << 4*triCount << endl; mesh->placeIteratorAtBegining(); for (unsigned i=0; i<triCount; ++i) { const CCLib::TriangleSummitsIndexes* tsi = mesh->getNextTriangleIndexes(); //DGM: getNextTriangleIndexes is faster for mesh groups! outFile << "3 " << tsi->i1 << " " << tsi->i2 << " " << tsi->i3 << endl; } } else { // write cell data outFile << "CELLS " << ptsCount << " " << 2*ptsCount << endl; for (unsigned i=0; i<ptsCount; ++i) outFile << "1 " << i << endl; outFile << "CELL_TYPES " << ptsCount << endl; for (unsigned i=0; i<ptsCount; ++i) outFile << "1 " << endl; } outFile << "POINT_DATA " << ptsCount << endl; // write normals if (vertices->hasNormals()) { outFile << "NORMALS Normals "<< floatType << endl; for (unsigned i=0; i<ptsCount; ++i) { const CCVector3& N = vertices->getPointNormal(i); outFile << N.x << " " << N.y << " " << N.z << endl; } } // write colors if (vertices->hasColors()) { outFile << "COLOR_SCALARS RGB 3" << endl; for (unsigned i=0; i<ptsCount; ++i) { const colorType* C = vertices->getPointColor(i); outFile << (float)C[0]/(float)MAX_COLOR_COMP << " " << (float)C[1]/(float)MAX_COLOR_COMP << " " << (float)C[2]/(float)MAX_COLOR_COMP << endl; } } // write scalar field(s)? if (vertices->isA(CC_TYPES::POINT_CLOUD)) { ccPointCloud* pointCloud = static_cast<ccPointCloud*>(vertices); unsigned sfCount = pointCloud->getNumberOfScalarFields(); for (unsigned i=0;i<sfCount;++i) { CCLib::ScalarField* sf = pointCloud->getScalarField(i); outFile << "SCALARS " << QString(sf->getName()).replace(" ","_") << (sizeof(ScalarType)==4 ? " float" : " double") << " 1" << endl; outFile << "LOOKUP_TABLE default" << endl; for (unsigned j=0;j<ptsCount; ++j) outFile << sf->getValue(j) << endl; } } else //virtual point cloud, we only have access to its currently displayed scalar field { if (vertices->hasScalarFields()) { outFile << "SCALARS ScalarField" << (sizeof(ScalarType)==4 ? " float" : " double") << " 1" << endl; outFile << "LOOKUP_TABLE default" << endl; for (unsigned j=0;j<ptsCount; ++j) outFile << vertices->getPointDisplayedDistance(j) << endl; } } file.close(); return CC_FERR_NO_ERROR; }
CC_FILE_ERROR PVFilter::saveToFile(ccHObject* entity, const char* filename) { if (!entity || !filename) return CC_FERR_BAD_ARGUMENT; ccHObject::Container clouds; if (entity->isKindOf(CC_POINT_CLOUD)) clouds.push_back(entity); else entity->filterChildren(clouds, true, CC_POINT_CLOUD); if (clouds.empty()) { ccConsole::Error("No point cloud in input selection!"); return CC_FERR_BAD_ENTITY_TYPE; } else if (clouds.size()>1) { ccConsole::Error("Can't save more than one cloud per PV file!"); return CC_FERR_BAD_ENTITY_TYPE; } //the cloud to save ccGenericPointCloud* theCloud = static_cast<ccGenericPointCloud*>(clouds[0]); //and its scalar field CCLib::ScalarField* sf = 0; if (theCloud->isA(CC_POINT_CLOUD)) sf = static_cast<ccPointCloud*>(theCloud)->getCurrentDisplayedScalarField(); if (!sf) ccConsole::Warning("No displayed scalar field! Values will all be 0!\n"); unsigned numberOfPoints = theCloud->size(); if (numberOfPoints==0) { ccConsole::Error("Cloud is empty!"); return CC_FERR_BAD_ENTITY_TYPE; } //open binary file for writing FILE* theFile = fopen(filename , "wb"); if (!theFile) return CC_FERR_WRITING; //Has the cloud been recentered? const double* shift = theCloud->getOriginalShift(); if (fabs(shift[0])+fabs(shift[0])+fabs(shift[0])>0.0) ccConsole::Warning(QString("[PVFilter::save] Can't recenter cloud %1 on PV file save!").arg(theCloud->getName())); //progress dialog ccProgressDialog pdlg(true); //cancel available CCLib::NormalizedProgress nprogress(&pdlg,numberOfPoints); pdlg.setMethodTitle("Save PV file"); char buffer[256]; sprintf(buffer,"Points: %i",numberOfPoints); pdlg.setInfo(buffer); pdlg.start(); float wBuff[3]; float val=0.0; for (unsigned i=0;i<numberOfPoints;i++) { //conversion to float const CCVector3* P = theCloud->getPoint(i); wBuff[0]=float(P->x); wBuff[1]=float(P->y); wBuff[2]=float(P->z); if (fwrite(wBuff,sizeof(float),3,theFile) < 0) {fclose(theFile);return CC_FERR_WRITING;} if (sf) val = (float)sf->getValue(i); if (fwrite(&val,sizeof(float),1,theFile) < 0) {fclose(theFile);return CC_FERR_WRITING;} if (!nprogress.oneStep()) break; } fclose(theFile); return CC_FERR_NO_ERROR; }
CC_FILE_ERROR ShpFilter::loadFile(QString filename, ccHObject& container, LoadParameters& parameters) { QFile file(filename); if (!file.open(QIODevice::ReadOnly)) return CC_FERR_READING; //global shift CCVector3d Pshift(0,0,0); //read header (refer to ESRI Shapefile Technical Description) if (file.size() < 100) return CC_FERR_MALFORMED_FILE; char header[100]; file.read(header,100); int32_t fileLength = 0; { /*** WARNING: the beginning of the header is written with big endianness! ***/ const char* _header = header; //Byte 0: SHP code const int32_t code = qFromBigEndian<int32_t>(*reinterpret_cast<const int32_t*>(_header)); if (code != 9994) { return CC_FERR_MALFORMED_FILE; } _header += 4; //Byte 4: unused (20 bytes) _header += 20; //Byte 24: file length (will be written... later ;) fileLength = qFromBigEndian<int32_t>(*reinterpret_cast<const int32_t*>(_header)); fileLength *= 2; //fileLength is measured in 16-bit words _header += 4; /*** WARNING: from now on, we only read data with little endianness! ***/ //Byte 28: file verion const int32_t version = qFromLittleEndian<int32_t>(*reinterpret_cast<const int32_t*>(_header)); _header += 4; //Byte 32: shape type int32_t shapeTypeInt = qFromLittleEndian<int32_t>(*reinterpret_cast<const int32_t*>(_header)); _header += 4; ccLog::Print(QString("[SHP] Version: %1 - type: %2").arg(version).arg(ToString(static_cast<ESRI_SHAPE_TYPE>(shapeTypeInt)))); //X and Y bounaries //Byte 36: box X min double xMin = qFromLittleEndian<double>(*reinterpret_cast<const double*>(_header)); _header += 8; //Byte 44: box Y min double xMax = qFromLittleEndian<double>(*reinterpret_cast<const double*>(_header)); _header += 8; //Byte 52: box X max double yMin = qFromLittleEndian<double>(*reinterpret_cast<const double*>(_header)); _header += 8; //Byte 60: box Y max double yMax = qFromLittleEndian<double>(*reinterpret_cast<const double*>(_header)); _header += 8; //Z bounaries //Unused, with value 0.0, if not Measured or Z type //Byte 68: box Z min double zMin = qFromLittleEndian<double>(*reinterpret_cast<const double*>(_header)); _header += 8; //Byte 76: box Z max double zMax = qFromLittleEndian<double>(*reinterpret_cast<const double*>(_header)); _header += 8; CCVector3d Pmin(xMin,yMin,zMin); if (HandleGlobalShift(Pmin,Pshift,parameters)) { ccLog::Warning("[SHP] Entities will be recentered! Translation: (%.2f,%.2f,%.2f)",Pshift.x,Pshift.y,Pshift.z); } //M bounaries (M = measures) //Byte 84: M min double mMin = qFromLittleEndian<double>(*reinterpret_cast<const double*>(_header)); _header += 8; //Byte 92: M max double mMax = qFromLittleEndian<double>(*reinterpret_cast<const double*>(_header)); _header += 8; } assert(fileLength >= 100); if (fileLength < 100) { assert(false); return CC_FERR_MALFORMED_FILE; } fileLength -= 100; if (fileLength == 0) { return CC_FERR_NO_LOAD; } //load shapes CC_FILE_ERROR error = CC_FERR_NO_ERROR; ccPointCloud* singlePoints = 0; qint64 pos = file.pos(); while (fileLength >= 12) { file.seek(pos); assert(pos + fileLength == file.size()); //load shape record in main SHP file { file.read(header,8); //Byte 0: Record Number int32_t recordNumber = qFromBigEndian<int32_t>(*reinterpret_cast<const int32_t*>(header)); //Record numbers begin at 1 //Byte 4: Content Length int32_t recordSize = qFromBigEndian<int32_t>(*reinterpret_cast<const int32_t*>(header+4)); //Record numbers begin at 1 recordSize *= 2; //recordSize is measured in 16-bit words fileLength -= 8; pos += 8; if (fileLength < recordSize) { assert(false); error = CC_FERR_MALFORMED_FILE; break; } fileLength -= recordSize; pos += recordSize; //Record start (byte 0): Shape Type if (recordSize < 4) { assert(false); error = CC_FERR_MALFORMED_FILE; break; } file.read(header,4); recordSize -= 4; int32_t shapeTypeInt = qToLittleEndian<int32_t>(*reinterpret_cast<const int32_t*>(header)); ccLog::Print(QString("[SHP] Record #%1 - type: %2 (%3 bytes)").arg(recordNumber).arg(ToString(static_cast<ESRI_SHAPE_TYPE>(shapeTypeInt))).arg(recordSize)); switch (shapeTypeInt) { case SHP_POLYLINE: case SHP_POLYLINE_Z: case SHP_POLYGON: case SHP_POLYGON_Z: error = LoadPolyline(file,container,recordNumber,static_cast<ESRI_SHAPE_TYPE>(shapeTypeInt),Pshift); break; case SHP_MULTI_POINT: case SHP_MULTI_POINT_Z: case SHP_MULTI_POINT_M: error = LoadCloud(file,container,recordNumber,static_cast<ESRI_SHAPE_TYPE>(shapeTypeInt),Pshift); break; case SHP_POINT: case SHP_POINT_Z: case SHP_POINT_M: error = LoadSinglePoint(file,singlePoints,static_cast<ESRI_SHAPE_TYPE>(shapeTypeInt),Pshift); break; //case SHP_MULTI_PATCH: // error = LoadMesh(file,recordSize); // break; case SHP_NULL_SHAPE: //ignored break; default: //unhandled entity ccLog::Warning("[SHP] Unhandled type!"); break; } } if (error != CC_FERR_NO_ERROR) break; } if (singlePoints) { if (singlePoints->size() == 0) { delete singlePoints; singlePoints = 0; } else { CCLib::ScalarField* sf = singlePoints->getScalarField(0); if (sf) { sf->computeMinAndMax(); singlePoints->showSF(true); } container.addChild(singlePoints); } } return error; }
CC_FILE_ERROR UltFilter::loadFile(const char* filename, ccHObject& container, bool alwaysDisplayLoadDialog/*=true*/, bool* coordinatesShiftEnabled/*=0*/, double* coordinatesShift/*=0*/) { //ccConsole::Print("[BinFilter::loadFile] Opening binary file '%s'...\n",filename); assert(filename); //file size long size = QFileInfo(filename).size(); if ( size == 0 || ((size % sizeof(MarkersFrame)) != 0)) return CC_FERR_MALFORMED_FILE; //number of transformations in file long count = size / sizeof(MarkersFrame); ccConsole::Print("[TransBuffer] Found %i trans. in file '%s'",count,filename); if (count<1) return CC_FERR_NO_LOAD; ccPointCloud* cloud = new ccPointCloud(); if (!cloud->reserve(count) || !cloud->enableScalarField()) { delete cloud; return CC_FERR_NOT_ENOUGH_MEMORY; } ccProgressDialog pdlg(true); pdlg.setMethodTitle("Open Ult File"); CCLib::NormalizedProgress nprogress(&pdlg,count); pdlg.reset(); pdlg.setInfo(qPrintable(QString("Transformations: %1").arg(count))); pdlg.start(); QApplication::processEvents(); FILE* fp = fopen(filename,"rb"); if (!fp) { delete cloud; return CC_FERR_READING; } //which marker is the reference? QMessageBox::StandardButton tibiaIsRef = QMessageBox::question(0, "Choose reference", "Tibia as reference (yes)? Or femur (no)? Or none (no to all)", QMessageBox::Yes | QMessageBox::No | QMessageBox::NoToAll, QMessageBox::Yes ); MARKER_ROLE referenceRole = MARKER_LOCALIZER; if (tibiaIsRef == QMessageBox::Yes) referenceRole = MARKER_TIBIA; else if (tibiaIsRef == QMessageBox::No) referenceRole = MARKER_FEMUR; //To apply a predefined pointer tip //CCVector3 tip(0,0,0); CCVector3 tip(-90.07f, -17.68f, 18.29f); MarkersFrame currentframe; MarkerState& currentMarker = currentframe.states[MARKER_POINTER]; MarkerState* referenceMarker = 0; if (referenceRole != MARKER_LOCALIZER) referenceMarker = currentframe.states+referenceRole; unsigned MarkersFrameSize = sizeof(MarkersFrame); unsigned realCount=0; for (long i=0;i<count;++i) { if (fread(¤tframe,MarkersFrameSize,1,fp)==0) { fclose(fp); delete cloud; return CC_FERR_READING; } if (currentMarker.visible && (!referenceMarker || referenceMarker->visible)) { CCVector3 P(tip); ccGLMatrix trans = currentMarker.pos; if (referenceMarker) trans = referenceMarker->pos.inverse() * trans; trans.apply(P); cloud->addPoint(P); cloud->setPointScalarValue(realCount,currentMarker.pos.timestamp); ++realCount; } if (!nprogress.oneStep()) break; } fclose(fp); if (realCount==0) { delete cloud; return CC_FERR_NO_LOAD; } cloud->resize(realCount); //we update scalar field CCLib::ScalarField* sf = cloud->getCurrentInScalarField(); if (sf) { sf->setPositive(true); sf->computeMinAndMax(); cloud->setCurrentDisplayedScalarField(cloud->getCurrentInScalarFieldIndex()); } container.addChild(cloud); return CC_FERR_NO_ERROR; }
void ccRasterizeTool::generateRaster() const { #ifdef CC_GDAL_SUPPORT if (!m_cloud || !m_grid.isValid()) return; GDALAllRegister(); ccLog::PrintDebug("(GDAL drivers: %i)", GetGDALDriverManager()->GetDriverCount()); const char *pszFormat = "GTiff"; GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName(pszFormat); if (!poDriver) { ccLog::Error("[GDAL] Driver %s is not supported", pszFormat); return; } char** papszMetadata = poDriver->GetMetadata(); if( !CSLFetchBoolean( papszMetadata, GDAL_DCAP_CREATE, FALSE ) ) { ccLog::Error("[GDAL] Driver %s doesn't support Create() method", pszFormat); return; } //which (and how many) bands shall we create? bool heightBand = true; //height by default bool densityBand = false; bool allSFBands = false; int sfBandIndex = -1; //scalar field index int totalBands = 0; bool interpolateSF = (getTypeOfSFInterpolation() != INVALID_PROJECTION_TYPE); ccPointCloud* pc = m_cloud->isA(CC_TYPES::POINT_CLOUD) ? static_cast<ccPointCloud*>(m_cloud) : 0; bool hasSF = interpolateSF && pc && !m_grid.scalarFields.empty(); RasterExportOptionsDlg reoDlg; reoDlg.dimensionsLabel->setText(QString("%1 x %2").arg(m_grid.width).arg(m_grid.height)); reoDlg.exportHeightsCheckBox->setChecked(heightBand); reoDlg.exportDensityCheckBox->setChecked(densityBand); reoDlg.exportDisplayedSFCheckBox->setEnabled(hasSF); reoDlg.exportAllSFCheckBox->setEnabled(hasSF); reoDlg.exportAllSFCheckBox->setChecked(allSFBands); if (!reoDlg.exec()) return; //we ask the output filename AFTER displaying the export parameters ;) QString outputFilename; { QSettings settings; settings.beginGroup(ccPS::HeightGridGeneration()); QString imageSavePath = settings.value("savePathImage",QApplication::applicationDirPath()).toString(); outputFilename = QFileDialog::getSaveFileName(0,"Save height grid raster",imageSavePath+QString("/raster.tif"),"geotiff (*.tif)"); if (outputFilename.isNull()) return; //save current export path to persistent settings settings.setValue("savePathImage",QFileInfo(outputFilename).absolutePath()); } heightBand = reoDlg.exportHeightsCheckBox->isChecked(); densityBand = reoDlg.exportDensityCheckBox->isChecked(); if (hasSF) { assert(pc); allSFBands = reoDlg.exportAllSFCheckBox->isChecked() && hasSF; if (!allSFBands && reoDlg.exportDisplayedSFCheckBox->isChecked()) { sfBandIndex = pc->getCurrentDisplayedScalarFieldIndex(); if (sfBandIndex < 0) ccLog::Warning("[Rasterize] Cloud has no active (displayed) SF!"); } } totalBands = heightBand ? 1 : 0; if (densityBand) { ++totalBands; } if (allSFBands) { assert(hasSF); for (size_t i=0; i<m_grid.scalarFields.size(); ++i) if (m_grid.scalarFields[i]) ++totalBands; } else if (sfBandIndex >= 0) { ++totalBands; } if (totalBands == 0) { ccLog::Warning("[Rasterize] Warning, can't output a raster with no band! (check export parameters)"); return; } //data type GDALDataType dataType = (std::max(sizeof(PointCoordinateType),sizeof(ScalarType)) > 4 ? GDT_Float64 : GDT_Float32); char **papszOptions = NULL; GDALDataset* poDstDS = poDriver->Create(qPrintable(outputFilename), static_cast<int>(m_grid.width), static_cast<int>(m_grid.height), totalBands, dataType, papszOptions); if (!poDstDS) { ccLog::Error("[GDAL] Failed to create output raster (not enough memory?)"); return; } ccBBox box = getCustomBBox(); assert(box.isValid()); //vertical dimension const unsigned char Z = getProjectionDimension(); assert(Z >= 0 && Z <= 2); const unsigned char X = Z == 2 ? 0 : Z +1; const unsigned char Y = X == 2 ? 0 : X +1; double shiftX = box.minCorner().u[X]; double shiftY = box.minCorner().u[Y]; double stepX = m_grid.gridStep; double stepY = m_grid.gridStep; if (pc) { const CCVector3d& shift = pc->getGlobalShift(); shiftX -= shift.u[X]; shiftY -= shift.u[Y]; double scale = pc->getGlobalScale(); assert(scale != 0); stepX /= scale; stepY /= scale; } double adfGeoTransform[6] = { shiftX, //top left x stepX, //w-e pixel resolution (can be negative) 0, //0 shiftY, //top left y 0, //0 stepY //n-s pixel resolution (can be negative) }; poDstDS->SetGeoTransform( adfGeoTransform ); //OGRSpatialReference oSRS; //oSRS.SetUTM( 11, TRUE ); //oSRS.SetWellKnownGeogCS( "NAD27" ); //char *pszSRS_WKT = NULL; //oSRS.exportToWkt( &pszSRS_WKT ); //poDstDS->SetProjection( pszSRS_WKT ); //CPLFree( pszSRS_WKT ); double* scanline = (double*) CPLMalloc(sizeof(double)*m_grid.width); int currentBand = 0; //exort height band? if (heightBand) { GDALRasterBand* poBand = poDstDS->GetRasterBand(++currentBand); assert(poBand); poBand->SetColorInterpretation(GCI_Undefined); EmptyCellFillOption fillEmptyCellsStrategy = getFillEmptyCellsStrategy(fillEmptyCellsComboBox); double emptyCellHeight = 0; switch (fillEmptyCellsStrategy) { case LEAVE_EMPTY: emptyCellHeight = m_grid.minHeight-1.0; poBand->SetNoDataValue(emptyCellHeight); //should be transparent! break; case FILL_MINIMUM_HEIGHT: emptyCellHeight = m_grid.minHeight; break; case FILL_MAXIMUM_HEIGHT: emptyCellHeight = m_grid.maxHeight; break; case FILL_CUSTOM_HEIGHT: emptyCellHeight = getCustomHeightForEmptyCells(); break; case FILL_AVERAGE_HEIGHT: emptyCellHeight = m_grid.meanHeight; break; default: assert(false); } for (unsigned j=0; j<m_grid.height; ++j) { const RasterCell* aCell = m_grid.data[j]; for (unsigned i=0; i<m_grid.width; ++i,++aCell) { scanline[i] = aCell->h == aCell->h ? aCell->h : emptyCellHeight; } if (poBand->RasterIO( GF_Write, 0, static_cast<int>(j), static_cast<int>(m_grid.width), 1, scanline, static_cast<int>(m_grid.width), 1, GDT_Float64, 0, 0 ) != CE_None) { ccLog::Error("[GDAL] An error occurred while writing the height band!"); if (scanline) CPLFree(scanline); GDALClose( (GDALDatasetH) poDstDS ); return; } } } //export density band if (densityBand) { GDALRasterBand* poBand = poDstDS->GetRasterBand(++currentBand); assert(poBand); poBand->SetColorInterpretation(GCI_Undefined); for (unsigned j=0; j<m_grid.height; ++j) { const RasterCell* aCell = m_grid.data[j]; for (unsigned i=0; i<m_grid.width; ++i,++aCell) { scanline[i] = aCell->nbPoints; } if (poBand->RasterIO( GF_Write, 0, static_cast<int>(j), static_cast<int>(m_grid.width), 1, scanline, static_cast<int>(m_grid.width), 1, GDT_Float64, 0, 0 ) != CE_None) { ccLog::Error("[GDAL] An error occurred while writing the height band!"); if (scanline) CPLFree(scanline); GDALClose( (GDALDatasetH) poDstDS ); return; } } } //export SF bands if (allSFBands || sfBandIndex >= 0) { for (size_t k=0; k<m_grid.scalarFields.size(); ++k) { double* _sfGrid = m_grid.scalarFields[k]; if (_sfGrid && (allSFBands || sfBandIndex == static_cast<int>(k))) //valid SF grid { GDALRasterBand* poBand = poDstDS->GetRasterBand(++currentBand); double sfNanValue = static_cast<double>(CCLib::ScalarField::NaN()); poBand->SetNoDataValue(sfNanValue); //should be transparent! assert(poBand); poBand->SetColorInterpretation(GCI_Undefined); for (unsigned j=0; j<m_grid.height; ++j) { const RasterCell* aCell = m_grid.data[j]; for (unsigned i=0; i<m_grid.width; ++i,++_sfGrid,++aCell) { scanline[i] = aCell->nbPoints ? *_sfGrid : sfNanValue; } if (poBand->RasterIO( GF_Write, 0, static_cast<int>(j), static_cast<int>(m_grid.width), 1, scanline, static_cast<int>(m_grid.width), 1, GDT_Float64, 0, 0 ) != CE_None) { //the corresponding SF should exist on the input cloud CCLib::ScalarField* formerSf = pc->getScalarField(static_cast<int>(k)); assert(formerSf); ccLog::Error(QString("[GDAL] An error occurred while writing the '%1' scalar field band!").arg(formerSf->getName())); k = m_grid.scalarFields.size(); //quick stop break; } } } } } if (scanline) CPLFree(scanline); scanline = 0; /* Once we're done, close properly the dataset */ GDALClose( (GDALDatasetH) poDstDS ); ccLog::Print(QString("[Rasterize] Raster '%1' succesfully saved").arg(outputFilename)); #else assert(false); ccLog::Error("[Rasterize] GDAL not supported by this version! Can't generate a raster..."); #endif }
//==================================================loadFile=================================================// CC_FILE_ERROR PlyFilter::loadFile(QString filename, ccHObject& container, LoadParameters& parameters) { //reset statics! s_triCount = 0;//三角面片的个数 s_unsupportedPolygonType = false;//支持多边形类型 s_texCoordCount = 0;//纹理坐标个数 s_invalidTexCoordinates = false;//纹理坐标无效 s_totalScalarCount = 0;// s_IntensityCount = 0;// s_ColorCount = 0;//颜色个数 s_NormalCount = 0;//法向量个数 s_PointCount = 0;//点的个数 s_PointDataCorrupted = false; s_loadParameters = parameters; s_Pshift = CCVector3d(0,0,0); /****************/ /*** Header ***/ /****************/ //open a PLY file for reading p_ply ply = ply_open(qPrintable(filename), NULL, 0, NULL); if (!ply) return CC_FERR_READING; //ccLog::PrintDebug(QString("[PLY] Opening file '%1' ...").arg(filename)); ccLog::PrintDebug(QString("[PLY] 打开文件 '%1' ...").arg(filename)); if (!ply_read_header(ply)) { ply_close(ply); return CC_FERR_WRONG_FILE_TYPE; } //storage mode: little/big endian e_ply_storage_mode storage_mode; get_plystorage_mode(ply,&storage_mode); /*****************/ /*** Texture ***/ /*****************/ //eventual texture file declared in the comments (keyword: TEXTUREFILE) QString textureFileName; //texture coordinates TextureCoordsContainer* texCoords = 0; /******************/ /*** Comments ***/ /******************/ { const char* lastComment = NULL; //display comments while ((lastComment = ply_get_next_comment(ply, lastComment))) { ccLog::Print("[PLY][Comment] %s",lastComment); //specific case: TextureFile 'filename.ext' if (QString(lastComment).toUpper().startsWith("TEXTUREFILE ")) textureFileName = QString(lastComment).mid(12).trimmed(); } } /*******************************/ /*** Elements & properties ***/ /*******************************/ //Point-based elements (points, colors, normals, etc.) std::vector<plyElement> pointElements; //Mesh-based elements (vertices, etc.) std::vector<plyElement> meshElements; //Point-based element properties (coordinates, color components, etc.) std::vector<plyProperty> stdProperties; //Mesh-based element properties (vertex indexes, etc.) std::vector<plyProperty> listProperties; //last read element plyElement lastElement; lastElement.elem = 0; while ((lastElement.elem = ply_get_next_element(ply, lastElement.elem))) { //we get next element info ply_get_element_info(lastElement.elem, &lastElement.elementName, &lastElement.elementInstances); if (lastElement.elementInstances == 0) { ccLog::Warning("[PLY] Element '%s' was ignored as it has 0 instance!",lastElement.elementName); continue; } lastElement.properties.clear(); lastElement.propertiesCount=0; lastElement.isList=false; //printf("Element: %s\n",lastElement.elementName); //last read property plyProperty lastProperty; lastProperty.prop = 0; lastProperty.elemIndex = 0; while ((lastProperty.prop = ply_get_next_property(lastElement.elem,lastProperty.prop))) { //we get next property info ply_get_property_info(lastProperty.prop, &lastProperty.propName, &lastProperty.type, &lastProperty.length_type, &lastProperty.value_type); //printf("\tProperty: %s (%s)\n",lastProperty.propName,e_ply_type_names[lastProperty.type]); if (lastProperty.type == 16) //PLY_LIST lastElement.isList = true; lastElement.properties.push_back(lastProperty); ++lastElement.propertiesCount; } //if we have a "mesh-like" element if (lastElement.isList) { //we store its properties in 'listProperties' for (size_t i=0; i<lastElement.properties.size(); ++i) { plyProperty& prop = lastElement.properties[i]; prop.elemIndex = (int)meshElements.size(); //we only keep track of lists (we can't handle per triangle scalars) if (prop.type == 16) listProperties.push_back(prop); else { ccLog::Warning("[PLY] Unhandled property: [%s:%s] (%s)", lastElement.elementName, prop.propName, e_ply_type_names[prop.type]); } } meshElements.push_back(lastElement); } else //else if we have a "point-like" element { //we store its properties in 'stdProperties' for (size_t i=0; i<lastElement.properties.size(); ++i) { plyProperty& prop = lastElement.properties[i]; prop.elemIndex = (int)pointElements.size(); stdProperties.push_back(prop); } pointElements.push_back(lastElement); } } //We need some points at least! if (pointElements.empty()) { ply_close(ply); return CC_FERR_NO_LOAD; } /**********************/ /*** Objects info ***/ /**********************/ { const char* lastObjInfo = NULL; while ((lastObjInfo = ply_get_next_obj_info(ply, lastObjInfo))) ccLog::Print("[PLY][Info] %s",lastObjInfo); } /****************/ /*** Dialog ***/ /****************/ //properties indexes (0 = unassigned) static const unsigned nStdProp = 10; int stdPropIndexes[nStdProp] = {0,0,0,0,0,0,0,0,0,0}; int& xIndex = stdPropIndexes[0]; int& yIndex = stdPropIndexes[1]; int& zIndex = stdPropIndexes[2]; int& nxIndex = stdPropIndexes[3]; int& nyIndex = stdPropIndexes[4]; int& nzIndex = stdPropIndexes[5]; int& rIndex = stdPropIndexes[6]; int& gIndex = stdPropIndexes[7]; int& bIndex = stdPropIndexes[8]; int& iIndex = stdPropIndexes[9]; std::vector<int> sfPropIndexes; //int& sfIndex = stdPropIndexes[10]; static const unsigned nListProp = 2; int listPropIndexes[nListProp] = {0,0}; int& facesIndex = listPropIndexes[0]; int& texCoordsIndex = listPropIndexes[1]; //Combo box items for standard properties (coordinates, color components, etc.) QStringList stdPropsText; stdPropsText << QString("None"); { for (int i=1; i<=static_cast<int>(stdProperties.size()); ++i) { plyProperty& pp = stdProperties[i-1]; QString itemText = QString("%1 - %2 [%3]").arg(pointElements[pp.elemIndex].elementName).arg(pp.propName).arg(e_ply_type_names[pp.type]); assert(pp.type!=16); //we don't want any PLY_LIST here stdPropsText << itemText; QString elementName = QString(pointElements[pp.elemIndex].elementName).toUpper(); QString propName = QString(pp.propName).toUpper(); if (nxIndex == 0 && (propName.contains("NX") || (elementName.contains("NORM") && propName.endsWith("X")))) nxIndex = i; else if (nyIndex == 0 && (propName.contains("NY") || (elementName.contains("NORM") && propName.endsWith("Y")))) nyIndex = i; else if (nzIndex == 0 && (propName.contains("NZ") || (elementName.contains("NORM") && propName.endsWith("Z")))) nzIndex = i; else if (rIndex == 0 && (propName.contains("RED") || (elementName.contains("COL") && propName.endsWith("R")))) rIndex = i; else if (gIndex == 0 && (propName.contains("GREEN") || (elementName.contains("COL") && propName.endsWith("G")))) gIndex = i; else if (bIndex == 0 && (propName.contains("BLUE") || (elementName.contains("COL") && propName.endsWith("B")))) bIndex = i; else if (iIndex == 0 && (propName.contains("INTENSITY") || propName.contains("GRAY") || propName.contains("GREY") || (elementName.contains("COL") && propName.endsWith("I")))) iIndex = i; else if (elementName.contains("VERT") || elementName.contains("POINT")) { if (propName.contains("SCAL")) sfPropIndexes.push_back(i); else if (xIndex == 0 && propName.endsWith("X")) xIndex = i; else if (yIndex == 0 && propName.endsWith("Y")) yIndex = i; else if (zIndex == 0 && propName.endsWith("Z")) zIndex = i; } else if (propName.contains("SCAL") || propName.contains("VAL")) sfPropIndexes.push_back(i); } } //Combo box items for list properties (vertex indexes, etc.) QStringList listPropsText; { listPropsText << QString("None"); for (int i=0; i<static_cast<int>(listProperties.size()); ++i) { plyProperty& pp = listProperties[i]; QString itemText = QString("%0 - %1 [%2]").arg(meshElements[pp.elemIndex].elementName).arg(pp.propName).arg(e_ply_type_names[pp.type]); assert(pp.type==16); //we only want PLY_LIST here listPropsText << itemText; QString elementName = QString(meshElements[pp.elemIndex].elementName).toUpper(); QString propName = QString(pp.propName).toUpper(); if (elementName.contains("FACE") || elementName.contains("TRI")) { if (facesIndex == 0 && propName.contains("IND")) facesIndex = i+1; if (texCoordsIndex == 0 && propName.contains("COORD")) texCoordsIndex = i+1; } } } //combo-box max visible items int stdPropsCount = stdPropsText.count(); int listPropsCount = listPropsText.count(); //we need at least 2 coordinates! if (stdPropsCount < 2) { ccLog::Warning("[PLY] This ply file has less than 2 properties defined! (not even X and Y ;)"); return CC_FERR_MALFORMED_FILE; } else if (stdPropsCount < 4 && !parameters.alwaysDisplayLoadDialog) { //brute force heuristic xIndex = 1; yIndex = 2; zIndex = (stdPropsCount > 3 ? 3 : 0); facesIndex = (listPropsCount > 1 ? 1 : 0); } else { //we count all assigned properties int assignedStdProperties = 0; { for (unsigned i=0; i<nStdProp; ++i) if (stdPropIndexes[i] > 0) ++assignedStdProperties; } int assignedListProperties = 0; { for (unsigned i=0; i<nListProp; ++i) if (listPropIndexes[i] > 0) ++assignedListProperties; } if ( parameters.alwaysDisplayLoadDialog || stdPropsCount > assignedStdProperties+1 //+1 because of the first item in the combo box ('none') || listPropsCount > assignedListProperties+1 ) //+1 because of the first item in the combo box ('none') { PlyOpenDlg pod/*(MainWindow::TheInstance())*/; pod.plyTypeEdit->setText(e_ply_storage_mode_names[storage_mode]); pod.elementsEdit->setText(QString::number(pointElements.size())); pod.propertiesEdit->setText(QString::number(listProperties.size()+stdProperties.size())); //we fill all combo-boxes with all items pod.setDefaultComboItems(stdPropsText); pod.setListComboItems(listPropsText); //try to restore previous context (if any) bool hasAPreviousContext = false; if (!pod.restorePreviousContext(hasAPreviousContext)) { if (hasAPreviousContext) ccLog::Warning("[PLY] Too many differences with the previous file, we reset the dialog."); //Set default/guessed element pod.xComboBox->setCurrentIndex(xIndex); pod.yComboBox->setCurrentIndex(yIndex); pod.zComboBox->setCurrentIndex(zIndex); pod.rComboBox->setCurrentIndex(rIndex); pod.gComboBox->setCurrentIndex(gIndex); pod.bComboBox->setCurrentIndex(bIndex); pod.iComboBox->setCurrentIndex(iIndex); pod.sfComboBox->setCurrentIndex(sfPropIndexes.empty() ? 0 : sfPropIndexes.front()); for (size_t j=1; j<sfPropIndexes.size(); ++j) pod.addSFComboBox(sfPropIndexes[j]); pod.nxComboBox->setCurrentIndex(nxIndex); pod.nyComboBox->setCurrentIndex(nyIndex); pod.nzComboBox->setCurrentIndex(nzIndex); pod.facesComboBox->setCurrentIndex(facesIndex); pod.textCoordsComboBox->setCurrentIndex(texCoordsIndex); } //We show the dialog (or we try to skip it ;) if (parameters.alwaysDisplayLoadDialog && !pod.canBeSkipped() && !pod.exec()) { ply_close(ply); return CC_FERR_CANCELED_BY_USER; } //Force events processing (to hide dialog) QCoreApplication::processEvents(); xIndex = pod.xComboBox->currentIndex(); yIndex = pod.yComboBox->currentIndex(); zIndex = pod.zComboBox->currentIndex(); nxIndex = pod.nxComboBox->currentIndex(); nyIndex = pod.nyComboBox->currentIndex(); nzIndex = pod.nzComboBox->currentIndex(); rIndex = pod.rComboBox->currentIndex(); gIndex = pod.gComboBox->currentIndex(); bIndex = pod.bComboBox->currentIndex(); iIndex = pod.iComboBox->currentIndex(); facesIndex = pod.facesComboBox->currentIndex(); texCoordsIndex = pod.textCoordsComboBox->currentIndex(); //get (non null) SF properties sfPropIndexes.clear(); { for (size_t j=0; j<pod.m_sfCombos.size(); ++j) if (pod.m_sfCombos[j]->currentIndex() > 0) sfPropIndexes.push_back(pod.m_sfCombos[j]->currentIndex()); } } } /*************************/ /*** Callbacks setup ***/ /*************************/ //Main point cloud ccPointCloud* cloud = new ccPointCloud("unnamed - Cloud"); /* POINTS (X,Y,Z) */ unsigned numberOfPoints = 0; assert(xIndex != yIndex && xIndex != zIndex && yIndex != zIndex); //POINTS (X) if (xIndex > 0) { long flags = ELEM_POS_0; //X coordinate if (xIndex > yIndex && xIndex > zIndex) flags |= ELEM_EOL; plyProperty& pp = stdProperties[xIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, vertex_cb, cloud, flags); numberOfPoints = pointElements[pp.elemIndex].elementInstances; } //POINTS (Y) if (yIndex > 0) { long flags = ELEM_POS_1; //Y coordinate if (yIndex > xIndex && yIndex > zIndex) flags |= ELEM_EOL; plyProperty& pp = stdProperties[yIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, vertex_cb, cloud, flags); if (numberOfPoints > 0) { if ((long)numberOfPoints != pointElements[pp.elemIndex].elementInstances) { ccLog::Warning("[PLY] Bad/uncompatible assignation of point properties!"); delete cloud; ply_close(ply); return CC_FERR_BAD_ENTITY_TYPE; } } else numberOfPoints = pointElements[pp.elemIndex].elementInstances; } //POINTS (Z) if (zIndex > 0) { long flags = ELEM_POS_2; //Z coordinate if (zIndex > xIndex && zIndex > yIndex) flags |= ELEM_EOL; plyProperty& pp = stdProperties[zIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, vertex_cb, cloud, flags); if (numberOfPoints > 0) { if ((long)numberOfPoints != pointElements[pp.elemIndex].elementInstances) { ccLog::Warning("[PLY] Bad/uncompatible assignation of point properties!"); delete cloud; ply_close(ply); return CC_FERR_BAD_ENTITY_TYPE; } } else numberOfPoints = pointElements[pp.elemIndex].elementInstances; } if (numberOfPoints == 0 || !cloud->reserveThePointsTable(numberOfPoints)) { delete cloud; ply_close(ply); return CC_FERR_NOT_ENOUGH_MEMORY; } /* NORMALS (X,Y,Z) */ unsigned numberOfNormals=0; assert(nxIndex == 0 || (nxIndex != nyIndex && nxIndex != nzIndex)); assert(nyIndex == 0 || (nyIndex != nxIndex && nyIndex != nzIndex)); assert(nzIndex == 0 || (nzIndex != nxIndex && nzIndex != nyIndex)); //NORMALS (X) if (nxIndex > 0) { long flags = ELEM_POS_0; //Nx if (nxIndex > nyIndex && nxIndex > nzIndex) flags |= ELEM_EOL; plyProperty& pp = stdProperties[nxIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, normal_cb, cloud, flags); numberOfNormals = pointElements[pp.elemIndex].elementInstances; } //NORMALS (Y) if (nyIndex > 0) { long flags = ELEM_POS_1; //Ny if (nyIndex > nxIndex && nyIndex > nzIndex) flags |= ELEM_EOL; plyProperty& pp = stdProperties[nyIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, normal_cb, cloud, flags); numberOfNormals = std::max(numberOfNormals, (unsigned)pointElements[pp.elemIndex].elementInstances); } //NORMALS (Z) if (nzIndex > 0) { long flags = ELEM_POS_2; //Nz if (nzIndex > nxIndex && nzIndex > nyIndex) flags |= ELEM_EOL; plyProperty& pp = stdProperties[nzIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, normal_cb, cloud, flags); numberOfNormals = std::max(numberOfNormals, (unsigned)pointElements[pp.elemIndex].elementInstances); } //We check that the number of normals corresponds to the number of points if (numberOfNormals > 0) { if (numberOfPoints != numberOfNormals) { ccLog::Warning("[PLY] The number of normals doesn't match the number of points!"); delete cloud; ply_close(ply); return CC_FERR_BAD_ENTITY_TYPE; } if (!cloud->reserveTheNormsTable()) { delete cloud; ply_close(ply); return CC_FERR_NOT_ENOUGH_MEMORY; } cloud->showNormals(true); } /* COLORS (R,G,B) */ unsigned numberOfColors=0; assert(rIndex == 0 || (rIndex != gIndex && rIndex != bIndex)); assert(gIndex == 0 || (gIndex != rIndex && gIndex != bIndex)); assert(bIndex == 0 || (bIndex != rIndex && bIndex != gIndex)); if (rIndex > 0) { long flags = ELEM_POS_0; //R if (rIndex > gIndex && rIndex > bIndex) flags |= ELEM_EOL; plyProperty& pp = stdProperties[rIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, rgb_cb, cloud, flags); numberOfColors = pointElements[pp.elemIndex].elementInstances; } if (gIndex > 0) { long flags = ELEM_POS_1; //G if (gIndex > rIndex && gIndex > bIndex) flags |= ELEM_EOL; plyProperty& pp = stdProperties[gIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, rgb_cb, cloud, flags); numberOfColors = std::max(numberOfColors, (unsigned)pointElements[pp.elemIndex].elementInstances); } if (bIndex > 0) { long flags = ELEM_POS_2; //B if (bIndex > rIndex && bIndex > gIndex) flags |= ELEM_EOL; plyProperty& pp = stdProperties[bIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, rgb_cb, cloud, flags); numberOfColors = std::max(numberOfColors, (unsigned)pointElements[pp.elemIndex].elementInstances); } /* Intensity (I) */ //INTENSITE (G) if (iIndex > 0) { if (numberOfColors > 0) { ccLog::Error("Can't import colors AND intensity (intensities will be ignored)!"); ccLog::Warning("[PLY] intensities will be ignored"); } else { plyProperty pp = stdProperties[iIndex-1]; ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, grey_cb, cloud, 0); numberOfColors = pointElements[pp.elemIndex].elementInstances; } } //We check that the number of colors corresponds to the number of points if (numberOfColors > 0) { if (numberOfPoints != numberOfColors) { ccLog::Warning("The number of colors doesn't match the number of points!"); delete cloud; ply_close(ply); return CC_FERR_BAD_ENTITY_TYPE; } if (!cloud->reserveTheRGBTable()) { delete cloud; ply_close(ply); return CC_FERR_NOT_ENOUGH_MEMORY; } cloud->showColors(true); } /* SCALAR FIELDS (SF) */ { for (size_t i=0; i<sfPropIndexes.size(); ++i) { int sfIndex = sfPropIndexes[i]; plyProperty& pp = stdProperties[sfIndex-1]; unsigned numberOfScalars = pointElements[pp.elemIndex].elementInstances; //does the number of scalars matches the number of points? if (numberOfPoints != numberOfScalars) { ccLog::Error(QString("Scalar field #%1: the number of scalars doesn't match the number of points (they will be ignored)!").arg(i+1)); ccLog::Warning(QString("[PLY] Scalar field #%1 ignored!").arg(i+1)); numberOfScalars = 0; } else { QString qPropName(pp.propName); if (qPropName.startsWith("scalar_") && qPropName.length() > 7) { //remove the 'scalar_' prefix added when saving SF with CC! qPropName = qPropName.mid(7).replace('_',' '); } int sfIdx = cloud->addScalarField(qPrintable(qPropName)); if (sfIdx >= 0) { CCLib::ScalarField* sf = cloud->getScalarField(sfIdx); assert(sf); if (sf->reserve(numberOfScalars)) { ply_set_read_cb(ply, pointElements[pp.elemIndex].elementName, pp.propName, scalar_cb, sf, 1); } else { cloud->deleteScalarField(sfIdx); sfIdx = -1; } } if (sfIdx < 0) { ccLog::Error(QString("Scalar field #%1: not enough memory to load scalar field (they will be ignored)!").arg(i+1)); ccLog::Warning(QString("[PLY] Scalar field #%1 ignored!").arg(i+1)); } } } } /* MESH FACETS (TRI) */ ccMesh* mesh = 0; unsigned numberOfFacets = 0; if (facesIndex > 0) { plyProperty& pp = listProperties[facesIndex-1]; assert(pp.type==16); //we only accept PLY_LIST here! mesh = new ccMesh(cloud); numberOfFacets = meshElements[pp.elemIndex].elementInstances; if (!mesh->reserve(numberOfFacets)) { ccLog::Error("Not enough memory to load facets (they will be ignored)!"); ccLog::Warning("[PLY] Mesh ignored!"); delete mesh; mesh = 0; numberOfFacets = 0; } else { ply_set_read_cb(ply, meshElements[pp.elemIndex].elementName, pp.propName, face_cb, mesh, 0); } } if (texCoordsIndex > 0) { plyProperty& pp = listProperties[texCoordsIndex-1]; assert(pp.type == 16); //we only accept PLY_LIST here! texCoords = new TextureCoordsContainer(); texCoords->link(); long numberOfCoordinates = meshElements[pp.elemIndex].elementInstances; assert(numberOfCoordinates == numberOfFacets); if (!texCoords->reserve(numberOfCoordinates*3)) { ccLog::Error("Not enough memory to load texture coordinates (they will be ignored)!"); ccLog::Warning("[PLY] Texture coordinates ignored!"); texCoords->release(); texCoords = 0; } else { ply_set_read_cb(ply, meshElements[pp.elemIndex].elementName, pp.propName, texCoords_cb, texCoords, 0); } } ccProgressDialog pDlg(false); if (parameters.alwaysDisplayLoadDialog) { //pDlg.setInfo("Loading in progress..."); //pDlg.setMethodTitle("PLY file"); pDlg.setInfo("正在加载..."); pDlg.setMethodTitle("PLY 文件"); pDlg.setRange(0,0); pDlg.show(); QApplication::processEvents(); } //let 'Rply' do the job;) int success = 0; try { success = ply_read(ply); } catch(...) { success = -1; } ply_close(ply); if (success < 1) { if (mesh) delete mesh; delete cloud; return CC_FERR_READING; } //we check mesh if (mesh && mesh->size() == 0) { if (s_unsupportedPolygonType) ccLog::Error("Mesh is not triangular! (unsupported)"); else ccLog::Error("Mesh is empty!"); delete mesh; mesh=0; } if (texCoords && (s_invalidTexCoordinates || s_texCoordCount != 3*mesh->size())) { ccLog::Error("Invalid texture coordinates! (they will be ignored)"); texCoords->release(); texCoords=0; } //we save parameters parameters = s_loadParameters; //we update scalar field(s) { for (unsigned i=0; i<cloud->getNumberOfScalarFields(); ++i) { CCLib::ScalarField* sf = cloud->getScalarField(i); assert(sf); sf->computeMinAndMax(); if (i == 0) { cloud->setCurrentDisplayedScalarField(0); cloud->showSF(true); } } } if (mesh) { assert(s_triCount > 0); //check number of loaded facets against 'theoretical' number if (s_triCount<numberOfFacets) { mesh->resize(s_triCount); ccLog::Warning("[PLY] Missing vertex indexes!"); } //check that vertex indices start at 0 unsigned minVertIndex=numberOfPoints,maxVertIndex=0; for (unsigned i=0;i<s_triCount;++i) { const CCLib::TriangleSummitsIndexes* tri = mesh->getTriangleIndexes(i); if (tri->i1 < minVertIndex) minVertIndex = tri->i1; else if (tri->i1 > maxVertIndex) maxVertIndex = tri->i1; if (tri->i2 < minVertIndex) minVertIndex = tri->i2; else if (tri->i2 > maxVertIndex) maxVertIndex = tri->i2; if (tri->i3 < minVertIndex) minVertIndex = tri->i3; else if (tri->i3 > maxVertIndex) maxVertIndex = tri->i3; } if (maxVertIndex>=numberOfPoints) { if (maxVertIndex == numberOfPoints && minVertIndex > 0) { ccLog::Warning("[PLY] Vertex indices seem to be shifted (+1)! We will try to 'unshift' indices (otherwise file is corrupted...)"); for (unsigned i=0;i<s_triCount;++i) { CCLib::TriangleSummitsIndexes* tri = mesh->getTriangleIndexes(i); --tri->i1; --tri->i2; --tri->i3; } } else //file is definitely corrupted! { ccLog::Warning("[PLY] Invalid vertex indices!"); delete mesh; delete cloud; return CC_FERR_MALFORMED_FILE; } } mesh->addChild(cloud); cloud->setEnabled(false); cloud->setName("Vertices"); //cloud->setLocked(true); //DGM: no need to lock it as it is only used by one mesh! //associated texture if (texCoords) { if (!textureFileName.isEmpty()) { QString textureFilePath = QFileInfo(filename).absolutePath() + QString('/') + textureFileName; ccMaterial::Shared material(new ccMaterial(textureFileName)); if (material->loadAndSetTexture(textureFilePath)) { if (mesh->reservePerTriangleTexCoordIndexes() && mesh->reservePerTriangleMtlIndexes()) { const QImage texture = material->getTexture(); ccLog::Print(QString("[PLY][Texture] Successfully loaded texture '%1' (%2x%3 pixels)").arg(textureFileName).arg(texture.width()).arg(texture.height())); //materials ccMaterialSet* materials = new ccMaterialSet("materials"); material->setDiffuse(ccColor::bright); material->setSpecular(ccColor::darker); material->setAmbient(ccColor::darker); materials->push_back(material); mesh->setMaterialSet(materials); mesh->setTexCoordinatesTable(texCoords); for (unsigned i=0;i<mesh->size();++i) { mesh->addTriangleMtlIndex(0); mesh->addTriangleTexCoordIndexes(i*3,i*3+1,i*3+2); } mesh->showMaterials(true); } else { ccLog::Warning("[PLY][Texture] Failed to reserve per-triangle texture coordinates! (not enough memory?)"); } } else { ccLog::Warning(QString("[PLY][Texture] Failed to load texture '%1'").arg(textureFilePath)); } } else { ccLog::Warning("[PLY][Texture] Texture coordinates loaded without an associated image! (we look for the 'TextureFile' keyword in comments)"); } } if (cloud->hasColors()) mesh->showColors(true); if (cloud->hasDisplayedScalarField()) mesh->showSF(true); if (cloud->hasNormals()) mesh->showNormals(true); else { //DGM: normals can be per-vertex or per-triangle so it's better to let the user do it himself later //Moreover it's not always good idea if the user doesn't want normals (especially in ccViewer!) //mesh->computeNormals(); ccLog::Warning("[PLY] Mesh has no normal! You can manually compute them (select it then call \"Edit > Normals > Compute\")"); } if (mesh->hasMaterials()) mesh->showNormals(false); container.addChild(mesh); } else { container.addChild(cloud); } if (texCoords) { texCoords->release(); texCoords = 0; } return CC_FERR_NO_ERROR; }
ccPointCloud* cc2Point5DimEditor::convertGridToCloud( const std::vector<ExportableFields>& exportedFields, bool interpolateSF, bool resampleInputCloud, ccGenericPointCloud* inputCloud, bool fillEmptyCells, double emptyCellsHeight) const { if (!m_grid.isValid()) return 0; unsigned pointsCount = (fillEmptyCells ? m_grid.width * m_grid.height : m_grid.validCellCount); if (pointsCount == 0) { ccLog::Warning("[Rasterize] Empty grid!"); return 0; } ccPointCloud* cloudGrid = 0; if (resampleInputCloud) { CCLib::ReferenceCloud refCloud(inputCloud); if (refCloud.reserve(m_grid.nonEmptyCellCount)) { for (unsigned j=0; j<m_grid.height; ++j) { for (unsigned i=0; i<m_grid.width; ++i) { const RasterCell& cell = m_grid.data[j][i]; if (cell.nbPoints) //non empty cell { refCloud.addPointIndex(cell.pointIndex); } } } assert(refCloud.size() != 0); cloudGrid = inputCloud->isA(CC_TYPES::POINT_CLOUD) ? static_cast<ccPointCloud*>(inputCloud)->partialClone(&refCloud) : ccPointCloud::From(&refCloud,inputCloud); cloudGrid->setPointSize(0); //to avoid display issues //even if we have already resampled the original cloud we may have to create new points and/or scalar fields //if (!interpolateSF && !fillEmptyCells) // return cloudGrid; } else { ccLog::Warning("[Rasterize] Not enough memory!"); return 0; } } else { cloudGrid = new ccPointCloud("grid"); } assert(cloudGrid); //shall we generate per-cell fields as well? std::vector<CCLib::ScalarField*> exportedSFs; if (!exportedFields.empty()) { exportedSFs.resize(exportedFields.size(),0); for (size_t i=0; i<exportedFields.size(); ++i) { int sfIndex = -1; switch (exportedFields[i]) { case PER_CELL_HEIGHT: case PER_CELL_COUNT: case PER_CELL_MIN_HEIGHT: case PER_CELL_MAX_HEIGHT: case PER_CELL_AVG_HEIGHT: case PER_CELL_HEIGHT_STD_DEV: case PER_CELL_HEIGHT_RANGE: sfIndex = cloudGrid->addScalarField(qPrintable(GetDefaultFieldName(exportedFields[i]))); break; default: assert(false); break; } if (sfIndex < 0) { ccLog::Warning("[Rasterize] Couldn't allocate scalar field(s)! Try to free some memory ..."); break; } exportedSFs[i] = cloudGrid->getScalarField(sfIndex); assert(exportedSFs[i]); } } //the resampled cloud already contains the points corresponding to 'filled' cells so we will only //need to add the empty ones (if requested) if ((!resampleInputCloud || fillEmptyCells) && !cloudGrid->reserve(pointsCount)) { ccLog::Warning("[Rasterize] Not enough memory!"); delete cloudGrid; return 0; } //vertical dimension const unsigned char Z = getProjectionDimension(); assert(Z >= 0 && Z <= 2); const unsigned char X = Z == 2 ? 0 : Z +1; const unsigned char Y = X == 2 ? 0 : X +1; //cloud bounding-box ccBBox box = getCustomBBox(); assert(box.isValid()); //we work with doubles as grid step can be much smaller than the cloud coordinates! double Py = box.minCorner().u[Y]; //as the 'non empty cells points' are already in the cloud //we must take care of where we put the scalar fields values! unsigned nonEmptyCellIndex = 0; for (unsigned j=0; j<m_grid.height; ++j) { const RasterCell* aCell = m_grid.data[j]; double Px = box.minCorner().u[X]; for (unsigned i=0; i<m_grid.width; ++i,++aCell) { if (aCell->h == aCell->h) //valid cell { //if we haven't resampled the original cloud, we must add the point //corresponding to this non-empty cell if (!resampleInputCloud || aCell->nbPoints == 0) { CCVector3 Pf( static_cast<PointCoordinateType>(Px), static_cast<PointCoordinateType>(Py), static_cast<PointCoordinateType>(aCell->h) ); cloudGrid->addPoint(Pf); } //fill the associated SFs assert(exportedSFs.size() >= exportedFields.size()); assert(!inputCloud || nonEmptyCellIndex < inputCloud->size()); for (size_t i=0; i<exportedSFs.size(); ++i) { CCLib::ScalarField* sf = exportedSFs[i]; ScalarType sVal = NAN_VALUE; switch (exportedFields[i]) { case PER_CELL_HEIGHT: sVal = static_cast<ScalarType>(aCell->h); break; case PER_CELL_COUNT: sVal = static_cast<ScalarType>(aCell->nbPoints); break; case PER_CELL_MIN_HEIGHT: sVal = static_cast<ScalarType>(aCell->minHeight); break; case PER_CELL_MAX_HEIGHT: sVal = static_cast<ScalarType>(aCell->maxHeight); break; case PER_CELL_AVG_HEIGHT: sVal = static_cast<ScalarType>(aCell->avgHeight); break; case PER_CELL_HEIGHT_STD_DEV: sVal = static_cast<ScalarType>(aCell->stdDevHeight); break; case PER_CELL_HEIGHT_RANGE: sVal = static_cast<ScalarType>(aCell->maxHeight - aCell->minHeight); break; default: assert(false); break; } if (resampleInputCloud) sf->setValue(nonEmptyCellIndex,sVal); else sf->addElement(sVal); } ++nonEmptyCellIndex; } else if (fillEmptyCells) //empty cell { //even if we have resampled the original cloud, we must add the point //corresponding to this empty cell { CCVector3 Pf( static_cast<PointCoordinateType>(Px), static_cast<PointCoordinateType>(Py), static_cast<PointCoordinateType>(emptyCellsHeight) ); cloudGrid->addPoint(Pf); } assert(exportedSFs.size() == exportedFields.size()); for (size_t i=0; i<exportedSFs.size(); ++i) { if (!exportedSFs[i]) { continue; } if (exportedFields[i] == PER_CELL_HEIGHT) { //we set the point height to the default height ScalarType s = static_cast<ScalarType>(emptyCellsHeight); exportedSFs[i]->addElement(s); } else { exportedSFs[i]->addElement(NAN_VALUE); } } } Px += m_grid.gridStep; } Py += m_grid.gridStep; } assert(exportedSFs.size() == exportedFields.size()); for (size_t i=0; i<exportedSFs.size(); ++i) { CCLib::ScalarField* sf = exportedSFs[i]; if (sf) { sf->computeMinAndMax(); } } //take care of former scalar fields if (!resampleInputCloud) { if (interpolateSF && inputCloud && inputCloud->isA(CC_TYPES::POINT_CLOUD)) { ccPointCloud* pc = static_cast<ccPointCloud*>(inputCloud); for (size_t k=0; k<m_grid.scalarFields.size(); ++k) { double* _sfGrid = m_grid.scalarFields[k]; if (_sfGrid) //valid SF grid { //the corresponding SF should exist on the input cloud ccScalarField* formerSf = static_cast<ccScalarField*>(pc->getScalarField(static_cast<int>(k))); assert(formerSf); //we try to create an equivalent SF on the output grid int sfIdx = cloudGrid->addScalarField(formerSf->getName()); if (sfIdx < 0) //if we aren't lucky, the input cloud already had a SF with CC_HEIGHT_GRID_FIELD_NAME as name sfIdx = cloudGrid->addScalarField(qPrintable(QString(formerSf->getName()).append(".old"))); if (sfIdx < 0) { ccLog::Warning("[Rasterize] Couldn't allocate a new scalar field for storing SF '%s' values! Try to free some memory ...",formerSf->getName()); } else { ccScalarField* sf = static_cast<ccScalarField*>(cloudGrid->getScalarField(sfIdx)); assert(sf); //set sf values unsigned n = 0; const ScalarType emptyCellSFValue = CCLib::ScalarField::NaN(); for (unsigned j=0; j<m_grid.height; ++j) { const RasterCell* aCell = m_grid.data[j]; for (unsigned i=0; i<m_grid.width; ++i, ++_sfGrid, ++aCell) { if (aCell->nbPoints) { ScalarType s = static_cast<ScalarType>(*_sfGrid); sf->setValue(n++,s); } else if (fillEmptyCells) { sf->setValue(n++,emptyCellSFValue); } } } sf->computeMinAndMax(); sf->importParametersFrom(formerSf); assert(sf->currentSize() == pointsCount); } } } } } else { for (size_t k=0; k<cloudGrid->getNumberOfScalarFields(); ++k) { CCLib::ScalarField* sf = cloudGrid->getScalarField(static_cast<int>(k)); sf->resize(cloudGrid->size(),true,NAN_VALUE); } } QString gridName = QString("raster(%1)").arg(m_grid.gridStep); if (inputCloud) { gridName.prepend(inputCloud->getName() + QString(".")); } cloudGrid->setName(gridName); return cloudGrid; }
CC_FILE_ERROR VTKFilter::loadFile(const char* filename, ccHObject& container, bool alwaysDisplayLoadDialog/*=true*/, bool* coordinatesShiftEnabled/*=0*/, CCVector3d* coordinatesShift/*=0*/) { //open ASCII file for reading QFile file(filename); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return CC_FERR_READING; QTextStream inFile(&file); //read header QString nextline = inFile.readLine(); if (!nextline.startsWith("# vtk")) return CC_FERR_MALFORMED_FILE; //comment nextline = inFile.readLine(); ccLog::Print(QString("[VTK] ")+nextline); ccMesh* mesh = 0; ccPointCloud* vertices = 0; std::vector<int> indexes; //global so as to avoid unnecessary mem. allocations QString lastSfName; bool acceptLookupTables = true; QString fileType = inFile.readLine().toUpper(); if (fileType.startsWith("BINARY")) { //binary not supported yet! ccLog::Error("VTK binary format not supported yet!"); return CC_FERR_WRONG_FILE_TYPE; } else if (fileType.startsWith("ASCII")) { //allow blank lines QString dataType; if (!GetNextNonEmptyLine(inFile,dataType)) return CC_FERR_MALFORMED_FILE; if (!dataType.startsWith("DATASET")) return CC_FERR_MALFORMED_FILE; dataType.remove(0,8); if (dataType.startsWith("POLYDATA")) { vertices = new ccPointCloud("vertices"); mesh = new ccMesh(vertices); } else if (dataType.startsWith("UNSTRUCTURED_GRID")) { vertices = new ccPointCloud("unnamed - VTK unstructured grid"); } else { ccLog::Error(QString("VTK entity '%1' is not supported!").arg(dataType)); return CC_FERR_WRONG_FILE_TYPE; } } //loop on keywords/data CC_FILE_ERROR error = CC_FERR_NO_ERROR; CCVector3d Pshift(0,0,0); while (error == CC_FERR_NO_ERROR) { if (!GetNextNonEmptyLine(inFile,nextline)) break; //end of file assert(!nextline.isEmpty()); if (nextline.startsWith("POINTS")) { QStringList parts = nextline.split(" ",QString::SkipEmptyParts); if (parts.size() != 3) { error=CC_FERR_MALFORMED_FILE; break; } bool ok = false; unsigned ptsCount = parts[1].toInt(&ok); if (!ok) { error = CC_FERR_MALFORMED_FILE; break; } //QString dataFormat = parts[3].toUpper(); //char buffer[8]; //unsigned char datSize = 4; //if (dataFormat == "DOUBLE") //{ // datSize = 8; //} //else if (dataFormat != "FLOAT") //{ // ccLog::Error(QString("Non floating point data (%1) is not supported!").arg(dataFormat)); // error = CC_FERR_WRONG_FILE_TYPE; // break; //} if (!vertices->reserve(ptsCount)) { error = CC_FERR_NOT_ENOUGH_MEMORY; break; } for (unsigned i=0; i<ptsCount; ++i) { nextline = inFile.readLine(); parts = nextline.split(" ",QString::SkipEmptyParts); if (parts.size() != 3) { error = CC_FERR_MALFORMED_FILE; break; } double Pd[3] = {0,0,0}; for (unsigned char j=0; j<3; ++j) { Pd[j] = parts[j].toDouble(&ok); if (!ok) { ccLog::Warning("[VTK] Element #%1 of POINTS data is corrupted!",i); error = CC_FERR_MALFORMED_FILE; break; } } //first point: check for 'big' coordinates if (i == 0) { bool shiftAlreadyEnabled = (coordinatesShiftEnabled && *coordinatesShiftEnabled && coordinatesShift); if (shiftAlreadyEnabled) Pshift = *coordinatesShift; bool applyAll = false; if ( sizeof(PointCoordinateType) < 8 && ccCoordinatesShiftManager::Handle(Pd,0,alwaysDisplayLoadDialog,shiftAlreadyEnabled,Pshift,0,applyAll)) { vertices->setGlobalShift(Pshift); ccLog::Warning("[VTKFilter::loadFile] Cloud has been recentered! Translation: (%.2f,%.2f,%.2f)",Pshift.x,Pshift.y,Pshift.z); //we save coordinates shift information if (applyAll && coordinatesShiftEnabled && coordinatesShift) { *coordinatesShiftEnabled = true; *coordinatesShift = Pshift; } } } vertices->addPoint(CCVector3( static_cast<PointCoordinateType>(Pd[0] + Pshift.x), static_cast<PointCoordinateType>(Pd[1] + Pshift.y), static_cast<PointCoordinateType>(Pd[2] + Pshift.z)) ); } //end POINTS } else if (nextline.startsWith("POLYGONS") || nextline.startsWith("TRIANGLE_STRIPS")) { QStringList parts = nextline.split(" ",QString::SkipEmptyParts); if (parts.size() != 3) { error = CC_FERR_MALFORMED_FILE; break; } //current type name (i.e. POLYGONS or TRIANGLE_STRIPS) QString typeName = parts[0]; bool isPolygon = (typeName == "POLYGONS"); bool ok = false; unsigned elemCount = parts[1].toUInt(&ok); if (!ok) { error = CC_FERR_MALFORMED_FILE; break; } unsigned totalElements = parts[2].toUInt(&ok); if (!ok) { error = CC_FERR_MALFORMED_FILE; break; } assert(mesh); if (!mesh) { ccLog::Warning(QString("[VTK] We found %1 data while file is not composed of POLYDATA!").arg(typeName)); mesh = new ccMesh(vertices); //however, we can still try to load it? } for (unsigned i=0; i<elemCount; ++i) { nextline = inFile.readLine(); parts = nextline.split(" ",QString::SkipEmptyParts); if (parts.empty()) { error = CC_FERR_MALFORMED_FILE; break; } unsigned vertCount = parts[0].toUInt(&ok); if (!ok || static_cast<int>(vertCount) >= parts.size()) { error = CC_FERR_MALFORMED_FILE; break; } else if (vertCount < 3) { ccLog::Warning(QString("[VTK] Element #%1 of %2 data is corrupted! (not enough indexes)").arg(i).arg(typeName)); } if (isPolygon && (vertCount != 3 && vertCount != 4)) //quads are easy to handle as well! { ccLog::Warning(QString("[VTK] POLYGON element #%1 has an unhandled size (> 4 vertices)").arg(i)); continue; } //reserve mem to. store indexes if (indexes.size() < vertCount) { try { indexes.resize(vertCount); } catch (std::bad_alloc) { error = CC_FERR_NOT_ENOUGH_MEMORY; break; } } //decode indexes for (unsigned j=0; j<vertCount; ++j) { indexes[j] = parts[j+1].toUInt(&ok); if (!ok) { ccLog::Warning(QString("[VTK] Element #%1 of %2 data is corrupted! (invalid index value)").arg(i).arg(typeName)); error = CC_FERR_MALFORMED_FILE; break; } } //add the triangles { assert(vertCount > 2); unsigned triCount = vertCount-2; if (mesh->size() + triCount > mesh->maxSize()) { if (!mesh->reserve(mesh->size()+triCount+256)) //take some advance to avoid too many allocations { error = CC_FERR_NOT_ENOUGH_MEMORY; break; } } if (isPolygon) { //triangle or quad mesh->addTriangle(indexes[0],indexes[1],indexes[2]); if (vertCount == 4) mesh->addTriangle(indexes[0],indexes[2],indexes[3]); } else { //triangle strip for (unsigned j=0; j<triCount; ++j) mesh->addTriangle(indexes[j],indexes[j+1],indexes[j+2]); } } } if (mesh->size() != 0 && mesh->size() < mesh->maxSize()) { mesh->resize(mesh->size()); } //end POLYGONS or TRIANGLE_STRIPS } else if (nextline.startsWith("NORMALS")) { unsigned ptsCount = vertices->size(); if (vertices->size() == 0) { error = CC_FERR_MALFORMED_FILE; break; } else { bool loadNormals = vertices->reserveTheNormsTable(); if (!loadNormals) ccLog::Warning("[VTK] Not enough memory to load normals!"); for (unsigned i=0; i<ptsCount; ++i) { nextline = inFile.readLine(); if (loadNormals) { QStringList parts = nextline.split(" ",QString::SkipEmptyParts); if (parts.size() != 3) { error = CC_FERR_MALFORMED_FILE; break; } CCVector3 N; for (unsigned char j=0; j<3; ++j) { bool ok; N.u[j] = (PointCoordinateType)parts[j].toDouble(&ok); if (!ok) { ccLog::Warning("[VTK] Element #%1 of NORMALS data is corrupted!",i); error = CC_FERR_MALFORMED_FILE; break; } } vertices->addNorm(N); } } } //end NORMALS } else if (nextline.startsWith("COLOR_SCALARS")) { unsigned ptsCount = vertices->size(); if (vertices->size() == 0) { error = CC_FERR_MALFORMED_FILE; break; } else { bool loadRGBColors = vertices->reserveTheRGBTable(); if (!loadRGBColors) ccLog::Warning("[VTK] Not enough memory to load RGB colors!"); for (unsigned i=0; i<ptsCount; ++i) { nextline = inFile.readLine(); if (loadRGBColors) { QStringList parts = nextline.split(" ",QString::SkipEmptyParts); if (parts.size() != 3) { error = CC_FERR_MALFORMED_FILE; break; } colorType rgb[3]; for (unsigned char j=0; j<3; ++j) { bool ok; rgb[j] = (colorType)(parts[j].toDouble(&ok) * (double)MAX_COLOR_COMP); if (!ok) { ccLog::Warning("[VTK] Element #%1 of COLOR_SCALARS data is corrupted!",i); error = CC_FERR_MALFORMED_FILE; break; } } vertices->addRGBColor(rgb); } } } //end COLOR_SCALARS } else if (nextline.startsWith("SCALARS")) { QStringList parts = nextline.split(" ",QString::SkipEmptyParts); lastSfName = "ScalarField"; if (parts.size() > 1) lastSfName = parts[1].replace("_"," "); //SF already exists? if (vertices->getScalarFieldIndexByName(qPrintable(lastSfName)) >= 0) lastSfName += QString(" (%1)").arg(vertices->getNumberOfScalarFields()); //end of SCALARS } else if (nextline.startsWith("LOOKUP_TABLE") || nextline.startsWith("VECTORS")) { unsigned ptsCount = vertices->size(); QStringList parts = nextline.split(" ",QString::SkipEmptyParts); QString itemName = parts[0]; if (parts.size() > 2) { bool ok = false; int valCount = parts[2].toUInt(&ok); if (ok) ptsCount = valCount; } bool createSF = (vertices->size() == ptsCount && vertices->size() != 0); if (acceptLookupTables && !createSF) { ccLog::Warning(QString("[VTK] field %1 has not the right number of points (will be ignored)").arg(itemName)); } createSF &= acceptLookupTables; if (createSF && lastSfName.isNull()) { ccLog::Warning(QString("[VTK] field %1 has no name (will be ignored)").arg(itemName)); createSF = false; } //create scalar field? int newSFIndex = createSF ? vertices->addScalarField(qPrintable(lastSfName)) : -1; CCLib::ScalarField* sf = newSFIndex >= 0 ? vertices->getScalarField(newSFIndex) : 0; lastSfName.clear(); //name is "consumed" for (unsigned i=0; i<ptsCount; ++i) { nextline = inFile.readLine(); if (sf) //otherwise we simply skip the line { QStringList parts = nextline.split(" ",QString::SkipEmptyParts); if (parts.size() != 1) { //get rid of the scalar field :( vertices->deleteScalarField(newSFIndex); sf = 0; if (i == 0) { ccLog::Warning(QString("[VTK] %1 field with more than one element can't be imported as scalar fields!").arg(itemName)); } else { error = CC_FERR_MALFORMED_FILE; break; } } else { bool ok; ScalarType d = static_cast<ScalarType>(nextline.toDouble(&ok)); sf->setValue(i, ok ? d : NAN_VALUE); } } } if (sf) { sf->computeMinAndMax(); vertices->setCurrentDisplayedScalarField(newSFIndex); vertices->showSF(true); } //end of SCALARS } else if (nextline.startsWith("POINT_DATA")) { //check that the number of 'point_data' match the number of points QStringList parts = nextline.split(" ",QString::SkipEmptyParts); acceptLookupTables = false; if (parts.size() > 1) { bool ok; unsigned dataCount = parts[1].toUInt(&ok); if (ok && vertices && dataCount == vertices->size()) { acceptLookupTables = true; } } if (!acceptLookupTables) { ccLog::Warning("[VTK] The number of 'POINT_DATA' doesn't match the number of loaded points... lookup tables will be ignored"); } } else //unhandled property (CELLS, CELL_TYPES, etc.) { QStringList parts = nextline.split(" ",QString::SkipEmptyParts); if (parts.size() < 2) { ccLog::Warning(QString("[VTK] Unhandled element: %1").arg(parts[0])); error = CC_FERR_MALFORMED_FILE; break; } bool ok; unsigned elements = parts[1].toUInt(&ok); if (!ok) { error = CC_FERR_MALFORMED_FILE; break; } for (unsigned i=0; i<elements; ++i) { inFile.readLine(); //ignore } //end unhandled property } if (error != CC_FERR_NO_ERROR) break; } if (error != CC_FERR_NO_ERROR) { if (mesh) delete mesh; if (vertices) delete vertices; return CC_FERR_MALFORMED_FILE; } file.close(); if (mesh && mesh->size() == 0) { delete mesh; mesh = 0; } if (vertices->size() == 0) { delete vertices; return CC_FERR_NO_LOAD; } if (mesh) { container.addChild(mesh); mesh->setVisible(true); mesh->addChild(vertices); vertices->setVisible(false); vertices->setEnabled(false); vertices->setName("Vertices"); vertices->setLocked(true); //DGM: no need to lock it as it is only used by one mesh! //DGM: normals can be per-vertex or per-triangle so it's better to let the user do it himself later //Moreover it's not always good idea if the user doesn't want normals (especially in ccViewer!) //if (!mesh->hasNormals()) // mesh->computeNormals(); ccLog::Warning("[VTK] Mesh has no normal! You can manually compute them (select it then call \"Edit > Normals > Compute\")"); mesh->showNormals(mesh->hasNormals()); if (vertices->hasScalarFields()) { vertices->setCurrentDisplayedScalarField(0); mesh->showSF(true); } if (vertices->hasColors()) mesh->showColors(true); } else { container.addChild(vertices); vertices->setVisible(true); if (vertices->hasNormals()) vertices->showNormals(true); if (vertices->hasScalarFields()) { vertices->setCurrentDisplayedScalarField(0); vertices->showSF(true); } if (vertices->hasColors()) vertices->showColors(true); } return CC_FERR_NO_ERROR; }
CC_FILE_ERROR BinFilter::LoadFileV1(QFile& in, ccHObject& container, unsigned nbScansTotal, const LoadParameters& parameters) { ccLog::Print("[BIN] Version 1.0"); if (nbScansTotal > 99) { if (QMessageBox::question(0, QString("Oups"), QString("Hum, do you really expect %1 point clouds?").arg(nbScansTotal), QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) return CC_FERR_WRONG_FILE_TYPE; } else if (nbScansTotal == 0) { return CC_FERR_NO_LOAD; } ccProgressDialog pdlg(true, parameters.parentWidget); pdlg.setMethodTitle(QObject::tr("Open Bin file (old style)")); for (unsigned k=0; k<nbScansTotal; k++) { HeaderFlags header; unsigned nbOfPoints = 0; if (ReadEntityHeader(in, nbOfPoints, header) < 0) { return CC_FERR_READING; } //Console::print("[BinFilter::loadModelFromBinaryFile] Entity %i : %i points, color=%i, norms=%i, dists=%i\n",k,nbOfPoints,color,norms,distances); if (nbOfPoints == 0) { //Console::print("[BinFilter::loadModelFromBinaryFile] rien a faire !\n"); continue; } //progress for this cloud CCLib::NormalizedProgress nprogress(&pdlg, nbOfPoints); if (parameters.alwaysDisplayLoadDialog) { pdlg.reset(); pdlg.setInfo(QObject::tr("cloud %1/%2 (%3 points)").arg(k + 1).arg(nbScansTotal).arg(nbOfPoints)); pdlg.start(); QApplication::processEvents(); } //Cloud name char cloudName[256] = "unnamed"; if (header.name) { for (int i=0; i<256; ++i) { if (in.read(cloudName+i,1) < 0) { //Console::print("[BinFilter::loadModelFromBinaryFile] Error reading the cloud name!\n"); return CC_FERR_READING; } if (cloudName[i] == 0) { break; } } //we force the end of the name in case it is too long! cloudName[255] = 0; } else { sprintf(cloudName,"unnamed - Cloud #%u",k); } //Cloud name char sfName[1024] = "unnamed"; if (header.sfName) { for (int i=0; i<1024; ++i) { if (in.read(sfName+i,1) < 0) { //Console::print("[BinFilter::loadModelFromBinaryFile] Error reading the cloud name!\n"); return CC_FERR_READING; } if (sfName[i] == 0) break; } //we force the end of the name in case it is too long! sfName[1023] = 0; } else { strcpy(sfName,"Loaded scalar field"); } //Creation ccPointCloud* loadedCloud = new ccPointCloud(cloudName); if (!loadedCloud) return CC_FERR_NOT_ENOUGH_MEMORY; unsigned fileChunkPos = 0; unsigned fileChunkSize = std::min(nbOfPoints,CC_MAX_NUMBER_OF_POINTS_PER_CLOUD); loadedCloud->reserveThePointsTable(fileChunkSize); if (header.colors) { loadedCloud->reserveTheRGBTable(); loadedCloud->showColors(true); } if (header.normals) { loadedCloud->reserveTheNormsTable(); loadedCloud->showNormals(true); } if (header.scalarField) loadedCloud->enableScalarField(); unsigned lineRead = 0; int parts = 0; const ScalarType FORMER_HIDDEN_POINTS = (ScalarType)-1.0; //lecture du fichier for (unsigned i=0; i<nbOfPoints; ++i) { if (lineRead == fileChunkPos+fileChunkSize) { if (header.scalarField) loadedCloud->getCurrentInScalarField()->computeMinAndMax(); container.addChild(loadedCloud); fileChunkPos = lineRead; fileChunkSize = std::min(nbOfPoints-lineRead,CC_MAX_NUMBER_OF_POINTS_PER_CLOUD); char partName[64]; ++parts; sprintf(partName,"%s.part_%i",cloudName,parts); loadedCloud = new ccPointCloud(partName); loadedCloud->reserveThePointsTable(fileChunkSize); if (header.colors) { loadedCloud->reserveTheRGBTable(); loadedCloud->showColors(true); } if (header.normals) { loadedCloud->reserveTheNormsTable(); loadedCloud->showNormals(true); } if (header.scalarField) loadedCloud->enableScalarField(); } float Pf[3]; if (in.read((char*)Pf,sizeof(float)*3) < 0) { //Console::print("[BinFilter::loadModelFromBinaryFile] Error reading the %ith entity point !\n",k); return CC_FERR_READING; } loadedCloud->addPoint(CCVector3::fromArray(Pf)); if (header.colors) { unsigned char C[3]; if (in.read((char*)C,sizeof(ColorCompType)*3) < 0) { //Console::print("[BinFilter::loadModelFromBinaryFile] Error reading the %ith entity colors !\n",k); return CC_FERR_READING; } loadedCloud->addRGBColor(C); } if (header.normals) { CCVector3 N; if (in.read((char*)N.u,sizeof(float)*3) < 0) { //Console::print("[BinFilter::loadModelFromBinaryFile] Error reading the %ith entity norms !\n",k); return CC_FERR_READING; } loadedCloud->addNorm(N); } if (header.scalarField) { double D; if (in.read((char*)&D,sizeof(double)) < 0) { //Console::print("[BinFilter::loadModelFromBinaryFile] Error reading the %ith entity distance!\n",k); return CC_FERR_READING; } ScalarType d = static_cast<ScalarType>(D); loadedCloud->setPointScalarValue(i,d); } lineRead++; if (parameters.alwaysDisplayLoadDialog && !nprogress.oneStep()) { loadedCloud->resize(i+1-fileChunkPos); k=nbScansTotal; i=nbOfPoints; } } if (parameters.alwaysDisplayLoadDialog) { pdlg.stop(); QApplication::processEvents(); } if (header.scalarField) { CCLib::ScalarField* sf = loadedCloud->getCurrentInScalarField(); assert(sf); sf->setName(sfName); //replace HIDDEN_VALUES by NAN_VALUES for (unsigned i=0; i<sf->currentSize(); ++i) { if (sf->getValue(i) == FORMER_HIDDEN_POINTS) sf->setValue(i,NAN_VALUE); } sf->computeMinAndMax(); loadedCloud->setCurrentDisplayedScalarField(loadedCloud->getCurrentInScalarFieldIndex()); loadedCloud->showSF(true); } container.addChild(loadedCloud); } return CC_FERR_NO_ERROR; }
CC_FILE_ERROR BinFilter::loadFileV1(QFile& in, ccHObject& container, unsigned nbScansTotal) { if (nbScansTotal>99) { if (QMessageBox::question(0, QString("Oups"), QString("Hum, do you really expect %1 point clouds?").arg(nbScansTotal))==QMessageBox::No) return CC_FERR_WRONG_FILE_TYPE; } else if (nbScansTotal==0) { return CC_FERR_NO_LOAD; } ccProgressDialog pdlg(true); pdlg.setMethodTitle("Open Bin file (old style)"); for (unsigned k=0;k<nbScansTotal;k++) { HeaderFlags header; unsigned nbOfPoints=0; if (ReadEntityHeader(in,nbOfPoints,header) < 0) return CC_FERR_READING; //Console::print("[BinFilter::loadModelFromBinaryFile] Entity %i : %i points, color=%i, norms=%i, dists=%i\n",k,nbOfPoints,color,norms,distances); //progress for this cloud CCLib::NormalizedProgress nprogress(&pdlg,nbOfPoints); pdlg.reset(); char buffer[256]; sprintf(buffer,"cloud %i/%i (%i points)",k+1,nbScansTotal,nbOfPoints); pdlg.setInfo(buffer); pdlg.start(); QApplication::processEvents(); if (nbOfPoints==0) { //Console::print("[BinFilter::loadModelFromBinaryFile] rien a faire !\n"); continue; } //Cloud name char cloudName[256]="unnamed"; if (header.name) { for (int i=0;i<256;++i) { if (in.read(cloudName+i,1)<0) { //Console::print("[BinFilter::loadModelFromBinaryFile] Error reading the cloud name!\n"); return CC_FERR_READING; } if (cloudName[i]==0) break; } //we force the end of the name in case it is too long! cloudName[255]=0; } else { sprintf(cloudName,"unnamed - Cloud #%i",k); } //Cloud name char sfName[1024]="unnamed"; if (header.sfName) { for (int i=0;i<1024;++i) { if (in.read(sfName+i,1)<0) { //Console::print("[BinFilter::loadModelFromBinaryFile] Error reading the cloud name!\n"); return CC_FERR_READING; } if (sfName[i]==0) break; } //we force the end of the name in case it is too long! sfName[1023]=0; } else { strcpy(sfName,"Loaded scalar field"); } //Creation ccPointCloud* loadedCloud = new ccPointCloud(cloudName); if (!loadedCloud) return CC_FERR_NOT_ENOUGH_MEMORY; unsigned fileChunkPos = 0; unsigned fileChunkSize = ccMin(nbOfPoints,CC_MAX_NUMBER_OF_POINTS_PER_CLOUD); loadedCloud->reserveThePointsTable(fileChunkSize); if (header.colors) { loadedCloud->reserveTheRGBTable(); loadedCloud->showColors(true); } if (header.normals) { loadedCloud->reserveTheNormsTable(); loadedCloud->showNormals(true); } if (header.scalarField) loadedCloud->enableScalarField(); CCVector3 P; unsigned char C[3]; double D; //does the associated scalar field is negative? bool negSF = false; unsigned lineReaded=0; int parts = 0; //lecture du fichier for (unsigned i=0;i<nbOfPoints;++i) { if (lineReaded == fileChunkPos+fileChunkSize) { if (header.scalarField) loadedCloud->getCurrentInScalarField()->computeMinAndMax(); container.addChild(loadedCloud); fileChunkPos = lineReaded; fileChunkSize = ccMin(nbOfPoints-lineReaded,CC_MAX_NUMBER_OF_POINTS_PER_CLOUD); char partName[64]; ++parts; sprintf(partName,"%s.part_%i",cloudName,parts); loadedCloud = new ccPointCloud(partName); loadedCloud->reserveThePointsTable(fileChunkSize); if (header.colors) { loadedCloud->reserveTheRGBTable(); loadedCloud->showColors(true); } if (header.normals) { loadedCloud->reserveTheNormsTable(); loadedCloud->showNormals(true); } if (header.scalarField) loadedCloud->enableScalarField(); } if (in.read((char*)P.u,sizeof(float)*3)<0) { //Console::print("[BinFilter::loadModelFromBinaryFile] Error reading the %ith entity point !\n",k); return CC_FERR_READING; } loadedCloud->addPoint(P); if (header.colors) { if (in.read((char*)C,sizeof(colorType)*3)<0) { //Console::print("[BinFilter::loadModelFromBinaryFile] Error reading the %ith entity colors !\n",k); return CC_FERR_READING; } loadedCloud->addRGBColor(C); } if (header.normals) { if (in.read((char*)P.u,sizeof(float)*3)<0) { //Console::print("[BinFilter::loadModelFromBinaryFile] Error reading the %ith entity norms !\n",k); return CC_FERR_READING; } loadedCloud->addNorm(P.u); } if (header.scalarField) { if (in.read((char*)&D,sizeof(double))<0) { //Console::print("[BinFilter::loadModelFromBinaryFile] Error reading the %ith entity distance !\n",k); return CC_FERR_READING; } DistanceType d = (DistanceType)D; //if there are negative values, we test if they are particular values (HIDDEN_VALUE, etc.) //or not particular (in which case we have a non strictly positive SF) if (d<0.0 && !negSF) { //we must test if the value is a particular one if (d != HIDDEN_VALUE && d != OUT_VALUE && d != SEGMENTED_VALUE) negSF = true; } loadedCloud->setPointScalarValue(i,d); } lineReaded++; if (!nprogress.oneStep()) { loadedCloud->resize(i+1-fileChunkPos); k=nbScansTotal; i=nbOfPoints; } } if (header.scalarField) { CCLib::ScalarField* sf = loadedCloud->getCurrentInScalarField(); assert(sf); sf->setName(sfName); sf->setPositive(!negSF); sf->computeMinAndMax(); loadedCloud->setCurrentDisplayedScalarField(loadedCloud->getCurrentInScalarFieldIndex()); loadedCloud->showSF(true); } container.addChild(loadedCloud); } return CC_FERR_NO_ERROR; }