diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 8e71229145..71eafff711 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -35,6 +35,7 @@ #include #include "../ifcparse/Hdf5Settings.h" +#include "../ifcparse/IfcHdf5File.h" #include "../ifcgeom/IfcGeomIterator.h" @@ -72,6 +73,7 @@ void printUsage(const boost::program_options::options_description& generic_optio << " .xml XML Property definitions and decomposition tree" << std::endl << " .svg SVG Scalable Vector Graphics (2d floor plan)" << std::endl << " .hdf HDF5 Efficient binary IFC serialization" << std::endl + << " .ifc IFC-SPF Standard p21 IFC serialization" << std::endl << std::endl << "Command line options" << std::endl << generic_options << std::endl << "Advanced options" << std::endl << geom_options << std::endl; @@ -303,6 +305,28 @@ int main(int argc, char** argv) { write_log(); // std::cin.get(); return exit_code; + } else if (output_extension == ".ifc") { + bool success = false; + const std::string input_extension = input_filename.substr(input_filename.size() - 4); + if (input_extension == ".ifc") { + IfcParse::IfcFile f; + if (f.Init(input_filename)) { + std::ofstream of(output_filename.c_str()); + of << f; + success = true; + } else { + Logger::Message(Logger::LOG_ERROR, "Unable to parse .ifc file"); + } + } else if (input_extension == ".hdf") { + std::ofstream fs(output_filename.c_str()); + IfcParse::IfcHdf5File::convert_to_spf(input_filename, fs); + success = true; + } else { + Logger::Message(Logger::LOG_ERROR, "Unknown input extension"); + } + + write_log(); + return success ? 0 : 1; } IfcGeom::IteratorSettings settings; diff --git a/src/ifcparse/IfcHdf5File.cpp b/src/ifcparse/IfcHdf5File.cpp index d23971d95d..ea76c20173 100644 --- a/src/ifcparse/IfcHdf5File.cpp +++ b/src/ifcparse/IfcHdf5File.cpp @@ -1,5 +1,7 @@ #include "../ifcparse/IfcHdf5File.h" +#include "../ifcparse/IfcWrite.h" +#include #include #include #include @@ -12,7 +14,8 @@ static boost::optional< std::vector > no_instances = boost::none; class type_mapper { public: - type_mapper(IfcParse::IfcFile& ifc_file, H5::H5File* hdf5_file, const IfcParse::Hdf5Settings& settings); + type_mapper(); + type_mapper(IfcParse::IfcFile* ifc_file, H5::H5File* hdf5_file, const IfcParse::Hdf5Settings& settings); H5::DataType* commit(H5::DataType* dt, const std::string& name); @@ -30,7 +33,7 @@ private: bool padded_; bool referenced_; - IfcParse::IfcFile& ifc_file_; + IfcParse::IfcFile* ifc_file_; H5::H5File* hdf5_file_; H5::Group schema_group_; H5::DataType* instance_reference_; @@ -140,7 +143,8 @@ void advance(void*& ptr, size_t n) { template void write(void*& ptr, const T& t) { - *((T*)ptr) = t; + // *((T*)ptr) = t; + memcpy(ptr, &t, sizeof(T)); advance(ptr, sizeof(T)); } @@ -416,7 +420,9 @@ public: break; case H5T_STRING: if (datatype_ == H5::StrType(H5::PredType::C_S1, H5T_VARIABLE)) { - write(ptr, new char(0)); + // write(ptr, new char(0)); + memset(ptr, 0, sizeof(char*)); + advance(ptr, sizeof(char*)); } else { write_string_of_size(ptr, datatype_.getSize(), ""); } @@ -818,20 +824,53 @@ std::string type_mapper::flatten_aggregate_name(const IfcParse::parameter_type* } } -type_mapper::type_mapper(IfcParse::IfcFile& ifc_file, H5::H5File* hdf5_file, const IfcParse::Hdf5Settings& settings) +type_mapper::type_mapper() { + default_type_names_[IfcParse::simple_type::logical_type] = "logical"; + default_type_names_[IfcParse::simple_type::boolean_type] = "boolean"; + default_type_names_[IfcParse::simple_type::binary_type] = "binary"; + default_type_names_[IfcParse::simple_type::real_type] = "real"; + default_type_names_[IfcParse::simple_type::number_type] = "number"; + default_type_names_[IfcParse::simple_type::string_type] = "string"; + default_type_names_[IfcParse::simple_type::integer_type] = "integer"; + + default_cpp_type_names_[IfcUtil::Argument_BOOL] = "boolean"; + default_cpp_type_names_[IfcUtil::Argument_DOUBLE] = "real"; + default_cpp_type_names_[IfcUtil::Argument_STRING] = "string"; + default_cpp_type_names_[IfcUtil::Argument_INT] = "integer"; +} + +type_mapper::type_mapper(IfcParse::IfcFile* ifc_file, H5::H5File* hdf5_file, const IfcParse::Hdf5Settings& settings) : ifc_file_(ifc_file) , hdf5_file_(hdf5_file) , settings_(settings) , padded_(settings_.profile() == IfcParse::Hdf5Settings::padded || settings_.profile() == IfcParse::Hdf5Settings::padded_referenced) , referenced_(settings_.profile() == IfcParse::Hdf5Settings::standard_referenced || settings_.profile() == IfcParse::Hdf5Settings::padded_referenced) { - schema_group_ = hdf5_file_->openGroup(ifc_file_.schema()->name() + "_encoding"); - - default_types_.resize(IfcParse::simple_type::datatype_COUNT); default_type_names_.resize(IfcParse::simple_type::datatype_COUNT); default_cpp_type_names_.resize(IfcUtil::Argument_UNKNOWN); - declared_types_.resize(IfcSchema::Type::UNDEFINED); + default_type_names_[IfcParse::simple_type::logical_type] = "logical"; + default_type_names_[IfcParse::simple_type::boolean_type] = "boolean"; + default_type_names_[IfcParse::simple_type::binary_type] = "binary"; + default_type_names_[IfcParse::simple_type::real_type] = "real"; + default_type_names_[IfcParse::simple_type::number_type] = "number"; + default_type_names_[IfcParse::simple_type::string_type] = "string"; + default_type_names_[IfcParse::simple_type::integer_type] = "integer"; + + default_cpp_type_names_[IfcUtil::Argument_BOOL] = "boolean"; + default_cpp_type_names_[IfcUtil::Argument_DOUBLE] = "real"; + default_cpp_type_names_[IfcUtil::Argument_STRING] = "string"; + default_cpp_type_names_[IfcUtil::Argument_INT] = "integer"; + + if (!ifc_file_ && !hdf5_file_) { + // This mapper is only used to construct leaf names for select compounds + return; + } + + schema_group_ = hdf5_file_->openGroup(ifc_file_->schema()->name() + "_encoding"); + + default_types_.resize(IfcParse::simple_type::datatype_COUNT); + declared_types_.resize(IfcSchema::Type::UNDEFINED); if (referenced_) { instance_reference_ = commit(new H5::PredType(H5::PredType::STD_REF_DSETREG), "_HDF_INSTANCE_REFERENCE_HANDLE_"); @@ -862,19 +901,6 @@ type_mapper::type_mapper(IfcParse::IfcFile& ifc_file, H5::H5File* hdf5_file, con default_types_[IfcParse::simple_type::number_type] = &H5::PredType::NATIVE_DOUBLE; default_types_[IfcParse::simple_type::string_type] = new H5::StrType(H5::PredType::C_S1, H5T_VARIABLE); default_types_[IfcParse::simple_type::integer_type] = &H5::PredType::NATIVE_INT; - - default_type_names_[IfcParse::simple_type::logical_type] = "logical"; - default_type_names_[IfcParse::simple_type::boolean_type] = "boolean"; - default_type_names_[IfcParse::simple_type::binary_type] = "binary"; - default_type_names_[IfcParse::simple_type::real_type] = "real"; - default_type_names_[IfcParse::simple_type::number_type] = "number"; - default_type_names_[IfcParse::simple_type::string_type] = "string"; - default_type_names_[IfcParse::simple_type::integer_type] = "integer"; - - default_cpp_type_names_[IfcUtil::Argument_BOOL] = "boolean"; - default_cpp_type_names_[IfcUtil::Argument_DOUBLE] = "real"; - default_cpp_type_names_[IfcUtil::Argument_STRING] = "string"; - default_cpp_type_names_[IfcUtil::Argument_INT] = "integer"; } H5::DataType* type_mapper::commit(H5::DataType* dt, const std::string& name) { @@ -1067,6 +1093,8 @@ std::pair type_mapper::make_select_leaf(const std::string name; const H5::DataType* dt = 0; + const bool should_return_type = ifc_file_ != nullptr && hdf5_file_ != nullptr; + if (decl) { while (decl->as_type_declaration()) { const IfcParse::parameter_type* leaf_pt = decl->as_type_declaration()->declared_type(); @@ -1100,21 +1128,21 @@ std::pair type_mapper::make_select_leaf(const std::vector< IfcParse::IfcHdf5File::compound_member > members; members.push_back(std::make_pair(std::string("length"), new H5::PredType(H5::PredType::NATIVE_UINT32))); members.push_back(std::make_pair(std::string("data"), new H5::StrType(H5::PredType::C_S1, (size_t)visitor.max_length()[0] + 1))); - dt = create_compound(members); + if (should_return_type) dt = create_compound(members); } else { - dt = default_types_[st->declared_type()]; + if (should_return_type) dt = default_types_[st->declared_type()]; } break; } else if (at) { name = flatten_aggregate_name(at); - dt = (*this)(at); + if (should_return_type) dt = (*this)(at); break; } } } - if (!dt) { + if (!dt && should_return_type) { if (decl->as_entity()) { name = "instance"; dt = instance_reference_; @@ -1249,7 +1277,7 @@ public: H5::CompType* type_mapper::operator()(const IfcParse::entity* e, const boost::optional< std::vector >&, int, const hsize_t*) { // List of entity instances of this type, no subtypes - IfcEntityList::ptr incl_subtypes = ifc_file_.entitiesByType(e->name()); + IfcEntityList::ptr incl_subtypes = ifc_file_->entitiesByType(e->name()); IfcEntityList::ptr instances(new IfcEntityList); if (incl_subtypes) { for (auto it = incl_subtypes->begin(); it != incl_subtypes->end(); ++it) { @@ -1399,7 +1427,7 @@ H5::EnumType* type_mapper::operator()(const IfcParse::enumeration_type* en, cons } void type_mapper::operator()() { - const IfcParse::schema_definition& schema = *ifc_file_.schema(); + const IfcParse::schema_definition& schema = *ifc_file_->schema(); for (auto it = schema.enumeration_types().begin(); it != schema.enumeration_types().end(); ++it) { declared_types_[(*it)->type()] = commit((*this)(*it), (*it)->name()); @@ -1891,7 +1919,7 @@ void IfcParse::IfcHdf5File::write_schema(const IfcParse::schema_definition& sche // init_default_types(); - mapper_ = new type_mapper(ifc_file, file, settings_); + mapper_ = new type_mapper(&ifc_file, file, settings_); (*(type_mapper*)mapper_)(); }; @@ -2677,4 +2705,335 @@ void IfcParse::IfcHdf5File::write_population(IfcFile& f) { } } -*/ \ No newline at end of file +*/ + +bool is_instance_ref(H5::DataType* dt) { + if (dt->getClass() != H5T_COMPOUND) return false; + H5::CompType* ct = (H5::CompType*) dt; + return ct->getMemberName(0) == "_HDF5_dataset_index_"; +} + +template +T read_(void* buffer, H5::DataType* dt) { + (void*)dt; + T t; + memcpy(&t, buffer, sizeof(T)); + return t; +} + +template +T read(void* buffer, H5::DataType* dt) { + return read_(buffer, dt); +} + +template <> +int read(void* buffer, H5::DataType* dt) { + // TODO: Check for overflow + // TODO: Check signed + int i; + switch (dt->getSize()) { + case 1: i = (int) read_::type>(buffer, dt); break; + case 2: i = (int) read_::type>(buffer, dt); break; + case 4: i = (int) read_::type>(buffer, dt); break; + case 8: i = (int) read_::type>(buffer, dt); break; + default: throw std::runtime_error("Unexpected integer width"); + } + return i; +} + +template <> +double read(void* buffer, H5::DataType* dt) { + double d; + switch (dt->getSize()) { + case 4: d = read_::type>(buffer, dt); break; + case 8: d = read_::type>(buffer, dt); break; + default: throw std::runtime_error("Unexpected float width"); + } + return d; +} + +static const std::bitset<64> many_trues(std::numeric_limits::max()); +static const std::string no_name = "NO_NAME"; + +class instance_resolver { +private: + H5::H5File& file_; + std::vector dataset_names_; + +public: + instance_resolver(H5::H5File& file) + : file_(file) + { + H5::Group schema_group = file_.openGroup("IFC2X3_encoding"); + H5::Attribute names = schema_group.openAttribute("iso_10303_26_data_set_names"); + H5::DataType datatype = names.getDataType(); + if (datatype.getClass() != H5T_STRING || H5Tis_variable_str(datatype.getId()) <= 0) { + throw std::runtime_error("Expected variable string attribute"); + } + + H5::DataSpace dataspace = names.getSpace(); + if (dataspace.getSimpleExtentNdims() != 1) { + throw std::runtime_error("Expected one-dimensional attribute"); + } + + hsize_t dim; + dataspace.getSimpleExtentDims(&dim, NULL); + dataset_names_.reserve(dim); + + void* buffer; + uint8_t* ptr; + buffer = ptr = new uint8_t[datatype.getSize() * (size_t) dim]; + names.read(datatype, buffer); + + while (dim--) { + dataset_names_.push_back(*(char**) ptr); + ptr += datatype.getSize(); + } + + H5Dvlen_reclaim(datatype.getId(), dataspace.getId(), H5P_DEFAULT, buffer); + delete[] buffer; + + dataspace.close(); + datatype.close(); + names.close(); + schema_group.close(); + } + + size_t instance_name(void* data, H5::CompType* dt) { + hsize_t pair[2]; + for (int i = 0; i < 2; ++i) { + H5::DataType mt = dt->getMemberDataType(i); + size_t offset = dt->getMemberOffset(i); + pair[i] = read((uint8_t*)data + offset, &mt); + } + + const std::string& nm = dataset_names_[pair[0]]; + + H5::DataSet dataset = file_.openDataSet("population/" + nm + "_instances"); + H5::CompType datatype = dataset.getCompType(); + H5::DataSpace dataspace = dataset.getSpace(); + if (dataspace.getSimpleExtentNdims() != 1) { + throw std::runtime_error("Expected one-dimensional data"); + } + dataspace.selectElements(H5S_SELECT_SET, 1, pair + 1); + const hsize_t one = 1; + H5::DataSpace memspace(1, &one); + + uint8_t* buffer = new uint8_t[datatype.getSize()]; + dataset.read(buffer, datatype, memspace, dataspace); + + int idx = datatype.getMemberIndex("Entity-Instance-Identifier"); + size_t inst_name_offset = datatype.getMemberOffset(idx); + H5::DataType member_type = datatype.getMemberDataType(idx); + + size_t inst_name = read((uint8_t*)buffer + inst_name_offset, &member_type); + + member_type.close(); + memspace.close(); + datatype.close(); + dataspace.close(); + dataset.close(); + + delete[] buffer; + + return inst_name; + } +}; + +void visit(instance_resolver& resolver, std::ostream& output, void* buffer, H5::DataType* dt, const std::string& name = no_name, const std::bitset<64>& bitmask = many_trues) { + if (dt->getClass() == H5T_COMPOUND) { + + // std::vector schema_attributes; + std::vector derived; + std::vector attribute_names; + std::vector::const_iterator attribute_name_it; + if (&name != &no_name) { + auto entity = get_schema().declaration_by_name(IfcSchema::Type::FromString(boost::to_upper_copy(name)))->as_entity(); + auto attributes = entity->all_attributes(); + std::transform(attributes.begin(), attributes.end(), std::back_inserter(attribute_names), [](const IfcParse::entity::attribute* attr) { + return attr->name(); + }); + derived = entity->derived(); + attribute_name_it = attribute_names.begin(); + } + + H5::CompType* ct = (H5::CompType*) dt; + const auto is_ref = is_instance_ref(dt); + if (is_ref) { + output << "#" << resolver.instance_name(buffer, ct); + } else { + int ifc_idx = 0; + const std::bitset<64>* mask = &bitmask; + std::string select_valuation; + bool is_instance = false; + bool is_select = false; + bool is_selected_simple_type = false; + bool select_valuation_encountered = false; + + for (int i = 0; i < ct->getNmembers(); ++i) { + const std::string member_name = ct->getMemberName(i); + + const size_t offs = ct->getMemberOffset(i); + void* member_ptr = (uint8_t*)buffer + offs; + H5::DataType member_type = ct->getMemberDataType(i); + + if (member_name == "set_unset_bitmap") { + mask = new std::bitset<64>(read(member_ptr, &member_type)); + } else if (member_name == "Entity-Instance-Identifier") { + is_instance = true; + output << "#" << read(member_ptr, &member_type) << "=" << boost::to_upper_copy(name) << "("; + } else if (member_name == "type_code") { + is_select = true; + IfcSchema::Type::Enum ty = (IfcSchema::Type::Enum) read(member_ptr, &member_type); + auto decl = get_schema().declaration_by_name(ty); + if (decl->as_entity()) { + select_valuation = "instance-value"; + } else { + std::string type_string = IfcSchema::Type::ToString(ty); + boost::to_upper(type_string); + output << type_string << "("; + IfcParse::Hdf5Settings settings; + select_valuation = type_mapper(nullptr, nullptr, settings).make_select_leaf(decl, no_instances).first; + is_selected_simple_type = true; + } + } else { + if (is_select) { + if (member_name == select_valuation) { + visit(resolver, output, member_ptr, &member_type, name, many_trues); + select_valuation_encountered = true; + } + } else { + while (member_name != *attribute_name_it) { + if (ifc_idx) { + output << ","; + } + attribute_name_it++; + ifc_idx++; + output << "*"; + } + if (ifc_idx) { + output << ","; + } + if ((*mask)[ifc_idx]) { + visit(resolver, output, member_ptr, &member_type, name, many_trues); + } else { + output << "$"; + } + ifc_idx++; + attribute_name_it++; + } + } + member_type.close(); + } + if (is_select && !select_valuation_encountered) { + throw std::runtime_error("Select valuation not encountered in compound"); + } + if (mask != &many_trues) { + delete mask; + } + if (is_selected_simple_type || is_instance) { + output << ")"; + if (is_instance) { + output << ";" << std::endl; + } + } + } + } else if (dt->getClass() == H5T_VLEN) { + hvl_t* ht = (hvl_t*)buffer; + H5::VarLenType* vt = (H5::VarLenType*) dt; + H5::DataType dt2 = vt->getSuper(); + output << "("; + for (size_t i = 0; i < ht->len; ++i) { + if (i) { + output << ","; + } + visit(resolver, output, (uint8_t*)ht->p + i * dt2.getSize(), &dt2); + } + output << ")"; + dt2.close(); + } else if (dt->getClass() == H5T_STRING) { + const bool is_varlen = H5Tis_variable_str(dt->getId()) > 0; + if (is_varlen) { + const char* c = read(buffer, dt); + output << "'" << c << "'"; + } else { + throw std::runtime_error("blurgfh"); + } + } else if (dt->getClass() == H5T_INTEGER) { + output << read(buffer, dt); + } else if (dt->getClass() == H5T_FLOAT) { + output << IfcWrite::format(read(buffer, dt)); + } else if (dt->getClass() == H5T_ENUM) { + const char *enum_value; + char *to_free; + enum_value = to_free = H5Tget_member_name(dt->getId(), read(buffer, dt)); + char substr[2] = { 0,0 }; + const char BOOLEAN[] = "BOOLEAN-"; + const char LOGICAL[] = "LOGICAL-"; + if (strstr(enum_value, BOOLEAN) || strstr(enum_value, LOGICAL)) { + // Hack to convert BOOLEAN-TRUE et al to T + // Boolean and logical are of the same length coincedentally + substr[0] = enum_value[strlen(BOOLEAN)]; + enum_value = substr; + } + output << "." << enum_value << "."; + H5free_memory(to_free); + } else { + throw std::runtime_error("Unexpected datatype encountered"); + } +} + +struct hdf5_output_info { + H5::H5File& file; + std::ostream& output; +}; + +herr_t iterate(hid_t, const char *name_, const H5O_info_t *object_info, void *op_data) { + H5::H5File& f = ((hdf5_output_info*)op_data)->file; + std::ostream& output = ((hdf5_output_info*)op_data)->output; + + instance_resolver resolver(f); + + if (object_info->type == H5O_TYPE_DATASET) { + std::string name = name_; + + H5::DataSet dataset = f.openDataSet(name); + H5::DataType datatype = dataset.getDataType(); + H5::DataSpace dataspace = dataset.getSpace(); + if (dataspace.getSimpleExtentNdims() != 1) { + throw std::runtime_error("Expected one-dimensional data"); + } + hsize_t dim; + dataspace.getSimpleExtentDims(&dim, NULL); + uint8_t* buffer, *ptr; + buffer = ptr = new uint8_t[(size_t) dim * datatype.getSize()]; + dataset.read(buffer, datatype); + + auto slash = name.find_last_of('/'); + if (slash != std::string::npos) { + name = name.substr(slash + 1); + } + auto under = name.find('_'); + if (under != std::string::npos) { + name = name.substr(0, under); + } + + while (dim--) { + visit(resolver, output, ptr, &datatype, name); + ptr += datatype.getSize(); + } + + datatype.close(); + dataspace.close(); + dataset.close(); + + delete[] buffer; + } + return 0; +} + +void IfcParse::IfcHdf5File::convert_to_spf(const std::string& name, std::ostream& output) { + H5::H5File f(name, H5F_ACC_RDONLY); + hdf5_output_info info{ f,output }; + H5Ovisit(f.getId(), H5_INDEX_NAME, H5_ITER_NATIVE, iterate, &info); +} diff --git a/src/ifcparse/IfcHdf5File.h b/src/ifcparse/IfcHdf5File.h index 2894f4a8b1..9d63253bc2 100644 --- a/src/ifcparse/IfcHdf5File.h +++ b/src/ifcparse/IfcHdf5File.h @@ -159,6 +159,8 @@ namespace IfcParse { write_schema(schema_, ifcfile_); write_population(ifcfile_); }; + + static void convert_to_spf(const std::string& name, std::ostream& output); }; } diff --git a/src/ifcparse/IfcWrite.cpp b/src/ifcparse/IfcWrite.cpp index efe15a361f..22a5d36f3d 100644 --- a/src/ifcparse/IfcWrite.cpp +++ b/src/ifcparse/IfcWrite.cpp @@ -326,6 +326,55 @@ public: int operator()(const IfcEntityListList::ptr& i) const { return i->size(); } }; +// The REAL token definition from the IFC SPF standard does not necessarily match +// the output of the C++ ostream formatting operation. +// REAL = [ SIGN ] DIGIT { DIGIT } "." { DIGIT } [ "E" [ SIGN ] DIGIT { DIGIT } ] . +std::string IfcWrite::format(double d) { + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << std::setprecision(std::numeric_limits::digits10 + 1) << d; + const std::string str = oss.str(); + oss.str(""); + std::string::size_type e = str.find('e'); + if (e == std::string::npos) { + e = str.find('E'); + } + const std::string mantissa = str.substr(0, e); + oss << mantissa; + if (mantissa.find('.') == std::string::npos) { + oss << "."; + } + if (e != std::string::npos) { + oss << "E"; + oss << str.substr(e + 1); + } + return oss.str(); +} + +std::string IfcWrite::format(const boost::dynamic_bitset<>& b) { + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss.put('"'); + oss << std::hex << std::setw(1); + unsigned c = (unsigned)b.size(); + unsigned n = (4 - (c % 4)) & 3; + oss << n; + for (unsigned i = 0; i < c + n;) { + unsigned accum = 0; + for (int j = 0; j < 4; ++j, ++i) { + unsigned bit = i < n ? 0 : b.test(c - i + n - 1) ? 1 : 0; + accum |= bit << (3 - j); + } + oss << accum; + } + oss.put('"'); + return oss.str(); +} + +std::string IfcWrite::format(bool b) { + return b ? ".T." : ".F."; +} + class StringBuilderVisitor : public boost::static_visitor { private: StringBuilderVisitor(const StringBuilderVisitor&); //N/A @@ -340,55 +389,7 @@ private: } data << ")"; } - // The REAL token definition from the IFC SPF standard does not necessarily match - // the output of the C++ ostream formatting operation. - // REAL = [ SIGN ] DIGIT { DIGIT } "." { DIGIT } [ "E" [ SIGN ] DIGIT { DIGIT } ] . - std::string format_double(const double& d) { - std::ostringstream oss; - oss.imbue(std::locale::classic()); - oss << std::setprecision(std::numeric_limits::digits10) << d; - const std::string str = oss.str(); - oss.str(""); - std::string::size_type e = str.find('e'); - if (e == std::string::npos) { - e = str.find('E'); - } - const std::string mantissa = str.substr(0,e); - oss << mantissa; - if (mantissa.find('.') == std::string::npos) { - oss << "."; - } - if (e != std::string::npos) { - oss << "E"; - oss << str.substr(e+1); - } - return oss.str(); - } - - std::string format_bool(bool b) { - return b ? ".T." : ".F."; - } - - std::string format_binary(const boost::dynamic_bitset<>& b) { - std::ostringstream oss; - oss.imbue(std::locale::classic()); - oss.put('"'); - oss << std::hex << std::setw(1); - unsigned c = (unsigned)b.size(); - unsigned n = (4 - (c % 4)) & 3; - oss << n; - for (unsigned i = 0; i < c + n;) { - unsigned accum = 0; - for (int j = 0; j < 4; ++j, ++i) { - unsigned bit = i < n ? 0 : b.test(c - i + n - 1) ? 1 : 0; - accum |= bit << (3-j); - } - oss << accum; - } - oss.put('"'); - return oss.str(); - } - + bool upper; public: StringBuilderVisitor(std::ostringstream& stream, bool upper = false) @@ -396,9 +397,9 @@ public: void operator()(const boost::none_t& /*i*/) { data << "$"; } void operator()(const IfcWriteArgument::Derived& /*i*/) { data << "*"; } void operator()(const int& i) { data << i; } - void operator()(const bool& i) { data << format_bool(i); } - void operator()(const double& i) { data << format_double(i); } - void operator()(const boost::dynamic_bitset<>& i) { data << format_binary(i); } + void operator()(const bool& i) { data << IfcWrite::format(i); } + void operator()(const double& i) { data << IfcWrite::format(i); } + void operator()(const boost::dynamic_bitset<>& i) { data << IfcWrite::format(i); } void operator()(const std::string& i) { std::string s = i; if (upper) { @@ -470,7 +471,7 @@ void StringBuilderVisitor::serialize(const std::vector& i) { data << "("; for (std::vector::const_iterator it = i.begin(); it != i.end(); ++it) { if (it != i.begin()) data << ","; - data << format_double(*it); + data << IfcWrite::format(*it); } data << ")"; } @@ -480,7 +481,7 @@ void StringBuilderVisitor::serialize(const std::vector& i) { data << "("; for (std::vector::const_iterator it = i.begin(); it != i.end(); ++it) { if (it != i.begin()) data << ","; - data << format_bool(*it); + data << IfcWrite::format(*it); } data << ")"; } @@ -490,7 +491,7 @@ void StringBuilderVisitor::serialize(const std::vector< boost::dynamic_bitset<> data << "("; for (std::vector< boost::dynamic_bitset<> >::const_iterator it = i.begin(); it != i.end(); ++it) { if (it != i.begin()) data << ","; - data << format_binary(*it); + data << IfcWrite::format(*it); } data << ")"; } diff --git a/src/ifcparse/IfcWrite.h b/src/ifcparse/IfcWrite.h index d12a80ae50..c1dbd46c1c 100644 --- a/src/ifcparse/IfcWrite.h +++ b/src/ifcparse/IfcWrite.h @@ -37,6 +37,10 @@ namespace IfcWrite { + std::string format(bool b); + std::string format(double d); + std::string format(const boost::dynamic_bitset<>& b); + /// This class is a writable container for attributes. A fundamental /// difference with the attribute types counterparts defined in the /// IfcParse namespace is that this class has a Boost.Variant member