Merge pull request #18 from OmooLab/feature/dev

Feature/dev
This commit is contained in:
Ma Nan
2024-06-05 09:40:33 +08:00
committed by GitHub
15 changed files with 174 additions and 221 deletions

View File

@@ -22,7 +22,7 @@ jobs:
# id: draft_release
# uses: cardinalby/git-get-release-action@1.2.4
# with:
# releaseName: Draft
# releaseName: Upload
# env:
# GITHUB_TOKEN: ${{ github.token }}
@@ -70,5 +70,38 @@ jobs:
with:
upload_url: ${{ needs.draft_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: ./package.zip
asset_name: BioxelNodes_${{ needs.draft_release.outputs.version }}.zip
asset_name: BioxelNodes_Addon_${{ needs.draft_release.outputs.version }}.zip
asset_content_type: application/zip
upload_blender_extension:
name: Upload Blender Extension
needs: draft_release
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
lfs: 'true'
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.11
- name: Zip Extension
run: |
pip download SimpleITK --dest bioxelnodes/wheels --only-binary=:all: --python-version=3.11 --platform=macosx_11_0_arm64
pip download SimpleITK --dest bioxelnodes/wheels --only-binary=:all: --python-version=3.11 --platform=win_amd64
rm -r bioxelnodes/externalpackage
cp extension/__init__.py bioxelnodes/__init__.py
cp extension/preferences.py bioxelnodes/preferences.py
cp extension/blender_manifest.toml bioxelnodes/blender_manifest.toml
zip -r package.zip bioxelnodes
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.draft_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: ./package.zip
asset_name: BioxelNodes_Extension_${{ needs.draft_release.outputs.version }}.zip
asset_content_type: application/zip

View File

@@ -15,7 +15,7 @@ Before us, there have been many tutorials and add-ons for importing volumetric d
Below are some examples with Bioxel Nodes. Thanks to Cycles Render, the volumetric data can be rendered with great detail:
![gallery](docs/assets/gallery.png)
![cover](docs/assets/cover.png)
The "Bioxel" in "Bioxel Nodes", is a combination of the words "Bio-" and "Voxel". Bioxel is a voxel that stores biological data. We are developing a toolkit around Bioxel for better biological data visualization. but before its release, we made this Blender version of bioxels toolkit first, in order to let more people to have fun with volumetric data. [Getting Started](https://omoolab.github.io/BioxelNodes/latest/getting-started)

View File

@@ -57,7 +57,7 @@ def unregister():
#################################################
def get_all_submodules(directory):
return list(iter_submodules(directory, directory.name))
return list(iter_submodules(directory, __package__))
def iter_submodules(path, package_name):

View File

@@ -76,7 +76,8 @@ class PackageInstaller():
# Set up logging configuration
logfile_path = Path(self.log_path, f"{logfile_name}.log")
logging.basicConfig(filename=logfile_path, level=logging.INFO)
logging.basicConfig(filename=logfile_path,
level=logging.INFO, encoding='utf-8')
# Return logger object
return logging.getLogger()
@@ -248,6 +249,7 @@ class PackageInstaller():
result = subprocess.run(cmd_list, timeout=timeout,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
try:
if result.returncode != 0:
log.error('Command failed: %s', cmd_list)
log.error('stdout: %s', result.stdout.decode())
@@ -255,6 +257,8 @@ class PackageInstaller():
else:
log.info('Command succeeded: %s', cmd_list)
log.info('stdout: %s', result.stdout.decode())
except:
...
# return the command list, return code, stdout, and stderr as a tuple
return result

View File

@@ -23,199 +23,3 @@ def create_points_obj(points, name="points"):
bm.to_mesh(mesh)
bm.free() # always do this when finished
return obj
# class ImportDICOMPointsDialog(bpy.types.Operator):
# bl_idname = "bioxelnodes.import_dicom_points_dialog"
# bl_label = "Volume Data as Bioxels"
# bl_description = "Import Volume Data as Bioxels (VDB)"
# bl_options = {'UNDO'}
# filepath: bpy.props.StringProperty(
# subtype="FILE_PATH",
# options={'HIDDEN'}
# ) # type: ignore
# bioxels_shape: bpy.props.IntVectorProperty(
# name="Bioxels Shape (ReadOnly)",
# min=0,
# default=(100, 100, 100)
# ) # type: ignore
# bioxel_size: bpy.props.FloatProperty(
# name="Bioxel Size",
# soft_min=0.1, soft_max=10.0,
# min=1e-2, max=1e2,
# default=1,
# update=on_bioxel_size_changed
# ) # type: ignore
# orig_spacing: bpy.props.FloatVectorProperty(
# name="Original Spacing",
# default=(1, 1, 1),
# update=on_orig_spacing_changed
# ) # type: ignore
# orig_shape: bpy.props.IntVectorProperty(
# name="Original Shape",
# default=(100, 100, 100),
# options={'HIDDEN'}
# ) # type: ignore
# auto: bpy.props.BoolProperty(
# name="Auto Setting",
# default=False,
# options={'HIDDEN'}
# ) # type: ignore
# scene_scale: bpy.props.FloatProperty(
# name="Scene Scale",
# soft_min=0.001, soft_max=100.0,
# min=1e-6, max=1e6,
# default=0.01,
# ) # type: ignore
# do_add_segmentnode: bpy.props.BoolProperty(
# name="Add Segment Node",
# default=True,
# ) # type: ignore
# do_change_render_setting: bpy.props.BoolProperty(
# name="Change Render Setting",
# default=True,
# ) # type: ignore
# def execute(self, context):
# files = get_data_files(self.filepath)
# name = Path(self.filepath).parent.name
# image = sitk.ReadImage(files)
# bioxel_size = float(self.bioxel_size)
# orig_spacing = tuple(self.orig_spacing)
# image_spacing = image.GetSpacing()
# image_shape = image.GetSize()
# bioxels_spacing = (
# image_spacing[0] / orig_spacing[0] * bioxel_size,
# image_spacing[1] / orig_spacing[1] * bioxel_size,
# image_spacing[2] / orig_spacing[2] * bioxel_size
# )
# bioxels_shape = (
# int(image_shape[0] / bioxel_size * orig_spacing[0]),
# int(image_shape[1] / bioxel_size * orig_spacing[1]),
# int(image_shape[2] / bioxel_size * orig_spacing[2]),
# )
# print("Resampling...")
# image = sitk.Resample(
# image1=image,
# size=bioxels_shape,
# transform=sitk.Transform(),
# interpolator=sitk.sitkLinear,
# outputOrigin=image.GetOrigin(),
# outputSpacing=bioxels_spacing,
# outputDirection=image.GetDirection(),
# defaultPixelValue=0,
# outputPixelType=image.GetPixelID(),
# )
# print("Orienting to RAS...")
# image = sitk.DICOMOrient(image, 'RAS')
# array = sitk.GetArrayFromImage(image)
# orig_dtype = str(array.dtype)
# print(f"Coverting Dtype from {orig_dtype} to float...")
# array = array.astype(float)
# # ITK indices, by convention, are [i,j,k] while NumPy indices are [k,j,i]
# # https://www.slicer.org/wiki/Coordinate_systems
# # ITK Numpy 3D
# # R (ight) i -> k -> x
# # A (nterior) j -> j -> y
# # S (uperior) k -> i -> z
# array = np.transpose(array)
# bioxels_max = float(np.max(array))
# bioxels_min = float(np.min(array))
# bioxels_shape = array.shape
# print("Bioxel Size:", bioxel_size)
# print("Bioxels Shape:", bioxels_shape)
# bioxels_offset = 0.0
# if bioxels_min < 0 and orig_dtype[0] != "u":
# bioxels_offset = -bioxels_min
# array = array + np.full_like(array, bioxels_offset)
# bioxels_max = float(np.max(array))
# bioxels_min = float(np.min(array))
# print("Offseted Max:", bioxels_max)
# print("Offseted Min:", bioxels_min)
# points = []
# for x in range(array.shape[0]):
# for y in range(array.shape[1]):
# for z in range(array.shape[2]):
# points.append({
# "pos": (x, y, z),
# "value": array[x, y, z],
# })
# # # Build VDB
# # grid = vdb.FloatGrid()
# # grid.copyFromArray(array.copy())
# # # After sitk.DICOMOrient(), origin and direction will also orient base on LPS
# # # so we need to convert them into RAS
# # mat_lps2ras = axis_conversion(
# # from_forward='-Z',
# # from_up='-Y',
# # to_forward='-Z',
# # to_up='Y'
# # ).to_4x4()
# # mat_location = mathutils.Matrix.Translation(
# # mathutils.Vector(image.GetOrigin())
# # )
# # mat_rotation = mathutils.Matrix(
# # np.array(image.GetDirection()).reshape((3, 3))
# # ).to_4x4()
# # mat_scale = mathutils.Matrix.Scale(
# # bioxel_size, 4
# # )
# # transfrom = mat_lps2ras @ mat_location @ mat_rotation @ mat_scale
# obj = create_points_obj(points)
# # Make transformation
# scene_scale = float(self.scene_scale)
# # (S)uperior -Z -> Y
# # (A)osterior Y -> Z
# mat_ras2blender = axis_conversion(
# from_forward='-Z',
# from_up='Y',
# to_forward='Y',
# to_up='Z'
# ).to_4x4()
# mat_scene_scale = mathutils.Matrix.Scale(
# scene_scale, 4
# )
# obj.matrix_world = mat_ras2blender @ mat_scene_scale
# self.report({"INFO"}, "Successfully Imported")
# return {'FINISHED'}
# def invoke(self, context, event):
# # print(tuple(self.orig_shape))
# self.auto = True
# self.bioxel_size = 1
# context.window_manager.invoke_props_dialog(self)
# return {'RUNNING_MODAL'}

View File

@@ -1,7 +1,7 @@
import bpy
from pathlib import Path
import shutil
from bioxelnodes.utils import get_all_layers, get_container, get_container_layers
from .utils import get_all_layers, get_container, get_container_layers
def save_layer(layer, output_dir):

View File

@@ -7,7 +7,7 @@ import bpy
def add_driver_to_node_factory(source_prop, target_prop):
callback_str = f"""
import bpy
from bioxelnodes.utils import add_direct_driver, get_bioxels_obj
from .utils import add_direct_driver, get_bioxels_obj
bioxels_obj = get_bioxels_obj(bpy.context.active_object)
if bioxels_obj:
container_obj = bioxels_obj.parent
@@ -26,7 +26,7 @@ else:
def set_prop_to_node_factory(source_prop, target_prop):
callback_str = f"""
import bpy
from bioxelnodes.utils import get_bioxels_obj
from .utils import get_bioxels_obj
bioxels_obj = get_bioxels_obj(bpy.context.active_object)
if bioxels_obj:
container_obj = bioxels_obj.parent

View File

@@ -26,5 +26,4 @@ class BioxelNodesPreferences(bpy.types.AddonPreferences, ExternalPackagePreferen
layout.label(text="Configuration")
layout.prop(self, 'cache_dir')
layout.prop(self, "do_change_render_setting")

BIN
docs/assets/cover.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

View File

@@ -4,8 +4,19 @@ Currently only support Blender 4.0 or above, make sure you have the correct vers
## Add-on Installation
Download the latest version https://github.com/OmooLab/BioxelNodes/releases/latest
In Blender, Edit > Preferences > Add-ons > Install, select the `BioxelNodes_{version}.zip` you just downloaded.
#### For Blender 4.2 or higher
Download the **Extension** version `BioxelNodes_Extension_{version}.zip` from https://github.com/OmooLab/BioxelNodes/releases/latest
In Blender, Edit > Preferences > Extensions > Install from Disk, select the zip file you just downloaded.
Thats it!
> If it cannot be enable, just reboot blender.
#### For Blender 4.0 or 4.1
Download the **Addon** version `BioxelNodes_Addon_{version}.zip` from https://github.com/OmooLab/BioxelNodes/releases/latest
In Blender, Edit > Preferences > Add-ons > Install, select the zip file you just downloaded.
The add-on requires a third-party python dependency called SimpleITK, click `Install SimpleITK` button below to install the dependency. After clicking, blender may get stuck, it is downloading and installing, just wait for a moment. After that, click `Reboot Blender` button.

View File

@@ -13,7 +13,7 @@ Before us, there have been many tutorials and add-ons for importing volumetric d
Below are some examples with Bioxel Nodes. Thanks to Cycles Render, the volumetric data can be rendered with great detail:
![gallery](assets/gallery.png)
![cover](assets/cover.png)
The "Bioxel" in "Bioxel Nodes", is a combination of the words "Bio-" and "Voxel". Bioxel is a voxel that stores biological data. We are developing a toolkit around Bioxel for better biological data visualization. but before its release, we made this Blender version of bioxels toolkit first, in order to let more people to have fun with volumetric data. [Getting Started](https://omoolab.github.io/BioxelNodes/latest/getting-started)

22
extension/__init__.py Normal file
View File

@@ -0,0 +1,22 @@
import bpy
from . import auto_load
from . import menus
auto_load.init()
def register():
auto_load.register()
menus.add()
bpy.types.Scene.bioxel_layer_dir = bpy.props.StringProperty(
name="Bioxel Layers Directory",
subtype='DIR_PATH',
default="//"
)
def unregister():
menus.remove()
auto_load.unregister()

View File

@@ -0,0 +1,56 @@
schema_version = "1.0.0"
# Example of manifest file for a Blender extension
# Change the values according to your extension
id = "bioxelnodes"
version = "0.2.0"
name = "Bioxel Nodes"
tagline = "For scientific volumetric data visualization in Blender"
maintainer = "Ma Nan <icrdr2010@outlook.com>"
# Supported types: "add-on", "theme"
type = "add-on"
# Optional: add-ons can list which resources they will require:
# * "files" (for access of any filesystem operations)
# * "network" (for internet access)
# * "clipboard" (to read and/or write the system clipboard)
# * "camera" (to capture photos and videos)
# * "microphone" (to capture audio)
permissions = ["files"]
# Optional link to documentation, support, source files, etc
website = "https://omoolab.github.io/BioxelNodes/latest"
# Optional list defined by Blender and server, see:
# https://docs.blender.org/manual/en/dev/extensions/tags.html
tags = ["Geometry Nodes", "Render", "Material"]
blender_version_min = "4.2.0"
# Optional: maximum supported Blender version
# blender_version_max = "5.1.0"
# License conforming to https://spdx.org/licenses/ (use "SPDX: prefix)
# https://docs.blender.org/manual/en/dev/extensions/licenses.html
license = ["SPDX:MIT"]
# Optional: required by some licenses.
copyright = ["2024 OmooLab"]
# Optional list of supported platforms. If omitted, the extension will be available in all operating systems.
platforms = ["windows-amd64", "macos-x86_64"]
# Other supported platforms: "windows-arm64", "macos-x86_64"
# TODO: externalpackage to wheels
# Optional: bundle 3rd party Python modules.
# https://docs.blender.org/manual/en/dev/extensions/python_wheels.html
wheels = [
"./wheels/SimpleITK-2.3.1-cp311-cp311-win_amd64.whl",
"./wheels/SimpleITK-2.3.1-cp311-cp311-macosx_11_0_arm64.whl",
]
# Optional: build setting.
# https://docs.blender.org/manual/en/dev/extensions/command_line_arguments.html#command-line-args-extension-build
# [build]
# paths_exclude_pattern = [
# "/.git/"
# "__pycache__/"
# ]

23
extension/preferences.py Normal file
View File

@@ -0,0 +1,23 @@
import bpy
from pathlib import Path
class BioxelNodesPreferences(bpy.types.AddonPreferences):
bl_idname = __package__
cache_dir: bpy.props.StringProperty(
name="Set Cache Directory",
subtype='DIR_PATH',
default=str(Path(Path.home(), '.bioxelnodes'))
) # type: ignore
do_change_render_setting: bpy.props.BoolProperty(
name="Change Render Setting",
default=True,
) # type: ignore
def draw(self, context):
layout = self.layout
layout.label(text="Configuration")
layout.prop(self, 'cache_dir')
layout.prop(self, "do_change_render_setting")

View File

@@ -57,6 +57,7 @@ theme:
extra:
version:
provider: mike
default: latest
extra_css:
- stylesheets/extra.css