Skip to content

Load IFC file, traverse geometry & meta data, with super high performance & super low memory footprint

License

Notifications You must be signed in to change notification settings

ifcquery/IfcFileLoader

Repository files navigation

IfcFileLoader

This is a sample project to demonstrate how to use web-ifc (https://github.com/ThatOpen/engine_web-ifc) in a native (non WASM) C++ project.

It demonstrates how to load an IFC file, traverse geometry & meta data, with super high performance & super low memory footprint

This repository contains project files for VS, to compile the application on Windows.

Compiling on Linux/Mac OS is also possible, but currently not part of the repository.

All external dependencies are in src/external, and already referenced in the VS project. No need to build those as separate libraries.

Check out a full IFC 3D viewer using the same technology: https://github.com/ifcquery/ifcsplitandmerge IfcSplitAndMerge: https://github.com/ifcquery/ifcsplitandmerge

How to open an IFC file and read entity attributes with web-ifc

Web-ifc has a so called tape reader as internal data structure, i.e. not an object oriented model. Web-ifc parses the STEP file content by loading the file content in chunks, then replacing the entity IDs with a uint32_t, the data type string with a CRC type code (uint32_t), and then the arguments according to their type.

image

So the text file content turns into a mix of binary 1-byte tokens, binary values like uint32_t for IDs, double values for floating point numbers, text remains text.

This approach results in a much smaller memory footprint compared to an object oriented model, and loading is much faster.

With the entity ID as offset, the loader jumps to any entity. With an offset [0, n] (n=number of arguments), the argument content can be read.

Here is a basic example how to open an IFC file and read directly from the tape (token stream):

struct LoaderSettings {
	bool COORDINATE_TO_ORIGIN = false;
	uint16_t CIRCLE_SEGMENTS = 12;
	uint32_t TAPE_SIZE = 67108864; // 64 MByte, probably no need for anyone other than web-ifc devs to change this
	uint32_t MEMORY_LIMIT = UINT64_MAX * 0.5;
	uint16_t LINEWRITER_BUFFER = 10000;
};

webifc::schema::IfcSchemaManager schemaManager;
LoaderSettings fileLoadingSettings;
webifc::parsing::IfcLoader loader (fileLoadingSettings.TAPE_SIZE, fileLoadingSettings.MEMORY_LIMIT, fileLoadingSettings.LINEWRITER_BUFFER, schemaManager);


std::string fileName = "IfcOpenHouse_IFC4.ifc";
std::filesystem::path pathToFile = std::filesystem::absolute(fileName);
if (!std::filesystem::exists(pathToFile)) {
	std::cout << "File does not exist: " + pathToFile.string();
	return 0;
}

size_t fileSize = std::filesystem::file_size(pathToFile);


loader.LoadFile([&](char* dest, size_t sourceOffset, size_t destSize) {
	// this lambda is called for each chunk, so the content can be read from any source, stream etc.
	size_t length = std::min(static_cast<size_t>(fileSize - sourceOffset), destSize);
	std::ifstream file(pathToFile, std::ios::binary);
	if (file) {
		file.seekg(sourceOffset);
		file.read(dest, length);
		return static_cast<size_t>(file.gcount());
	}
	return size_t(0);
	});

std::vector<uint32_t> IfcProjectEntities = loader.GetExpressIDsWithType(webifc::schema::IFCPROJECT);
if (IfcProjectEntities.size() == 0) {
	std::cout << "No IfcProject entity found in the file.";
	return -1;
}
// IfcProject ----------------------------------------------------------------------------
//		IfcGloballyUniqueId							GlobalId;
//		IfcOwnerHistory								OwnerHistory;				//optional
//		IfcLabel									Name;						//optional
//		IfcText										Description;				//optional
//		IfcLabel									ObjectType;					//optional
//		IfcLabel									LongName;					//optional
//		IfcLabel									Phase;						//optional
//		std::vector<IfcRepresentationContext> 		RepresentationContexts;		//optional
//		IfcUnitAssignment							UnitsInContext;				//optional

uint32_t ifcProjectID = IfcProjectEntities[0];
std::string projectGUID = "";
std::string projectName = "DefaultProject";

// #12=IFCPROJECT('2Xw1_iVsn7OudAGdIJr3pp',#5,'IfcOpenHouse',$,$,$,$,(#41,#47),#11);
loader.MoveToArgumentOffset(ifcProjectID, 0); // GlobalId is argument 0
auto tokenTypeGUID = loader.GetTokenType();
loader.StepBack();
if (tokenTypeGUID == webifc::parsing::STRING) {
	projectGUID = loader.GetStringArgument();   // read string "2Xw1_iVsn7OudAGdIJr3pp"
}

loader.MoveToArgumentOffset(ifcProjectID, 2); // Name is argument 2
auto tokenTypeName = loader.GetTokenType();
loader.StepBack();
if (tokenTypeName == webifc::parsing::STRING) {
	projectName = loader.GetStringArgument();  // read string "IfcOpenHouse"
}

// load project hierarchy: child-parent relations
std::unordered_map<uint32_t, std::unordered_map<uint32_t, uint32_t> > mapElement2Children;    // [parentID] = { childID, relationID };
std::unordered_map<uint32_t, std::pair<uint32_t, uint32_t> > mapElement2ParentRelationObject;  // [elementEntityID] = { parentID, relationID };

std::vector<uint32_t> relAggregates = loader.GetExpressIDsWithType(webifc::schema::IFCRELAGGREGATES);
// IfcRelAggregates -----------------------------------------------------------
//		IfcGloballyUniqueId					GlobalId;
//		IfcOwnerHistory						OwnerHistory;				//optional
//		IfcLabel							Name;						//optional
//		IfcText								Description;				//optional
//		IfcObjectDefinition					RelatingObject;				4
//		std::vector<IfcObjectDefinition>	RelatedObjects;				5
for (auto relationID : relAggregates) {
	uint32_t parentAttributeNumber = 4;
	loader.MoveToArgumentOffset(relationID, parentAttributeNumber);
	auto tokenTypeParentEntity = loader.GetTokenType();
	loader.StepBack();
	if (tokenTypeParentEntity != webifc::parsing::REF) {
		// The argument is non-optional, but sometimes it is missing in IFC files anyway. Can be ignored safely
		continue;
	}
	uint32_t parentTag = loader.GetRefArgument();
	uint32_t setOfChildrenAttributeNumber = 5; // IfcRelContainedInSpatialStructure: 4
	loader.MoveToArgumentOffset(relationID, setOfChildrenAttributeNumber);
	auto tokenTypeChildren = loader.GetTokenType();
	loader.StepBack();
	if (tokenTypeChildren != webifc::parsing::SET_BEGIN) {
		// could be SET_END in case of an empty set
		continue;
	}
	const std::vector<uint32_t> RelatedElementsTapeOffsets = loader.GetSetArgument();
	for (const uint32_t& tapeOffset : RelatedElementsTapeOffsets) {
		uint32_t elementExpressID = loader.GetRefArgument(tapeOffset);
		uint32_t elementType = loader.GetLineType(elementExpressID);
		mapElement2ParentRelationObject[elementExpressID] = { parentTag, relationID };
		mapElement2Children[parentTag].emplace(elementExpressID, relationID);
	}
}

Download, open the sln file, compile & run:

image

Support and implementation services for web-ifc in desktop applications, as well as 3D graphics and Qt UI are available on www.IfcSplitAndMerge.com

There is also the existing web based application ecosystem and community for web-ifc: https://github.com/ThatOpen/engine_web-ifc

About

Load IFC file, traverse geometry & meta data, with super high performance & super low memory footprint

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published