Source code for snnib.blender.utils.mesh_utils

"""functions to manipulate and read from blender meshes/objects

Exceptions

Classes

Functions
    - 'add_spline2data()' -- adds a spline to some existing curve
    - 'apply_rotation()' -- applies the objects rotation (similar to ctrl + A > apply rotation)
    - 'get_bbox()' -- returns bounding box of some object

Other Objects
"""

#%%imports
import bpy

import logging
import numpy as np
from typing import List, Tuple, Union

logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)

#%%definitions
[docs] def add_spline2data( data:bpy.types.Curve, coords:List[List[float]], scale:Union[List[float],float]=1.0, handle_type:Union[List[str],str]='AUTO', ) -> bpy.types.Spline: """adds a new (bezier) spline with vertices at `coords` to `data` Parameters - `data` - `bpy.types.Curve` - curve object to add a spline to - `coords` - `List[List[float]]` - coordinates of the vertices defining the new spline - `scale` - `List[float]`, `float`, optional - scale to apply to the vertices' handles - if `float` - applied to all handles - the default is `1.0` - `handle_type` - `List[str]`, `str`, optional - handle type to use for each of the generated vertices - if `str` - applied to all handles - the default is `'AUTO'` Raises Returns - `spline` - `bpy.types.Spline` - generated spline Dependencies - `bpy` - `logging` - `numpy` - `typing` """ scale = [scale]*len(coords) if not isinstance(scale, list) else scale handle_type = [handle_type]*len(coords) if not isinstance(handle_type, list) else handle_type #init spline spline = data.splines.new(type='BEZIER') #deal with first point (splines are initilized with a single vertex) spline.bezier_points[0].co = coords[0] #deal with remaining coords for cidx in range(1, len(coords)): spline.bezier_points.add(1) #append a point bp = spline.bezier_points[cidx] #reference to new point bp.co = coords[cidx] #set coordinates #adjust handle position (needs to be done after creation because context of all coords needed) for bp in spline.bezier_points: #start with auto to get frame of reference (by default handles are at `bp.co`) bp.handle_left_type = 'AUTO' bp.handle_right_type = 'AUTO' #apply handle type (might override handle scaling) bp.handle_left_type = handle_type[cidx] bp.handle_right_type = handle_type[cidx] #scale handles left_vec = bp.handle_left - bp.co right_vec = bp.handle_right - bp.co bp.handle_left = bp.co + left_vec * scale[cidx] bp.handle_right = bp.co + right_vec * scale[cidx] return spline
[docs] def apply_rotation( obj:bpy.types.Object ): """applies rotation to `obj` - equivalent to calling `Ctrl + a > apply rotation` in the UI Parameters - `obj` - `bpy.types.Object` - object to apply rotation to Raises Returns Dependencies - `bpy` - `logging` - `numpy` - `typing` """ #get pure rotation rot_mat = obj.rotation_euler.to_matrix().to_4x4() #bake into vertices for v in obj.data.vertices: v.co = rot_mat @ v.co #reset object rotation obj.rotation_euler = (0, 0, 0) return {'FINISHED'}
[docs] def get_bbox( obj:bpy.types.Object ) -> Tuple[np.ndarray,np.ndarray]: """returns bounding box of `obj` Parameters - `obj` - `bpy.types.Object` - object to get bounding box of Raises Returns - `min` - `np.ndarray` - minimum of the bounding box as `[x,y,z]` - `max` - `np.ndarray` - maximum of the bounding box as `[x,y,z]` Dependencies - `bpy` - `logging` - `numpy` - `typing` """ min = np.array([v.co for v in obj.data.vertices]).min(axis=0) max = np.array([v.co for v in obj.data.vertices]).max(axis=0) return min, max
#%%registration def register(): pass def unregister(): pass