15 Commits

Author SHA1 Message Date
nan
d9ca6de09f feat: Remove unused asset library removal function 2026-07-10 21:21:38 +08:00
nan
a372f44655 Merge branch 'main' of https://github.com/OmooLab/BioxelNodes into 5.0 2026-07-09 13:18:20 +08:00
Nan
d216bdf138 Keep asset library cleanup on unregister (#64)
* feat: Add Bioxel asset catalog and layer management functionality

- Introduced a new asset catalog definition file for Blender to manage Bioxel assets.
- Implemented layer caching and snapshot functionality in `layer.py`, allowing for VDB file generation and low-resolution snapshots.
- Created node management functions in `node.py` for handling Bioxel nodes within Blender's Geometry Node Editor.
- Added an operator to extract mesh data from selected nodes in `structure.py`.
- Developed UI panels in `panels.py` for layer management, including import options and layer library display.

* Add unit tests for bioxel module and documentation

- Created README.md for test instructions and categories.
- Implemented comprehensive unit tests for the Data, Layer, and utility functions in the bioxel module.
- Added tests for reading NRRD and DICOM formats, data properties, and edge cases.
- Included tests for real data files to ensure functionality with actual datasets.

* feat: Update .gitattributes and .gitignore for improved asset management and ignore patterns

chore: Add app.json and omoolab.css for Obsidian integration

refactor: Clean up AGENTS.md and remove outdated test files

* feat: Update .gitignore to include scipy_ndimage directory

* feat: Update release workflow and manifest for improved dependency management and layer handling

* feat: Update .gitignore and add binary files for scipy_ndimage across multiple platforms

* feat: Add OPSX commands for archiving, exploring, and proposing changes

* feat: Refactor asset library management and update manifest for improved organization

* feat: Downgrade h5py version to 3.14.0 in requirements and manifest

* feat: Update Help operator and panel labels to use English terminology

* Translate documentation to Chinese, add support format details, and enhance styling with new fonts. Update mkdocs configuration for multilingual support and improve package dependencies.

* feat: Remove default version setting from extra configuration in mkdocs.yml

* fix: Update documentation links to point to the correct URLs for Chinese and English versions

* Add documentation for basic usage, supported formats, and performance tips

- Created English and Chinese documentation for basic usage of the add-on, including data download, import, extraction, and rendering steps.
- Added support format documentation listing compatible file types for the add-on.
- Included links to open databases for volumetric data.
- Provided performance improvement tips for voxel reconstruction and rendering, including settings for low-resolution previews and balanced rendering configurations.

* Add OpenSpec skills for change management and exploration

- Implement openspec-apply-change skill for applying tasks from OpenSpec changes.
- Create openspec-archive-change skill for archiving completed changes.
- Introduce openspec-explore skill for exploring ideas and clarifying requirements.
- Add openspec-propose skill for proposing new changes with generated artifacts.
- Migrate source command skills: source-command-opsx-apply, source-command-opsx-archive, source-command-opsx-explore, and source-command-opsx-propose for improved command handling.

* feat: Update version to 2.0.1, refactor asset library handling, and enhance layer import process

* fix: downgrade scipy version to 1.16.3 in build.py and pyproject.toml

* Remove deprecated modules and functions from bioxel package

- Deleted _ni_support.py, _utils.py, and dtype.py as they contained unused or redundant functions.
- Removed the resize function and related utilities from _warps.py to streamline image processing.
- Cleaned up the skimage module by removing unnecessary imports and functions.

* feat: Add SKIP_MODULE_NAMES to exclude specific modules during import and refactor io_worker import logic

* refactor: Remove known limitations section from documentation for EEVEE NEXT support

* feat: Implement CLI command registration for bioxelnodes_import_worker and refactor io_worker for improved module handling

* chore: Update version to 2.0.2 in project files and dependencies

* feat: Enhance volumetric data parsing by adding progress updates and refactoring worker processes

* feat: Enhance asset library management with status checks and error handling

* feat: Update version to 2.0.3 and adjust dependencies in project files
2026-07-09 12:40:25 +08:00
nan
2b83588590 feat: Update version to 2.0.3 and adjust dependencies in project files 2026-07-09 12:31:56 +08:00
nan
77f9ec3cac feat: Enhance asset library management with status checks and error handling 2026-07-09 11:53:09 +08:00
nan
7d8481f247 Merge branch 'main' of https://github.com/OmooLab/BioxelNodes into 5.0 2026-07-09 00:50:44 +08:00
Nan
1ced263441 5.0 (#63)
* feat: Add Bioxel asset catalog and layer management functionality

- Introduced a new asset catalog definition file for Blender to manage Bioxel assets.
- Implemented layer caching and snapshot functionality in `layer.py`, allowing for VDB file generation and low-resolution snapshots.
- Created node management functions in `node.py` for handling Bioxel nodes within Blender's Geometry Node Editor.
- Added an operator to extract mesh data from selected nodes in `structure.py`.
- Developed UI panels in `panels.py` for layer management, including import options and layer library display.

* Add unit tests for bioxel module and documentation

- Created README.md for test instructions and categories.
- Implemented comprehensive unit tests for the Data, Layer, and utility functions in the bioxel module.
- Added tests for reading NRRD and DICOM formats, data properties, and edge cases.
- Included tests for real data files to ensure functionality with actual datasets.

* feat: Update .gitattributes and .gitignore for improved asset management and ignore patterns

chore: Add app.json and omoolab.css for Obsidian integration

refactor: Clean up AGENTS.md and remove outdated test files

* feat: Update .gitignore to include scipy_ndimage directory

* feat: Update release workflow and manifest for improved dependency management and layer handling

* feat: Update .gitignore and add binary files for scipy_ndimage across multiple platforms

* feat: Add OPSX commands for archiving, exploring, and proposing changes

* feat: Refactor asset library management and update manifest for improved organization

* feat: Downgrade h5py version to 3.14.0 in requirements and manifest

* feat: Update Help operator and panel labels to use English terminology

* Translate documentation to Chinese, add support format details, and enhance styling with new fonts. Update mkdocs configuration for multilingual support and improve package dependencies.

* feat: Remove default version setting from extra configuration in mkdocs.yml

* fix: Update documentation links to point to the correct URLs for Chinese and English versions

* Add documentation for basic usage, supported formats, and performance tips

- Created English and Chinese documentation for basic usage of the add-on, including data download, import, extraction, and rendering steps.
- Added support format documentation listing compatible file types for the add-on.
- Included links to open databases for volumetric data.
- Provided performance improvement tips for voxel reconstruction and rendering, including settings for low-resolution previews and balanced rendering configurations.

* Add OpenSpec skills for change management and exploration

- Implement openspec-apply-change skill for applying tasks from OpenSpec changes.
- Create openspec-archive-change skill for archiving completed changes.
- Introduce openspec-explore skill for exploring ideas and clarifying requirements.
- Add openspec-propose skill for proposing new changes with generated artifacts.
- Migrate source command skills: source-command-opsx-apply, source-command-opsx-archive, source-command-opsx-explore, and source-command-opsx-propose for improved command handling.

* feat: Update version to 2.0.1, refactor asset library handling, and enhance layer import process

* fix: downgrade scipy version to 1.16.3 in build.py and pyproject.toml

* Remove deprecated modules and functions from bioxel package

- Deleted _ni_support.py, _utils.py, and dtype.py as they contained unused or redundant functions.
- Removed the resize function and related utilities from _warps.py to streamline image processing.
- Cleaned up the skimage module by removing unnecessary imports and functions.

* feat: Add SKIP_MODULE_NAMES to exclude specific modules during import and refactor io_worker import logic

* refactor: Remove known limitations section from documentation for EEVEE NEXT support

* feat: Implement CLI command registration for bioxelnodes_import_worker and refactor io_worker for improved module handling

* chore: Update version to 2.0.2 in project files and dependencies

* feat: Enhance volumetric data parsing by adding progress updates and refactoring worker processes
2026-07-09 00:50:34 +08:00
nan
8116b2446b feat: Enhance volumetric data parsing by adding progress updates and refactoring worker processes 2026-07-09 00:06:05 +08:00
nan
ea76d1af2e chore: Update version to 2.0.2 in project files and dependencies 2026-07-08 22:45:21 +08:00
nan
3756330ea3 feat: Implement CLI command registration for bioxelnodes_import_worker and refactor io_worker for improved module handling 2026-07-08 22:43:46 +08:00
nan
433cfa0180 Merge branch 'main' of https://github.com/OmooLab/BioxelNodes into 5.0 2026-07-08 13:49:42 +08:00
Nan
e9ea4c5248 5.0 (#62)
* feat: Add Bioxel asset catalog and layer management functionality

- Introduced a new asset catalog definition file for Blender to manage Bioxel assets.
- Implemented layer caching and snapshot functionality in `layer.py`, allowing for VDB file generation and low-resolution snapshots.
- Created node management functions in `node.py` for handling Bioxel nodes within Blender's Geometry Node Editor.
- Added an operator to extract mesh data from selected nodes in `structure.py`.
- Developed UI panels in `panels.py` for layer management, including import options and layer library display.

* Add unit tests for bioxel module and documentation

- Created README.md for test instructions and categories.
- Implemented comprehensive unit tests for the Data, Layer, and utility functions in the bioxel module.
- Added tests for reading NRRD and DICOM formats, data properties, and edge cases.
- Included tests for real data files to ensure functionality with actual datasets.

* feat: Update .gitattributes and .gitignore for improved asset management and ignore patterns

chore: Add app.json and omoolab.css for Obsidian integration

refactor: Clean up AGENTS.md and remove outdated test files

* feat: Update .gitignore to include scipy_ndimage directory

* feat: Update release workflow and manifest for improved dependency management and layer handling

* feat: Update .gitignore and add binary files for scipy_ndimage across multiple platforms

* feat: Add OPSX commands for archiving, exploring, and proposing changes

* feat: Refactor asset library management and update manifest for improved organization

* feat: Downgrade h5py version to 3.14.0 in requirements and manifest

* feat: Update Help operator and panel labels to use English terminology

* Translate documentation to Chinese, add support format details, and enhance styling with new fonts. Update mkdocs configuration for multilingual support and improve package dependencies.

* feat: Remove default version setting from extra configuration in mkdocs.yml

* fix: Update documentation links to point to the correct URLs for Chinese and English versions

* Add documentation for basic usage, supported formats, and performance tips

- Created English and Chinese documentation for basic usage of the add-on, including data download, import, extraction, and rendering steps.
- Added support format documentation listing compatible file types for the add-on.
- Included links to open databases for volumetric data.
- Provided performance improvement tips for voxel reconstruction and rendering, including settings for low-resolution previews and balanced rendering configurations.

* Add OpenSpec skills for change management and exploration

- Implement openspec-apply-change skill for applying tasks from OpenSpec changes.
- Create openspec-archive-change skill for archiving completed changes.
- Introduce openspec-explore skill for exploring ideas and clarifying requirements.
- Add openspec-propose skill for proposing new changes with generated artifacts.
- Migrate source command skills: source-command-opsx-apply, source-command-opsx-archive, source-command-opsx-explore, and source-command-opsx-propose for improved command handling.

* feat: Update version to 2.0.1, refactor asset library handling, and enhance layer import process

* fix: downgrade scipy version to 1.16.3 in build.py and pyproject.toml

* Remove deprecated modules and functions from bioxel package

- Deleted _ni_support.py, _utils.py, and dtype.py as they contained unused or redundant functions.
- Removed the resize function and related utilities from _warps.py to streamline image processing.
- Cleaned up the skimage module by removing unnecessary imports and functions.

* feat: Add SKIP_MODULE_NAMES to exclude specific modules during import and refactor io_worker import logic

* refactor: Remove known limitations section from documentation for EEVEE NEXT support
2026-07-08 13:48:52 +08:00
nan
ec87d59d65 Merge remote-tracking branch 'origin/main' into 5.0 2026-07-08 13:42:49 +08:00
nan
0e71ff6075 refactor: Remove known limitations section from documentation for EEVEE NEXT support 2026-07-08 13:08:46 +08:00
Nan
ae7868d410 5.0 (#60)
* feat: Add Bioxel asset catalog and layer management functionality

- Introduced a new asset catalog definition file for Blender to manage Bioxel assets.
- Implemented layer caching and snapshot functionality in `layer.py`, allowing for VDB file generation and low-resolution snapshots.
- Created node management functions in `node.py` for handling Bioxel nodes within Blender's Geometry Node Editor.
- Added an operator to extract mesh data from selected nodes in `structure.py`.
- Developed UI panels in `panels.py` for layer management, including import options and layer library display.

* Add unit tests for bioxel module and documentation

- Created README.md for test instructions and categories.
- Implemented comprehensive unit tests for the Data, Layer, and utility functions in the bioxel module.
- Added tests for reading NRRD and DICOM formats, data properties, and edge cases.
- Included tests for real data files to ensure functionality with actual datasets.

* feat: Update .gitattributes and .gitignore for improved asset management and ignore patterns

chore: Add app.json and omoolab.css for Obsidian integration

refactor: Clean up AGENTS.md and remove outdated test files

* feat: Update .gitignore to include scipy_ndimage directory

* feat: Update release workflow and manifest for improved dependency management and layer handling

* feat: Update .gitignore and add binary files for scipy_ndimage across multiple platforms

* feat: Add OPSX commands for archiving, exploring, and proposing changes

* feat: Refactor asset library management and update manifest for improved organization

* feat: Downgrade h5py version to 3.14.0 in requirements and manifest

* feat: Update Help operator and panel labels to use English terminology

* Translate documentation to Chinese, add support format details, and enhance styling with new fonts. Update mkdocs configuration for multilingual support and improve package dependencies.

* feat: Remove default version setting from extra configuration in mkdocs.yml

* fix: Update documentation links to point to the correct URLs for Chinese and English versions

* Add documentation for basic usage, supported formats, and performance tips

- Created English and Chinese documentation for basic usage of the add-on, including data download, import, extraction, and rendering steps.
- Added support format documentation listing compatible file types for the add-on.
- Included links to open databases for volumetric data.
- Provided performance improvement tips for voxel reconstruction and rendering, including settings for low-resolution previews and balanced rendering configurations.
2026-05-26 16:01:29 +08:00
13 changed files with 215 additions and 149 deletions

View File

@@ -38,11 +38,6 @@ If you want to cite this work, you can cite it from Zenodo:
[![DOI](https://zenodo.org/badge/786623459.svg)](https://zenodo.org/badge/latestdoi/786623459) [![DOI](https://zenodo.org/badge/786623459.svg)](https://zenodo.org/badge/latestdoi/786623459)
## Known Limitations
- Only one cutter supported in EEVEE render
- Shader fail to work when convert to mesh.
- Section surface cannot be generated when convert to mesh (will be supported soon)
## Roadmap ## Roadmap

View File

@@ -49,15 +49,8 @@ Welcome to our [discord server](https://discord.gg/pYkNyq2TjE), if you have any
![eevee](https://omoolab.github.io/BioxelNodes/latest/assets/eevee.gif) ![eevee](https://omoolab.github.io/BioxelNodes/latest/assets/eevee.gif)
👍 EEVEE NEXT is absolutely AWESOME! Bioxel Nodes is fully support EEVEE NEXT now! However, there are some limitations: 👍 EEVEE NEXT is absolutely AWESOME! Bioxel Nodes is fully support EEVEE NEXT now!
1. Only one cutter supported.
2. EEVEE result is not that great as Cycles does.
## Known Limitations
- Only works with Cycles CPU , Cycles GPU (OptiX), EEVEE
- Section surface cannot be generated when convert to mesh (will be supported soon)
## Roadmap ## Roadmap

View File

@@ -49,15 +49,7 @@ Bioxel Nodes 是一款用于科学体数据可视化的 Blender 插件。它利
![eevee](https://omoolab.github.io/BioxelNodes/latest/assets/eevee.gif) ![eevee](https://omoolab.github.io/BioxelNodes/latest/assets/eevee.gif)
EEVEE NEXT 太棒了!Bioxel Nodes 现在全面支持 EEVEE NEXT!但仍有以下限制: EEVEE NEXT 太棒了!Bioxel Nodes 现在全面支持 EEVEE NEXT!
1. 仅支持一个切割器
2. EEVEE 渲染效果不如 Cycles
## 已知限制
- 仅支持 Cycles CPU、Cycles GPU (OptiX)、EEVEE
- 转换为网格时无法生成剖面(即将支持)
## 路线图 ## 路线图

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "bioxelnodes" name = "bioxelnodes"
version = "2.0.1" version = "2.0.4"
description = "" description = ""
authors = [{ name = "MaNan", email = "icrdr2010@outlook.com" }] authors = [{ name = "MaNan", email = "icrdr2010@outlook.com" }]
requires-python = ">=3.11.0,<3.12.dev0" requires-python = ">=3.11.0,<3.12.dev0"
@@ -18,7 +18,7 @@ dependencies = [
[dependency-groups] [dependency-groups]
dev = [ dev = [
"bpy>=4.2.0,<5", "bpy==5.0.0",
"pytest>=8.3.2,<9", "pytest>=8.3.2,<9",
"pytest-dependency>=0.6.0,<0.7", "pytest-dependency>=0.6.0,<0.7",
"pytest-cov>=5.0.0,<6", "pytest-cov>=5.0.0,<6",

View File

@@ -9,6 +9,8 @@ from . import menus
auto_load.init() auto_load.init()
CLI_COMMAND_NAME = "bioxelnodes_import_worker"
def register(): def register():
pcoll = previews.new() pcoll = previews.new()
@@ -41,9 +43,11 @@ def register():
auto_load.register() auto_load.register()
menus.add() menus.add()
register_cli_commands()
def unregister(): def unregister():
unregister_cli_commands()
menus.remove() menus.remove()
auto_load.unregister() auto_load.unregister()
@@ -56,3 +60,21 @@ def unregister():
for pcoll in PREVIEW_COLLECTIONS.values(): for pcoll in PREVIEW_COLLECTIONS.values():
previews.remove(pcoll) previews.remove(pcoll)
PREVIEW_COLLECTIONS.clear() PREVIEW_COLLECTIONS.clear()
def import_worker_cli(args):
from .operators import io_worker
return io_worker.main(args)
def register_cli_commands():
unregister_cli_commands()
bpy.utils.register_cli_command(CLI_COMMAND_NAME, import_worker_cli)
def unregister_cli_commands():
try:
bpy.utils.unregister_cli_command(CLI_COMMAND_NAME)
except Exception:
pass

View File

@@ -1,10 +1,20 @@
import bpy import bpy
from pathlib import Path from pathlib import Path
from .constants import NODE_LIB_DIRPATH from .constants import LATEST_NODE_LIB_PATH, NODE_LIB_DIRPATH, NODE_LIB_FILENAME
ASSET_LIBRARY_NAME = "O Bioxel" ASSET_LIBRARY_NAME = "O Bioxel"
ASSET_LIBRARY_READY = "READY"
ASSET_LIBRARY_OUTDATED = "OUTDATED"
ASSET_LIBRARY_MISSING = "MISSING"
STATUS_MESSAGES = {
"missing": "O Bioxel asset library is not installed.",
"missing_blend": "O Bioxel node library file is missing.",
"outdated": "O Bioxel node library needs update.",
"ready": "O Bioxel asset library is ready.",
}
def _asset_library_path(): def _asset_library_path():
@@ -19,6 +29,26 @@ def _asset_libraries():
return bpy.context.preferences.filepaths.asset_libraries return bpy.context.preferences.filepaths.asset_libraries
def _find_bioxel_asset_library():
lib_path = _normalized_path(_asset_library_path())
named_lib = None
for lib in _asset_libraries():
try:
if _normalized_path(lib.path) == lib_path:
return lib
if lib.name == ASSET_LIBRARY_NAME:
named_lib = lib
except Exception:
continue
return named_lib
def _node_lib_path_for_library(lib):
return _normalized_path(lib.path) / NODE_LIB_FILENAME
def get_bioxel_asset_library(): def get_bioxel_asset_library():
lib_path = _normalized_path(_asset_library_path()) lib_path = _normalized_path(_asset_library_path())
@@ -32,16 +62,47 @@ def get_bioxel_asset_library():
return None return None
def get_bioxel_asset_library_status():
lib = _find_bioxel_asset_library()
lib_path = _normalized_path(_asset_library_path())
if lib is None:
return {"code": ASSET_LIBRARY_MISSING, "reason": "missing",
"message": STATUS_MESSAGES["missing"]}
try:
installed_lib_path = _normalized_path(lib.path)
except Exception:
return {"code": ASSET_LIBRARY_MISSING, "reason": "missing",
"message": STATUS_MESSAGES["missing"]}
if not _node_lib_path_for_library(lib).is_file():
return {"code": ASSET_LIBRARY_MISSING, "reason": "missing_blend",
"message": STATUS_MESSAGES["missing_blend"]}
if installed_lib_path != lib_path:
return {"code": ASSET_LIBRARY_OUTDATED, "reason": "outdated",
"message": STATUS_MESSAGES["outdated"]}
return {"code": ASSET_LIBRARY_READY, "reason": "ready",
"message": STATUS_MESSAGES["ready"]}
def has_bioxel_asset_library(): def has_bioxel_asset_library():
return get_bioxel_asset_library() is not None return get_bioxel_asset_library_status()["code"] in {
ASSET_LIBRARY_READY,
ASSET_LIBRARY_OUTDATED,
}
def add_bioxel_asset_library(): def add_bioxel_asset_library():
if not NODE_LIB_DIRPATH.exists(): if not NODE_LIB_DIRPATH.exists():
raise FileNotFoundError(f"Node library path does not exist: {NODE_LIB_DIRPATH}") raise FileNotFoundError(f"Node library path does not exist: {NODE_LIB_DIRPATH}")
if not LATEST_NODE_LIB_PATH.is_file():
raise FileNotFoundError(f"Node library file does not exist: {LATEST_NODE_LIB_PATH}")
prefs = _asset_libraries() prefs = _asset_libraries()
lib = get_bioxel_asset_library() lib = _find_bioxel_asset_library()
if lib is None: if lib is None:
for item in prefs: for item in prefs:

View File

@@ -1,7 +1,7 @@
schema_version = "1.0.0" schema_version = "1.0.0"
id = "bioxelnodes" id = "bioxelnodes"
version = "2.0.1" version = "2.0.4"
name = "Bioxel Nodes" name = "Bioxel Nodes"
tagline = "For scientific volumetric data visualization in Blender" tagline = "For scientific volumetric data visualization in Blender"
maintainer = "Nan <icrdr2010@outlook.com>" maintainer = "Nan <icrdr2010@outlook.com>"

View File

@@ -13,7 +13,7 @@ import SimpleITK as sitk
from ..props import BIOXEL_Series from ..props import BIOXEL_Series
from ..utils import get_layer_obj, wrapped_label from ..utils import get_layer_obj, wrapped_label
from ..bioxel.parse import DICOM_EXTS, SUPPORT_EXTS, get_ext from ..bioxel.parse import DICOM_EXTS, SUPPORT_EXTS, get_ext, parse_volumetric_data
from ..utils import get_cache_dir, progress_update, progress_bar from ..utils import get_cache_dir, progress_update, progress_bar
from ..layer import get_layer_caches, set_layer_caches from ..layer import get_layer_caches, set_layer_caches
@@ -61,6 +61,14 @@ def remove_progress_bar_safe():
pass pass
def get_addon_module_name():
package_name = __package__ or "bioxelnodes.operators"
suffix = ".operators"
if package_name.endswith(suffix):
return package_name[: -len(suffix)]
return package_name
def start_worker_process(owner, command: str, payload: dict): def start_worker_process(owner, command: str, payload: dict):
job_dir = Path(tempfile.mkdtemp(prefix="bioxel_import_", dir=str(get_cache_dir()))) job_dir = Path(tempfile.mkdtemp(prefix="bioxel_import_", dir=str(get_cache_dir())))
config_path = job_dir / "config.json" config_path = job_dir / "config.json"
@@ -69,24 +77,29 @@ def start_worker_process(owner, command: str, payload: dict):
cancel_path = job_dir / "cancel" cancel_path = job_dir / "cancel"
log_path = job_dir / "worker.log" log_path = job_dir / "worker.log"
addon_name = get_addon_module_name()
config = { config = {
**payload, **payload,
"command": command, "command": command,
"addon_name": addon_name,
"progress_path": str(progress_path), "progress_path": str(progress_path),
"result_path": str(result_path), "result_path": str(result_path),
"cancel_path": str(cancel_path), "cancel_path": str(cancel_path),
} }
write_worker_json(config_path, config) write_worker_json(config_path, config)
write_worker_json(progress_path, {"factor": 0.0, "text": "Starting..."}) write_worker_json(
progress_path,
{"factor": 0.0, "text": "Starting..."},
)
worker_path = Path(__file__).with_name("io_worker.py") print(f"Starting Bioxel worker with addon module: {addon_name}")
cmd = [ cmd = [
bpy.app.binary_path, bpy.app.binary_path,
"--background", "--background",
"--factory-startup", "--addons",
"--python", addon_name,
str(worker_path), "--command",
"--", "bioxelnodes_import_worker",
str(config_path), str(config_path),
] ]
@@ -188,7 +201,6 @@ class ImportDataBase:
context.window_manager.fileselect_add(self) context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"} return {"RUNNING_MODAL"}
class ImportData(bpy.types.Operator, ImportDataBase): class ImportData(bpy.types.Operator, ImportDataBase):
bl_idname = "bioxel.import_data" bl_idname = "bioxel.import_data"
bl_label = "Import Data" bl_label = "Import Data"
@@ -291,64 +303,26 @@ class ParseVolumetricData(bpy.types.Operator):
def execute(self, context): def execute(self, context):
print("Collecting Meta Data...") print("Collecting Meta Data...")
self.is_cancelled = False progress_update(context, 0.0, "Collecting Meta Data...")
self.has_error = None
self.meta = None
self.label_count = 0
self.dtype = None
start_worker_process( def progress_callback(factor, text):
self, progress_update(context, factor, text)
"read_meta",
{
"filepath": self.filepath,
"series_id": self.series_id,
},
)
self._timer = context.window_manager.event_timer_add( try:
time_step=0.1, window=context.window series_id = self.series_id if self.series_id != "empty" else ""
) data, meta = parse_volumetric_data(
bpy.types.STATUSBAR_HT_header.append(progress_bar) data_file=self.filepath,
context.window_manager.modal_handler_add(self) series_id=series_id,
return {"RUNNING_MODAL"} progress_callback=progress_callback,
)
except Exception as e:
raise e
def modal(self, context, event): self.meta = meta
if event.type == "ESC": self.label_count = int(np.max(data))
cancel_worker_process(self, context) self.dtype = data.dtype
return {"PASS_THROUGH"}
if event.type != "TIMER":
return {"PASS_THROUGH"}
bpy.context.workspace.status_text_set_internal(None)
update_worker_progress(self, context)
if self.process.poll() is None:
return {"PASS_THROUGH"}
context.window_manager.event_timer_remove(self._timer)
remove_progress_bar_safe()
progress_update(context, 1.0) progress_update(context, 1.0)
result = read_worker_json(self.result_path)
if self.is_cancelled or (result and result.get("cancelled")):
self.report({"WARNING"}, "Canncelled by user.")
return {"CANCELLED"}
if not result:
self.report({"ERROR"}, f"Import worker failed. See log: {self.log_path}")
return {"CANCELLED"}
if not result.get("ok"):
print(result.get("traceback", ""))
self.report({"ERROR"}, result.get("error", "Import worker failed."))
return {"CANCELLED"}
self.meta = result["meta"]
self.label_count = int(result["label_count"])
self.dtype = np.dtype(result["dtype"])
for key, value in self.meta.items(): for key, value in self.meta.items():
print(f"{key}: {value}") print(f"{key}: {value}")

View File

@@ -1,24 +1,7 @@
import json import json
import sys
import traceback import traceback
from pathlib import Path from pathlib import Path
import numpy as np
import transforms3d
if __package__:
from ..bioxel.layer import Layer
from ..bioxel.parse import parse_volumetric_data
from ..layer import save_layers_to_cache
else:
PACKAGE_PARENT = Path(__file__).resolve().parents[2]
if str(PACKAGE_PARENT) not in sys.path:
sys.path.insert(0, str(PACKAGE_PARENT))
from bioxelnodes.bioxel.layer import Layer
from bioxelnodes.bioxel.parse import parse_volumetric_data
from bioxelnodes.layer import save_layers_to_cache
def get_layer_shape(bioxel_size: float, orig_shape: tuple, orig_spacing: tuple): def get_layer_shape(bioxel_size: float, orig_shape: tuple, orig_spacing: tuple):
shape = ( shape = (
@@ -72,6 +55,10 @@ def progress_callback_factory(progress_path: Path, cancel_path: Path, layer_name
def read_meta(config, progress_path: Path, cancel_path: Path): def read_meta(config, progress_path: Path, cancel_path: Path):
import numpy as np
from ..bioxel.parse import parse_volumetric_data
progress_callback = make_progress_writer(progress_path, cancel_path) progress_callback = make_progress_writer(progress_path, cancel_path)
series_id = config["series_id"] if config["series_id"] != "empty" else "" series_id = config["series_id"] if config["series_id"] != "empty" else ""
data, meta = parse_volumetric_data( data, meta = parse_volumetric_data(
@@ -94,6 +81,13 @@ def read_meta(config, progress_path: Path, cancel_path: Path):
def build_layers(config, progress_path: Path, cancel_path: Path): def build_layers(config, progress_path: Path, cancel_path: Path):
import numpy as np
import transforms3d
from ..bioxel.layer import Layer
from ..bioxel.parse import parse_volumetric_data
from ..layer import save_layers_to_cache
write_json(progress_path, {"factor": 0.0, "text": "Parsing Volumetirc Data..."}) write_json(progress_path, {"factor": 0.0, "text": "Parsing Volumetirc Data..."})
progress_callback = make_progress_writer(progress_path, cancel_path, scale=0.2) progress_callback = make_progress_writer(progress_path, cancel_path, scale=0.2)
data, meta = parse_volumetric_data( data, meta = parse_volumetric_data(
@@ -229,14 +223,19 @@ def build_layers(config, progress_path: Path, cancel_path: Path):
return {"cache_infos": cache_infos, "added_ids": [item["id"] for item in cache_infos]} return {"cache_infos": cache_infos, "added_ids": [item["id"] for item in cache_infos]}
def main(): def main(args):
config_path = Path(sys.argv[-1]) if not args:
print("Usage: blender --command bioxelnodes_import_worker <config_path>")
return 2
config_path = Path(args[0])
config = json.loads(config_path.read_text(encoding="utf-8")) config = json.loads(config_path.read_text(encoding="utf-8"))
progress_path = Path(config["progress_path"]) progress_path = Path(config["progress_path"])
result_path = Path(config["result_path"]) result_path = Path(config["result_path"])
cancel_path = Path(config["cancel_path"]) cancel_path = Path(config["cancel_path"])
try: try:
write_json(progress_path, {"factor": 0.0, "text": "Loading import modules..."})
if config["command"] == "read_meta": if config["command"] == "read_meta":
result = read_meta(config, progress_path, cancel_path) result = read_meta(config, progress_path, cancel_path)
elif config["command"] == "import_layers": elif config["command"] == "import_layers":
@@ -257,7 +256,4 @@ def main():
"traceback": traceback.format_exc(), "traceback": traceback.format_exc(),
}, },
) )
return 0
if __name__ == "__main__":
main()

View File

@@ -3,6 +3,7 @@ import shutil
import bpy import bpy
from ..asset_library import ASSET_LIBRARY_MISSING, get_bioxel_asset_library_status
from ..node import add_bioxel_node, get_layer_nodes, get_main_node_group from ..node import add_bioxel_node, get_layer_nodes, get_main_node_group
from ..utils import refresh_bioxel_panels from ..utils import refresh_bioxel_panels
from ..layer import get_layer_caches, set_layer_caches from ..layer import get_layer_caches, set_layer_caches
@@ -91,6 +92,11 @@ class AddLayerNode(bpy.types.Operator):
cache_id: bpy.props.StringProperty() # type: ignore # 图层ID cache_id: bpy.props.StringProperty() # type: ignore # 图层ID
def execute(self, context): def execute(self, context):
status = get_bioxel_asset_library_status()
if status["code"] == ASSET_LIBRARY_MISSING:
self.report({"ERROR"}, status["message"])
return {"CANCELLED"}
# 获取图层数据 # 获取图层数据
caches = get_layer_caches() caches = get_layer_caches()

View File

@@ -5,7 +5,12 @@ import bpy
from pathlib import Path from pathlib import Path
import shutil import shutil
from ..asset_library import add_bioxel_asset_library from ..asset_library import (
ASSET_LIBRARY_OUTDATED,
ASSET_LIBRARY_READY,
add_bioxel_asset_library,
get_bioxel_asset_library_status,
)
from ..constants import LATEST_NODE_LIB_PATH from ..constants import LATEST_NODE_LIB_PATH
from ..utils import ( from ..utils import (
get_all_layer_objs, get_all_layer_objs,
@@ -138,8 +143,17 @@ class AddAssetLibrary(bpy.types.Operator):
if area.type == "NODE_EDITOR": if area.type == "NODE_EDITOR":
area.tag_redraw() area.tag_redraw()
self.report({"INFO"}, "O Bioxel asset library is ready.") status = get_bioxel_asset_library_status()
return {"FINISHED"} if status["code"] == ASSET_LIBRARY_READY:
self.report({"INFO"}, status["message"])
return {"FINISHED"}
if status["code"] == ASSET_LIBRARY_OUTDATED:
self.report({"WARNING"}, status["message"])
return {"FINISHED"}
self.report({"ERROR"}, status["message"])
return {"CANCELLED"}
# 定义虚无化切换操作 # 定义虚无化切换操作

View File

@@ -1,7 +1,12 @@
from pathlib import Path from pathlib import Path
import bpy import bpy
from .asset_library import has_bioxel_asset_library from .asset_library import (
ASSET_LIBRARY_OUTDATED,
ASSET_LIBRARY_READY,
get_bioxel_asset_library_status,
has_bioxel_asset_library,
)
from .node import get_layer_nodes, get_main_node_group from .node import get_layer_nodes, get_main_node_group
from .utils import load_icon from .utils import load_icon
from .layer import get_layer_caches from .layer import get_layer_caches
@@ -78,13 +83,23 @@ class HeaderPanel(bpy.types.Panel, BioxelPanelBase):
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
status = get_bioxel_asset_library_status()
if not has_bioxel_asset_library(): if status["code"] == ASSET_LIBRARY_OUTDATED:
layout.operator(
AddAssetLibrary.bl_idname,
text="Update Nodes Library",
icon="ASSET_MANAGER",
)
layout.label(text=status["message"], icon="INFO")
elif status["code"] != ASSET_LIBRARY_READY:
layout.operator( layout.operator(
AddAssetLibrary.bl_idname, AddAssetLibrary.bl_idname,
text="Add Nodes Library", text="Add Nodes Library",
icon="ASSET_MANAGER", icon="ASSET_MANAGER",
) )
layout.label(text=status["message"], icon="INFO")
return return
layout.operator( layout.operator(

62
uv.lock generated
View File

@@ -25,7 +25,7 @@ wheels = [
[[package]] [[package]]
name = "bioxelnodes" name = "bioxelnodes"
version = "2.0.0" version = "2.0.4"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "h5py" }, { name = "h5py" },
@@ -60,7 +60,7 @@ requires-dist = [
{ name = "matplotlib", specifier = "==3.10.7" }, { name = "matplotlib", specifier = "==3.10.7" },
{ name = "mrcfile", specifier = "==1.5.1" }, { name = "mrcfile", specifier = "==1.5.1" },
{ name = "pyometiff", specifier = "==1.0.0" }, { name = "pyometiff", specifier = "==1.0.0" },
{ name = "scipy", specifier = "==1.17.1" }, { name = "scipy", specifier = "==1.16.3" },
{ name = "simpleitk", specifier = "==2.3.1" }, { name = "simpleitk", specifier = "==2.3.1" },
{ name = "transforms3d", specifier = "==0.4.2" }, { name = "transforms3d", specifier = "==0.4.2" },
] ]
@@ -68,7 +68,7 @@ requires-dist = [
[package.metadata.requires-dev] [package.metadata.requires-dev]
dev = [ dev = [
{ name = "autopep8", specifier = ">=2.3.1,<3" }, { name = "autopep8", specifier = ">=2.3.1,<3" },
{ name = "bpy", specifier = ">=4.2.0,<5" }, { name = "bpy", specifier = "==5.0.0" },
{ name = "mike", specifier = ">=2.1.2,<3" }, { name = "mike", specifier = ">=2.1.2,<3" },
{ name = "mkdocs", specifier = ">=1.6.0,<2" }, { name = "mkdocs", specifier = ">=1.6.0,<2" },
{ name = "mkdocs-click", specifier = ">=0.9.0" }, { name = "mkdocs-click", specifier = ">=0.9.0" },
@@ -84,7 +84,7 @@ dev = [
[[package]] [[package]]
name = "bpy" name = "bpy"
version = "4.2.0" version = "5.0.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "cython" }, { name = "cython" },
@@ -93,10 +93,10 @@ dependencies = [
{ name = "zstandard" }, { name = "zstandard" },
] ]
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/ea/5a/d085d433ad26687d7ba5be235c57679780a3b41316cd7ece1ee65694ebad/bpy-4.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e6e8857c9f483d42f5388642601080ad3e7488970cbc9e59efeedf4a8c26c90b", size = 207901162, upload-time = "2024-07-25T09:03:05.781Z" }, { url = "https://files.pythonhosted.org/packages/88/6c/87bd36315453b9a82f6392c31ed3c6eb504dfda4b591a0ca9b3a29715d07/bpy-5.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4a64f8862b29cc21358666d3b790fdf22060fe57e4626b3a8e1a879a8fecf961", size = 228454431, upload-time = "2025-11-18T17:09:51.446Z" },
{ url = "https://files.pythonhosted.org/packages/39/03/579ec8a0d592424bd5743138ff40f2eca1944e427546721cfff091dbe730/bpy-4.2.0-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:4fd0e9872dceb37209d298ccbe77e6c3d0421501ef71b464dde22d09607966ec", size = 220963874, upload-time = "2024-07-25T09:05:08.352Z" }, { url = "https://files.pythonhosted.org/packages/72/92/8b830f3f0cf8e548b2ac385c4a255aabb6b9e7dadde8149d7790ca296ab4/bpy-5.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:57804ccf770052a47e02f161d8a4401d93a2b1c120ff3ce9f8617466e55691f9", size = 382627218, upload-time = "2025-11-18T17:09:23.339Z" },
{ url = "https://files.pythonhosted.org/packages/0b/bc/5245f98dafa39166a4d4701d64077d281a36110e780011de5c38135089aa/bpy-4.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a8217c2bd7b3424d1f51ce0698c0bf65439de352f7f6ed7b9942e2a9e5eb121b", size = 519384061, upload-time = "2024-07-25T09:04:30.48Z" }, { url = "https://files.pythonhosted.org/packages/ae/01/bd9dc9806d5320a2caa4ad4f43ffdeebf70f8c398686d50d498a17de92b5/bpy-5.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:7b8b14b21006a487db88ff817eeb2d635073c24a8fb191b25c71b401641e1eaa", size = 340851458, upload-time = "2025-11-18T17:08:27.098Z" },
{ url = "https://files.pythonhosted.org/packages/c4/b0/6f18776208e13acb9bdbf3b5df9db9de42b09ab096aae68b171db422cccb/bpy-4.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:26ce6210deb8316816938cb1c62c7fd257a8ef38d0c20b9f031a7a47a1492cac", size = 315442011, upload-time = "2024-07-25T09:03:45.29Z" }, { url = "https://files.pythonhosted.org/packages/56/15/fe803465ecd93da65bb6900f7feb7e93a767d7b84a865a4b0882a0c27155/bpy-5.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:19d0922b5e1b62852fd826cf85e78a86324d3360cf1bda0bb5fb41c82005cb7c", size = 201997189, upload-time = "2025-11-18T17:08:45.541Z" },
] ]
[[package]] [[package]]
@@ -674,20 +674,18 @@ wheels = [
[[package]] [[package]]
name = "numpy" name = "numpy"
version = "2.0.1" version = "1.26.4"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1c/8a/0db635b225d2aa2984e405dc14bd2b0c324a0c312ea1bc9d283f2b83b038/numpy-2.0.1.tar.gz", hash = "sha256:485b87235796410c3519a699cfe1faab097e509e90ebb05dcd098db2ae87e7b3", size = 18872007, upload-time = "2024-07-21T13:49:20.698Z" } sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129, upload-time = "2024-02-06T00:26:44.495Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/29/d6/ff66f4f87518a435538e15cc9e0477a88398512a18783e748914f0daf5ea/numpy-2.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75b4e316c5902d8163ef9d423b1c3f2f6252226d1aa5cd8a0a03a7d01ffc6268", size = 21238269, upload-time = "2024-07-21T13:33:30.438Z" }, { url = "https://files.pythonhosted.org/packages/11/57/baae43d14fe163fa0e4c47f307b6b2511ab8d7d30177c491960504252053/numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", size = 20630554, upload-time = "2024-02-05T23:51:50.149Z" },
{ url = "https://files.pythonhosted.org/packages/c5/64/853cfc37494471e64ea9f7bf3bc3b4bb39450e6db5beeb05e2a66beef612/numpy-2.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6e4eeb6eb2fced786e32e6d8df9e755ce5be920d17f7ce00bc38fcde8ccdbf9e", size = 13334324, upload-time = "2024-07-21T13:33:51.455Z" }, { url = "https://files.pythonhosted.org/packages/1a/2e/151484f49fd03944c4a3ad9c418ed193cfd02724e138ac8a9505d056c582/numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", size = 13997127, upload-time = "2024-02-05T23:52:15.314Z" },
{ url = "https://files.pythonhosted.org/packages/d1/d8/597b4b2e396a77cbec677c9de33bb1789d5c3b66d653cb723d00eb331e99/numpy-2.0.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a1e01dcaab205fbece13c1410253a9eea1b1c9b61d237b6fa59bcc46e8e89343", size = 5298376, upload-time = "2024-07-21T13:34:01.627Z" }, { url = "https://files.pythonhosted.org/packages/79/ae/7e5b85136806f9dadf4878bf73cf223fe5c2636818ba3ab1c585d0403164/numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", size = 14222994, upload-time = "2024-02-05T23:52:47.569Z" },
{ url = "https://files.pythonhosted.org/packages/64/58/8664ff3747ac719ae1a5b9c0020533435158180a27f2f88a2b7a253bb623/numpy-2.0.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a8fc2de81ad835d999113ddf87d1ea2b0f4704cbd947c948d2f5513deafe5a7b", size = 6903817, upload-time = "2024-07-21T13:34:13.878Z" }, { url = "https://files.pythonhosted.org/packages/3a/d0/edc009c27b406c4f9cbc79274d6e46d634d139075492ad055e3d68445925/numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", size = 18252005, upload-time = "2024-02-05T23:53:15.637Z" },
{ url = "https://files.pythonhosted.org/packages/72/44/71ac0090d4ccb512fcac0ef0e5208248423a1ce30381541700470ac09b75/numpy-2.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a3d94942c331dd4e0e1147f7a8699a4aa47dffc11bf8a1523c12af8b2e91bbe", size = 13916254, upload-time = "2024-07-21T13:34:35.466Z" }, { url = "https://files.pythonhosted.org/packages/09/bf/2b1aaf8f525f2923ff6cfcf134ae5e750e279ac65ebf386c75a0cf6da06a/numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", size = 13885297, upload-time = "2024-02-05T23:53:42.16Z" },
{ url = "https://files.pythonhosted.org/packages/ef/27/39622993e8688a1f05898a3c3b2836b856f79c06637ebd4b71cb35cc9b18/numpy-2.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15eb4eca47d36ec3f78cde0a3a2ee24cf05ca7396ef808dda2c0ddad7c2bde67", size = 19540260, upload-time = "2024-07-21T13:35:09.114Z" }, { url = "https://files.pythonhosted.org/packages/df/a0/4e0f14d847cfc2a633a1c8621d00724f3206cfeddeb66d35698c4e2cf3d2/numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", size = 18093567, upload-time = "2024-02-05T23:54:11.696Z" },
{ url = "https://files.pythonhosted.org/packages/6a/26/a32b5a6b3f090860aeefb3619bfea09f717d73908bd65e69e8ab0cac9c07/numpy-2.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b83e16a5511d1b1f8a88cbabb1a6f6a499f82c062a4251892d9ad5d609863fb7", size = 19938602, upload-time = "2024-07-21T13:35:38.996Z" }, { url = "https://files.pythonhosted.org/packages/d2/b7/a734c733286e10a7f1a8ad1ae8c90f2d33bf604a96548e0a4a3a6739b468/numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", size = 5968812, upload-time = "2024-02-05T23:54:26.453Z" },
{ url = "https://files.pythonhosted.org/packages/34/b6/a88a9953d0be231c67aa0b3714d6138507490753beaa927f0b33f20cdca2/numpy-2.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f87fec1f9bc1efd23f4227becff04bd0e979e23ca50cc92ec88b38489db3b55", size = 14425300, upload-time = "2024-07-21T13:36:01.419Z" }, { url = "https://files.pythonhosted.org/packages/3f/6b/5610004206cf7f8e7ad91c5a85a8c71b2f2f8051a0c0c4d5916b76d6cbb2/numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", size = 15811913, upload-time = "2024-02-05T23:54:53.933Z" },
{ url = "https://files.pythonhosted.org/packages/e4/e2/e763e102bea9c188b43ea144a91c22bec669736889a6e0be0235d64666d7/numpy-2.0.1-cp311-cp311-win32.whl", hash = "sha256:36d3a9405fd7c511804dc56fc32974fa5533bdeb3cd1604d6b8ff1d292b819c4", size = 6467652, upload-time = "2024-07-21T13:36:13.469Z" },
{ url = "https://files.pythonhosted.org/packages/3d/67/928e8f0d5c7fd32f32fb5caf92b186a1b3826dbaf5a294e13a976d6c38b6/numpy-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:08458fbf403bff5e2b45f08eda195d4b0c9b35682311da5a5a0a0925b11b9bd8", size = 16559280, upload-time = "2024-07-21T13:36:44.641Z" },
] ]
[[package]] [[package]]
@@ -949,23 +947,23 @@ wheels = [
[[package]] [[package]]
name = "scipy" name = "scipy"
version = "1.17.1" version = "1.16.3"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "numpy" }, { name = "numpy" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } sdist = { url = "https://files.pythonhosted.org/packages/0a/ca/d8ace4f98322d01abcd52d381134344bf7b431eba7ed8b42bdea5a3c2ac9/scipy-1.16.3.tar.gz", hash = "sha256:01e87659402762f43bd2fee13370553a17ada367d42e7487800bf2916535aecb", size = 30597883, upload-time = "2025-10-28T17:38:54.068Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675, upload-time = "2026-02-23T00:16:00.13Z" }, { url = "https://files.pythonhosted.org/packages/9b/5f/6f37d7439de1455ce9c5a556b8d1db0979f03a796c030bafdf08d35b7bf9/scipy-1.16.3-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:40be6cf99e68b6c4321e9f8782e7d5ff8265af28ef2cd56e9c9b2638fa08ad97", size = 36630881, upload-time = "2025-10-28T17:31:47.104Z" },
{ url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057, upload-time = "2026-02-23T00:16:09.456Z" }, { url = "https://files.pythonhosted.org/packages/7c/89/d70e9f628749b7e4db2aa4cd89735502ff3f08f7b9b27d2e799485987cd9/scipy-1.16.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:8be1ca9170fcb6223cc7c27f4305d680ded114a1567c0bd2bfcbf947d1b17511", size = 28941012, upload-time = "2025-10-28T17:31:53.411Z" },
{ url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032, upload-time = "2026-02-23T00:16:17.358Z" }, { url = "https://files.pythonhosted.org/packages/a8/a8/0e7a9a6872a923505dbdf6bb93451edcac120363131c19013044a1e7cb0c/scipy-1.16.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:bea0a62734d20d67608660f69dcda23e7f90fb4ca20974ab80b6ed40df87a005", size = 20931935, upload-time = "2025-10-28T17:31:57.361Z" },
{ url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533, upload-time = "2026-02-23T00:16:25.791Z" }, { url = "https://files.pythonhosted.org/packages/bd/c7/020fb72bd79ad798e4dbe53938543ecb96b3a9ac3fe274b7189e23e27353/scipy-1.16.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:2a207a6ce9c24f1951241f4693ede2d393f59c07abc159b2cb2be980820e01fb", size = 23534466, upload-time = "2025-10-28T17:32:01.875Z" },
{ url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057, upload-time = "2026-02-23T00:16:36.931Z" }, { url = "https://files.pythonhosted.org/packages/be/a0/668c4609ce6dbf2f948e167836ccaf897f95fb63fa231c87da7558a374cd/scipy-1.16.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:532fb5ad6a87e9e9cd9c959b106b73145a03f04c7d57ea3e6f6bb60b86ab0876", size = 33593618, upload-time = "2025-10-28T17:32:06.902Z" },
{ url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300, upload-time = "2026-02-23T00:16:49.108Z" }, { url = "https://files.pythonhosted.org/packages/ca/6e/8942461cf2636cdae083e3eb72622a7fbbfa5cf559c7d13ab250a5dbdc01/scipy-1.16.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0151a0749efeaaab78711c78422d413c583b8cdd2011a3c1d6c794938ee9fdb2", size = 35899798, upload-time = "2025-10-28T17:32:12.665Z" },
{ url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333, upload-time = "2026-02-23T00:17:01.293Z" }, { url = "https://files.pythonhosted.org/packages/79/e8/d0f33590364cdbd67f28ce79368b373889faa4ee959588beddf6daef9abe/scipy-1.16.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7180967113560cca57418a7bc719e30366b47959dd845a93206fbed693c867e", size = 36226154, upload-time = "2025-10-28T17:32:17.961Z" },
{ url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314, upload-time = "2026-02-23T00:17:12.576Z" }, { url = "https://files.pythonhosted.org/packages/39/c1/1903de608c0c924a1749c590064e65810f8046e437aba6be365abc4f7557/scipy-1.16.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:deb3841c925eeddb6afc1e4e4a45e418d19ec7b87c5df177695224078e8ec733", size = 38878540, upload-time = "2025-10-28T17:32:23.907Z" },
{ url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512, upload-time = "2026-02-23T00:17:23.424Z" }, { url = "https://files.pythonhosted.org/packages/f1/d0/22ec7036ba0b0a35bccb7f25ab407382ed34af0b111475eb301c16f8a2e5/scipy-1.16.3-cp311-cp311-win_amd64.whl", hash = "sha256:53c3844d527213631e886621df5695d35e4f6a75f620dca412bcd292f6b87d78", size = 38722107, upload-time = "2025-10-28T17:32:29.921Z" },
{ url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248, upload-time = "2026-02-23T00:17:34.561Z" }, { url = "https://files.pythonhosted.org/packages/7b/60/8a00e5a524bb3bf8898db1650d350f50e6cffb9d7a491c561dc9826c7515/scipy-1.16.3-cp311-cp311-win_arm64.whl", hash = "sha256:9452781bd879b14b6f055b26643703551320aa8d79ae064a71df55c00286a184", size = 25506272, upload-time = "2025-10-28T17:32:34.577Z" },
] ]
[[package]] [[package]]