bool TabularFile::CheckFile(const std::string & filename, size_t & first_data_line, size_t & n_columns, bool & read_last_line, std::string & last_line) { // Check if the last line of the file consists of data // and make initial guess if the last line shoud be read (not reading when equal to 0 or -999) std::ifstream in_file0; OpenRead(in_file0, filename); last_line = FindLastNonEmptyLine(in_file0); std::vector<std::string> tokens = GetTokens(last_line); read_last_line = true; if (!IsType<double>(tokens[0])) { read_last_line = false; } else { for (size_t i = 0; i < tokens.size(); ++i) { if (!IsType<double>(tokens[i])) read_last_line = false; else { if(atof(tokens[i].c_str()) == 0.0 || atof(tokens[i].c_str()) == -999.0) read_last_line = false; } } } std::ifstream in_file; OpenRead(in_file, filename); int line_number = 0; std::string line; while (GetNextNonEmptyLine(in_file, line_number, line)) { std::vector<std::string> tokens = GetTokens(line); if (IsType<double>(tokens[0])) { first_data_line = line_number; n_columns = tokens.size(); for (size_t i = 0; i < n_columns; ++i) { if (!IsType<double>(tokens[i])) return false; } return true; } } return false; }
CC_FILE_ERROR VTKFilter::loadFile(QString filename, ccHObject& container, LoadParameters& parameters) { //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; unsigned lastDataSize = 0; 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); bool skipReadLine = false; while (error == CC_FERR_NO_ERROR) { if (!skipReadLine && !GetNextNonEmptyLine(inFile,nextline)) break; //end of file skipReadLine = false; 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; } //warning: multiple points can be stored on a single line! unsigned iPt = 0; CCVector3d Pd(0,0,0); unsigned coordIndex = 0; while (iPt < ptsCount) { nextline = inFile.readLine(); parts = nextline.split(" ",QString::SkipEmptyParts); for (int i=0; i<parts.size(); ++i) { Pd.u[coordIndex] = parts[i].toDouble(&ok); if (!ok) { ccLog::Warning("[VTK] Element #%1 of POINTS data is corrupted!",iPt); error = CC_FERR_MALFORMED_FILE; iPt = ptsCount; break; } if (coordIndex == 2) { //first point: check for 'big' coordinates if (iPt == 0) { if (HandleGlobalShift(Pd,Pshift,parameters)) { vertices->setGlobalShift(Pshift); ccLog::Warning("[VTKFilter::loadFile] Cloud has been recentered! Translation: (%.2f,%.2f,%.2f)",Pshift.x,Pshift.y,Pshift.z); } } CCVector3 P = CCVector3::fromArray((Pd + Pshift).u); vertices->addPoint(P); coordIndex = 0; ++iPt; } else { ++coordIndex; } } } //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 (const 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")) { if (lastDataSize == 0) lastDataSize = vertices->size(); if (lastDataSize == 0) { error = CC_FERR_MALFORMED_FILE; break; } bool loadNormals = false; if (lastDataSize == vertices->size()) { if (!vertices->reserveTheNormsTable()) ccLog::Warning("[VTK] Not enough memory to load normals!"); else loadNormals = true; } //warning: multiple normals can be stored on a single line! unsigned iNorm = 0; CCVector3 N; unsigned coordIndex = 0; while (iNorm < lastDataSize) { nextline = inFile.readLine(); QStringList parts = nextline.split(" ",QString::SkipEmptyParts); for (int i=0; i<parts.size(); ++i) { bool ok; N.u[coordIndex] = static_cast<PointCoordinateType>(parts[i].toDouble(&ok)); if (!ok) { ccLog::Warning("[VTK] Element #%1 of NORMALS data is corrupted!",iNorm); error = CC_FERR_MALFORMED_FILE; iNorm = lastDataSize; break; } if (coordIndex == 2) { if (loadNormals) vertices->addNorm(N); coordIndex = 0; ++iNorm; } else { ++coordIndex; } } } lastDataSize = 0; //lastDataSize is consumed //end NORMALS } else if (nextline.startsWith("COLOR_SCALARS")) { if (lastDataSize == 0) lastDataSize = vertices->size(); if (lastDataSize == 0) { error = CC_FERR_MALFORMED_FILE; break; } bool loadRGBColors = vertices->reserveTheRGBTable(); if (!loadRGBColors) ccLog::Warning("[VTK] Not enough memory to load RGB colors!"); //warning: multiple colors can be stored on a single line! unsigned iCol = 0; colorType rgb[3]; unsigned coordIndex = 0; while (iCol < lastDataSize) { nextline = inFile.readLine(); QStringList parts = nextline.split(" ",QString::SkipEmptyParts); for (int i=0; i<parts.size(); ++i) { bool ok; rgb[coordIndex] = static_cast<colorType>(parts[i].toDouble(&ok) * ccColor::MAX); if (!ok) { ccLog::Warning("[VTK] Element #%1 of COLOR_SCALARS data is corrupted!",iCol); error = CC_FERR_MALFORMED_FILE; iCol = lastDataSize; break; } if (coordIndex == 2) { if (loadRGBColors) vertices->addRGBColor(rgb); coordIndex = 0; ++iCol; } else { ++coordIndex; } } } lastDataSize = 0; //lastDataSize is consumed //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")) { bool expected = (lastDataSize != 0); assert(!acceptLookupTables || expected); //i.e. lastDataSize shouldn't be 0 for 'accepted' lookup tables 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) lastDataSize = valCount; } else if (!expected) { ccLog::Warning(QString("[VTK] field %1 has no size?!").arg(itemName)); error = CC_FERR_MALFORMED_FILE; break; } bool createSF = (vertices->size() == lastDataSize && 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 || expected); if (createSF && lastSfName.isNull()) { ccLog::Warning(QString("[VTK] field %1 has no name (will be ignored)").arg(itemName)); createSF = false; } else if (!expected) { ccLog::Warning(QString("[VTK] field %1 was not expected (will be ignored)").arg(itemName)); } //create scalar field? ccScalarField* sf = 0; if (createSF) { sf = new ccScalarField(qPrintable(lastSfName)); if (!sf->reserve(lastDataSize)) { ccLog::Warning(QString("[VTK] Not enough memory to load scalar field' %1' (will be ignored)").arg(lastSfName)); sf->release(); sf = 0; } } lastSfName.clear(); //name is "consumed" //warning: multiple colors can be stored on a single line! unsigned iScal = 0; while (iScal < lastDataSize) { nextline = inFile.readLine(); QStringList parts = nextline.split(" ",QString::SkipEmptyParts); if (expected) { for (int i=0; i<parts.size(); ++i) { bool ok; ScalarType d = static_cast<ScalarType>(parts[i].toDouble(&ok)); if (!ok) { ccLog::Warning("[VTK] Element #%1 of LOOKUP_TABLE/VECTORS data is corrupted!",iScal); error = CC_FERR_MALFORMED_FILE; if (sf) { sf->release(); sf = 0; } iScal = lastDataSize; break; } if (sf) sf->addElement(d); ++iScal; } } else { //hard to guess the right format, but an unexpected field seem to always be //organized as 'one element per line' ++iScal; } } lastDataSize = 0; //lastDataSize is "consumed" acceptLookupTables = false; if (sf) { sf->computeMinAndMax(); int newSFIndex = vertices->addScalarField(sf); if (newSFIndex == 0) 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; lastDataSize = parts[1].toUInt(&ok); acceptLookupTables = ok && vertices; } } else if (nextline.startsWith("FIELD")) { QStringList parts = nextline.split(" ",QString::SkipEmptyParts); if (parts.size() < 2) { error = CC_FERR_MALFORMED_FILE; break; } bool ok; unsigned elements = parts[2].toUInt(&ok); if (!ok) { error = CC_FERR_MALFORMED_FILE; break; } elements *= 2; //we don't know how to handle those properly but at least //we know that for FIELD elements, there's 2 lines per element... for (unsigned i=0; i<elements; ++i) { inFile.readLine(); //ignore } } 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; } if (nextline.startsWith("CELL_DATA")) { //read next line (in case we actually know how to read it! if (!GetNextNonEmptyLine(inFile,nextline)) { error = CC_FERR_MALFORMED_FILE; break; } skipReadLine = true; if ( nextline.startsWith("SCALARS") || nextline.startsWith("NORMALS") || nextline.startsWith("COLOR_SCALARS")) { lastDataSize = elements; acceptLookupTables = false; //this property is for triangles! continue; } } //we'll try to blindly skip the elements... for (unsigned i=0; i<elements; ++i) { inFile.readLine(); //ignore } //end unhandled property } if (error != CC_FERR_NO_ERROR) break; } file.close(); if (vertices && vertices->size() == 0) { delete vertices; vertices = 0; if (error == CC_FERR_NO_ERROR) error = CC_FERR_NO_LOAD; } if (mesh && (mesh->size() == 0 || vertices == 0)) { delete mesh; mesh = 0; if (error == CC_FERR_NO_ERROR) error = CC_FERR_NO_LOAD; } if (mesh) { container.addChild(mesh); mesh->setVisible(true); mesh->addChild(vertices); 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 if (vertices) { 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 error; }
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; }