/******************************************************************************** * * * This file is part of IfcOpenShell. * * * * IfcOpenShell is free software: you can redistribute it and/or modify * * it under the terms of the Lesser GNU General Public License as published by * * the Free Software Foundation, either version 3.0 of the License, or * * (at your option) any later version. * * * * IfcOpenShell is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * Lesser GNU General Public License for more details. * * * * You should have received a copy of the Lesser GNU General Public License * * along with this program. If not, see . * * * ********************************************************************************/ #ifndef WEBFEDERATION_H #define WEBFEDERATION_H #include "FederationMath.h" #include #include #include class ViewportCore; // Federation state for the web viewer: the concepts a host page needs to place // several models in one scene — a federation unit, a false origin, and a // per-model transform and display name. // // Deliberately NOT an .ifcfed reader. The desktop Federation class is a // document model (QObject, JSON persistence, groups, cloud manifests) and its // model sources are local filesystem paths, which mean nothing in a browser. // What the web needs is the underlying concepts, exposed so JS can drive them; // a host page that wants .ifcfed can parse the JSON itself and call these. // // Models are keyed by the JS-side source id, NOT the session model id. The // source id exists synchronously the moment a File or URL is registered, // whereas the session model id is minted deep inside the async range-read // chain. Keying on the source id is what lets a caller set a transform BEFORE // the load finishes, so the model appears already in place instead of jumping // once its state is applied afterwards. class WebFederation { public: explicit WebFederation(ViewportCore& core) : core_(core) {} // ---- Federation-wide ----------------------------------------------- void setConfig(const FederationConfig& cfg); const FederationConfig& config() const { return config_; } // Marks the origin as explicitly authored, which suppresses the automatic // guess in onModelLoaded. A host that sets an origin means it. void setFalseOrigin(const FederatedFalseOrigin& origin); const FederatedFalseOrigin& falseOrigin() const { return false_origin_; } bool falseOriginIsExplicit() const { return false_origin_explicit_; } // ---- Per-model ------------------------------------------------------- void setModelTransformation(int source_id, const ModelTransformation& xf); void clearModelTransformation(int source_id); void setModelName(int source_id, std::string name); std::string modelName(int source_id) const; // Session model id for a source, or 0 when that source has not finished // loading. Session ids start at 1, so 0 is unambiguous. std::uint32_t sessionModelId(int source_id) const; // ---- Lifecycle ------------------------------------------------------- // Call when a sidecar load completes. Binds the source to its session model // id, applies whatever state was staged against the source id, and — for // the first model only, and only when no origin was set explicitly — runs // the false-origin guess. void onModelLoaded(int source_id, std::uint32_t session_model_id); // A model that did not come from a JS source — the embedded sample, read // synchronously from MEMFS. It has no source id and so cannot carry a // per-model transform (a host that wants to place a model adds it as a // source), but it must still take part in the false-origin guess. // Otherwise a page showing the sample renders it unshifted, which for a // georeferenced model means out at its surveyor coordinates. void onModelLoadedWithoutSource(std::uint32_t session_model_id); // Drop all state. Pairs with ViewportCore::resetScene so a fresh // federation does not inherit the previous one's origin or transforms. void clear(); private: // Push the composed false-origin matrix; every model recomposes against it. void applyFalseOrigin(); // Push one model's composed transform. No-op until the model has loaded, // since composing needs its georef. void applyModelTransformation(int source_id); // Position + grid-north heading that puts the first model near the origin // instead of out at its surveyor coordinates. void guessFalseOriginFrom(std::uint32_t session_model_id); struct ModelState { std::uint32_t session_model_id = 0; // 0 until loaded std::string name; ModelTransformation transformation; bool has_transformation = false; }; ViewportCore& core_; FederationConfig config_; FederatedFalseOrigin false_origin_; bool false_origin_explicit_ = false; bool guessed_false_origin_ = false; std::unordered_map models_; }; #endif // WEBFEDERATION_H