evaluate hidden
This commit is contained in:
parent
2b98e9bc69
commit
0e14fa19f7
1 changed files with 195 additions and 147 deletions
|
|
@ -3,6 +3,7 @@ from bpy.types import Object, Mesh
|
||||||
import bpy
|
import bpy
|
||||||
import zlib, struct
|
import zlib, struct
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
|
from contextlib import contextmanager, ExitStack
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from numpy import dtype, float64
|
from numpy import dtype, float64
|
||||||
|
|
@ -17,6 +18,74 @@ TYPES_NAME = 'DEFAULT'
|
||||||
|
|
||||||
class MeshCaptureException(Exception): pass
|
class MeshCaptureException(Exception): pass
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def get_evaluated_mesh(obj: Object):
|
||||||
|
"""
|
||||||
|
Context manager yielding (eval_obj, mesh) for a Blender object.
|
||||||
|
Applies all modifiers even if the object is hidden in viewport or in a hidden/disabled collection,
|
||||||
|
WITHOUT changing any visibility settings on the original object or its collections.
|
||||||
|
"""
|
||||||
|
if obj.mode == 'EDIT':
|
||||||
|
obj.update_from_editmode()
|
||||||
|
|
||||||
|
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||||
|
is_hidden = obj.hide_viewport or obj.hide_get()
|
||||||
|
|
||||||
|
mesh = None
|
||||||
|
eval_obj = None
|
||||||
|
is_temp = False
|
||||||
|
temp_obj = None
|
||||||
|
|
||||||
|
# Try direct evaluation first
|
||||||
|
try:
|
||||||
|
eval_obj = obj.evaluated_get(depsgraph)
|
||||||
|
m = eval_obj.to_mesh(preserve_all_data_layers=True, depsgraph=depsgraph)
|
||||||
|
if m and len(m.vertices) > 0 and not is_hidden and (not obj.modifiers or len(m.vertices) != len(obj.data.vertices)):
|
||||||
|
mesh = m
|
||||||
|
elif m:
|
||||||
|
eval_obj.to_mesh_clear()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if mesh is None:
|
||||||
|
# Fallback for hidden object / excluded collection
|
||||||
|
try:
|
||||||
|
temp_obj = obj.copy()
|
||||||
|
temp_obj.hide_viewport = False
|
||||||
|
bpy.context.scene.collection.objects.link(temp_obj)
|
||||||
|
|
||||||
|
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||||
|
depsgraph.update()
|
||||||
|
|
||||||
|
eval_obj = temp_obj.evaluated_get(depsgraph)
|
||||||
|
mesh = eval_obj.to_mesh(preserve_all_data_layers=True, depsgraph=depsgraph)
|
||||||
|
is_temp = True
|
||||||
|
except Exception:
|
||||||
|
if temp_obj is not None:
|
||||||
|
try:
|
||||||
|
bpy.context.scene.collection.objects.unlink(temp_obj)
|
||||||
|
bpy.data.objects.remove(temp_obj, do_unlink=True)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
mesh = None
|
||||||
|
eval_obj = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
yield eval_obj, mesh
|
||||||
|
finally:
|
||||||
|
if mesh is not None and eval_obj is not None:
|
||||||
|
try:
|
||||||
|
eval_obj.to_mesh_clear()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if is_temp and temp_obj is not None:
|
||||||
|
try:
|
||||||
|
bpy.context.scene.collection.objects.unlink(temp_obj)
|
||||||
|
bpy.data.objects.remove(temp_obj, do_unlink=True)
|
||||||
|
bpy.context.evaluated_depsgraph_get().update()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
class SlicingObject():
|
class SlicingObject():
|
||||||
name: str
|
name: str
|
||||||
parent: str
|
parent: str
|
||||||
|
|
@ -34,7 +103,6 @@ class SlicingObject():
|
||||||
self.attributes = {}
|
self.attributes = {}
|
||||||
self.modifiers = []
|
self.modifiers = []
|
||||||
|
|
||||||
depsgraph = bpy.context.evaluated_depsgraph_get()
|
|
||||||
assert bpy.context.scene.unit_settings
|
assert bpy.context.scene.unit_settings
|
||||||
scene_scale: float = bpy.context.scene.unit_settings.scale_length
|
scene_scale: float = bpy.context.scene.unit_settings.scale_length
|
||||||
|
|
||||||
|
|
@ -44,10 +112,7 @@ class SlicingObject():
|
||||||
self.modifiers = [{'param_id': m.field, 'param_value': m.value_str} for m in ns.modifiers]
|
self.modifiers = [{'param_id': m.field, 'param_value': m.value_str} for m in ns.modifiers]
|
||||||
|
|
||||||
if obj:
|
if obj:
|
||||||
if obj.mode == 'EDIT':
|
self.mesh, self.attributes = objects_to_tris([obj], 1000 * scene_scale)
|
||||||
obj.update_from_editmode()
|
|
||||||
eval_objects = obj.evaluated_get(depsgraph)
|
|
||||||
self.mesh, self.attributes = objects_to_tris([eval_objects], 1000 * scene_scale)
|
|
||||||
else:
|
else:
|
||||||
self.mesh = np.empty((0, 4, 3))
|
self.mesh = np.empty((0, 4, 3))
|
||||||
self.attributes = {}
|
self.attributes = {}
|
||||||
|
|
@ -347,19 +412,17 @@ class SlicingGroup():
|
||||||
|
|
||||||
def _extract_metadata(self, objs) -> None:
|
def _extract_metadata(self, objs) -> None:
|
||||||
import math
|
import math
|
||||||
depsgraph = bpy.context.evaluated_depsgraph_get()
|
assert bpy.context.scene and bpy.context.scene.unit_settings
|
||||||
scale_tx = 1000. * bpy.context.scene.unit_settings.scale_length #type: ignore
|
scale_tx = 1000. * bpy.context.scene.unit_settings.scale_length #type: ignore
|
||||||
|
|
||||||
self.wipe_tower_xy = self.center_xy[0:2]
|
self.wipe_tower_xy = self.center_xy[0:2]
|
||||||
for obj in objs:
|
for obj in objs:
|
||||||
if obj.mode == 'EDIT':
|
with get_evaluated_mesh(obj) as (eval_obj, _m):
|
||||||
obj.update_from_editmode()
|
if eval_obj and hasattr(eval_obj, 'nodeslicer'):
|
||||||
eval_obj = obj.evaluated_get(depsgraph)
|
if eval_obj.nodeslicer.object_type == 'WipeTower':
|
||||||
if hasattr(eval_obj, 'nodeslicer'):
|
self.wipe_tower_xy = np.array(eval_obj.location[0:2]) * scale_tx
|
||||||
if eval_obj.nodeslicer.object_type == 'WipeTower':
|
self.wipe_tower_rotation_deg = round(eval_obj.rotation_euler[2]*180/math.pi,5)
|
||||||
self.wipe_tower_xy = np.array(eval_obj.location[0:2]) * scale_tx
|
break
|
||||||
self.wipe_tower_rotation_deg = round(eval_obj.rotation_euler[2]*180/math.pi,5)
|
|
||||||
break
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -434,139 +497,135 @@ class TriMesh():
|
||||||
self.matrix_world = m
|
self.matrix_world = m
|
||||||
|
|
||||||
def _cached_objects_to_tris(obj_names: tuple[str, ...], matrices: tuple[tuple[float, ...], ...], scale: float) -> tuple[NDArray[float64], dict[str, dict]]:
|
def _cached_objects_to_tris(obj_names: tuple[str, ...], matrices: tuple[tuple[float, ...], ...], scale: float) -> tuple[NDArray[float64], dict[str, dict]]:
|
||||||
# Implementation remains largely the same but uses names to fetch objects
|
|
||||||
tris_count: int = 0
|
tris_count: int = 0
|
||||||
meshes: list[TriMesh] = []
|
meshes: list[TriMesh] = []
|
||||||
|
|
||||||
for name in obj_names:
|
with ExitStack() as stack:
|
||||||
obj = bpy.data.objects.get(name)
|
for name in obj_names:
|
||||||
if not obj: continue
|
obj = bpy.data.objects.get(name)
|
||||||
|
if not obj: continue
|
||||||
try:
|
|
||||||
depsgraph = bpy.context.evaluated_depsgraph_get()
|
eval_obj, mesh = stack.enter_context(get_evaluated_mesh(obj))
|
||||||
# We must use the evaluated object to get modifiers
|
if not mesh: continue
|
||||||
obj_eval = obj.evaluated_get(depsgraph)
|
mesh.calc_loop_triangles()
|
||||||
mesh: Mesh = obj_eval.to_mesh(preserve_all_data_layers=True, depsgraph=depsgraph)
|
|
||||||
except: continue
|
|
||||||
mesh.calc_loop_triangles()
|
|
||||||
|
|
||||||
cur_length = len(mesh.loop_triangles)
|
cur_length = len(mesh.loop_triangles)
|
||||||
if cur_length == 0: continue
|
if cur_length == 0: continue
|
||||||
|
|
||||||
tris_count += cur_length
|
tris_count += cur_length
|
||||||
|
|
||||||
meshes += [TriMesh(
|
meshes += [TriMesh(
|
||||||
mesh,
|
mesh,
|
||||||
len(mesh.vertices),
|
len(mesh.vertices),
|
||||||
len(mesh.loop_triangles),
|
len(mesh.loop_triangles),
|
||||||
np.array(obj.matrix_world.transposed())
|
np.array(obj.matrix_world.transposed())
|
||||||
)]
|
)]
|
||||||
|
|
||||||
tris_flat: np.ndarray = np.empty(tris_count * 4 * 3, dtype=np.float64)
|
tris_flat: np.ndarray = np.empty(tris_count * 4 * 3, dtype=np.float64)
|
||||||
tris: np.ndarray = tris_flat.reshape(-1, 4, 3)
|
tris: np.ndarray = tris_flat.reshape(-1, 4, 3)
|
||||||
|
|
||||||
# Identify all FACE domain attributes
|
# Identify all FACE domain attributes
|
||||||
face_attributes_info = {}
|
face_attributes_info = {}
|
||||||
for trimesh in meshes:
|
for trimesh in meshes:
|
||||||
for attr in trimesh.mesh.attributes:
|
for attr in trimesh.mesh.attributes:
|
||||||
if attr.domain == 'FACE':
|
if attr.domain == 'FACE':
|
||||||
face_attributes_info[attr.name] = attr.data_type
|
face_attributes_info[attr.name] = attr.data_type
|
||||||
|
|
||||||
blender_type_map = {
|
blender_type_map = {
|
||||||
'BOOLEAN': 'bool',
|
'BOOLEAN': 'bool',
|
||||||
'INT': 'int',
|
'INT': 'int',
|
||||||
'FLOAT': 'float',
|
'FLOAT': 'float',
|
||||||
'FLOAT_VECTOR': 'float_vector',
|
'FLOAT_VECTOR': 'float_vector',
|
||||||
'FLOAT_COLOR': 'color',
|
'FLOAT_COLOR': 'color',
|
||||||
'BYTE_COLOR': 'color',
|
'BYTE_COLOR': 'color',
|
||||||
}
|
|
||||||
|
|
||||||
attributes_data = {}
|
|
||||||
for name, data_type in face_attributes_info.items():
|
|
||||||
if data_type not in blender_type_map: continue
|
|
||||||
|
|
||||||
my_type = blender_type_map[data_type]
|
|
||||||
|
|
||||||
if data_type == 'BOOLEAN':
|
|
||||||
dtype, shape_ext = np.bool_, ()
|
|
||||||
elif data_type == 'INT':
|
|
||||||
dtype, shape_ext = np.int32, ()
|
|
||||||
elif data_type == 'FLOAT':
|
|
||||||
dtype, shape_ext = np.float32, ()
|
|
||||||
elif data_type == 'FLOAT_VECTOR':
|
|
||||||
dtype, shape_ext = np.float32, (3,)
|
|
||||||
elif data_type in ('BYTE_COLOR', 'FLOAT_COLOR'):
|
|
||||||
dtype, shape_ext = np.float32, (4,)
|
|
||||||
else: continue
|
|
||||||
|
|
||||||
attributes_data[name] = {
|
|
||||||
"domain": "face",
|
|
||||||
"type": my_type,
|
|
||||||
"values": np.zeros((tris_count,) + shape_ext, dtype=dtype)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
col_idx = 0
|
attributes_data = {}
|
||||||
for trimesh in meshes:
|
for name, data_type in face_attributes_info.items():
|
||||||
tris_v_i_flat: np.ndarray = np.empty(trimesh.length_tris * 3, dtype=np.int32)
|
if data_type not in blender_type_map: continue
|
||||||
tris_v_n_flat: np.ndarray = np.empty(trimesh.length_tris * 3, dtype=np.float64)
|
|
||||||
tris_verts_flat: np.ndarray = np.empty(trimesh.length_verts * 3, dtype=np.float64)
|
|
||||||
|
|
||||||
trimesh.mesh.loop_triangles.foreach_get("vertices", tris_v_i_flat)
|
|
||||||
trimesh.mesh.loop_triangles.foreach_get("normal", tris_v_n_flat)
|
|
||||||
trimesh.mesh.vertices.foreach_get("co", tris_verts_flat)
|
|
||||||
|
|
||||||
tris_v_i: np.ndarray = tris_v_i_flat.reshape((-1, 3))
|
|
||||||
tris_v_n: np.ndarray = tris_v_n_flat.reshape((-1, 3))
|
|
||||||
tris_verts: np.ndarray = tris_verts_flat.reshape((-1, 3))
|
|
||||||
|
|
||||||
homogeneous_verts: NDArray[float64] = np.hstack((tris_verts, np.ones((tris_verts.shape[0], 1)))) # type: ignore
|
|
||||||
tx_verts = homogeneous_verts @ trimesh.matrix_world
|
|
||||||
tx_verts = (tx_verts[:, :3]) * scale
|
|
||||||
|
|
||||||
homogeneous_norm: NDArray[float64] = np.hstack((tris_v_n, np.ones((tris_v_n.shape[0], 1)))) # type: ignore
|
|
||||||
tx_norm = homogeneous_norm @ trimesh.matrix_world.T
|
|
||||||
tx_norm = tx_norm[:, :3]
|
|
||||||
tx_norm = tx_norm / np.linalg.norm(tx_norm, axis=1, keepdims=True)
|
|
||||||
tx_norm = tx_norm[:, np.newaxis, :]
|
|
||||||
|
|
||||||
tx_tris = tx_verts[tris_v_i]
|
|
||||||
|
|
||||||
tris_coords_and_norm: NDArray[float64] = np.concatenate((tx_tris, tx_norm), axis=1)
|
|
||||||
|
|
||||||
tris[col_idx:col_idx + trimesh.length_tris,:] = tris_coords_and_norm
|
|
||||||
|
|
||||||
# Attributes
|
|
||||||
if attributes_data:
|
|
||||||
poly_indices = np.empty(trimesh.length_tris, dtype=np.int32)
|
|
||||||
trimesh.mesh.loop_triangles.foreach_get("polygon_index", poly_indices)
|
|
||||||
|
|
||||||
for name, attr_dict in attributes_data.items():
|
my_type = blender_type_map[data_type]
|
||||||
if name in trimesh.mesh.attributes:
|
|
||||||
attr = trimesh.mesh.attributes[name]
|
if data_type == 'BOOLEAN':
|
||||||
data_len = len(attr.data)
|
dtype, shape_ext = np.bool_, ()
|
||||||
|
elif data_type == 'INT':
|
||||||
if attr.data_type == 'FLOAT_VECTOR':
|
dtype, shape_ext = np.int32, ()
|
||||||
raw_values = np.empty(data_len * 3, dtype=np.float32)
|
elif data_type == 'FLOAT':
|
||||||
attr.data.foreach_get("vector", raw_values)
|
dtype, shape_ext = np.float32, ()
|
||||||
raw_values = raw_values.reshape(-1, 3)
|
elif data_type == 'FLOAT_VECTOR':
|
||||||
elif attr.data_type == 'FLOAT_COLOR':
|
dtype, shape_ext = np.float32, (3,)
|
||||||
raw_values = np.empty(data_len * 4, dtype=np.float32)
|
elif data_type in ('BYTE_COLOR', 'FLOAT_COLOR'):
|
||||||
attr.data.foreach_get("color", raw_values)
|
dtype, shape_ext = np.float32, (4,)
|
||||||
raw_values = raw_values.reshape(-1, 4)
|
else: continue
|
||||||
elif attr.data_type == 'BYTE_COLOR':
|
|
||||||
temp_values = np.empty(data_len * 4, dtype=np.uint8)
|
attributes_data[name] = {
|
||||||
attr.data.foreach_get("color", temp_values)
|
"domain": "face",
|
||||||
raw_values = temp_values.astype(np.float32) / 255.0
|
"type": my_type,
|
||||||
raw_values = raw_values.reshape(-1, 4)
|
"values": np.zeros((tris_count,) + shape_ext, dtype=dtype)
|
||||||
else:
|
}
|
||||||
raw_values = np.empty(data_len, dtype=attr_dict["values"].dtype)
|
|
||||||
attr.data.foreach_get("value", raw_values)
|
col_idx = 0
|
||||||
|
for trimesh in meshes:
|
||||||
|
tris_v_i_flat: np.ndarray = np.empty(trimesh.length_tris * 3, dtype=np.int32)
|
||||||
|
tris_v_n_flat: np.ndarray = np.empty(trimesh.length_tris * 3, dtype=np.float64)
|
||||||
|
tris_verts_flat: np.ndarray = np.empty(trimesh.length_verts * 3, dtype=np.float64)
|
||||||
|
|
||||||
|
trimesh.mesh.loop_triangles.foreach_get("vertices", tris_v_i_flat)
|
||||||
|
trimesh.mesh.loop_triangles.foreach_get("normal", tris_v_n_flat)
|
||||||
|
trimesh.mesh.vertices.foreach_get("co", tris_verts_flat)
|
||||||
|
|
||||||
|
tris_v_i: np.ndarray = tris_v_i_flat.reshape((-1, 3))
|
||||||
|
tris_v_n: np.ndarray = tris_v_n_flat.reshape((-1, 3))
|
||||||
|
tris_verts: np.ndarray = tris_verts_flat.reshape((-1, 3))
|
||||||
|
|
||||||
|
homogeneous_verts: NDArray[float64] = np.hstack((tris_verts, np.ones((tris_verts.shape[0], 1)))) # type: ignore
|
||||||
|
tx_verts = homogeneous_verts @ trimesh.matrix_world
|
||||||
|
tx_verts = (tx_verts[:, :3]) * scale
|
||||||
|
|
||||||
|
homogeneous_norm: NDArray[float64] = np.hstack((tris_v_n, np.ones((tris_v_n.shape[0], 1)))) # type: ignore
|
||||||
|
tx_norm = homogeneous_norm @ trimesh.matrix_world.T
|
||||||
|
tx_norm = tx_norm[:, :3]
|
||||||
|
tx_norm = tx_norm / np.linalg.norm(tx_norm, axis=1, keepdims=True)
|
||||||
|
tx_norm = tx_norm[:, np.newaxis, :]
|
||||||
|
|
||||||
|
tx_tris = tx_verts[tris_v_i]
|
||||||
|
|
||||||
|
tris_coords_and_norm: NDArray[float64] = np.concatenate((tx_tris, tx_norm), axis=1)
|
||||||
|
|
||||||
|
tris[col_idx:col_idx + trimesh.length_tris,:] = tris_coords_and_norm
|
||||||
|
|
||||||
|
# Attributes
|
||||||
|
if attributes_data:
|
||||||
|
poly_indices = np.empty(trimesh.length_tris, dtype=np.int32)
|
||||||
|
trimesh.mesh.loop_triangles.foreach_get("polygon_index", poly_indices)
|
||||||
|
|
||||||
|
for name, attr_dict in attributes_data.items():
|
||||||
|
if name in trimesh.mesh.attributes:
|
||||||
|
attr = trimesh.mesh.attributes[name]
|
||||||
|
data_len = len(attr.data)
|
||||||
|
|
||||||
attr_dict["values"][col_idx:col_idx + trimesh.length_tris] = raw_values[poly_indices]
|
if attr.data_type == 'FLOAT_VECTOR':
|
||||||
|
raw_values = np.empty(data_len * 3, dtype=np.float32)
|
||||||
|
attr.data.foreach_get("vector", raw_values)
|
||||||
|
raw_values = raw_values.reshape(-1, 3)
|
||||||
|
elif attr.data_type == 'FLOAT_COLOR':
|
||||||
|
raw_values = np.empty(data_len * 4, dtype=np.float32)
|
||||||
|
attr.data.foreach_get("color", raw_values)
|
||||||
|
raw_values = raw_values.reshape(-1, 4)
|
||||||
|
elif attr.data_type == 'BYTE_COLOR':
|
||||||
|
temp_values = np.empty(data_len * 4, dtype=np.uint8)
|
||||||
|
attr.data.foreach_get("color", temp_values)
|
||||||
|
raw_values = temp_values.astype(np.float32) / 255.0
|
||||||
|
raw_values = raw_values.reshape(-1, 4)
|
||||||
|
else:
|
||||||
|
raw_values = np.empty(data_len, dtype=attr_dict["values"].dtype)
|
||||||
|
attr.data.foreach_get("value", raw_values)
|
||||||
|
|
||||||
|
attr_dict["values"][col_idx:col_idx + trimesh.length_tris] = raw_values[poly_indices]
|
||||||
|
|
||||||
col_idx += trimesh.length_tris
|
col_idx += trimesh.length_tris
|
||||||
|
|
||||||
return tris, attributes_data
|
return tris, attributes_data
|
||||||
|
|
||||||
def objects_to_tris(objects: list[Object], scale) -> tuple[NDArray[float64], dict[str, dict]]:
|
def objects_to_tris(objects: list[Object], scale) -> tuple[NDArray[float64], dict[str, dict]]:
|
||||||
obj_names = tuple(o.name for o in objects)
|
obj_names = tuple(o.name for o in objects)
|
||||||
|
|
@ -584,17 +643,10 @@ def _cached_capture_mesh_edges(
|
||||||
obj = bpy.data.objects.get(obj_name)
|
obj = bpy.data.objects.get(obj_name)
|
||||||
if not obj: return [], [], [], {}
|
if not obj: return [], [], [], {}
|
||||||
|
|
||||||
try:
|
with get_evaluated_mesh(obj) as (eval_obj, mesh):
|
||||||
depsgraph = bpy.context.evaluated_depsgraph_get()
|
if not mesh or not mesh.vertices:
|
||||||
obj_eval = obj.evaluated_get(depsgraph)
|
return [], [], [], {}
|
||||||
mesh = obj_eval.to_mesh(preserve_all_data_layers=True, depsgraph=depsgraph)
|
|
||||||
except Exception:
|
|
||||||
return [], [], [], {}
|
|
||||||
|
|
||||||
if not mesh or not mesh.vertices:
|
|
||||||
return [], [], [], {}
|
|
||||||
|
|
||||||
try:
|
|
||||||
world_mat = np.array(matrix_tuple).reshape(4, 4)
|
world_mat = np.array(matrix_tuple).reshape(4, 4)
|
||||||
num_verts = len(mesh.vertices)
|
num_verts = len(mesh.vertices)
|
||||||
verts = np.empty((num_verts, 3), dtype=np.float64)
|
verts = np.empty((num_verts, 3), dtype=np.float64)
|
||||||
|
|
@ -655,10 +707,6 @@ def _cached_capture_mesh_edges(
|
||||||
|
|
||||||
return final_paths, final_e_mults, final_f_rates, final_extras
|
return final_paths, final_e_mults, final_f_rates, final_extras
|
||||||
|
|
||||||
finally:
|
|
||||||
if hasattr(obj_eval, "to_mesh_clear"):
|
|
||||||
obj_eval.to_mesh_clear()
|
|
||||||
|
|
||||||
|
|
||||||
def capture_mesh_edges(obj: Object, e_attr_name: str = "e_mult", f_attr_name: str = "f_rate", extra_attrs: dict[str, str] | None = None) -> tuple[list[NDArray[float64]], list[NDArray[float64]], list[NDArray[float64]], dict[str, list[NDArray[float64]]]]:
|
def capture_mesh_edges(obj: Object, e_attr_name: str = "e_mult", f_attr_name: str = "f_rate", extra_attrs: dict[str, str] | None = None) -> tuple[list[NDArray[float64]], list[NDArray[float64]], list[NDArray[float64]], dict[str, list[NDArray[float64]]]]:
|
||||||
if not obj or not obj.data: return [], [], [], {}
|
if not obj or not obj.data: return [], [], [], {}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue