Source code for procfunc.ops.uv

from typing import Literal

import bpy
import numpy as np

import procfunc as pf
from procfunc import types as t
from procfunc.ops._util import execute_mesh_op


def _ensure_uv_layer(obj: bpy.types.Object, uv_name: str) -> None:
    """Ensure a UV layer exists with the given name, removing any existing one first."""
    mesh = obj.data
    if uv_name in mesh.uv_layers:
        mesh.uv_layers.remove(mesh.uv_layers[uv_name])
    new_layer = mesh.uv_layers.new(name=uv_name)
    mesh.uv_layers.active = new_layer


[docs] @pf.tracer.primitive(mutates=["mutates_obj"]) def cube_project( mutates_obj: t.MeshObject, vertex_mask: np.ndarray | None = None, edge_mask: np.ndarray | None = None, face_mask: np.ndarray | None = None, uv_name: str = "UVMap", cube_size: float = 1.0, correct_aspect: bool = True, clip_to_bounds: bool = False, scale_to_bounds: bool = False, ) -> None: """ Project the UV vertices of the mesh over the six faces of a cube Based on bpy.ops.uv.cube_project Args: vertex_mask: Boolean array selecting vertices to project. edge_mask: Boolean array selecting edges to project. face_mask: Boolean array selecting faces to project. uv_name: Name of the UV layer to create/replace. """ _ensure_uv_layer(mutates_obj.item(), uv_name) execute_mesh_op( bpy.ops.uv.cube_project, mutates_obj, vertex_mask=vertex_mask, edge_mask=edge_mask, face_mask=face_mask, cube_size=cube_size, correct_aspect=correct_aspect, clip_to_bounds=clip_to_bounds, scale_to_bounds=scale_to_bounds, )
[docs] @pf.tracer.primitive(mutates=["mutates_obj"]) def cylinder_project( mutates_obj: t.MeshObject, vertex_mask: np.ndarray | None = None, edge_mask: np.ndarray | None = None, face_mask: np.ndarray | None = None, uv_name: str = "UVMap", direction: Literal[ "VIEW_ON_EQUATOR", "VIEW_ON_POLES", "ALIGN_TO_OBJECT" ] = "VIEW_ON_EQUATOR", align: Literal["POLAR_ZX", "POLAR_ZY"] = "POLAR_ZX", pole: Literal["PINCH", "FAN"] = "PINCH", seam: bool = False, radius: float = 1.0, correct_aspect: bool = True, clip_to_bounds: bool = False, scale_to_bounds: bool = False, ) -> None: """ Project the UV vertices of the mesh over the curved wall of a cylinder Based on bpy.ops.uv.cylinder_project Args: vertex_mask: Boolean array selecting vertices to project. edge_mask: Boolean array selecting edges to project. face_mask: Boolean array selecting faces to project. uv_name: Name of the UV layer to create/replace. """ _ensure_uv_layer(mutates_obj.item(), uv_name) execute_mesh_op( bpy.ops.uv.cylinder_project, mutates_obj, vertex_mask=vertex_mask, edge_mask=edge_mask, face_mask=face_mask, direction=direction, align=align, pole=pole, seam=seam, radius=radius, correct_aspect=correct_aspect, clip_to_bounds=clip_to_bounds, scale_to_bounds=scale_to_bounds, )
[docs] @pf.tracer.primitive(mutates=["mutates_obj"]) def project_from_view( mutates_obj: t.MeshObject, vertex_mask: np.ndarray | None = None, edge_mask: np.ndarray | None = None, face_mask: np.ndarray | None = None, uv_name: str = "UVMap", orthographic: bool = False, camera_bounds: bool = True, correct_aspect: bool = True, clip_to_bounds: bool = False, scale_to_bounds: bool = False, ) -> None: """ Project the UV vertices of the mesh as seen in current 3D view Based on bpy.ops.uv.project_from_view Args: vertex_mask: Boolean array selecting vertices to project. edge_mask: Boolean array selecting edges to project. face_mask: Boolean array selecting faces to project. uv_name: Name of the UV layer to create/replace. """ _ensure_uv_layer(mutates_obj.item(), uv_name) execute_mesh_op( bpy.ops.uv.project_from_view, mutates_obj, vertex_mask=vertex_mask, edge_mask=edge_mask, face_mask=face_mask, orthographic=orthographic, camera_bounds=camera_bounds, correct_aspect=correct_aspect, clip_to_bounds=clip_to_bounds, scale_to_bounds=scale_to_bounds, )
[docs] @pf.tracer.primitive(mutates=["mutates_obj"]) def smart_project( mutates_obj: t.MeshObject, vertex_mask: np.ndarray | None = None, edge_mask: np.ndarray | None = None, face_mask: np.ndarray | None = None, uv_name: str = "UVMap", angle_limit: float = 1.15192, margin_method: Literal["SCALED", "ADD", "FRACTION"] = "SCALED", rotate_method: Literal[ "AXIS_ALIGNED", "AXIS_ALIGNED_X", "AXIS_ALIGNED_Y" ] = "AXIS_ALIGNED_Y", island_margin: float = 0.0, area_weight: float = 0.0, correct_aspect: bool = True, scale_to_bounds: bool = False, ) -> None: """ Projection unwraps the selected faces of mesh objects Based on bpy.ops.uv.smart_project Args: vertex_mask: Boolean array selecting vertices to project. edge_mask: Boolean array selecting edges to project. face_mask: Boolean array selecting faces to project. uv_name: Name of the UV layer to create/replace. """ _ensure_uv_layer(mutates_obj.item(), uv_name) execute_mesh_op( bpy.ops.uv.smart_project, mutates_obj, vertex_mask=vertex_mask, edge_mask=edge_mask, face_mask=face_mask, angle_limit=angle_limit, margin_method=margin_method, rotate_method=rotate_method, island_margin=island_margin, area_weight=area_weight, correct_aspect=correct_aspect, scale_to_bounds=scale_to_bounds, )
[docs] @pf.tracer.primitive(mutates=["mutates_obj"]) def sphere_project( mutates_obj: t.MeshObject, vertex_mask: np.ndarray | None = None, edge_mask: np.ndarray | None = None, face_mask: np.ndarray | None = None, uv_name: str = "UVMap", direction: Literal[ "VIEW_ON_EQUATOR", "VIEW_ON_POLES", "ALIGN_TO_OBJECT" ] = "VIEW_ON_EQUATOR", align: Literal["POLAR_ZX", "POLAR_ZY"] = "POLAR_ZX", pole: Literal["PINCH", "FAN"] = "PINCH", seam: bool = False, correct_aspect: bool = True, clip_to_bounds: bool = False, scale_to_bounds: bool = False, ) -> None: """ Project the UV vertices of the mesh over the curved surface of a sphere Based on bpy.ops.uv.sphere_project Args: vertex_mask: Boolean array selecting vertices to project. edge_mask: Boolean array selecting edges to project. face_mask: Boolean array selecting faces to project. uv_name: Name of the UV layer to create/replace. """ _ensure_uv_layer(mutates_obj.item(), uv_name) execute_mesh_op( bpy.ops.uv.sphere_project, mutates_obj, vertex_mask=vertex_mask, edge_mask=edge_mask, face_mask=face_mask, direction=direction, align=align, pole=pole, seam=seam, correct_aspect=correct_aspect, clip_to_bounds=clip_to_bounds, scale_to_bounds=scale_to_bounds, )
[docs] @pf.tracer.primitive(mutates=["mutates_obj"]) def unwrap( mutates_obj: t.MeshObject, vertex_mask: np.ndarray | None = None, edge_mask: np.ndarray | None = None, face_mask: np.ndarray | None = None, uv_name: str = "UVMap", method: Literal["ANGLE_BASED", "CONFORMAL"] = "ANGLE_BASED", fill_holes: bool = True, correct_aspect: bool = True, use_subsurf_data: bool = False, margin_method: Literal["SCALED", "ADD", "FRACTION"] = "SCALED", margin: float = 0.001, ) -> None: """ Unwrap the mesh of the object being edited Based on bpy.ops.uv.unwrap Args: vertex_mask: Boolean array selecting vertices to unwrap. edge_mask: Boolean array selecting edges to unwrap. face_mask: Boolean array selecting faces to unwrap. uv_name: Name of the UV layer to create/replace. """ _ensure_uv_layer(mutates_obj.item(), uv_name) execute_mesh_op( bpy.ops.uv.unwrap, mutates_obj, vertex_mask=vertex_mask, edge_mask=edge_mask, face_mask=face_mask, method=method, fill_holes=fill_holes, correct_aspect=correct_aspect, use_subsurf_data=use_subsurf_data, margin_method=margin_method, margin=margin, )