8 Commits

Author SHA1 Message Date
Ma Nan
92f85ff1f3 Merge pull request #45 from OmooLab/feature/dev
fix: relocate cannot work on missing layer
2024-09-19 12:28:35 +08:00
63c593f09a fix: relocate cannot work on missing layer
fix: import fail if label are all zero
fix: add cutter name to node
fix: cannot append node lib file on NAS (UNC path)
fix: join component cause disappear
feat: smooth label
feat: better topo on cutout by range
2024-09-19 12:27:39 +08:00
Ma Nan
3c5ff64afd Merge pull request #44 from OmooLab/feature/dev
Feature/dev
2024-09-13 17:12:41 +08:00
0ebc6dafbd fix cannot extract wire 2024-09-13 17:00:00 +08:00
c22ab2c85b Merge branch 'main' into feature/dev 2024-09-13 16:43:51 +08:00
133702619e fix: join component cause missing
feat: extract mesh from node
2024-09-13 16:42:18 +08:00
Ma Nan
e384798184 Merge pull request #43 from OmooLab/feature/dev
fix: cannot decode utf-8 issue
2024-09-11 15:36:19 +08:00
80da400891 fix: cannot decode utf-8 issue 2024-09-11 15:17:00 +08:00
16 changed files with 262 additions and 100 deletions

Binary file not shown.

View File

@@ -77,7 +77,7 @@ class Layer():
for f in range(self.frame_count):
mask_frame = mask[f, :, :, :]
if smooth > 0:
mask_frame = scipy.minimum_filter(mask_frame.astype(np.float32),
mask_frame = scipy.median_filter(mask_frame.astype(np.float32),
mode="nearest",
size=smooth)
# mask_frame = scipy.median_filter(
@@ -87,7 +87,7 @@ class Layer():
for f in range(self.frame_count):
mask_frame = mask[:, :, :]
if smooth > 0:
mask_frame = scipy.minimum_filter(mask_frame.astype(np.float32),
mask_frame = scipy.median_filter(mask_frame.astype(np.float32),
mode="nearest",
size=smooth)
# mask_frame = scipy.median_filter(
@@ -119,10 +119,6 @@ class Layer():
if progress_callback:
progress_callback(f, self.frame_count)
# frame = ski.resize(data[f, :, :, :],
# shape,
# preserve_range=True,
# anti_aliasing=data.dtype.kind != "b")
frame = data[f, :, :, :, :]
if smooth > 0:
frame = scipy.median_filter(frame.astype(np.float32),

View File

@@ -61,7 +61,8 @@ def get_ext(filepath: Path) -> str:
elif filepath.name.endswith(".map.gz"):
return ".map.gz"
else:
return filepath.suffix
suffix = filepath.suffix
return "" if len(suffix) > 5 else suffix
def get_filename(filepath: Path):
@@ -310,6 +311,7 @@ def parse_volumetric_data(data_file: str, series_id="", progress_callback=None):
def get_meta(key):
try:
stirng = reader.GetMetaData(0, key).removesuffix(" ")
stirng.encode('utf-8')
if stirng in ["No study description",
"No series description",
""]:

View File

@@ -2,7 +2,7 @@ from ast import literal_eval
from pathlib import Path
import bpy
from ..constants import NODE_LIB_DIRPATH, VERSIONS
from ..constants import LATEST_NODE_LIB_PATH, NODE_LIB_DIRPATH, VERSIONS
from ..utils import get_cache_dir
@@ -244,7 +244,7 @@ def get_node_lib_path(node_version):
def local_lib_not_updated():
addon_version = VERSIONS[0]["node_version"]
addon_lib_path = get_node_lib_path(addon_version)
addon_lib_path = LATEST_NODE_LIB_PATH
use_local = False
for node_group in bpy.data.node_groups:
@@ -258,3 +258,13 @@ def local_lib_not_updated():
not_update = get_node_version() != addon_version
return use_local and not_update
def get_output_node(node_group):
try:
output_node = get_nodes_by_type(node_group,
'NodeGroupOutput')[0]
except:
output_node = node_group.nodes.new("NodeGroupOutput")
return output_node

View File

@@ -8,7 +8,7 @@ from mathutils import Matrix, Vector
from .layer import Layer, layer_to_obj, obj_to_layer
from .common import (get_container_layer_objs,
get_layer_prop_value,
get_nodes_by_type,
get_nodes_by_type, get_output_node,
move_node_to_node)
from .node import add_node_to_graph
from ..utils import get_use_link
@@ -82,11 +82,7 @@ def add_layers(layers: list[Layer],
cache_dir: str):
node_group = container_obj.modifiers[0].node_group
try:
output_node = get_nodes_by_type(node_group,
'NodeGroupOutput')[0]
except:
output_node = node_group.nodes.new("NodeGroupOutput")
output_node = get_output_node(node_group)
for i, layer in enumerate(layers):
layer_obj = layer_to_obj(layer, container_obj, cache_dir)

View File

@@ -1,54 +1,55 @@
from pathlib import Path
import bpy
from .common import get_file_prop, get_node_lib_path, set_file_prop
from ..exceptions import Incompatible, NoFound
from .common import get_file_prop, set_file_prop
from ..exceptions import NoFound
from ..constants import VERSIONS
from ..constants import LATEST_NODE_LIB_PATH, VERSIONS
def get_node_group(node_type: str, use_link=True):
def get_node_tree(node_type: str, use_link=True):
# unannotate below for local debug in node lib file.
# node_group = bpy.data.node_groups[node_type]
# return node_group
# added node is always from latest node version
addon_version = VERSIONS[0]["node_version"]
addon_lib_path = get_node_lib_path(addon_version)
addon_lib_path = LATEST_NODE_LIB_PATH
if get_file_prop("node_version") is None:
set_file_prop("node_version", addon_version)
local_lib_path = None
local_lib = None
for node_group in bpy.data.node_groups:
if node_group.name.startswith("BioxelNodes"):
lib = node_group.library
if lib:
lib_path = Path(bpy.path.abspath(lib.filepath)).resolve()
if lib_path != addon_lib_path:
local_lib_path = lib_path
local_lib = lib.filepath
break
# local lib first
lib_path = local_lib_path or addon_lib_path
bpy.ops.wm.append('EXEC_DEFAULT',
directory=f"{lib_path.as_posix()}/NodeTree",
filename=node_type,
link=use_link,
use_recursive=True,
do_reuse_local_id=True)
node_group = bpy.data.node_groups.get(node_type)
lib_path_str = local_lib or str(addon_lib_path)
if node_group is None:
with bpy.data.libraries.load(lib_path_str,
link=use_link,
relative=True) as (data_from, data_to):
data_to.node_groups = [n for n in data_from.node_groups
if n == node_type]
node_tree = data_to.node_groups[0]
if node_tree is None:
raise NoFound('No custom node found')
return node_group
return node_tree
def assign_node_group(node, node_type: str):
node.node_tree = bpy.data.node_groups[node_type]
def assign_node_tree(node, node_tree):
node.node_tree = node_tree
node.width = 200.0
node.name = node_type
node.name = node_tree.name
return node
@@ -61,9 +62,9 @@ def add_node_to_graph(node_name: str, node_group, node_label=None, use_link=True
if node.select:
node.select = False
get_node_group(node_type, use_link)
node_tree = get_node_tree(node_type, use_link)
node = node_group.nodes.new("GeometryNodeGroup")
assign_node_group(node, node_type)
assign_node_tree(node, node_tree)
node.label = node_label
node.show_options = False

View File

@@ -1,7 +1,7 @@
schema_version = "1.0.0"
id = "bioxelnodes"
version = "1.0.0"
version = "1.0.2"
name = "Bioxel Nodes"
tagline = "For scientific volumetric data visualization in Blender"
maintainer = "Ma Nan <icrdr2010@outlook.com>"
@@ -10,7 +10,7 @@ website = "https://omoolab.github.io/BioxelNodes/latest"
tags = ["Geometry Nodes", "Render", "Import-Export"]
blender_version_min = "4.2.0"
license = ["SPDX:MIT"]
license = ['SPDX:GPL-3.0-or-later']
copyright = ["2024 OmooLab"]
platforms = ["windows-x64", "linux-x64", "macos-arm64", "macos-x64"]

View File

@@ -1,12 +1,31 @@
from pathlib import Path
VERSIONS = [{"label": "Current", "node_version": (1, 0, 0)},
VERSIONS = [{"label": "Latest", "node_version": (1, 0, 2)},
{"label": "v0.3.x", "node_version": (0, 3, 3)},
{"label": "v0.2.x", "node_version": (0, 2, 9)}]
NODE_LIB_DIRPATH = Path(Path(__file__).parent,
"assets/Nodes").resolve()
LATEST_NODE_LIB_PATH = lib_path = Path(NODE_LIB_DIRPATH,
"BioxelNodes_latest.blend").resolve()
COMPONENT_OUTPUT_NODES = [
"CutoutByThreshold",
"CutoutByRange",
"CutoutByHue",
"JoinComponent",
"SetProperties",
"SetColor",
"SetColorByColor",
"SetColorByColor",
"SetColorByRamp2",
"SetColorByRamp3",
"SetColorByRamp4",
"SetColorByRamp5",
"Cut",
]
MENU_ITEMS = [
{
'label': 'Component',
@@ -179,9 +198,9 @@ MENU_ITEMS = [
'icon': 'MODIFIER',
'items': [
{
'label': 'Fetch Surface',
'label': 'Fetch Mesh',
'icon': 'OUTLINER_OB_MESH',
'name': 'FetchSurface',
'name': 'FetchMesh',
'description': ''
},
{

View File

@@ -10,16 +10,19 @@ from .operators.layer import (FetchLayer, RelocateLayer, RetimeLayer, RenameLaye
RemoveSelectedLayers, SaveSelectedLayersCache,
ResampleLayer, SignScalar, CombineLabels,
FillByLabel, FillByThreshold, FillByRange)
from .operators.container import (AddLocator, AddSlicer, ExtractShapeWire,
from .operators.container import (AddLocator, AddSlicer, ContainerProps,
SaveContainerLayersCache, RemoveContainerMissingLayers,
SaveContainer, LoadContainer,
AddPieCutter, AddPlaneCutter,
AddCylinderCutter, AddCubeCutter, AddSphereCutter,
ExtractBboxWire, ExtractSurface, ExtractVolume, ContainerProps)
ExtractBboxWire, ExtractMesh, ExtractShapeWire,
ExtractNodeMesh, ExtractNodeBboxWire, ExtractNodeShapeWire)
from .operators.io import (ImportAsLabel, ImportAsScalar, ImportAsColor)
from .operators.misc import (CleanTemp,
ReLinkNodeLib, RemoveAllMissingLayers, RenderSettingPreset, SaveAllLayersCache, SaveNodeLib, SliceViewer)
ReLinkNodeLib, RemoveAllMissingLayers,
RenderSettingPreset, SaveAllLayersCache,
SaveNodeLib, SliceViewer)
class IncompatibleMenu(bpy.types.Menu):
@@ -76,8 +79,7 @@ class ExtractFromContainerMenu(bpy.types.Menu):
def draw(self, context):
layout = self.layout
layout.operator(ExtractSurface.bl_idname)
layout.operator(ExtractVolume.bl_idname)
layout.operator(ExtractMesh.bl_idname)
layout.operator(ExtractShapeWire.bl_idname)
layout.operator(ExtractBboxWire.bl_idname)
@@ -199,8 +201,8 @@ class BioxelNodesTopbarMenu(bpy.types.Menu):
layout.operator(RemoveAllMissingLayers.bl_idname)
layout.separator()
layout.operator(SaveNodeLib.bl_idname)
layout.menu(ReLinkNodeLibMenu.bl_idname)
layout.operator(SaveNodeLib.bl_idname)
layout.separator()
layout.menu(DangerZoneMenu.bl_idname)
@@ -257,6 +259,11 @@ class NodeContextMenu(bpy.types.Menu):
def draw(self, context):
layout = self.layout
layout.operator(ExtractNodeMesh.bl_idname)
layout.operator(ExtractNodeShapeWire.bl_idname)
layout.operator(ExtractNodeBboxWire.bl_idname)
layout.separator()
layout.operator(SaveSelectedLayersCache.bl_idname,
icon=SaveSelectedLayersCache.bl_icon)
layout.operator(RemoveSelectedLayers.bl_idname)

View File

@@ -1,13 +1,15 @@
import bpy
import bmesh
from ..constants import COMPONENT_OUTPUT_NODES
from ..utils import get_cache_dir, get_use_link, select_object
from ..bioxel.io import load_container, save_container
from ..bioxelutils.container import container_to_obj, obj_to_container
from ..bioxelutils.node import add_node_to_graph
from ..bioxelutils.common import (get_container_layer_objs, get_container_obj,
get_nodes_by_type, is_missing_layer,
from ..bioxelutils.common import (get_container_layer_objs, get_container_obj, get_node_type,
get_nodes_by_type, get_output_node, is_missing_layer,
move_node_between_nodes,
move_node_to_node,
get_all_layer_objs)
@@ -77,7 +79,7 @@ class LoadContainer(bpy.types.Operator):
return {'RUNNING_MODAL'}
class ExtractObject():
class ExtractNodeObject():
bl_options = {'UNDO'}
def execute(self, context):
@@ -87,6 +89,26 @@ class ExtractObject():
self.report({"WARNING"}, "Cannot find any bioxel container.")
return {'FINISHED'}
# nodes = [node for node in context.selected_nodes
# if get_node_type(node).removeprefix("BioxelNodes_") in COMPONENT_OUTPUT_NODES]
if len(context.selected_nodes) == 0:
self.report({"WARNING"}, "No node selected.")
return {'FINISHED'}
selected_node = context.selected_nodes[0]
container_node_group = container_obj.modifiers[0].node_group
container_output_node = get_output_node(container_node_group)
if len(container_output_node.inputs[0].links) == 0:
pre_socket = None
else:
pre_socket = container_output_node.inputs[0].links[0].from_socket
container_node_group.links.new(selected_node.outputs[0],
container_output_node.inputs[0])
bpy.ops.mesh.primitive_cube_add(
size=2, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1))
obj = bpy.context.active_object
@@ -106,6 +128,15 @@ class ExtractObject():
fetch_mesh_node.inputs[0].default_value = container_obj
node_group.links.new(fetch_mesh_node.outputs[0], output_node.inputs[0])
for modifier in obj.modifiers:
bpy.ops.object.modifier_apply(modifier=modifier.name)
obj.data.materials.clear()
if pre_socket:
container_node_group.links.new(pre_socket,
container_output_node.inputs[0])
select_object(obj)
self.report({"INFO"}, f"Successfully picked")
@@ -113,24 +144,92 @@ class ExtractObject():
return {'FINISHED'}
class ExtractSurface(bpy.types.Operator, ExtractObject):
bl_idname = "bioxelnodes.fetch_mesh"
bl_label = "Extract Surface"
bl_description = "Extract Surface"
class ExtractNodeMesh(bpy.types.Operator, ExtractNodeObject):
bl_idname = "bioxelnodes.extract_node_mesh"
bl_label = "Extract Mesh"
bl_description = "Extract Mesh"
bl_icon = "OUTLINER_OB_MESH"
object_type = "Surface"
object_type = "Mesh"
class ExtractVolume(bpy.types.Operator, ExtractObject):
bl_idname = "bioxelnodes.pick_volume"
bl_label = "Extract Volume"
bl_description = "Extract Volume"
bl_icon = "OUTLINER_OB_VOLUME"
object_type = "Volume"
class ExtractNodeShapeWire(bpy.types.Operator, ExtractNodeObject):
bl_idname = "bioxelnodes.extract_node_shape_wire"
bl_label = "Extract Shape Wire"
bl_description = "Extract Shape Wire"
bl_icon = "FILE_VOLUME"
object_type = "ShapeWire"
class ExtractNodeBboxWire(bpy.types.Operator, ExtractNodeObject):
bl_idname = "bioxelnodes.extract_node_bbox_wire"
bl_label = "Extract Bbox Wire"
bl_description = "Extract Bbox Wire"
bl_icon = "MESH_CUBE"
object_type = "BboxWire"
class ExtractObject():
bl_options = {'UNDO'}
def execute(self, context):
container_obj = get_container_obj(context.object)
if container_obj is None:
self.report({"WARNING"}, "Cannot find any bioxel container.")
return {'FINISHED'}
bpy.ops.mesh.primitive_cube_add(
size=2, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1))
obj = bpy.context.active_object
obj.name = f"{container_obj.name}_{self.object_type}"
bpy.ops.node.new_geometry_nodes_modifier()
modifier = obj.modifiers[0]
node_group = modifier.node_group
output_node = get_output_node(node_group)
fetch_mesh_node = add_node_to_graph(f"Fetch{self.object_type}",
node_group,
node_label=f"Fetch {self.object_type}",
use_link=get_use_link())
fetch_mesh_node.inputs[0].default_value = container_obj
if self.object_type == "Mesh":
node_group.links.new(
fetch_mesh_node.outputs[0], output_node.inputs[0])
else:
curve_to_mesh_node = node_group.nodes.new(
"GeometryNodeCurveToMesh")
node_group.links.new(
fetch_mesh_node.outputs[0], curve_to_mesh_node.inputs[0])
node_group.links.new(
curve_to_mesh_node.outputs[0], output_node.inputs[0])
for modifier in obj.modifiers:
bpy.ops.object.modifier_apply(modifier=modifier.name)
obj.data.materials.clear()
select_object(obj)
self.report({"INFO"}, f"Successfully picked")
return {'FINISHED'}
class ExtractMesh(bpy.types.Operator, ExtractObject):
bl_idname = "bioxelnodes.extract_mesh"
bl_label = "Extract Mesh"
bl_description = "Extract Mesh"
bl_icon = "OUTLINER_OB_MESH"
object_type = "Mesh"
class ExtractShapeWire(bpy.types.Operator, ExtractObject):
bl_idname = "bioxelnodes.pick_shape_wire"
bl_idname = "bioxelnodes.extract_shape_wire"
bl_label = "Extract Shape Wire"
bl_description = "Extract Shape Wire"
bl_icon = "FILE_VOLUME"
@@ -138,7 +237,7 @@ class ExtractShapeWire(bpy.types.Operator, ExtractObject):
class ExtractBboxWire(bpy.types.Operator, ExtractObject):
bl_idname = "bioxelnodes.pick_bbox_wire"
bl_idname = "bioxelnodes.extract_bbox_wire"
bl_label = "Extract Bbox Wire"
bl_description = "Extract Bbox Wire"
bl_icon = "MESH_CUBE"
@@ -244,7 +343,7 @@ class AddCutter():
if len(cut_nodes) == 0:
cutter_node = add_node_to_graph("ObjectCutter",
node_group,
node_label=name,
node_label=f"{self.cutter_type.capitalize()} Cutter",
use_link=get_use_link())
cutter_node.inputs[0].default_value = self.cutter_type.capitalize()
cutter_node.inputs[1].default_value = cutter_obj
@@ -253,8 +352,8 @@ class AddCutter():
node_group,
use_link=get_use_link())
output_node = get_nodes_by_type(node_group,
'NodeGroupOutput')[0]
output_node = get_output_node(node_group)
if len(output_node.inputs[0].links) == 0:
node_group.links.new(cut_node.outputs[0],
output_node.inputs[0])
@@ -277,7 +376,7 @@ class AddCutter():
node_name="ObjectCutter",
node_label=name)
node = bpy.context.active_node
node.inputs[0].default_value = name
node.inputs[0].default_value = self.cutter_type.capitalize()
node.inputs[1].default_value = cutter_obj
return {'FINISHED'}
@@ -368,8 +467,8 @@ class AddSlicer(bpy.types.Operator):
slice_node.inputs[1].default_value = slicer_obj
output_node = get_nodes_by_type(node_group,
'NodeGroupOutput')[0]
output_node = get_output_node(node_group)
if len(output_node.inputs[0].links) == 0:
node_group.links.new(slice_node.outputs[0],
output_node.inputs[0])
@@ -419,8 +518,8 @@ class AddLocator(bpy.types.Operator):
parent_node.inputs[1].default_value = locator_obj
output_node = get_nodes_by_type(node_group,
'NodeGroupOutput')[0]
output_node = get_output_node(node_group)
if len(output_node.inputs[0].links) == 0:
node_group.links.new(parent_node.outputs[0],
output_node.inputs[0])

View File

@@ -164,7 +164,7 @@ class ParseVolumetricData(bpy.types.Operator):
read_as: bpy.props.EnumProperty(name="Read as",
default="SCALAR",
items=[("SCALAR", "Scalar", ""),
("LABEL", "Labels", ""),
("LABEL", "Label", ""),
("COLOR", "Color", "")]) # type: ignore
series_id: bpy.props.EnumProperty(name="Select Series",
@@ -284,6 +284,10 @@ class ParseVolumetricData(bpy.types.Operator):
self.report({"ERROR"}, "Invaild label data.")
return {'CANCELLED'}
if self.label_count == 0:
self.report({"ERROR"}, "Get no label.")
return {'CANCELLED'}
orig_shape = self.meta['xyz_shape']
orig_spacing = self.meta['spacing']
@@ -391,6 +395,7 @@ class ParseVolumetricData(bpy.types.Operator):
def get_meta(key):
try:
stirng = single.GetMetaData(key).removesuffix(" ")
stirng.encode('utf-8')
if stirng in ["No study description",
"No series description",
""]:
@@ -416,13 +421,15 @@ class ParseVolumetricData(bpy.types.Operator):
if series_id == "":
series_id = "empty"
series_items[series_id] = "{:<20} {:>1}".format(f"{study_description}>{series_description}({series_modality})",
f"({size_x}x{size_y})x{count}")
label = "{:<20} {:>1}".format(f"{study_description}>{series_description}({series_modality})",
f"({size_x}x{size_y})x{count}")
for key, value in series_items.items():
series_items[series_id] = label
for series_id, label in series_items.items():
series_item = self.series_ids.add()
series_item.id = key
series_item.label = value
series_item.id = series_id
series_item.label = label
if len(series_items.keys()) > 1:
self.skip_series_select = False
@@ -497,10 +504,13 @@ class ImportVolumetricDataDialog(bpy.types.Operator):
label_count: bpy.props.IntProperty() # type: ignore
smooth: bpy.props.IntProperty(name="Smooth Size (Larger takes longer time)",
default=3) # type: ignore
read_as: bpy.props.EnumProperty(name="Read as",
default="SCALAR",
items=[("SCALAR", "Scalar", ""),
("LABEL", "Labels", ""),
("LABEL", "Label", ""),
("COLOR", "Color", "")]) # type: ignore
bioxel_size: bpy.props.FloatProperty(name="Bioxel Size (Larger size means small resolution)",
@@ -613,12 +623,15 @@ class ImportVolumetricDataDialog(bpy.types.Operator):
progress_callback = progress_callback_factory(name_i,
progress,
progress_step)
label_data = data == np.full_like(data, i+1)
label_data = label_data.astype(np.float32)
try:
layer = Layer(data=data == np.full_like(data, i+1),
layer = Layer(data=label_data,
name=name_i,
kind=kind)
layer.resize(shape=shape,
smooth=self.smooth,
progress_callback=progress_callback)
layer.affine = affine
@@ -921,6 +934,8 @@ class ImportVolumetricDataDialog(bpy.types.Operator):
if self.read_as == "SCALAR":
panel.prop(self, "split_channel",
text=f"Split Channel as Multi Layer")
elif self.read_as == "LABEL":
panel.prop(self, "smooth")
panel.label(
text="Dimension Order: [Frame, (X-axis,Y-axis,Z-axis), Channel]")

View File

@@ -4,7 +4,6 @@ import bpy
import numpy as np
from ..exceptions import NoContent
from ..bioxel.layer import Layer
from ..bioxelutils.node import add_node_to_graph
@@ -199,7 +198,7 @@ class RetimeLayer(bpy.types.Operator, LayerOperator):
return self.execute(context)
class RelocateLayer(bpy.types.Operator, LayerOperator):
class RelocateLayer(bpy.types.Operator):
bl_idname = "bioxelnodes.relocate_layer"
bl_label = "Relocate Layer Cache"
bl_description = "Relocate layer cache"
@@ -207,6 +206,16 @@ class RelocateLayer(bpy.types.Operator, LayerOperator):
filepath: bpy.props.StringProperty(subtype="FILE_PATH") # type: ignore
def execute(self, context):
layer_obj = get_selected_layer(context)
if layer_obj == None:
self.report({"WARNING"}, "Get no layer.")
return {'FINISHED'}
self.operate(layer_obj, context)
return {'FINISHED'}
def operate(self, layer_obj, context):
layer_obj.data.filepath = self.filepath
@@ -259,7 +268,7 @@ class ResampleLayer(bpy.types.Operator, OutputLayerOperator):
bl_description = "Resample value"
bl_icon = "ALIASED"
smooth: bpy.props.IntProperty(name="Smooth Iteration",
smooth: bpy.props.IntProperty(name="Smooth Size",
default=0,
soft_min=0, soft_max=5,
options={"SKIP_SAVE"}) # type: ignore
@@ -389,7 +398,7 @@ class FillByThreshold(bpy.types.Operator, FillOperator):
if self.invert else data > self.threshold
new_layer = orig_layer.copy()
new_layer.fill(self.fill_value, mask, 3)
new_layer.fill(self.fill_value, mask, 0)
new_layer.name = self.new_layer_name \
or f"{orig_layer.name}_F-{self.threshold:.2f}"
return new_layer
@@ -419,7 +428,7 @@ class FillByRange(bpy.types.Operator, FillOperator):
(data > self.from_min) & (data < self.from_max)
new_layer = orig_layer.copy()
new_layer.fill(self.fill_value, mask, 3)
new_layer.fill(self.fill_value, mask, 0)
new_layer.name = self.new_layer_name \
or f"{orig_layer.name}_F-{self.from_min:.2f}-{self.from_max:.2f}"
return new_layer
@@ -431,7 +440,7 @@ class FillByLabel(bpy.types.Operator, FillOperator):
bl_description = "Fill value by label"
bl_icon = "MESH_CAPSULE"
smooth: bpy.props.IntProperty(name="Smooth Iteration",
smooth: bpy.props.IntProperty(name="Smooth Size",
default=0,
soft_min=0, soft_max=5,
options={"SKIP_SAVE"}) # type: ignore
@@ -451,7 +460,7 @@ class FillByLabel(bpy.types.Operator, FillOperator):
mask = 1 - mask
new_layer = orig_layer.copy()
new_layer.fill(self.fill_value, mask, 3)
new_layer.fill(self.fill_value, mask, 0)
new_layer.name = self.new_layer_name \
or f"{orig_layer.name}_F-{label_layer.name}"
return new_layer

View File

@@ -5,7 +5,7 @@ import shutil
from ..bioxelutils.common import get_all_layer_objs, get_node_lib_path, get_node_version, is_missing_layer, set_file_prop
from .layer import RemoveLayers, SaveLayersCache
from ..constants import NODE_LIB_DIRPATH, VERSIONS
from ..constants import LATEST_NODE_LIB_PATH, VERSIONS
from ..utils import get_cache_dir
@@ -19,7 +19,8 @@ class ReLinkNodeLib(bpy.types.Operator):
def execute(self, context):
node_version = VERSIONS[self.index]['node_version']
lib_path = get_node_lib_path(node_version)
lib_path = get_node_lib_path(node_version) \
if self.index != 0 else LATEST_NODE_LIB_PATH
node_libs = []
for node_group in bpy.data.node_groups:
@@ -56,16 +57,23 @@ class SaveNodeLib(bpy.types.Operator):
def execute(self, context):
node_version = get_node_version()
if node_version is None:
node_version = VERSIONS[0]["node_version"]
else:
if node_version not in [v["node_version"] for v in VERSIONS]:
node_version = VERSIONS[0]["node_version"]
lib_path = get_node_lib_path(node_version)
if node_version == VERSIONS[0]["node_version"]:
lib_path = LATEST_NODE_LIB_PATH
else:
lib_path = get_node_lib_path(node_version)
version_str = "v"+".".join([str(i) for i in list(node_version)])
lib_dir = bpy.path.abspath(self.lib_dir)
local_lib_path: Path = Path(lib_dir, lib_path.name).resolve()
local_lib_path: Path = Path(lib_dir,
f"BioxelNodes_{version_str}.blend").resolve()
node_lib_path: Path = lib_path
blend_path = Path(bpy.path.abspath("//")).resolve()

View File

@@ -1,7 +1,7 @@
import bpy
from ..bioxelutils.common import is_incompatible, local_lib_not_updated
from ..bioxelutils.node import assign_node_group, get_node_group
from ..bioxelutils.node import assign_node_tree, get_node_tree
from ..utils import get_use_link
@@ -41,14 +41,14 @@ class AddNode(bpy.types.Operator):
"Current addon verison is not compatible to this file. If you insist on editing this file please keep the same addon version.")
return {'CANCELLED'}
get_node_group(self.node_type, get_use_link())
node_tree = get_node_tree(self.node_type, get_use_link())
bpy.ops.node.add_node(
'INVOKE_DEFAULT',
type='GeometryNodeGroup',
use_transform=True
)
node = bpy.context.active_node
assign_node_group(node, self.node_type)
assign_node_tree(node, node_tree)
node.label = self.node_label
node.show_options = False

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "bioxelnodes"
version = "1.0.0"
version = "1.0.2"
description = ""
authors = ["Ma Nan <icrdr2010@outlook.com>"]
license = "MIT"