mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-01 09:26:26 +00:00
ifcviewer-web: stream getObjects() per model instead of one scene-sized JSON
getObjects() is on every real host page's path — a click hands back an object id, and resolving it to a GlobalId/name/type needs the element tables (the JS layer also builds its GUID index from this call). The implementation materialised a vector of ElementRef (three fresh std::strings per element), serialised the entire scene into one JSON string grown by +=, and UTF8ToString'd the whole thing — several hundred MB simultaneously alive at ~600k elements. The wasm heap never returns pages, so that transient became the session's permanent floor. ViewportCore::visitModelElements hands out one model's elements as slices into its string table (no per-element copies), and the export serialises straight from those, one model per batch, reusing one string whose capacity grows only to the largest model. The JS side accumulates batches and resolves the same array as before — the page API is unchanged. Peak is now one model's JSON instead of the scene's. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4514,6 +4514,36 @@ std::vector<ViewportCore::ElementRef> ViewportCore::elements() const {
|
||||
return out;
|
||||
}
|
||||
|
||||
void ViewportCore::visitModelElements(
|
||||
int model_index,
|
||||
const std::function<void(const ElementSlices&)>& visit) const {
|
||||
const std::vector<std::uint32_t> ids = modelIdsInLoadOrder();
|
||||
if (model_index < 0 || std::size_t(model_index) >= ids.size()) return;
|
||||
auto it = models_gpu_.find(ids[std::size_t(model_index)]);
|
||||
if (it == models_gpu_.end()) return;
|
||||
const ModelGpuData& m = it->second;
|
||||
auto slice = [&m](std::uint32_t offset, std::uint32_t length,
|
||||
const char*& out_ptr, std::uint32_t& out_len) {
|
||||
// Out-of-range or zero-length offsets mean "no string was written".
|
||||
if (length > 0 && std::size_t(offset) + length <= m.string_table.size()) {
|
||||
out_ptr = m.string_table.data() + offset;
|
||||
out_len = length;
|
||||
} else {
|
||||
out_ptr = nullptr;
|
||||
out_len = 0;
|
||||
}
|
||||
};
|
||||
for (const ElementTableRecord& e : m.elements) {
|
||||
ElementSlices out;
|
||||
out.object_id = e.object_id;
|
||||
out.source_id = m.web_source_id;
|
||||
slice(e.guid_offset, e.guid_length, out.guid, out.guid_len);
|
||||
slice(e.name_offset, e.name_length, out.name, out.name_len);
|
||||
slice(e.type_offset, e.type_length, out.type, out.type_len);
|
||||
visit(out);
|
||||
}
|
||||
}
|
||||
|
||||
bool ViewportCore::elementForObject(std::uint32_t object_id, ElementRef& out) const {
|
||||
InstanceCompose::InstanceLookup lk;
|
||||
if (!findInstance(object_id, lk)) return false;
|
||||
|
||||
Reference in New Issue
Block a user