From 3a67cf6b9ebb537c7bf4331d578afe6a5f077875 Mon Sep 17 00:00:00 2001 From: StasJ Date: Wed, 18 Mar 2026 23:56:22 -0600 Subject: [PATCH] Fix #3802 --- apps/pythonapi/CMakeLists.txt | 4 +- apps/pythonapi/example_scripts/CMakeLists.txt | 122 ++++++++++++++++++ .../animation_example.py | 0 .../annotation_example.py | 0 .../camera_example.py | 0 .../dataset_example.py | 0 .../example_utils.py | 0 .../flow_example.py | 0 .../numpy_example.py | 0 .../rotate_video.py | 0 .../transfer_function_example.py | 0 .../visualizer_widget_example.py | 0 .../workflow_example.py | 0 .../xarray_example.py | 0 apps/pythonapi/examples/CMakeLists.txt | 72 ----------- apps/pythonapi/vapor/__init__.py | 4 +- apps/pythonapi/vapor/config.py | 13 ++ apps/pythonapi/vapor/session.py | 4 +- conda/vapor/meta.yaml | 1 + lib/render/WireFrameRenderer.cpp | 1 - lib/vapi/RenderManager.cpp | 17 ++- lib/vapi/RenderManager.h | 6 +- lib/vapi/Session.cpp | 5 +- lib/vapi/Session.h | 3 +- 24 files changed, 168 insertions(+), 84 deletions(-) create mode 100644 apps/pythonapi/example_scripts/CMakeLists.txt rename apps/pythonapi/{examples => example_scripts}/animation_example.py (100%) rename apps/pythonapi/{examples => example_scripts}/annotation_example.py (100%) rename apps/pythonapi/{examples => example_scripts}/camera_example.py (100%) rename apps/pythonapi/{examples => example_scripts}/dataset_example.py (100%) rename apps/pythonapi/{examples => example_scripts}/example_utils.py (100%) rename apps/pythonapi/{examples => example_scripts}/flow_example.py (100%) rename apps/pythonapi/{examples => example_scripts}/numpy_example.py (100%) rename apps/pythonapi/{examples => example_scripts}/rotate_video.py (100%) rename apps/pythonapi/{examples => example_scripts}/transfer_function_example.py (100%) rename apps/pythonapi/{examples => example_scripts}/visualizer_widget_example.py (100%) rename apps/pythonapi/{examples => example_scripts}/workflow_example.py (100%) rename apps/pythonapi/{examples => example_scripts}/xarray_example.py (100%) delete mode 100644 apps/pythonapi/examples/CMakeLists.txt diff --git a/apps/pythonapi/CMakeLists.txt b/apps/pythonapi/CMakeLists.txt index 45c95887ec..2dbdf7ecec 100644 --- a/apps/pythonapi/CMakeLists.txt +++ b/apps/pythonapi/CMakeLists.txt @@ -58,8 +58,8 @@ endfunction() copy(vapor ${PYTHON_API_DIR} EXCLUDE cmake.py __pycache__ widget.js jquery.js) copy(setup.py ${PYTHON_API_DIR}) -copy(examples ${PYTHON_API_DIR} EXCLUDE CMakeLists.txt) -add_subdirectory (examples) +copy(example_scripts ${PYTHON_API_DIR} EXCLUDE CMakeLists.txt) +add_subdirectory (example_scripts) add_custom_command( OUTPUT "${PYTHON_API_DIR}/vapor/widget.js" diff --git a/apps/pythonapi/example_scripts/CMakeLists.txt b/apps/pythonapi/example_scripts/CMakeLists.txt new file mode 100644 index 0000000000..59e4a41711 --- /dev/null +++ b/apps/pythonapi/example_scripts/CMakeLists.txt @@ -0,0 +1,122 @@ +set(ALL_NOTEBOOKS "") +set(ALL_MARIMO_NOTEBOOKS "") + +function(NOTEBOOK FILE OUT) + include(CMakeParseArguments) + cmake_parse_arguments(PARSE_ARGV 2 + "NOTEBOOK" + "" + "" + "" + ) + + get_filename_component(FILE "${FILE}" REALPATH) + + add_custom_command( + OUTPUT "${OUT}" + DEPENDS "${FILE}" + COMMAND cat "${FILE}" | jupytext --to ipynb > "${OUT}" + ) + + list(APPEND ALL_NOTEBOOKS "${OUT}") + set(ALL_NOTEBOOKS "${ALL_NOTEBOOKS}" PARENT_SCOPE) +endfunction() + + +function(CONV_MARIMO FILE OUT) + include(CMakeParseArguments) + cmake_parse_arguments(PARSE_ARGV 2 + "CONV_MARIMO" + "" + "" + "" + ) + + get_filename_component(FILE "${FILE}" REALPATH) + + add_custom_command( + OUTPUT "${OUT}" + DEPENDS "${FILE}" + COMMAND marimo -q -y convert "${FILE}" -o "${OUT}" + ) + + list(APPEND ALL_MARIMO_NOTEBOOKS "${OUT}") + set(ALL_MARIMO_NOTEBOOKS "${ALL_MARIMO_NOTEBOOKS}" PARENT_SCOPE) +endfunction() + + +# copy(vapor ${PYTHON_API_DIR} EXCLUDE cmake.py __pycache__) +# copy(setup.py ${PYTHON_API_DIR}) + +find_program(JUPYTEXT "jupytext") +if (JUPYTEXT) + set(EXAMPLE_NOTEBOOK_OUT_DIR "${PYTHON_API_DIR}/example_jupyter_notebooks") + file(MAKE_DIRECTORY ${EXAMPLE_NOTEBOOK_OUT_DIR}) + + copy(example_utils.py ${EXAMPLE_NOTEBOOK_OUT_DIR}) + notebook(numpy_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/numpy_example.ipynb) + notebook(xarray_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/xarray_example.ipynb) + notebook(dataset_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/dataset_example.ipynb) + notebook(flow_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/flow_example.ipynb) + notebook(annotation_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/annotation_example.ipynb) + notebook(camera_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/camera_example.ipynb) + notebook(transfer_function_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/transfer_function_example.ipynb) + notebook(workflow_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/workflow_example.ipynb) + notebook(animation_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/animation_example.ipynb) + notebook(visualizer_widget_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/visualizer_widget_example.ipynb) + notebook(rotate_video.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/rotate_video.ipynb) + + add_custom_target( + example_notebooks + ALL + DEPENDS ${ALL_NOTEBOOKS} + ) +else() + message(WARNING "jupytext not found") + message(WARNING "Skipping generation of jupyter notebooks") +endif() + +find_program(MARIMO "marimo") +if (JUPYTEXT AND MARIMO) + set(EXAMPLE_MARIMO_OUT_DIR "${PYTHON_API_DIR}/example_marimo_notebooks") + file(MAKE_DIRECTORY ${EXAMPLE_MARIMO_OUT_DIR}) + + copy(example_utils.py ${EXAMPLE_MARIMO_OUT_DIR}) + conv_marimo(${EXAMPLE_NOTEBOOK_OUT_DIR}/numpy_example.ipynb ${EXAMPLE_MARIMO_OUT_DIR}/numpy_example.py) + conv_marimo(${EXAMPLE_NOTEBOOK_OUT_DIR}/xarray_example.ipynb ${EXAMPLE_MARIMO_OUT_DIR}/xarray_example.py) + conv_marimo(${EXAMPLE_NOTEBOOK_OUT_DIR}/dataset_example.ipynb ${EXAMPLE_MARIMO_OUT_DIR}/dataset_example.py) + conv_marimo(${EXAMPLE_NOTEBOOK_OUT_DIR}/flow_example.ipynb ${EXAMPLE_MARIMO_OUT_DIR}/flow_example.py) + conv_marimo(${EXAMPLE_NOTEBOOK_OUT_DIR}/annotation_example.ipynb ${EXAMPLE_MARIMO_OUT_DIR}/annotation_example.py) + conv_marimo(${EXAMPLE_NOTEBOOK_OUT_DIR}/camera_example.ipynb ${EXAMPLE_MARIMO_OUT_DIR}/camera_example.py) + conv_marimo(${EXAMPLE_NOTEBOOK_OUT_DIR}/transfer_function_example.ipynb ${EXAMPLE_MARIMO_OUT_DIR}/transfer_function_example.py) + conv_marimo(${EXAMPLE_NOTEBOOK_OUT_DIR}/workflow_example.ipynb ${EXAMPLE_MARIMO_OUT_DIR}/workflow_example.py) + conv_marimo(${EXAMPLE_NOTEBOOK_OUT_DIR}/animation_example.ipynb ${EXAMPLE_MARIMO_OUT_DIR}/animation_example.py) + conv_marimo(${EXAMPLE_NOTEBOOK_OUT_DIR}/visualizer_widget_example.ipynb ${EXAMPLE_MARIMO_OUT_DIR}/visualizer_widget_example.py) + conv_marimo(${EXAMPLE_NOTEBOOK_OUT_DIR}/rotate_video.ipynb ${EXAMPLE_MARIMO_OUT_DIR}/rotate_video.py) + + add_custom_target( + example_marimo_notebooks + ALL + DEPENDS ${ALL_MARIMO_NOTEBOOKS} + ) +else() + message(WARNING "marimo (or jupytext) not found") + message(WARNING "Skipping generation of marimo notebooks") +endif() + +if (CONDA_BUILD) + set(CONDA_SITE_PACKAGE_DIR "$ENV{SP_DIR}") + install( + DIRECTORY "${PYTHON_API_DIR}/example_scripts" + DESTINATION "${CONDA_SITE_PACKAGE_DIR}/vapor" + COMPONENT Dependencies + ) + if (JUPYTEXT) + install( + DIRECTORY "${EXAMPLE_NOTEBOOK_OUT_DIR}" + DESTINATION "${CONDA_SITE_PACKAGE_DIR}/vapor" + COMPONENT Dependencies + ) + endif() +endif() + diff --git a/apps/pythonapi/examples/animation_example.py b/apps/pythonapi/example_scripts/animation_example.py similarity index 100% rename from apps/pythonapi/examples/animation_example.py rename to apps/pythonapi/example_scripts/animation_example.py diff --git a/apps/pythonapi/examples/annotation_example.py b/apps/pythonapi/example_scripts/annotation_example.py similarity index 100% rename from apps/pythonapi/examples/annotation_example.py rename to apps/pythonapi/example_scripts/annotation_example.py diff --git a/apps/pythonapi/examples/camera_example.py b/apps/pythonapi/example_scripts/camera_example.py similarity index 100% rename from apps/pythonapi/examples/camera_example.py rename to apps/pythonapi/example_scripts/camera_example.py diff --git a/apps/pythonapi/examples/dataset_example.py b/apps/pythonapi/example_scripts/dataset_example.py similarity index 100% rename from apps/pythonapi/examples/dataset_example.py rename to apps/pythonapi/example_scripts/dataset_example.py diff --git a/apps/pythonapi/examples/example_utils.py b/apps/pythonapi/example_scripts/example_utils.py similarity index 100% rename from apps/pythonapi/examples/example_utils.py rename to apps/pythonapi/example_scripts/example_utils.py diff --git a/apps/pythonapi/examples/flow_example.py b/apps/pythonapi/example_scripts/flow_example.py similarity index 100% rename from apps/pythonapi/examples/flow_example.py rename to apps/pythonapi/example_scripts/flow_example.py diff --git a/apps/pythonapi/examples/numpy_example.py b/apps/pythonapi/example_scripts/numpy_example.py similarity index 100% rename from apps/pythonapi/examples/numpy_example.py rename to apps/pythonapi/example_scripts/numpy_example.py diff --git a/apps/pythonapi/examples/rotate_video.py b/apps/pythonapi/example_scripts/rotate_video.py similarity index 100% rename from apps/pythonapi/examples/rotate_video.py rename to apps/pythonapi/example_scripts/rotate_video.py diff --git a/apps/pythonapi/examples/transfer_function_example.py b/apps/pythonapi/example_scripts/transfer_function_example.py similarity index 100% rename from apps/pythonapi/examples/transfer_function_example.py rename to apps/pythonapi/example_scripts/transfer_function_example.py diff --git a/apps/pythonapi/examples/visualizer_widget_example.py b/apps/pythonapi/example_scripts/visualizer_widget_example.py similarity index 100% rename from apps/pythonapi/examples/visualizer_widget_example.py rename to apps/pythonapi/example_scripts/visualizer_widget_example.py diff --git a/apps/pythonapi/examples/workflow_example.py b/apps/pythonapi/example_scripts/workflow_example.py similarity index 100% rename from apps/pythonapi/examples/workflow_example.py rename to apps/pythonapi/example_scripts/workflow_example.py diff --git a/apps/pythonapi/examples/xarray_example.py b/apps/pythonapi/example_scripts/xarray_example.py similarity index 100% rename from apps/pythonapi/examples/xarray_example.py rename to apps/pythonapi/example_scripts/xarray_example.py diff --git a/apps/pythonapi/examples/CMakeLists.txt b/apps/pythonapi/examples/CMakeLists.txt deleted file mode 100644 index 00454e7070..0000000000 --- a/apps/pythonapi/examples/CMakeLists.txt +++ /dev/null @@ -1,72 +0,0 @@ -set(ALL_NOTEBOOKS "") - -function(NOTEBOOK FILE OUT) - include(CMakeParseArguments) - cmake_parse_arguments(PARSE_ARGV 2 - "NOTEBOOK" - "" - "" - "" - ) - - get_filename_component(FILE "${FILE}" REALPATH) - - add_custom_command( - OUTPUT "${OUT}" - DEPENDS "${FILE}" - COMMAND cat "${FILE}" | jupytext --to ipynb > "${OUT}" - ) - - list(APPEND ALL_NOTEBOOKS "${OUT}") - set(ALL_NOTEBOOKS "${ALL_NOTEBOOKS}" PARENT_SCOPE) -endfunction() - - -# copy(vapor ${PYTHON_API_DIR} EXCLUDE cmake.py __pycache__) -# copy(setup.py ${PYTHON_API_DIR}) - -find_program(JUPYTEXT "jupytext") -if (JUPYTEXT) - set(EXAMPLE_NOTEBOOK_OUT_DIR "${PYTHON_API_DIR}/example_notebooks") - file(MAKE_DIRECTORY ${EXAMPLE_NOTEBOOK_OUT_DIR}) - - copy(example_utils.py ${EXAMPLE_NOTEBOOK_OUT_DIR}) - notebook(numpy_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/numpy_example.ipynb) - notebook(xarray_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/xarray_example.ipynb) - notebook(dataset_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/dataset_example.ipynb) - notebook(flow_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/flow_example.ipynb) - notebook(annotation_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/annotation_example.ipynb) - notebook(camera_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/camera_example.ipynb) - notebook(transfer_function_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/transfer_function_example.ipynb) - notebook(workflow_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/workflow_example.ipynb) - notebook(animation_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/animation_example.ipynb) - notebook(visualizer_widget_example.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/visualizer_widget_example.ipynb) - notebook(rotate_video.py ${EXAMPLE_NOTEBOOK_OUT_DIR}/rotate_video.ipynb) - - - add_custom_target( - example_notebooks - ALL - DEPENDS ${ALL_NOTEBOOKS} - ) -else() - message(WARNING "jupytext not found") - message(WARNING "Skipping generation of jupyter notebooks") -endif() - -if (CONDA_BUILD) - set(CONDA_SITE_PACKAGE_DIR "$ENV{SP_DIR}") - install( - DIRECTORY "${PYTHON_API_DIR}/examples" - DESTINATION "${CONDA_SITE_PACKAGE_DIR}/vapor" - COMPONENT Dependencies - ) - if (JUPYTEXT) - install( - DIRECTORY "${EXAMPLE_NOTEBOOK_OUT_DIR}" - DESTINATION "${CONDA_SITE_PACKAGE_DIR}/vapor" - COMPONENT Dependencies - ) - endif() -endif() - diff --git a/apps/pythonapi/vapor/__init__.py b/apps/pythonapi/vapor/__init__.py index b8c8890918..068fa72945 100644 --- a/apps/pythonapi/vapor/__init__.py +++ b/apps/pythonapi/vapor/__init__.py @@ -21,7 +21,7 @@ link.include('vapor/MyPython.h') link.Wasp.MyPython.IsRunningFromPython = True -link.include('vapor/GLContextProvider.h') -ctx = link.GLContextProvider.CreateContext() +link.include('vapor/RenderManager.h') +ctx = link.RenderManager.GetOSGLContext() print("OpenGL", ctx.GetVersion()) diff --git a/apps/pythonapi/vapor/config.py b/apps/pythonapi/vapor/config.py index a9828c6059..f9c786018f 100644 --- a/apps/pythonapi/vapor/config.py +++ b/apps/pythonapi/vapor/config.py @@ -106,3 +106,16 @@ def IsRunningFromIPython(): return True except NameError: return False + + +def IsRunningFromMarimo(): + try: + import marimo as mo + return mo.app_meta().mode in ("edit", "run") + except: + return False + + +def IsRunningFromNotebook(): + return IsRunningFromIPython() or IsRunningFromMarimo() + diff --git a/apps/pythonapi/vapor/session.py b/apps/pythonapi/vapor/session.py index 8cc5644ad8..6d351bfaaa 100644 --- a/apps/pythonapi/vapor/session.py +++ b/apps/pythonapi/vapor/session.py @@ -13,7 +13,7 @@ @link.FixModuleOwnership class Session(link.Session): def __init__(self): - super().__init__() + super().__init__(True) self.ce = super()._controlExec def NewRenderer(self, Class:Renderer, datasetName:str) -> Renderer: @@ -85,7 +85,7 @@ def Show(self): img = self.RenderToImage() - if config.IsRunningFromIPython(): + if config.IsRunningFromNotebook(): display(img) else: img.show() diff --git a/conda/vapor/meta.yaml b/conda/vapor/meta.yaml index 31262830f4..9eb8580774 100644 --- a/conda/vapor/meta.yaml +++ b/conda/vapor/meta.yaml @@ -22,6 +22,7 @@ requirements: - jupytext=1.13.8 # Generate notebooks - doxygen=1.9.1 - esbuild + - git # Both # (Needs to be specified manually in both rather than just in host because otherwise conda will install multiple versions of some packages and break the install) # ========================================== diff --git a/lib/render/WireFrameRenderer.cpp b/lib/render/WireFrameRenderer.cpp index 03b384aac0..7362b96353 100644 --- a/lib/render/WireFrameRenderer.cpp +++ b/lib/render/WireFrameRenderer.cpp @@ -152,7 +152,6 @@ void WireFrameRenderer::_drawCell(const GLuint *cellNodeIndices, int n, bool lay // void WireFrameRenderer::_buildCacheVertices(const Grid *grid, const Grid *heightGrid, vector &nodeMap, bool *GPUOutOfMemory) const { - WireFrameParams* wfp = (WireFrameParams *)GetActiveParams(); double mv = grid->GetMissingValue(); auto tmp = grid->GetDimensions(); auto dims = std::vector{tmp[0], tmp[1], tmp[2]}; diff --git a/lib/vapi/RenderManager.cpp b/lib/vapi/RenderManager.cpp index e583fad6a5..e997af954f 100644 --- a/lib/vapi/RenderManager.cpp +++ b/lib/vapi/RenderManager.cpp @@ -9,6 +9,7 @@ #include #include +#include #define INCLUDE_DEPRECATED_LEGACY_VECTOR_MATH #include @@ -16,7 +17,10 @@ using namespace VAPoR; -RenderManager::RenderManager(ControlExec *ce) : _controlExec(ce) {} +GLContext *RenderManager::_glContext = nullptr; + + +RenderManager::RenderManager(ControlExec *ce, bool useOSGLContext) : _controlExec(ce), _useOSGLContext(useOSGLContext) {} RenderManager::~RenderManager() { @@ -193,6 +197,9 @@ void RenderManager::setUpModelViewMatrix() int RenderManager::Render(String imagePath, bool fast) { + if (_useOSGLContext) + GetOSGLContext()->MakeCurrent(); + _controlExec->SyncWithParams(); // GL_ERR_BREAK(); @@ -263,4 +270,12 @@ String RenderManager::GetWinName() const return names[0]; } +GLContext *RenderManager::GetOSGLContext() +{ + if (_glContext == nullptr) + _glContext = GLContextProvider::CreateContext(); + return _glContext; +} + + VAPoR::ViewpointParams *RenderManager::getViewpointParams() const { return _controlExec->GetParamsMgr()->GetViewpointParams(GetWinName()); } diff --git a/lib/vapi/RenderManager.h b/lib/vapi/RenderManager.h index 4512426afd..d222ac04cf 100644 --- a/lib/vapi/RenderManager.h +++ b/lib/vapi/RenderManager.h @@ -1,6 +1,7 @@ #pragma once #include +#include //! \class RenderManager //! \ingroup VAPI @@ -9,14 +10,17 @@ class RenderManager { ControlExec *_controlExec; GLManager * _glManager = nullptr; + bool _useOSGLContext = false; + static GLContext * _glContext; public: - RenderManager(ControlExec *ce); + RenderManager(ControlExec *ce, bool useOSGLContext = false); ~RenderManager(); int Render(String imagePath, bool fast=false); void SetResolution(int width, int height); vector GetResolution() const; String GetWinName() const; + static GLContext *GetOSGLContext(); private: void getNearFarDist(const double posVec[3], const double dirVec[3], double &boxNear, double &boxFar); diff --git a/lib/vapi/Session.cpp b/lib/vapi/Session.cpp index 639b8e0d63..aee8d2ee6f 100644 --- a/lib/vapi/Session.cpp +++ b/lib/vapi/Session.cpp @@ -14,7 +14,8 @@ using namespace VAPoR; -Session::Session() +Session::Session(bool useOSGLContext) +: _useOSGLContext(useOSGLContext) { vector myParams; myParams.push_back(GUIStateParams::GetClassType()); @@ -148,7 +149,7 @@ void Session::Reset() if (_renderManager) delete _renderManager; - _renderManager = new RenderManager(_controlExec); + _renderManager = new RenderManager(_controlExec, _useOSGLContext); _controlExec->GetParamsMgr()->UndoRedoClear(); } diff --git a/lib/vapi/Session.h b/lib/vapi/Session.h index e3494f7995..e3fb0707e1 100644 --- a/lib/vapi/Session.h +++ b/lib/vapi/Session.h @@ -13,8 +13,9 @@ class Session { public: ControlExec * _controlExec = nullptr; RenderManager *_renderManager = nullptr; + bool _useOSGLContext = false; - Session(); + Session(bool useOSGLContext = false); virtual ~Session(); void CloseDataset(String name); void CloseAllDatasets();