Compare commits

...

6 Commits

Author SHA1 Message Date
Mucahit Bilal GOKER dd3d08aa68 dict -> Dict 2025-08-15 22:24:58 +03:00
Mucahit Bilal GOKER d3d4d44227 Merge remote-tracking branch 'origin/v3-dev' into bilal/cnx-2144-publish-properties-from-blender 2025-08-15 22:18:50 +03:00
Mucahit Bilal GOKER e50421d223 Merge branch 'v3-dev' into bilal/cnx-2144-publish-properties-from-blender 2025-07-22 12:44:28 +03:00
Mucahit Bilal GOKER fb7a1d93f8 reduce code duplication 2025-07-17 22:08:58 +03:00
Mucahit Bilal GOKER a8b367d243 move property extraction outside the loop 2025-07-17 21:53:45 +03:00
Mucahit Bilal GOKER db24ea7def extract custom properties 2025-07-17 17:17:14 +03:00
5 changed files with 84 additions and 12 deletions
@@ -1,5 +1,5 @@
import bpy
from typing import List
from typing import List, Dict
from bpy.types import Operator, Context, Object
from bpy.props import EnumProperty
from ..utils.model_card_utils import update_model_card_objects
@@ -89,7 +89,7 @@ class SPECKLE_OT_selection_filter_dialog(Operator):
row.label(text="Selection Summary", icon="OUTLINER_OB_GROUP_INSTANCE")
row.label(text=f"Total: {total_selected}", icon="OBJECT_DATA")
object_types: dict[str, int] = {}
object_types: Dict[str, int] = {}
for obj in selected_objects:
if obj.type not in object_types:
object_types[obj.type] = 1
@@ -113,7 +113,7 @@ class SPECKLE_OT_selection_filter_dialog(Operator):
)
def get_icon_for_type(self, obj_type: str) -> str:
icon_map: dict[str, str] = {
icon_map: Dict[str, str] = {
"MESH": "OUTLINER_OB_MESH",
"CURVE": "OUTLINER_OB_CURVE",
"SURFACE": "OUTLINER_OB_SURFACE",
@@ -5,7 +5,7 @@ from specklepy.objects.primitive import Interval
from specklepy.objects.base import Base
from mathutils import Matrix
from mathutils.geometry import interpolate_bezier
from .utils import nurb_make_curve, make_knots
from .utils import nurb_make_curve, make_knots, apply_cached_properties, extract_custom_properties
def curve_to_speckle(
@@ -21,15 +21,22 @@ def curve_to_speckle(
base = Base()
curves = []
# Extract custom properties once for all curves (shared curve data)
curve_properties = extract_custom_properties(curve_data)
for spline in curve_data.splines:
if spline.type == "BEZIER":
curves.append(
bezier_to_speckle(matrix, spline, blender_obj.name, scale_factor, units)
curve = bezier_to_speckle(
matrix, spline, blender_obj.name, scale_factor, units
)
apply_cached_properties(curve, curve_properties)
curves.append(curve)
elif spline.type == "NURBS":
curves.append(
nurbs_to_speckle(matrix, spline, blender_obj.name, scale_factor, units)
curve = nurbs_to_speckle(
matrix, spline, blender_obj.name, scale_factor, units
)
apply_cached_properties(curve, curve_properties)
curves.append(curve)
if curves:
base["@elements"] = curves
@@ -8,7 +8,7 @@ from mathutils import Vector as MVector
from specklepy.objects.base import Base
from specklepy.objects.geometry.mesh import Mesh
from .utils import get_submesh_id
from .utils import get_submesh_id, apply_cached_properties, extract_custom_properties
def mesh_to_speckle(
@@ -42,6 +42,9 @@ def mesh_to_speckle_meshes(
submeshes = []
# Extract custom properties once for all submeshes (shared mesh data)
mesh_properties = extract_custom_properties(data)
# sort material indices to ensure consistent ordering
for material_index in sorted(submesh_data.keys()):
mesh_area = 0
@@ -107,6 +110,9 @@ def mesh_to_speckle_meshes(
speckle_mesh.applicationId = get_submesh_id(blender_object, material_index)
# Add custom properties from the mesh data
apply_cached_properties(speckle_mesh, mesh_properties)
submeshes.append(speckle_mesh)
return submeshes
@@ -3,7 +3,7 @@ from typing import Optional
from specklepy.objects.data_objects import BlenderObject
from .curve_to_speckle import curve_to_speckle
from .mesh_to_speckle import mesh_to_speckle_meshes
from .utils import get_object_id, get_curve_element_id
from .utils import get_object_id, get_curve_element_id, extract_custom_properties
def convert_to_speckle(
@@ -13,7 +13,7 @@ def convert_to_speckle(
apply_modifiers: bool = True,
) -> Optional[BlenderObject]:
display_value = []
properties = {}
properties = extract_custom_properties(blender_object)
if blender_object.type == "CURVE":
# handle curve modifiers apply_modifiers is True
+60 -1
View File
@@ -1,7 +1,7 @@
import bpy
from bpy.types import ID, Object
import math
from typing import Tuple, Optional
from typing import Tuple, Optional, Dict, Any
OBJECT_NAME_SPECKLE_SEPARATOR = " -- "
SPECKLE_ID_LENGTH = 32
@@ -240,3 +240,62 @@ def get_curve_element_id(blender_object: Object, curve_index: int = 0) -> str:
def get_object_id(blender_object: Object) -> str:
return get_unique_id(blender_object)
def extract_custom_properties(blender_id: ID) -> Dict[str, Any]:
"""
Extract custom user-defined properties from a Blender ID datablock.
"""
properties = {}
# Get all custom property keys
for key in blender_id.keys():
# Skip system properties that start with underscore
if key.startswith("_"):
continue
try:
value = blender_id[key]
# Handle basic data types that Speckle can handle
if isinstance(value, (str, int, float, bool)):
properties[key] = value
elif isinstance(value, (list, tuple)):
# Handle arrays of basic types
if all(isinstance(item, (str, int, float, bool)) for item in value):
properties[key] = list(value)
elif type(value).__name__ == "IDPropertyArray":
# Handle Blender IDPropertyArray types (BoolArray, IntArray, FloatArray)
try:
# Convert to list using to_list() method if available, otherwise use list()
if hasattr(value, "to_list"):
array_list = value.to_list()
else:
array_list = list(value)
# Verify all items are basic types
if all(
isinstance(item, (str, int, float, bool)) for item in array_list
):
properties[key] = array_list
except (TypeError, ValueError):
# Skip arrays that can't be converted
continue
except (KeyError, TypeError):
# Skip properties that can't be accessed or have unsupported types
continue
return properties
def apply_custom_properties(speckle_obj, blender_data: ID) -> None:
"""Apply custom properties to a Speckle object if they exist."""
properties = extract_custom_properties(blender_data)
if properties:
speckle_obj.properties = properties
def apply_cached_properties(speckle_obj, properties: Dict[str, Any]) -> None:
"""Apply pre-extracted custom properties to a Speckle object if they exist."""
if properties:
speckle_obj.properties = properties