Files
blender-portable-repo/scripts/addons/lattice_magic/utils.py
T
2026-03-17 14:58:51 -06:00

122 lines
3.5 KiB
Python

# SPDX-FileCopyrightText: 2025 Blender Studio Tools Authors
#
# SPDX-License-Identifier: GPL-3.0-or-later
import bpy
from mathutils import Vector
from typing import List, Tuple
def clamp(val, _min=0, _max=1) -> float or int:
if val < _min:
return _min
if val > _max:
return _max
return val
def get_lattice_vertex_index(lattice: bpy.types.Lattice, xyz: List[int], do_clamp=True) -> int:
"""Get the index of a lattice vertex based on its position on the XYZ axes."""
# The lattice vertex indicies start in the -Y, -X, -Z corner,
# increase on X+, then moves to the next row on Y+, then moves up on Z+.
res_x, res_y, res_z = lattice.points_u, lattice.points_v, lattice.points_w
x, y, z = xyz[:]
if do_clamp:
x = clamp(x, 0, res_x)
y = clamp(y, 0, res_y)
z = clamp(z, 0, res_z)
assert x < res_x and y < res_y and z < res_z, "Error: Lattice vertex xyz index out of bounds"
index = (z * res_y * res_x) + (y * res_x) + x
return index
def get_lattice_vertex_xyz_position(lattice: bpy.types.Lattice, index: int) -> (int, int, int):
res_x, res_y, res_z = lattice.points_u, lattice.points_v, lattice.points_w
x = 0
remaining = index
z = int(remaining / (res_x * res_y))
remaining -= z * (res_x * res_y)
y = int(remaining / res_x)
remaining -= y * res_x
x = remaining # Maybe need to add or subtract 1 here?
return (x, y, z)
def get_lattice_point_original_position(lattice: bpy.types.Lattice, index: int) -> Vector:
"""Reset a lattice vertex to its original position."""
start_vec = Vector((-0.5, -0.5, -0.5))
if lattice.points_u == 1:
start_vec[0] = 0
if lattice.points_v == 1:
start_vec[1] = 0
if lattice.points_w == 1:
start_vec[2] = 0
unit_u = 1 / (lattice.points_u - 0.99)
unit_v = 1 / (lattice.points_v - 0.99)
unit_w = 1 / (lattice.points_w - 0.99)
unit_vec = Vector((unit_u, unit_v, unit_w))
xyz_vec = Vector(get_lattice_vertex_xyz_position(lattice, index))
return start_vec + xyz_vec * unit_vec
def simple_driver(
owner: bpy.types.ID,
driver_path: str,
target_ob: bpy.types.Object,
data_path: str,
array_index=-1,
) -> bpy.types.Driver:
if array_index > -1:
owner.driver_remove(driver_path, array_index)
driver = owner.driver_add(driver_path, array_index).driver
else:
owner.driver_remove(driver_path)
driver = owner.driver_add(driver_path).driver
driver.expression = 'var'
var = driver.variables.new()
var.targets[0].id = target_ob
var.targets[0].data_path = data_path
return driver
def bounding_box(points) -> Tuple[Vector, Vector]:
"""Return two vectors representing the lowest and highest coordinates of
a the bounding box of the passed points.
"""
lowest = points[0].copy()
highest = points[0].copy()
for p in points:
for i in range(len(p)):
if p[i] < lowest[i]:
lowest[i] = p[i]
if p[i] > highest[i]:
highest[i] = p[i]
return lowest, highest
def bounding_box_center(points) -> Vector:
"""Find the bounding box center of some points."""
bbox_low, bbox_high = bounding_box(points)
return bbox_low + (bbox_high - bbox_low) / 2
def bounding_box_center_of_objects(objects) -> Vector:
"""Find the bounding box center of some objects."""
all_points = []
for o in objects:
for p in o.bound_box:
all_points.append(o.matrix_world @ Vector(p))
return bounding_box_center(all_points)