From e377ffe7c9306fcd9b8d047acdfa5d52d25d397d Mon Sep 17 00:00:00 2001 From: Michael Jackson Date: Tue, 29 Oct 2024 12:22:53 -0400 Subject: [PATCH] READER: Implement Aztec .cpr/.crc reader for EBSD data. (#27) Signed-off-by: Michael Jackson --- CMakePresets.json | 4 + Source/Apps/ParseAztecProject.cpp | 105 +++ Source/Apps/SourceList.cmake | 4 + Source/Apps/make_ipf.cpp | 2 +- Source/EbsdLib/IO/AngleFileLoader.h | 3 +- Source/EbsdLib/IO/BrukerNano/H5EspritReader.h | 2 +- Source/EbsdLib/IO/EbsdReader.h | 2 +- Source/EbsdLib/IO/HKL/CprReader.cpp | 737 ++++++++++++++++++ Source/EbsdLib/IO/HKL/CprReader.h | 149 ++++ Source/EbsdLib/IO/HKL/CtfConstants.h | 15 + Source/EbsdLib/IO/HKL/CtfPhase.cpp | 5 +- Source/EbsdLib/IO/HKL/CtfPhase.h | 4 +- Source/EbsdLib/IO/HKL/CtfReader.h | 3 +- Source/EbsdLib/IO/HKL/DataParser.hpp | 17 + Source/EbsdLib/IO/HKL/H5CtfReader.h | 2 +- Source/EbsdLib/IO/HKL/H5OINAReader.h | 2 +- Source/EbsdLib/IO/HKL/SourceList.cmake | 2 + Source/EbsdLib/IO/TSL/AngPhase.cpp | 2 +- Source/EbsdLib/IO/TSL/AngPhase.h | 2 +- Source/EbsdLib/IO/TSL/H5AngReader.h | 2 +- Source/EbsdLib/IO/TSL/H5OIMReader.h | 2 +- Source/EbsdLib/Utilities/SourceList.cmake | 1 + Source/EbsdLib/Utilities/inipp.h | 332 ++++++++ 23 files changed, 1383 insertions(+), 16 deletions(-) create mode 100644 Source/Apps/ParseAztecProject.cpp create mode 100644 Source/EbsdLib/IO/HKL/CprReader.cpp create mode 100644 Source/EbsdLib/IO/HKL/CprReader.h create mode 100644 Source/EbsdLib/Utilities/inipp.h diff --git a/CMakePresets.json b/CMakePresets.json index a4d9085..37d9160 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -128,6 +128,10 @@ "VCPKG_HOST_TRIPLET": { "type": "STRING", "value": "arm64-osx-dynamic" + }, + "CMAKE_BUILD_TYPE": { + "type": "STRING", + "value": "Debug" } }, "environment": { diff --git a/Source/Apps/ParseAztecProject.cpp b/Source/Apps/ParseAztecProject.cpp new file mode 100644 index 0000000..d35849d --- /dev/null +++ b/Source/Apps/ParseAztecProject.cpp @@ -0,0 +1,105 @@ + +#include "EbsdLib/IO/HKL/CprReader.h" +#include "EbsdLib/LaueOps/LaueOps.h" + +#include + +namespace detail +{ +// const std::string k_CprPath = "/Volumes/OWC_Express_1M2/DREAM3D_Troubleshoot_Data/Benjamin_Layer1/Layer1.cpr"; +const std::string k_CprPath = "/Volumes/OWC_Express_1M2/CPR_CRC_Test_Data/dg7kv5mcv3-1/17NZ42_Dauphinetwinnedsample_nearlyuntwinnedgrains.cpr"; + +} // namespace detail + +std::string ConvertLaueGroupToString(int idx) +{ + switch(idx) + { + case 0: + return "Hexagonal_High"; + case 1: + return "Cubic_High"; + case 2: + return "Hexagonal_Low"; + case 3: + return "Cubic_Low"; + case 4: + return "Triclinic"; + case 5: + return "Monoclinic"; + case 6: + return "OrthoRhombic"; + case 7: + return "Tetragonal_Low"; + case 8: + return "Tetragonal_High"; + case 9: + return "Trigonal_Low"; + case 10: + return "Trigonal_High"; + case 11: + return "UnknownCrystalStructure"; + } + return "Undefined"; +} + +// ----------------------------------------------------------------------------- +int main(int argc, char* argv[]) +{ + + // if(argc != 3) + // { + // std::cout << "Program .cpr and .crc files as input." << std::endl; + // return 1; + // } + + CprReader reader; + reader.setFileName(detail::k_CprPath); + int error = reader.readHeaderOnly(); + if(error < 0) + { + std::cout << "Reading Header Failed: " << error << "\n"; + return 1; + } + // reader.printHeader(std::cout); + // Write out a compatible DREAM3D "Ensemble" File + auto phases = reader.getPhaseVector(); + std::cout << "[EnsembleInfo]\n" + << "Number_Phases=" << phases.size() << std::endl; + size_t idx = 0; + for(const auto& phase : phases) + { + std::cout << std::endl; + std::cout << "[" << ++idx << "]\n" + << "CrystalStructure=" << ConvertLaueGroupToString(phase->determineOrientationOpsIndex()) << "\n" + << "PhaseType=PrimaryPhase\n"; + } + + error = reader.readFile(); + if(error < 0) + { + std::cout << reader.getErrorMessage() << error << "\n"; + return 2; + } + + // Dump all the data to the command line + // std::cout << "Phase,Bands,Error,Euler1,Euler2,Euler3,MAD,BC,BS\n"; + // uint8_t* phasePtr = reader.getPhasePointer(); + // uint8_t* bandsPtr = reader.getBandCountPointer(); + // uint8_t* errorPtr = reader.getErrorPointer(); + // float* phi1Ptr = reader.getEuler1Pointer(); + // float* phiPtr = reader.getEuler2Pointer(); + // float* phi2Ptr = reader.getEuler3Pointer(); + // float* madPtr = reader.getMeanAngularDeviationPointer(); + // uint8_t* bcPtr = reader.getBandContrastPointer(); + // uint8_t* bsPtr = reader.getBandSlopePointer(); + // + // size_t numScanPoints = reader.getNumberOfElements(); + // for(size_t i = 0; i < numScanPoints; i++) + // { + // std::cout << static_cast(phasePtr[i]) << "," << static_cast(bandsPtr[i]) << "," << static_cast(errorPtr[i]) << "," << phi1Ptr[i] << "," << phiPtr[i] << "," << phi2Ptr[i] << "," + // << madPtr[i] << "," << static_cast(bcPtr[i]) << "," << static_cast(bsPtr[i]) << std::endl; + // } + + return 0; +} diff --git a/Source/Apps/SourceList.cmake b/Source/Apps/SourceList.cmake index ea96746..d47c6ed 100644 --- a/Source/Apps/SourceList.cmake +++ b/Source/Apps/SourceList.cmake @@ -26,6 +26,10 @@ target_include_directories(generate_ipf_legends PRIVATE "${EbsdLibProj_SOURCE_DIR}/3rdParty/canvas_ity/src") +add_executable(ParseAztecProject ${EbsdLibProj_SOURCE_DIR}/Source/Apps/ParseAztecProject.cpp) +target_link_libraries(ParseAztecProject PUBLIC EbsdLib) +target_include_directories(ParseAztecProject PUBLIC ${EbsdLibProj_SOURCE_DIR}/Source) + if(EbsdLib_INSTALL_FILES) install(FILES diff --git a/Source/Apps/make_ipf.cpp b/Source/Apps/make_ipf.cpp index d2d9c64..748cf63 100644 --- a/Source/Apps/make_ipf.cpp +++ b/Source/Apps/make_ipf.cpp @@ -49,7 +49,7 @@ class GenerateIPFColorsImpl std::vector laueOpsIndex(m_PhaseInfos.size()); for(size_t i = 0; i < laueOpsIndex.size(); i++) { - laueOpsIndex[i] = m_PhaseInfos[i]->determineLaueGroup(); + laueOpsIndex[i] = m_PhaseInfos[i]->determineOrientationOpsIndex(); } size_t totalPoints = m_CellEulerAngles.size() / 3; diff --git a/Source/EbsdLib/IO/AngleFileLoader.h b/Source/EbsdLib/IO/AngleFileLoader.h index c7dae8a..879711c 100644 --- a/Source/EbsdLib/IO/AngleFileLoader.h +++ b/Source/EbsdLib/IO/AngleFileLoader.h @@ -36,9 +36,8 @@ #pragma once #include -#include - #include +#include #include "EbsdLib/Core/EbsdDataArray.hpp" #include "EbsdLib/EbsdLib.h" diff --git a/Source/EbsdLib/IO/BrukerNano/H5EspritReader.h b/Source/EbsdLib/IO/BrukerNano/H5EspritReader.h index 0e1854a..96cdd54 100644 --- a/Source/EbsdLib/IO/BrukerNano/H5EspritReader.h +++ b/Source/EbsdLib/IO/BrukerNano/H5EspritReader.h @@ -65,7 +65,7 @@ class EbsdLib_EXPORT H5EspritReader : public EbsdReader /** * @brief Returns the name of the class for H5EspritReader */ - std::string getNameOfClass() const; + std::string getNameOfClass() const override; /** * @brief Returns the name of the class for H5EspritReader */ diff --git a/Source/EbsdLib/IO/EbsdReader.h b/Source/EbsdLib/IO/EbsdReader.h index b8a3d28..c93368c 100644 --- a/Source/EbsdLib/IO/EbsdReader.h +++ b/Source/EbsdLib/IO/EbsdReader.h @@ -66,7 +66,7 @@ class EbsdLib_EXPORT EbsdReader /** * @brief Returns the name of the class for EbsdReader */ - std::string getNameOfClass() const; + virtual std::string getNameOfClass() const; /** * @brief Returns the name of the class for EbsdReader */ diff --git a/Source/EbsdLib/IO/HKL/CprReader.cpp b/Source/EbsdLib/IO/HKL/CprReader.cpp new file mode 100644 index 0000000..768b042 --- /dev/null +++ b/Source/EbsdLib/IO/HKL/CprReader.cpp @@ -0,0 +1,737 @@ +// +// Created by Michael Jackson on 10/22/24. +// + +#include "CprReader.h" + +#include "CtfPhase.h" +#include "EbsdLib/Core/EbsdLibConstants.h" +#include "EbsdLib/Math/EbsdLibMath.h" +#include "EbsdLib/Utilities/inipp.h" + +#include +#include +#include +#include +#include + +namespace +{ + +/* +enum ColumnNames { + 'X' % 1 4 bytes + 'Y' % 2 " + 'phi1' % 3 " + 'Phi' % 4 " + 'phi2' % 5 " + 'MAD' % 6 " + 'BC' % 7 1 byte + 'BS' % 8 " + 'Unknown' % 9 " + 'Bands' % 10 " + 'Error' % 11 " + 'ReliabilityIndex' % 12 " +}; +*/ + +// std::array k_FieldByteSize = {0, 4, 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1}; +// std::array k_FieldNames = {"", "X", "Y", "phi1", "Phi", "phi2", "MAD", "BC", "BS", "Unknown", "Bands", "Error", "ReliabilityIndex"}; +// std::array k_FieldNumericTypes = {EbsdLib::NumericTypes::Type::UnknownNumType, +// EbsdLib::NumericTypes::Type::Float, EbsdLib::NumericTypes::Type::Float, EbsdLib::NumericTypes::Type::Float, +// EbsdLib::NumericTypes::Type::Float, EbsdLib::NumericTypes::Type::Float, EbsdLib::NumericTypes::Type::Float, +// EbsdLib::NumericTypes::Type::UInt8, EbsdLib::NumericTypes::Type::UInt8, EbsdLib::NumericTypes::Type::UInt8, +// EbsdLib::NumericTypes::Type::UInt8, EbsdLib::NumericTypes::Type::UInt8, EbsdLib::NumericTypes::Type::UInt8}; +// + +struct CrcFieldDefinition +{ + size_t ByteSize = 0; + std::string FieldName; + EbsdLib::NumericTypes::Type numericType = EbsdLib::NumericTypes::Type::UnknownNumType; +}; + +// clang-format off +std::array k_FieldDefinitions{CrcFieldDefinition{1, "Phase", EbsdLib::NumericTypes::Type::UInt8}, + CrcFieldDefinition{4, "X", EbsdLib::NumericTypes::Type::Float}, + CrcFieldDefinition{4, "Y", EbsdLib::NumericTypes::Type::Float}, + CrcFieldDefinition{4, "phi1", EbsdLib::NumericTypes::Type::Float}, + CrcFieldDefinition{4, "Phi", EbsdLib::NumericTypes::Type::Float}, + CrcFieldDefinition{4, "phi2", EbsdLib::NumericTypes::Type::Float}, + CrcFieldDefinition{4, "MAD", EbsdLib::NumericTypes::Type::Float}, + CrcFieldDefinition{1, "BC", EbsdLib::NumericTypes::Type::UInt8}, + CrcFieldDefinition{1, "BS", EbsdLib::NumericTypes::Type::UInt8}, + CrcFieldDefinition{1, "Unknown", EbsdLib::NumericTypes::Type::UInt8}, + CrcFieldDefinition{1, "Bands", EbsdLib::NumericTypes::Type::UInt8}, + CrcFieldDefinition{1, "Error", EbsdLib::NumericTypes::Type::UInt8}, + CrcFieldDefinition{4, "ReliabilityIndex", EbsdLib::NumericTypes::Type::Int32}}; +// clang-format on + +struct CrcDataParser +{ + CrcFieldDefinition FieldDefinition = {}; + uint8_t* destinationPtr = nullptr; + size_t readOffset = 0; + + void parse(uint8_t* buffer, size_t destinationIndex) const + { + // calculate the proper pointer offset + uint8_t* finalPtr = destinationPtr + (destinationIndex * FieldDefinition.ByteSize); + // Copy the bytes from the buffer into the final destination + std::memcpy(finalPtr, buffer + readOffset, FieldDefinition.ByteSize); + } +}; + +// Read a complete Scan Point +// loop over parserDefinitions->parse(buffer, currentIndex); + +template +T getFieldValue(inipp::Ini& iniParser, const std::string& section, const std::string& key) +{ + T value; + inipp::get_value(iniParser.sections[section], key, value); + return value; +} + +std::vector getLatticeConstants(inipp::Ini& iniParser, const std::string& section) +{ + float a; + float b; + float c; + float alpha; + float beta; + float gamma; + + inipp::get_value(iniParser.sections[section], "a", a); + inipp::get_value(iniParser.sections[section], "b", b); + inipp::get_value(iniParser.sections[section], "c", c); + inipp::get_value(iniParser.sections[section], "alpha", alpha); + inipp::get_value(iniParser.sections[section], "beta", beta); + inipp::get_value(iniParser.sections[section], "gamma", gamma); + + return {a, b, c, alpha, beta, gamma}; +} + +void ParseAndStoreHeaderEntry(inipp::Ini& iniParser, const std::string& section, const std::string& key, std::map& m_HeaderMap) +{ + EbsdHeaderEntry::Pointer p1 = m_HeaderMap[key]; + auto value = getFieldValue(iniParser, section, key); + p1->parseValue(value); +} + +std::vector CreateFieldParsers(const std::string& filename) +{ + try + { + if(!std::filesystem::exists(filename)) + { + return {}; + } + } catch(std::exception& e) + { + return {}; + } + // Open the file + std::ifstream is(filename); + + // Create an INI parser object and parse the file + inipp::Ini ini; + ini.parse(is); + + size_t fieldCount = getFieldValue(ini, "Fields", "Count"); + // std::cout << "Field Count: " << fieldCount << std::endl; + std::vector fieldOrder(fieldCount + 1); + + // Insert the Phase as the first field. I guess? + fieldOrder[0] = {k_FieldDefinitions[0], nullptr, 0}; + size_t currentOffset = 1; + size_t edxCurrentOffset = 1; + + for(int i = 1; i <= fieldCount; i++) + { + std::stringstream fieldNameStrm; + fieldNameStrm << "Field" << i; + auto fieldIndex = getFieldValue(ini, "Fields", fieldNameStrm.str()); + if(fieldIndex < 13) + { + CrcFieldDefinition fieldDefinition = k_FieldDefinitions[fieldIndex]; + fieldOrder[i] = {fieldDefinition, nullptr, currentOffset}; + currentOffset += fieldDefinition.ByteSize; + } + // Handle EDX (XRay EDS) data which seems to have field index values > 100. + if(fieldIndex > 100) + { + fieldNameStrm.str(""); // Clear the stringstream buffer + fieldNameStrm << "Window" << edxCurrentOffset; + auto elementName = getFieldValue(ini, "EDX Windows", fieldNameStrm.str()); + fieldOrder[i].FieldDefinition = {4, elementName, EbsdLib::NumericTypes::Type::Float}; + currentOffset += 4; + edxCurrentOffset++; + } + } + + // Handle EDX Data (XRay) + // According to other open source projects, these seem to always be 32 bit float data. + // fieldCount = getFieldValue(ini, "EDX Windows", "Count"); + // for(int i = 1; i <= fieldCount; i++) + // { + // std::stringstream fieldNameStrm; + // fieldNameStrm << "Window" << i; + // auto elementName = getFieldValue(ini, "EDX Windows", fieldNameStrm.str()); + // CrcFieldDefinition fieldDefinition = {4, elementName, EbsdLib::NumericTypes::Type::Float}; + // fieldOrder.push_back({fieldDefinition, nullptr, currentOffset}); + // currentOffset += fieldDefinition.ByteSize; + // } + + return fieldOrder; +} + +} // namespace + +// ----------------------------------------------------------------------------- +CprReader::CprReader() +{ + + // Initialize the map of header key to header value + m_HeaderMap[EbsdLib::Ctf::ChannelTextFile] = CtfStringHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::ChannelTextFile); + m_HeaderMap[EbsdLib::Ctf::Prj] = CtfStringHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::Prj); + m_HeaderMap[EbsdLib::Ctf::Author] = CtfStringHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::Author); + m_HeaderMap[EbsdLib::Ctf::JobMode] = CtfStringHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::JobMode); + + m_HeaderMap[EbsdLib::Ctf::xCells] = CtfHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::XCells); + m_HeaderMap[EbsdLib::Ctf::yCells] = CtfHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::YCells); + // m_HeaderMap[EbsdLib::Ctf::ZCells] = CtfHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::ZCells); + m_HeaderMap[EbsdLib::Ctf::GridDistX] = CtfHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::XStep); + m_HeaderMap[EbsdLib::Ctf::GridDistY] = CtfHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::YStep); + // m_HeaderMap[EbsdLib::Ctf::ZStep] = CtfHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::ZStep); + // m_HeaderMap[EbsdLib::Ctf::AcqE1] = CtfHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::AcqE1); + // m_HeaderMap[EbsdLib::Ctf::AcqE2] = CtfHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::AcqE2); + // m_HeaderMap[EbsdLib::Ctf::AcqE3] = CtfHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::AcqE3); + // m_HeaderMap[EbsdLib::Ctf::Euler] = CtfStringHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::Euler); + + m_HeaderMap[EbsdLib::Ctf::Magnification] = CtfHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::Mag); + m_HeaderMap[EbsdLib::Ctf::Coverage] = CtfHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::Coverage); + m_HeaderMap[EbsdLib::Ctf::Device] = CtfHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::Device); + m_HeaderMap[EbsdLib::Ctf::kV] = CtfHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::kV); + m_HeaderMap[EbsdLib::Ctf::TiltAngle] = CtfHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::TiltAngle); + m_HeaderMap[EbsdLib::Ctf::TiltAxis] = CtfHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::TiltAxis); + m_HeaderMap[EbsdLib::Ctf::NumPhases] = CtfHeaderEntry::NewEbsdHeaderEntry(EbsdLib::Ctf::NumPhases); + + setXCells(0); + setYCells(0); + // setZCells(1); +} + +// ----------------------------------------------------------------------------- +CprReader::~CprReader() = default; + +// ----------------------------------------------------------------------------- +void* CprReader::getPointerByName(const std::string& featureName) +{ + void* ptr = nullptr; + if(m_NamePointerMap.find(featureName) != m_NamePointerMap.end()) + { + ptr = m_NamePointerMap[featureName]->getVoidPointer(); + } + return ptr; +} + +std::vector CprReader::getPointerNames() const +{ + std::vector names; + for(const auto& entry : m_NamePointerMap) + { + names.push_back(entry.first); + } + return names; +} + +std::map CprReader::getPointerTypes() const +{ + std::map types; + for(const auto& entry : m_NamePointerMap) + { + types[entry.first] = entry.second->getNumericType(); + } + return types; +} + +// ----------------------------------------------------------------------------- +EbsdLib::NumericTypes::Type CprReader::getPointerType(const std::string& featureName) +{ + // std::cout << "featureName: " << featureName << std::endl; + if(featureName == EbsdLib::Ctf::ReliabilityIndex) + { + return EbsdLib::NumericTypes::Type::Int32; + } + if(featureName == EbsdLib::Ctf::Phase) + { + return EbsdLib::NumericTypes::Type::UInt8; + } + if(featureName == EbsdLib::Ctf::X) + { + return EbsdLib::NumericTypes::Type::Float; + } + if(featureName == EbsdLib::Ctf::Y) + { + return EbsdLib::NumericTypes::Type::Float; + } + if(featureName == EbsdLib::Ctf::Z) + { + return EbsdLib::NumericTypes::Type::Float; + } + if(featureName == EbsdLib::Ctf::Bands) + { + return EbsdLib::NumericTypes::Type::UInt8; + } + if(featureName == EbsdLib::Ctf::Error) + { + return EbsdLib::NumericTypes::Type::UInt8; + } + if(featureName == EbsdLib::Ctf::phi1) + { + return EbsdLib::NumericTypes::Type::Float; + } + if(featureName == EbsdLib::Ctf::Phi) + { + return EbsdLib::NumericTypes::Type::Float; + } + if(featureName == EbsdLib::Ctf::phi2) + { + return EbsdLib::NumericTypes::Type::Float; + } + if(featureName == EbsdLib::Ctf::MAD) + { + return EbsdLib::NumericTypes::Type::Float; + } + if(featureName == EbsdLib::Ctf::BC) + { + return EbsdLib::NumericTypes::Type::UInt8; + } + if(featureName == EbsdLib::Ctf::BS) + { + return EbsdLib::NumericTypes::Type::UInt8; + } + if(featureName == EbsdLib::Ctf::GrainIndex) + { + return EbsdLib::NumericTypes::Type::Int32; + } + if(featureName == EbsdLib::Ctf::GrainRandomColourR) + { + return EbsdLib::NumericTypes::Type::Int32; + } + if(featureName == EbsdLib::Ctf::GrainRandomColourG) + { + return EbsdLib::NumericTypes::Type::Int32; + } + if(featureName == EbsdLib::Ctf::GrainRandomColourB) + { + return EbsdLib::NumericTypes::Type::Int32; + } + // std::cout << "THIS IS NOT GOOD. Feature name: " << featureName << " was not found in the list" << std::endl; + return EbsdLib::NumericTypes::Type::UnknownNumType; +} + +// +// ----------------------------------------------------------------------------- +int CprReader::getTypeSize(const std::string& featureName) +{ + if(featureName == EbsdLib::Ctf::ReliabilityIndex) + { + return 4; + } + if(featureName == EbsdLib::Ctf::Phase) + { + return 1; + } + if(featureName == EbsdLib::Ctf::X) + { + return 4; + } + if(featureName == EbsdLib::Ctf::Y) + { + return 4; + } + if(featureName == EbsdLib::Ctf::Z) + { + return 4; + } + if(featureName == EbsdLib::Ctf::Bands) + { + return 1; + } + if(featureName == EbsdLib::Ctf::Error) + { + return 1; + } + if(featureName == EbsdLib::Ctf::phi1) + { + return 4; + } + if(featureName == EbsdLib::Ctf::Phi) + { + return 4; + } + if(featureName == EbsdLib::Ctf::phi2) + { + return 4; + } + if(featureName == EbsdLib::Ctf::MAD) + { + return 4; + } + if(featureName == EbsdLib::Ctf::BC) + { + return 1; + } + if(featureName == EbsdLib::Ctf::BS) + { + return 1; + } + if(featureName == EbsdLib::Ctf::GrainIndex) + { + return 4; + } + if(featureName == EbsdLib::Ctf::GrainRandomColourR) + { + return 4; + } + if(featureName == EbsdLib::Ctf::GrainRandomColourG) + { + return 4; + } + if(featureName == EbsdLib::Ctf::GrainRandomColourB) + { + return 4; + } + return 0; +} + +// ----------------------------------------------------------------------------- +int CprReader::readHeaderOnly() +{ + int err = 0; + + try + { + if(!std::filesystem::exists(getFileName())) + { + return -11; + } + } catch(std::exception& e) + { + return -10; + } + // Open the file + std::ifstream is(getFileName()); + + // Create an INI parser object and parse the file + inipp::Ini ini; + ini.parse(is); + + // Extract out the Phases + { + const std::string sectionName("Phases"); + int phaseCount = -1; + inipp::get_value(ini.sections[sectionName], "Count", phaseCount); + EbsdHeaderEntry::Pointer p1 = m_HeaderMap[EbsdLib::Ctf::NumPhases]; + auto value = getFieldValue(ini, sectionName, EbsdLib::Ctf::Count); + p1->parseValue(value); + // std::cout << "Phase Count: " << phaseCount << std::endl; + for(int phaseIndex = 1; phaseIndex <= phaseCount; phaseIndex++) + { + std::stringstream sectionNameStrm; + sectionNameStrm << "Phase" << phaseIndex; + + CtfPhase::Pointer phase = CtfPhase::New(); + phase->setPhaseIndex(phaseIndex); + + // Phase Name + phase->setPhaseName(getFieldValue(ini, sectionNameStrm.str(), "StructureName")); + + // Lattice Constants + phase->setLatticeConstants(getLatticeConstants(ini, sectionNameStrm.str())); + + // Laue Group + phase->setLaueGroup(static_cast(getFieldValue(ini, sectionNameStrm.str(), "LaueGroup"))); + + // Comment + phase->setComment(getFieldValue(ini, sectionNameStrm.str(), "Reference")); + + // Space Group + phase->setSpaceGroup(getFieldValue(ini, sectionNameStrm.str(), "SpaceGroup")); + + // internal 1 + phase->setInternal1(getFieldValue(ini, sectionNameStrm.str(), "ID1")); + + // Internal 2 + phase->setInternal2(getFieldValue(ini, sectionNameStrm.str(), "ID2")); + + m_PhaseVector.push_back(phase); + } + } + + { + const std::string sectionName("Job"); + ParseAndStoreHeaderEntry(ini, sectionName, EbsdLib::Ctf::Magnification, m_HeaderMap); + ParseAndStoreHeaderEntry(ini, sectionName, EbsdLib::Ctf::Coverage, m_HeaderMap); + ParseAndStoreHeaderEntry(ini, sectionName, EbsdLib::Ctf::Device, m_HeaderMap); + ParseAndStoreHeaderEntry(ini, sectionName, EbsdLib::Ctf::kV, m_HeaderMap); + ParseAndStoreHeaderEntry(ini, sectionName, EbsdLib::Ctf::TiltAngle, m_HeaderMap); + ParseAndStoreHeaderEntry(ini, sectionName, EbsdLib::Ctf::TiltAxis, m_HeaderMap); + + ParseAndStoreHeaderEntry(ini, sectionName, EbsdLib::Ctf::GridDistX, m_HeaderMap); + ParseAndStoreHeaderEntry(ini, sectionName, EbsdLib::Ctf::GridDistY, m_HeaderMap); + ParseAndStoreHeaderEntry(ini, sectionName, EbsdLib::Ctf::xCells, m_HeaderMap); + ParseAndStoreHeaderEntry(ini, sectionName, EbsdLib::Ctf::yCells, m_HeaderMap); + + setNumberOfElements(getXCells() * getYCells()); + } + + { + const std::string sectionName("General"); + ParseAndStoreHeaderEntry(ini, sectionName, EbsdLib::Ctf::Author, m_HeaderMap); + ParseAndStoreHeaderEntry(ini, sectionName, EbsdLib::Ctf::JobMode, m_HeaderMap); + } + + { + const std::string sectionName("General"); + ParseAndStoreHeaderEntry(ini, sectionName, EbsdLib::Ctf::Author, m_HeaderMap); + ParseAndStoreHeaderEntry(ini, sectionName, EbsdLib::Ctf::JobMode, m_HeaderMap); + } + + return err; +} + +// ----------------------------------------------------------------------------- +int CprReader::readFile() +{ + + int error = readHeaderOnly(); + + if(error < 0) + { + setErrorMessage("Reading header Failed"); + return error; + } + if(getXStep() == 0.0 || getYStep() == 0.0f) + { + setErrorMessage("Either the X Step or Y Step was Zero (0.0) which is NOT allowed. Please update the CTF file header with appropriate values."); + return -102; + } + + if(getXCells() == 0 || getYCells() == 0) + { + setErrorMessage("Either the X Cells or Y Cells was Zero (0) which is NOT allowed. Please update the CTF file header with appropriate values."); + return -103; + } + + std::vector scanPointParsers = CreateFieldParsers(getFileName()); + if(scanPointParsers.empty()) + { + return -100; + } + + size_t bufferSize = 0; + // Allocate all memory + size_t totalScanPoints = getNumberOfElements(); + bool didAllocate = false; + int index = 0; + for(auto& parser : scanPointParsers) + { + bufferSize += parser.FieldDefinition.ByteSize; + if(m_NamePointerMap.find(parser.FieldDefinition.FieldName) != m_NamePointerMap.end()) + { + std::stringstream ss; + ss << "Column Header '" << parser.FieldDefinition.FieldName << "' has been found multiple times in the Header Row. Please check the CTF file for mistakes."; + setErrorMessage(ss.str()); + return -110; + } + if(EbsdLib::NumericTypes::Type::UInt8 == parser.FieldDefinition.numericType) + { + UInt8Parser::Pointer dparser = UInt8Parser::New(nullptr, totalScanPoints, parser.FieldDefinition.FieldName, index); + didAllocate = dparser->allocateArray(totalScanPoints); + // Q_ASSERT_X(dparser->getVoidPointer() != nullptr, __FILE__, "Could not allocate memory for Integer data in CTF File."); + if(didAllocate) + { + ::memset(dparser->getVoidPointer(), 0xAB, sizeof(int32_t) * totalScanPoints); + m_NamePointerMap[parser.FieldDefinition.FieldName] = dparser; + parser.destinationPtr = reinterpret_cast(dparser->getVoidPointer()); + } + } + else if(EbsdLib::NumericTypes::Type::Float == parser.FieldDefinition.numericType) + { + FloatParser::Pointer dparser = FloatParser::New(nullptr, totalScanPoints, parser.FieldDefinition.FieldName, index); + didAllocate = dparser->allocateArray(totalScanPoints); + // Q_ASSERT_X(dparser->getVoidPointer() != nullptr, __FILE__, "Could not allocate memory for Integer data in CTF File."); + if(didAllocate) + { + ::memset(dparser->getVoidPointer(), 0xAB, sizeof(float) * totalScanPoints); + m_NamePointerMap[parser.FieldDefinition.FieldName] = dparser; + parser.destinationPtr = reinterpret_cast(dparser->getVoidPointer()); + } + } + else if(EbsdLib::NumericTypes::Type::Int32 == parser.FieldDefinition.numericType) + { + Int32Parser::Pointer dparser = Int32Parser::New(nullptr, totalScanPoints, parser.FieldDefinition.FieldName, index); + didAllocate = dparser->allocateArray(totalScanPoints); + // Q_ASSERT_X(dparser->getVoidPointer() != nullptr, __FILE__, "Could not allocate memory for Integer data in CTF File."); + if(didAllocate) + { + ::memset(dparser->getVoidPointer(), 0xAB, sizeof(float) * totalScanPoints); + m_NamePointerMap[parser.FieldDefinition.FieldName] = dparser; + parser.destinationPtr = reinterpret_cast(dparser->getVoidPointer()); + } + } + else + { + std::stringstream ss; + ss << "Column Header '" << parser.FieldDefinition.FieldName << "' is not a recognized column for this CRC Files. Please recheck your .cpr file and report this error to the DREAM3D developers."; + setErrorMessage(ss.str()); + return -107; + } + + if(!didAllocate) + { + setErrorCode(-106); + std::stringstream ss; + ss << "The CTF reader could not allocate memory for the data. Check the header for the number of X, Y and Z Cells."; + ss << "\n X Cells: " << getXCells(); + ss << "\n Y Cells: " << getYCells(); + // ss << "\n Z Cells: " << getzCells(); + ss << "\n Total Scan Points: " << totalScanPoints; + setErrorMessage(ss.str()); + return -106; // Could not allocate the memory + } + index++; + } + + // Make a buffer + std::vector buffer(bufferSize, 0); + + fs::path filePath(getFileName()); + // Replace the extension with ".crc" + fs::path crcPath = filePath.replace_extension(".crc"); + // Open the file for reading + FILE* inputFilePtr = std::fopen(crcPath.string().c_str(), "rb"); + if(inputFilePtr == nullptr) + { + return -1000; + } + + // Read through the file + for(size_t idx = 0; idx < totalScanPoints; idx++) + { + size_t elementsRead = std::fread(buffer.data(), 1, bufferSize, inputFilePtr); + if(elementsRead != buffer.size()) + { + fclose(inputFilePtr); + std::stringstream ss; + ss << "Error when reading the .crc file at file offset " << (idx * bufferSize) << ". We should have read " << bufferSize << " bytes but we only read " << elementsRead << " bytes."; + setErrorMessage(ss.str()); + setErrorCode(-1666); + return -1666; + } + for(const auto& parser : scanPointParsers) + { + parser.parse(buffer.data(), idx); + } + } + fclose(inputFilePtr); + + return error; +} + +#if 0 +#define PRINT_HTML_TABLE_ROW(p) \ + std::cout << "\n " << p->getKey() << "\n " << p->getHDFType() << "\n"; \ + std::cout << " Contains value for the header entry " << p->getKey() << "\n" << std::endl; +#else +#define PRINT_HTML_TABLE_ROW(p) +#endif + +// ----------------------------------------------------------------------------- +int CprReader::getXDimension() +{ + return getXCells(); +} + +// ----------------------------------------------------------------------------- +void CprReader::setXDimension(int xdim) +{ + setXCells(xdim); +} + +// ----------------------------------------------------------------------------- +int CprReader::getYDimension() +{ + return getYCells(); +} + +// ----------------------------------------------------------------------------- +void CprReader::setYDimension(int ydim) +{ + setYCells(ydim); +} + +// ----------------------------------------------------------------------------- +std::vector CprReader::getColumnNames() +{ + std::vector keys; + keys.reserve(m_NamePointerMap.size()); + for(const auto& entry : m_NamePointerMap) + { + keys.push_back(entry.first); + } + + return keys; +} + +#define CTF_PRINT_QSTRING(var, out) out << #var << ": " << get##var() << std::endl; + +#define CTF_PRINT_HEADER_VALUE(var, out) out << #var << ": " << get##var() << std::endl; + +// ----------------------------------------------------------------------------- +void CprReader::printHeader(std::ostream& out) +{ + std::cout << "-------------------- CprReader Header Values --------------------" << std::endl; + CTF_PRINT_QSTRING(Channel, out) + CTF_PRINT_QSTRING(Prj, out) + CTF_PRINT_QSTRING(Author, out) + CTF_PRINT_QSTRING(JobMode, out) + CTF_PRINT_HEADER_VALUE(XCells, out) + CTF_PRINT_HEADER_VALUE(YCells, out) + CTF_PRINT_HEADER_VALUE(XStep, out) + CTF_PRINT_HEADER_VALUE(YStep, out) + // CTF_PRINT_HEADER_VALUE(AcqE1, out); + // CTF_PRINT_HEADER_VALUE(AcqE2, out); + // CTF_PRINT_HEADER_VALUE(AcqE3, out); + // CTF_PRINT_QSTRING(Euler, out); + CTF_PRINT_HEADER_VALUE(Mag, out) + CTF_PRINT_HEADER_VALUE(Coverage, out) + CTF_PRINT_HEADER_VALUE(Device, out) + CTF_PRINT_HEADER_VALUE(KV, out) + CTF_PRINT_HEADER_VALUE(TiltAngle, out) + CTF_PRINT_HEADER_VALUE(TiltAxis, out) + CTF_PRINT_HEADER_VALUE(NumPhases, out) + int nPhases = getNumPhases(); + for(int p = 0; p < nPhases; ++p) + { + out << "### Phase " << p << std::endl; + m_PhaseVector[p]->printSelf(out); + } + + std::cout << "----------------------------------------" << std::endl; +} + +// ----------------------------------------------------------------------------- +std::string CprReader::getNameOfClass() const +{ + return {"CprReader"}; +} + +// ----------------------------------------------------------------------------- +std::string CprReader::ClassName() +{ + return {"CprReader"}; +} diff --git a/Source/EbsdLib/IO/HKL/CprReader.h b/Source/EbsdLib/IO/HKL/CprReader.h new file mode 100644 index 0000000..e9654c2 --- /dev/null +++ b/Source/EbsdLib/IO/HKL/CprReader.h @@ -0,0 +1,149 @@ +// +// Created by Michael Jackson on 10/22/24. +// +#pragma once + +#include +#include +#include +#include +#include + +#include "CtfConstants.h" +#include "CtfHeaderEntry.h" +#include "CtfPhase.h" +#include "DataParser.hpp" +#include "EbsdLib/Core/EbsdLibConstants.h" +#include "EbsdLib/Core/EbsdSetGetMacros.h" +#include "EbsdLib/EbsdLib.h" +#include "EbsdLib/IO/EbsdReader.h" + +#define CPR_READER_PTR_PROP(name, var, type) \ + type* get##name##Pointer() \ + { \ + return static_cast(getPointerByName(#var)); \ + } +/** + * @brief This class can parse an Oxford Instruments .cpr/.crc file combination. + * + * This class should successfully be able to read files even all phase types and including EDX data. + */ +class EbsdLib_EXPORT CprReader : public EbsdReader +{ +public: + CprReader(); + ~CprReader() override; + + /** + * @brief Returns the name of the class for CtfReader + */ + std::string getNameOfClass() const override; + + /** + * @brief Returns the name of the class for CtfReader + */ + static std::string ClassName(); + + using CtfIntHeaderType = CtfHeaderEntry; + using CtfFloatHeaderType = CtfHeaderEntry; + EBSDHEADER_INSTANCE_PROPERTY(CtfStringHeaderEntry, std::string, Channel, EbsdLib::Ctf::ChannelTextFile) + EBSDHEADER_INSTANCE_PROPERTY(CtfStringHeaderEntry, std::string, Prj, EbsdLib::Ctf::Prj) + EBSDHEADER_INSTANCE_PROPERTY(CtfStringHeaderEntry, std::string, Author, EbsdLib::Ctf::Author) + EBSDHEADER_INSTANCE_PROPERTY(CtfStringHeaderEntry, std::string, JobMode, EbsdLib::Ctf::JobMode) + + EBSDHEADER_INSTANCE_PROPERTY(CtfIntHeaderType, int, YCells, EbsdLib::Ctf::xCells) + EBSDHEADER_INSTANCE_PROPERTY(CtfIntHeaderType, int, XCells, EbsdLib::Ctf::yCells) + // EBSDHEADER_INSTANCE_PROPERTY(CtfIntHeaderType, int, ZCells, EbsdLib::Ctf::ZCells) + EBSDHEADER_INSTANCE_PROPERTY(CtfFloatHeaderType, float, XStep, EbsdLib::Ctf::GridDistX) + EBSDHEADER_INSTANCE_PROPERTY(CtfFloatHeaderType, float, YStep, EbsdLib::Ctf::GridDistY) + // EBSDHEADER_INSTANCE_PROPERTY(CtfFloatHeaderType, float, ZStep, EbsdLib::Ctf::ZStep) + // EBSDHEADER_INSTANCE_PROPERTY(CtfFloatHeaderType, float, AcqE1, EbsdLib::Ctf::AcqE1) + // EBSDHEADER_INSTANCE_PROPERTY(CtfFloatHeaderType, float, AcqE2, EbsdLib::Ctf::AcqE2) + // EBSDHEADER_INSTANCE_PROPERTY(CtfFloatHeaderType, float, AcqE3, EbsdLib::Ctf::AcqE3) + // EBSDHEADER_INSTANCE_PROPERTY(CtfStringHeaderEntry, std::string, Euler, EbsdLib::Ctf::Euler) + EBSDHEADER_INSTANCE_PROPERTY(CtfIntHeaderType, int, Mag, EbsdLib::Ctf::Magnification) + EBSDHEADER_INSTANCE_PROPERTY(CtfIntHeaderType, int, Coverage, EbsdLib::Ctf::Coverage) + EBSDHEADER_INSTANCE_PROPERTY(CtfIntHeaderType, int, Device, EbsdLib::Ctf::Device) + EBSDHEADER_INSTANCE_PROPERTY(CtfIntHeaderType, int, KV, EbsdLib::Ctf::kV) + EBSDHEADER_INSTANCE_PROPERTY(CtfFloatHeaderType, float, TiltAngle, EbsdLib::Ctf::TiltAngle) + EBSDHEADER_INSTANCE_PROPERTY(CtfFloatHeaderType, float, TiltAxis, EbsdLib::Ctf::TiltAxis) + EBSDHEADER_INSTANCE_PROPERTY(CtfIntHeaderType, int, NumPhases, EbsdLib::Ctf::NumPhases) + EBSD_INSTANCE_PROPERTY(std::vector, PhaseVector) + + CPR_READER_PTR_PROP(Phase, Phase, uint8_t) + CPR_READER_PTR_PROP(X, X, float) + CPR_READER_PTR_PROP(Y, Y, float) + // CPR_READER_PTR_PROP(Z, Z, float) + CPR_READER_PTR_PROP(BandCount, Bands, uint8_t) + CPR_READER_PTR_PROP(Error, Error, uint8_t) + CPR_READER_PTR_PROP(Euler1, phi1, float) + CPR_READER_PTR_PROP(Euler2, Phi, float) + CPR_READER_PTR_PROP(Euler3, phi2, float) + CPR_READER_PTR_PROP(MeanAngularDeviation, MAD, float) + CPR_READER_PTR_PROP(BandContrast, BC, uint8_t) + CPR_READER_PTR_PROP(BandSlope, BS, uint8_t) + CPR_READER_PTR_PROP(ReliabilityIndex, ReliabilityIndex, int32_t) + + /** + * @brief Returns the pointer to the data for a given feature + * @param featureName The name of the feature to return the pointer to. + */ + void* getPointerByName(const std::string& featureName) override; + + /** + * @brief Returns the string names of all the arrays that were allocated during the reading of the file + * @return + */ + std::vector getPointerNames() const; + + /** + * @brief Returns the types of data that each array holds. + * @return + */ + std::map getPointerTypes() const; + + /** + * @brief Returns an enumeration value that depicts the numerical + * primitive type that the data is stored as (Int, Float, etc). + * @param featureName The name of the feature. + */ + EbsdLib::NumericTypes::Type getPointerType(const std::string& featureName) override; + + int getTypeSize(const std::string& featureName); + + // DataParser::Pointer getParser(const std::string& featureName, void* ptr, size_t size); + + std::vector getColumnNames(); + + /** + * @brief Reads the complete HKL .ctf file. + * @return 1 on success + */ + int readFile() override; + + /** + * @brief Reads ONLY the header portion of the HKL .ctf file + * @return 1 on success + */ + int readHeaderOnly() override; + + // void readOnlySliceIndex(int slice); + + int getXDimension() override; + void setXDimension(int xdim) override; + int getYDimension() override; + void setYDimension(int ydim) override; + + void printHeader(std::ostream& out); + +private: + int m_SingleSliceRead = -1; + + std::map m_NamePointerMap; + +public: + CprReader(const CprReader&) = delete; // Copy Constructor Not Implemented + CprReader(CprReader&&) = delete; // Move Constructor Not Implemented + CprReader& operator=(const CprReader&) = delete; // Copy Assignment Not Implemented + CprReader& operator=(CprReader&&) = delete; // Move Assignment Not Implemented +}; diff --git a/Source/EbsdLib/IO/HKL/CtfConstants.h b/Source/EbsdLib/IO/HKL/CtfConstants.h index be33bbf..bf09021 100644 --- a/Source/EbsdLib/IO/HKL/CtfConstants.h +++ b/Source/EbsdLib/IO/HKL/CtfConstants.h @@ -132,6 +132,13 @@ DECLARE_STRING_CONST(Author) DECLARE_STRING_CONST(JobMode) DECLARE_STRING_CONST(XCells) DECLARE_STRING_CONST(YCells) + +DECLARE_STRING_CONST(GridDistX) +DECLARE_STRING_CONST(GridDistY) +DECLARE_STRING_CONST(xCells) +DECLARE_STRING_CONST(yCells) +DECLARE_STRING_CONST(Count) + DECLARE_STRING_CONST(ZCells) DECLARE_STRING_CONST(XStep) DECLARE_STRING_CONST(YStep) @@ -141,9 +148,11 @@ DECLARE_STRING_CONST(AcqE2) DECLARE_STRING_CONST(AcqE3) DECLARE_STRING_CONST(Euler) DECLARE_STRING_CONST(Mag) +DECLARE_STRING_CONST(Magnification) DECLARE_STRING_CONST(Coverage) DECLARE_STRING_CONST(Device) DECLARE_STRING_CONST(KV) +DECLARE_STRING_CONST(kV) DECLARE_STRING_CONST(TiltAngle) DECLARE_STRING_CONST(TiltAxis) const std::string NumPhases("Phases"); @@ -161,6 +170,7 @@ DECLARE_STRING_CONST(Comment) /* ******************************************************************************************************** */ // These are the names of the Data Columns // DO NOT CHANGE ANY OF THESE CONSTANTS, IT WILL MESS UP THE CTF Parser +DECLARE_STRING_CONST(ReliabilityIndex) DECLARE_STRING_CONST(Phase) DECLARE_STRING_CONST(X) DECLARE_STRING_CONST(Y) @@ -170,6 +180,11 @@ DECLARE_STRING_CONST(Error) DECLARE_STRING_CONST(Euler1) DECLARE_STRING_CONST(Euler2) DECLARE_STRING_CONST(Euler3) + +DECLARE_STRING_CONST(phi1) +DECLARE_STRING_CONST(Phi) +DECLARE_STRING_CONST(phi2) + DECLARE_STRING_CONST(MAD) DECLARE_STRING_CONST(BC) // BC DECLARE_STRING_CONST(BS) // BS diff --git a/Source/EbsdLib/IO/HKL/CtfPhase.cpp b/Source/EbsdLib/IO/HKL/CtfPhase.cpp index 1758a5b..666cf5e 100644 --- a/Source/EbsdLib/IO/HKL/CtfPhase.cpp +++ b/Source/EbsdLib/IO/HKL/CtfPhase.cpp @@ -35,6 +35,7 @@ #include "CtfPhase.h" +#include "EbsdLib/LaueOps/LaueOps.h" #include "EbsdLib/Utilities/EbsdStringUtils.hpp" // ----------------------------------------------------------------------------- @@ -119,7 +120,7 @@ void CtfPhase::printSelf(std::ostream& stream) << m_LatticeConstants[4] << ", " << m_LatticeConstants[5] << std::endl; stream << EbsdLib::Ctf::PhaseName << " " << m_PhaseName << std::endl; stream << EbsdLib::Ctf::LaueGroup << " " << m_LaueGroup << std::endl; - stream << EbsdLib::Ctf::SpaceGroup << " " << m_SpaceGroup << std::endl; + stream << EbsdLib::Ctf::SpaceGroup << " " << m_SpaceGroup << " " << LaueOps::GetOrientationOpsFromSpaceGroupNumber(m_SpaceGroup)->getSymmetryName() << std::endl; stream << EbsdLib::Ctf::Internal1 << " " << m_Internal1 << std::endl; stream << EbsdLib::Ctf::Internal2 << " " << m_Internal2 << std::endl; stream << EbsdLib::Ctf::Comment << " " << m_Comment << std::endl; @@ -128,7 +129,7 @@ void CtfPhase::printSelf(std::ostream& stream) // ----------------------------------------------------------------------------- // // ----------------------------------------------------------------------------- -unsigned int CtfPhase::determineLaueGroup() +unsigned int CtfPhase::determineOrientationOpsIndex() { EbsdLib::Ctf::LaueGroupTable symmetry = getLaueGroup(); diff --git a/Source/EbsdLib/IO/HKL/CtfPhase.h b/Source/EbsdLib/IO/HKL/CtfPhase.h index b759716..6b52d0f 100644 --- a/Source/EbsdLib/IO/HKL/CtfPhase.h +++ b/Source/EbsdLib/IO/HKL/CtfPhase.h @@ -132,9 +132,9 @@ class EbsdLib_EXPORT CtfPhase void printSelf(std::ostream& stream); /** - * @brief Returns the type of crystal structure for this phase. + * @brief Returns the index of the appropriate LaueOps class */ - unsigned int determineLaueGroup(); + unsigned int determineOrientationOpsIndex(); std::string getMaterialName(); diff --git a/Source/EbsdLib/IO/HKL/CtfReader.h b/Source/EbsdLib/IO/HKL/CtfReader.h index 3a4ce7a..248564e 100644 --- a/Source/EbsdLib/IO/HKL/CtfReader.h +++ b/Source/EbsdLib/IO/HKL/CtfReader.h @@ -73,7 +73,8 @@ class EbsdLib_EXPORT CtfReader : public EbsdReader /** * @brief Returns the name of the class for CtfReader */ - std::string getNameOfClass() const; + std::string getNameOfClass() const override; + /** * @brief Returns the name of the class for CtfReader */ diff --git a/Source/EbsdLib/IO/HKL/DataParser.hpp b/Source/EbsdLib/IO/HKL/DataParser.hpp index aa72c2b..e89e04c 100644 --- a/Source/EbsdLib/IO/HKL/DataParser.hpp +++ b/Source/EbsdLib/IO/HKL/DataParser.hpp @@ -101,6 +101,8 @@ class DataParser { } + virtual EbsdLib::NumericTypes::Type getNumericType() const = 0; + protected: DataParser() : m_ManageMemory(false) @@ -147,6 +149,11 @@ class UInt8Parser : public DataParser return std::string("UInt8Parser"); } + EbsdLib::NumericTypes::Type getNumericType() const override + { + return EbsdLib::NumericTypes::Type::UInt8; + } + int32_t IsA() const override { return 1; @@ -253,6 +260,11 @@ class Int32Parser : public DataParser return std::string("Int32Parser"); } + EbsdLib::NumericTypes::Type getNumericType() const override + { + return EbsdLib::NumericTypes::Type::Int32; + } + int32_t IsA() const override { return 1; @@ -357,6 +369,11 @@ class FloatParser : public DataParser return std::string("FloatParser"); } + EbsdLib::NumericTypes::Type getNumericType() const override + { + return EbsdLib::NumericTypes::Type::Float; + } + int32_t IsA() const override { return 2; diff --git a/Source/EbsdLib/IO/HKL/H5CtfReader.h b/Source/EbsdLib/IO/HKL/H5CtfReader.h index 0c654c3..54e8d71 100644 --- a/Source/EbsdLib/IO/HKL/H5CtfReader.h +++ b/Source/EbsdLib/IO/HKL/H5CtfReader.h @@ -86,7 +86,7 @@ class EbsdLib_EXPORT H5CtfReader : public CtfReader /** * @brief Returns the name of the class for H5CtfReader */ - std::string getNameOfClass() const; + std::string getNameOfClass() const override; /** * @brief Returns the name of the class for H5CtfReader */ diff --git a/Source/EbsdLib/IO/HKL/H5OINAReader.h b/Source/EbsdLib/IO/HKL/H5OINAReader.h index 8d1b368..c6ef77d 100644 --- a/Source/EbsdLib/IO/HKL/H5OINAReader.h +++ b/Source/EbsdLib/IO/HKL/H5OINAReader.h @@ -141,7 +141,7 @@ class EbsdLib_EXPORT H5OINAReader : public EbsdReader /** * @brief Returns the name of the class for H5OINAReader */ - std::string getNameOfClass() const; + std::string getNameOfClass() const override; /** * @brief Returns the name of the class for H5OINAReader diff --git a/Source/EbsdLib/IO/HKL/SourceList.cmake b/Source/EbsdLib/IO/HKL/SourceList.cmake index a971a64..7199766 100644 --- a/Source/EbsdLib/IO/HKL/SourceList.cmake +++ b/Source/EbsdLib/IO/HKL/SourceList.cmake @@ -3,6 +3,7 @@ set(HKL_SRCS ${EbsdLibProj_SOURCE_DIR}/Source/EbsdLib/IO/HKL/CtfReader.cpp ${EbsdLibProj_SOURCE_DIR}/Source/EbsdLib/IO/HKL/CtfPhase.cpp ${EbsdLibProj_SOURCE_DIR}/Source/EbsdLib/IO/HKL/CtfFields.cpp + ${EbsdLibProj_SOURCE_DIR}/Source/EbsdLib/IO/HKL/CprReader.cpp ) set(HKL_HDRS @@ -26,6 +27,7 @@ if(EbsdLib_ENABLE_HDF5) ${EbsdLibProj_SOURCE_DIR}/Source/EbsdLib/IO/HKL/H5CtfReader.h ${EbsdLibProj_SOURCE_DIR}/Source/EbsdLib/IO/HKL/H5CtfVolumeReader.h ${EbsdLibProj_SOURCE_DIR}/Source/EbsdLib/IO/HKL/H5OINAReader.h + ${EbsdLibProj_SOURCE_DIR}/Source/EbsdLib/IO/HKL/CprReader.h ) endif() diff --git a/Source/EbsdLib/IO/TSL/AngPhase.cpp b/Source/EbsdLib/IO/TSL/AngPhase.cpp index 1074cfa..446df27 100644 --- a/Source/EbsdLib/IO/TSL/AngPhase.cpp +++ b/Source/EbsdLib/IO/TSL/AngPhase.cpp @@ -288,7 +288,7 @@ void AngPhase::printSelf(std::stringstream& stream) // ----------------------------------------------------------------------------- // // ----------------------------------------------------------------------------- -unsigned int AngPhase::determineLaueGroup() +unsigned int AngPhase::determineOrientationOpsIndex() { uint32_t symmetry = getSymmetry(); unsigned int crystal_structure = EbsdLib::CrystalStructure::UnknownCrystalStructure; diff --git a/Source/EbsdLib/IO/TSL/AngPhase.h b/Source/EbsdLib/IO/TSL/AngPhase.h index 352501b..6584e58 100644 --- a/Source/EbsdLib/IO/TSL/AngPhase.h +++ b/Source/EbsdLib/IO/TSL/AngPhase.h @@ -215,7 +215,7 @@ class EbsdLib_EXPORT AngPhase /** * @brief Returns the type of crystal structure for this phase. */ - unsigned int determineLaueGroup(); + unsigned int determineOrientationOpsIndex(); private: std::string m_MaterialName = {}; diff --git a/Source/EbsdLib/IO/TSL/H5AngReader.h b/Source/EbsdLib/IO/TSL/H5AngReader.h index 96a6b18..7d6916c 100644 --- a/Source/EbsdLib/IO/TSL/H5AngReader.h +++ b/Source/EbsdLib/IO/TSL/H5AngReader.h @@ -111,7 +111,7 @@ class EbsdLib_EXPORT H5AngReader : public AngReader /** * @brief Returns the name of the class for H5AngReader */ - std::string getNameOfClass() const; + std::string getNameOfClass() const override; /** * @brief Returns the name of the class for H5AngReader */ diff --git a/Source/EbsdLib/IO/TSL/H5OIMReader.h b/Source/EbsdLib/IO/TSL/H5OIMReader.h index 3d31334..55b9c24 100644 --- a/Source/EbsdLib/IO/TSL/H5OIMReader.h +++ b/Source/EbsdLib/IO/TSL/H5OIMReader.h @@ -99,7 +99,7 @@ class EbsdLib_EXPORT H5OIMReader : public AngReader /** * @brief Returns the name of the class for H5OIMReader */ - std::string getNameOfClass() const; + std::string getNameOfClass() const override; /** * @brief Returns the name of the class for H5OIMReader */ diff --git a/Source/EbsdLib/Utilities/SourceList.cmake b/Source/EbsdLib/Utilities/SourceList.cmake index d9fbd72..8c520a4 100644 --- a/Source/EbsdLib/Utilities/SourceList.cmake +++ b/Source/EbsdLib/Utilities/SourceList.cmake @@ -21,6 +21,7 @@ set(EbsdLib_${DIR_NAME}_HDRS ${EbsdLibProj_SOURCE_DIR}/Source/EbsdLib/${DIR_NAME}/LatoBold.hpp ${EbsdLibProj_SOURCE_DIR}/Source/EbsdLib/${DIR_NAME}/LatoRegular.hpp ${EbsdLibProj_SOURCE_DIR}/Source/EbsdLib/${DIR_NAME}/CanvasUtilities.hpp + ${EbsdLibProj_SOURCE_DIR}/Source/EbsdLib/${DIR_NAME}/inipp.h ) set(EbsdLib_${DIR_NAME}_SRCS diff --git a/Source/EbsdLib/Utilities/inipp.h b/Source/EbsdLib/Utilities/inipp.h new file mode 100644 index 0000000..313a77a --- /dev/null +++ b/Source/EbsdLib/Utilities/inipp.h @@ -0,0 +1,332 @@ +/* +MIT License + +Copyright (c) 2017-2020 Matthias C. M. Troffaes + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace inipp +{ + +namespace detail +{ + +// trim functions based on http://stackoverflow.com/a/217605 + +template +inline void ltrim(std::basic_string& s, const std::locale& loc) +{ + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [&loc](CharT ch) { return !std::isspace(ch, loc); })); +} + +template +inline void rtrim(std::basic_string& s, const std::locale& loc) +{ + s.erase(std::find_if(s.rbegin(), s.rend(), [&loc](CharT ch) { return !std::isspace(ch, loc); }).base(), s.end()); +} + +template +inline void rtrim2(std::basic_string& s, UnaryPredicate pred) +{ + s.erase(std::find_if(s.begin(), s.end(), pred), s.end()); +} + +// string replacement function based on http://stackoverflow.com/a/3418285 + +template +inline bool replace(std::basic_string& str, const std::basic_string& from, const std::basic_string& to) +{ + auto changed = false; + size_t start_pos = 0; + while((start_pos = str.find(from, start_pos)) != std::basic_string::npos) + { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + changed = true; + } + return changed; +} + +} // namespace detail + +template +inline bool extract(const std::basic_string& value, T& dst) +{ + CharT c; + std::basic_istringstream is{value}; + T result; + if((is >> std::boolalpha >> result) && !(is >> c)) + { + dst = result; + return true; + } + else + { + return false; + } +} + +template +inline bool extract(const std::basic_string& value, std::basic_string& dst) +{ + dst = value; + return true; +} + +template +inline bool get_value(const std::map, std::basic_string>& sec, const std::basic_string& key, T& dst) +{ + const auto it = sec.find(key); + if(it == sec.end()) + return false; + return extract(it->second, dst); +} + +template +inline bool get_value(const std::map, std::basic_string>& sec, const CharT* key, T& dst) +{ + return get_value(sec, std::basic_string(key), dst); +} + +template +class Format +{ +public: + // used for generating + const CharT char_section_start; + const CharT char_section_end; + const CharT char_assign; + const CharT char_comment; + + // used for parsing + virtual bool is_section_start(CharT ch) const + { + return ch == char_section_start; + } + virtual bool is_section_end(CharT ch) const + { + return ch == char_section_end; + } + virtual bool is_assign(CharT ch) const + { + return ch == char_assign; + } + virtual bool is_comment(CharT ch) const + { + return ch == char_comment; + } + + // used for interpolation + const CharT char_interpol; + const CharT char_interpol_start; + const CharT char_interpol_sep; + const CharT char_interpol_end; + + Format(CharT section_start, CharT section_end, CharT assign, CharT comment, CharT interpol, CharT interpol_start, CharT interpol_sep, CharT interpol_end) + : char_section_start(section_start) + , char_section_end(section_end) + , char_assign(assign) + , char_comment(comment) + , char_interpol(interpol) + , char_interpol_start(interpol_start) + , char_interpol_sep(interpol_sep) + , char_interpol_end(interpol_end) + { + } + + Format() + : Format('[', ']', '=', ';', '$', '{', ':', '}') + { + } + + const std::basic_string local_symbol(const std::basic_string& name) const + { + return char_interpol + (char_interpol_start + name + char_interpol_end); + } + + const std::basic_string global_symbol(const std::basic_string& sec_name, const std::basic_string& name) const + { + return local_symbol(sec_name + char_interpol_sep + name); + } +}; + +template +class Ini +{ +public: + using String = std::basic_string; + using Section = std::map; + using Sections = std::map; + + Sections sections; + std::list errors; + std::shared_ptr> format; + + static const int max_interpolation_depth = 10; + + Ini() + : format(std::make_shared>()){}; + Ini(std::shared_ptr> fmt) + : format(fmt){}; + + void generate(std::basic_ostream& os) const + { + for(auto const& sec : sections) + { + os << format->char_section_start << sec.first << format->char_section_end << std::endl; + for(auto const& val : sec.second) + { + os << val.first << format->char_assign << val.second << std::endl; + } + os << std::endl; + } + } + + void parse(std::basic_istream& is) + { + String line; + String section; + const std::locale loc{"C"}; + while(std::getline(is, line)) + { + detail::ltrim(line, loc); + detail::rtrim(line, loc); + const auto length = line.length(); + if(length > 0) + { + const auto pos = std::find_if(line.begin(), line.end(), [this](CharT ch) { return format->is_assign(ch); }); + const auto& front = line.front(); + if(format->is_comment(front)) + { + } + else if(format->is_section_start(front)) + { + if(format->is_section_end(line.back())) + section = line.substr(1, length - 2); + else + errors.push_back(line); + } + else if(pos != line.begin() && pos != line.end()) + { + String variable(line.begin(), pos); + String value(pos + 1, line.end()); + detail::rtrim(variable, loc); + detail::ltrim(value, loc); + auto& sec = sections[section]; + if(sec.find(variable) == sec.end()) + sec.emplace(variable, value); + else + errors.push_back(line); + } + else + { + errors.push_back(line); + } + } + } + } + + void interpolate() + { + int global_iteration = 0; + auto changed = false; + // replace each "${variable}" by "${section:variable}" + for(auto& sec : sections) + replace_symbols(local_symbols(sec.first, sec.second), sec.second); + // replace each "${section:variable}" by its value + do + { + changed = false; + const auto syms = global_symbols(); + for(auto& sec : sections) + changed |= replace_symbols(syms, sec.second); + } while(changed && (max_interpolation_depth > global_iteration++)); + } + + void default_section(const Section& sec) + { + for(auto& sec2 : sections) + for(const auto& val : sec) + sec2.second.insert(val); + } + + void strip_trailing_comments() + { + const std::locale loc{"C"}; + for(auto& sec : sections) + for(auto& val : sec.second) + { + detail::rtrim2(val.second, [this](CharT ch) { return format->is_comment(ch); }); + detail::rtrim(val.second, loc); + } + } + + void clear() + { + sections.clear(); + errors.clear(); + } + +private: + using Symbols = std::vector>; + + const Symbols local_symbols(const String& sec_name, const Section& sec) const + { + Symbols result; + for(const auto& val : sec) + result.emplace_back(format->local_symbol(val.first), format->global_symbol(sec_name, val.first)); + return result; + } + + const Symbols global_symbols() const + { + Symbols result; + for(const auto& sec : sections) + for(const auto& val : sec.second) + result.emplace_back(format->global_symbol(sec.first, val.first), val.second); + return result; + } + + bool replace_symbols(const Symbols& syms, Section& sec) const + { + auto changed = false; + for(auto& sym : syms) + for(auto& val : sec) + changed |= detail::replace(val.second, sym.first, sym.second); + return changed; + } +}; + +} // namespace inipp