diff --git a/src/OpenSimCreator/Documents/Model/UndoableModelStatePair.cpp b/src/OpenSimCreator/Documents/Model/UndoableModelStatePair.cpp index 4224bf725..d7dfb4e0e 100644 --- a/src/OpenSimCreator/Documents/Model/UndoableModelStatePair.cpp +++ b/src/OpenSimCreator/Documents/Model/UndoableModelStatePair.cpp @@ -332,6 +332,11 @@ class osc::UndoableModelStatePair::Impl final { m_Scratch = std::move(p); } + void loadModel(const std::filesystem::path& path) + { + setModel(std::make_unique(path.string())); + } + UID getModelVersion() const { return m_Scratch.getModelVersion(); @@ -808,6 +813,11 @@ void osc::UndoableModelStatePair::setModel(std::unique_ptr newMo m_Impl->setModel(std::move(newModel)); } +void osc::UndoableModelStatePair::loadModel(const std::filesystem::path& p) +{ + m_Impl->loadModel(p); +} + void osc::UndoableModelStatePair::setModelVersion(UID version) { m_Impl->setModelVersion(version); diff --git a/src/OpenSimCreator/Documents/Model/UndoableModelStatePair.h b/src/OpenSimCreator/Documents/Model/UndoableModelStatePair.h index 67344fbbc..bda993423 100644 --- a/src/OpenSimCreator/Documents/Model/UndoableModelStatePair.h +++ b/src/OpenSimCreator/Documents/Model/UndoableModelStatePair.h @@ -97,6 +97,7 @@ namespace osc // note: mutating anything may trigger an automatic undo/redo save if `isDirty` returns `true` OpenSim::Model& updModel(); void setModel(std::unique_ptr); + void loadModel(const std::filesystem::path&); void setModelVersion(UID); private: diff --git a/src/OpenSimCreator/UI/PreviewExperimentalData/PreviewExperimentalDataTab.cpp b/src/OpenSimCreator/UI/PreviewExperimentalData/PreviewExperimentalDataTab.cpp index 491ac581c..c4b7949e9 100644 --- a/src/OpenSimCreator/UI/PreviewExperimentalData/PreviewExperimentalDataTab.cpp +++ b/src/OpenSimCreator/UI/PreviewExperimentalData/PreviewExperimentalDataTab.cpp @@ -1,10 +1,14 @@ #include "PreviewExperimentalDataTab.h" #include +#include +#include #include #include -#include #include +#include +#include +#include #include #include @@ -22,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +35,7 @@ #include #include +#include #include #include #include @@ -37,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -207,6 +214,9 @@ namespace DataSeriesPattern::forDatatype("_1", "_2", "_3", "_4"), DataSeriesPattern::forDatatype("_1", "_2", "_3"), DataSeriesPattern::forDatatype("_fx", "_fy", "_fz"), + + // extra + DataSeriesPattern::forDatatype("_x", "_y", "_z"), }; }; @@ -254,6 +264,7 @@ namespace return StorageSchema{std::move(annotations)}; } + const std::vector& annotations() const { return m_Annotations; } private: explicit StorageSchema(std::vector annotations) : m_Annotations{std::move(annotations)} @@ -339,14 +350,22 @@ namespace { // Refers to one data series within one annotated motion. class DataSeries final : public OpenSim::ModelComponent { + OpenSim_DECLARE_CONCRETE_OBJECT(DataSeries, OpenSim::ModelComponent); public: + OpenSim_DECLARE_PROPERTY(type, std::string, "the datatype of the data series"); + OpenSim_DECLARE_PROPERTY(column_offset, int, "index of the first column that contains this data series"); + explicit DataSeries( const std::shared_ptr& storage, const DataSeriesAnnotation& annotation) : m_Storage{storage}, m_Annotation{annotation} - {} + { + setName(annotation.label); + constructProperty_type(std::string{labelFor(annotation.dataType)}); + constructProperty_column_offset(annotation.firstColumnOffset); + } private: void generateDecorations( bool, @@ -369,20 +388,24 @@ namespace class AnnotatedMotion final : public OpenSim::ModelComponent { OpenSim_DECLARE_CONCRETE_OBJECT(AnnotatedMotion, OpenSim::ModelComponent); public: - - // Returns an `AnnotationMotion` that was loaded from the given filesystem + // Constructs an `AnnotationMotion` that was loaded from the given filesystem // path, or throws an `std::exception` if any error occurs. - static AnnotatedMotion fromFile(const std::filesystem::path& path) - { - return AnnotatedMotion{OpenSim::Storage{path.string()}}; - } + explicit AnnotatedMotion(const std::filesystem::path& path) : + AnnotatedMotion{std::make_shared(path.string())} + {} + private: - explicit AnnotatedMotion(OpenSim::Storage storage) : + explicit AnnotatedMotion(std::shared_ptr storage) : m_Storage{std::move(storage)}, - m_Schema{StorageSchema::parse(m_Storage)} - {} + m_Schema{StorageSchema::parse(*m_Storage)} + { + setName(m_Storage->getName()); - OpenSim::Storage m_Storage; + for (const auto& annotation : m_Schema.annotations()) { + addComponent(new DataSeries{m_Storage, annotation}); + } + } + std::shared_ptr m_Storage; StorageSchema m_Schema; }; } @@ -394,13 +417,61 @@ namespace class PreviewExperimentalDataUiState final { public: const std::shared_ptr& updSharedModelPtr() const { return m_Model; } + + void loadModelFile(const std::filesystem::path& p) + { + // TODO: port any motions over? + // TODO: rescrub? + m_Model->loadModel(p); + } + + void loadMotionFiles(std::vector paths) + { + if (paths.empty()) { + return; + } + + OpenSim::Model& model = m_Model->updModel(); + AnnotatedMotion* lastMotion = nullptr; + for (const std::filesystem::path& path : paths) { + lastMotion = new AnnotatedMotion{path}; + model.addModelComponent(lastMotion); + } + m_Model->setSelected(lastMotion); + InitializeModel(model); + InitializeState(model); + // TODO: rescrub + m_Model->commit("loaded motions"); + } private: std::shared_ptr m_Model = std::make_shared(); double m_ScrubTime = 0.0; }; + + class ReadonlyPropertiesEditorPanel final : public StandardPanelImpl { + public: + ReadonlyPropertiesEditorPanel( + std::string_view panelName, + IPopupAPI* api, + std::shared_ptr targetModel) : + + StandardPanelImpl{panelName}, + m_PropertiesEditor{api, targetModel, [model = targetModel](){ return model->getSelected(); }} + {} + private: + void impl_draw_content() final + { + ui::begin_disabled(); + m_PropertiesEditor.onDraw(); + ui::end_disabled(); + } + ObjectPropertiesEditor m_PropertiesEditor; + }; } -class osc::PreviewExperimentalDataTab::Impl final : public StandardTabImpl { +class osc::PreviewExperimentalDataTab::Impl final : + public StandardTabImpl, + public IPopupAPI { public: explicit Impl(const ParentPtr&) : StandardTabImpl{ICON_FA_DOT_CIRCLE " Experimental Data"} @@ -433,6 +504,13 @@ class osc::PreviewExperimentalDataTab::Impl final : public StandardTabImpl { return std::make_shared(panelName); } ); + m_PanelManager->register_toggleable_panel( + "Properties", + [this](std::string_view panelName) + { + return std::make_shared(panelName, this, m_UiState->updSharedModelPtr()); + } + ); } private: void impl_on_mount() final @@ -462,11 +540,23 @@ class osc::PreviewExperimentalDataTab::Impl final : public StandardTabImpl { m_PanelManager->on_draw(); } + void implPushPopup(std::unique_ptr) final + { + } + void drawToolbar() { if (BeginToolbar("##PreviewExperimentalDataToolbar", Vec2{5.0f, 5.0f})) { - ui::draw_button("todo"); + if (ui::draw_button("load model")) { + if (const auto path = prompt_user_to_select_file({"osim"})) { + m_UiState->loadModelFile(*path); + } + } + ui::same_line(); + if (ui::draw_button("load force")) { + m_UiState->loadMotionFiles(prompt_user_to_select_files({"sto", "mot", "trc"})); + } } ui::end_panel(); } diff --git a/src/OpenSimCreator/UI/Shared/ObjectPropertiesEditor.h b/src/OpenSimCreator/UI/Shared/ObjectPropertiesEditor.h index 8fc984fa4..f9ed0ff53 100644 --- a/src/OpenSimCreator/UI/Shared/ObjectPropertiesEditor.h +++ b/src/OpenSimCreator/UI/Shared/ObjectPropertiesEditor.h @@ -26,7 +26,7 @@ namespace osc ~ObjectPropertiesEditor() noexcept; // does not actually apply any property changes - the caller should check+apply the return value - [[nodiscard]] std::optional onDraw(); + std::optional onDraw(); private: class Impl;