mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 09:48:32 +00:00
ifcviewer: load rdb/ifc as property data source on sidecar hit
Sidecar hits skipped opening the underlying .rdb/.ifc, so ifcFile() was null and the property panel only showed cached name/type/guid. Now, after a sidecar hit, a background thread opens <stem>.rdb (preferred) or <stem>.ifc and hands the file to GeometryStreamer via setIfcFile(), with a dataSourceReady signal so the UI refreshes the current selection. Gated behind a new AppSettings::loadDataSource toggle (default on) so users can opt into geometry-only viewing; when off, the sidecar-hit thread is skipped and the stream-path ifc_file_ is released after the sidecar write completes. Also adds *.ifcview to the Add Files dialog filter so a cache can be opened directly without its source file present. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -18,10 +18,12 @@
|
||||
********************************************************************************/
|
||||
|
||||
#include "SceneLoader.h"
|
||||
#include "AppSettings.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
@@ -37,6 +39,7 @@ SceneLoader::SceneLoader(ViewportWindow* viewport, QObject* parent)
|
||||
|
||||
SceneLoader::~SceneLoader() {
|
||||
joinSidecarThread();
|
||||
joinDataSourceThreads();
|
||||
}
|
||||
|
||||
void SceneLoader::joinSidecarThread() {
|
||||
@@ -44,6 +47,13 @@ void SceneLoader::joinSidecarThread() {
|
||||
sidecar_read_thread_.join();
|
||||
}
|
||||
|
||||
void SceneLoader::joinDataSourceThreads() {
|
||||
for (auto& t : data_source_threads_) {
|
||||
if (t.joinable()) t.join();
|
||||
}
|
||||
data_source_threads_.clear();
|
||||
}
|
||||
|
||||
QString SceneLoader::filePath(uint32_t mid) const {
|
||||
auto it = models_.find(mid);
|
||||
return it == models_.end() ? QString() : it->second.file_path;
|
||||
@@ -187,10 +197,75 @@ void SceneLoader::applySidecarData(uint32_t mid, SidecarData data) {
|
||||
qint64 ms = model.load_timer.elapsed();
|
||||
emit loadedFromSidecar(mid, ms);
|
||||
|
||||
startDataSourceLoad(mid);
|
||||
|
||||
loading_model_id_ = 0;
|
||||
QTimer::singleShot(0, this, &SceneLoader::startNextLoad);
|
||||
}
|
||||
|
||||
// Match SidecarCache.cpp's sidecarPath() stem logic so we resolve the
|
||||
// data-source siblings against the same stem the sidecar was keyed on.
|
||||
static std::string pathStem(const std::string& path) {
|
||||
std::string p = path;
|
||||
while (!p.empty() && (p.back() == '/' || p.back() == '\\')) p.pop_back();
|
||||
auto slash = p.find_last_of("/\\");
|
||||
auto dot = p.find_last_of('.');
|
||||
return (dot != std::string::npos &&
|
||||
(slash == std::string::npos || dot > slash))
|
||||
? p.substr(0, dot)
|
||||
: p;
|
||||
}
|
||||
|
||||
void SceneLoader::startDataSourceLoad(uint32_t mid) {
|
||||
if (!AppSettings::instance().loadDataSource()) return;
|
||||
|
||||
auto it = models_.find(mid);
|
||||
if (it == models_.end()) return;
|
||||
|
||||
std::string original_path = it->second.file_path.toStdString();
|
||||
std::string stem = pathStem(original_path);
|
||||
|
||||
// Prefer RocksDB (foo.rdb) over SPF (foo.ifc) for fast random lookups.
|
||||
QString data_path;
|
||||
const QString rdb_candidate = QString::fromStdString(stem + ".rdb");
|
||||
const QString ifc_candidate = QString::fromStdString(stem + ".ifc");
|
||||
if (QFileInfo::exists(rdb_candidate)) {
|
||||
data_path = rdb_candidate;
|
||||
} else if (QFileInfo::exists(ifc_candidate)) {
|
||||
data_path = ifc_candidate;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string data_path_std = data_path.toStdString();
|
||||
data_source_threads_.emplace_back([this, mid, data_path_std]() {
|
||||
QElapsedTimer t; t.start();
|
||||
std::unique_ptr<ifcopenshell::file> file;
|
||||
try {
|
||||
file = std::make_unique<ifcopenshell::file>(
|
||||
data_path_std, ifcopenshell::FT_AUTODETECT, /*read_only=*/true);
|
||||
} catch (const std::exception& e) {
|
||||
qWarning(" Data source load failed: %s (%s)",
|
||||
data_path_std.c_str(), e.what());
|
||||
return;
|
||||
}
|
||||
qDebug(" Data source load: %lld ms (%s)", t.elapsed(), data_path_std.c_str());
|
||||
|
||||
auto shared = std::make_shared<std::unique_ptr<ifcopenshell::file>>(std::move(file));
|
||||
QMetaObject::invokeMethod(this, [this, mid, shared]() {
|
||||
auto it = models_.find(mid);
|
||||
if (it == models_.end()) return;
|
||||
auto* streamer = it->second.streamer;
|
||||
if (streamer == nullptr) return;
|
||||
// If the streamer already has a file (e.g. a later stream-fallback
|
||||
// path somehow populated it), don't clobber it.
|
||||
if (streamer->ifcFile() != nullptr) return;
|
||||
streamer->setIfcFile(std::move(*shared));
|
||||
emit dataSourceReady(mid);
|
||||
}, Qt::QueuedConnection);
|
||||
});
|
||||
}
|
||||
|
||||
void SceneLoader::onStreamerProgressChanged(int percent) {
|
||||
emit progressChanged(percent);
|
||||
}
|
||||
@@ -227,6 +302,13 @@ void SceneLoader::onStreamerFinished() {
|
||||
|
||||
qint64 ms = it->second.load_timer.elapsed();
|
||||
emit loadedFromStream(mid, ms);
|
||||
|
||||
// Slot(s) above run synchronously (sidecar write uses element_map_,
|
||||
// not ifcFile()); drop the parsed file now to save memory if the
|
||||
// user has opted out of keeping a property data source.
|
||||
if (!AppSettings::instance().loadDataSource()) {
|
||||
it->second.streamer->setIfcFile(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user