align rotations
This commit is contained in:
parent
a5e45c7439
commit
d081d1d42d
3 changed files with 209 additions and 10 deletions
|
@ -446,6 +446,8 @@ class FONT3D_OT_TestFont(bpy.types.Operator):
|
||||||
ob.constraints["Follow Path"].use_curve_follow = True
|
ob.constraints["Follow Path"].use_curve_follow = True
|
||||||
ob.constraints["Follow Path"].forward_axis = "FORWARD_X"
|
ob.constraints["Follow Path"].forward_axis = "FORWARD_X"
|
||||||
ob.constraints["Follow Path"].up_axis = "UP_Y"
|
ob.constraints["Follow Path"].up_axis = "UP_Y"
|
||||||
|
# samplecurve = nodes.new(type="GeometryNodeSampleCurve")
|
||||||
|
|
||||||
# butils.ShowMessageBox("WHAT","INFO","I don't really know what you mean, lsaidry")
|
# butils.ShowMessageBox("WHAT","INFO","I don't really know what you mean, lsaidry")
|
||||||
else:
|
else:
|
||||||
offset.x = advance
|
offset.x = advance
|
||||||
|
|
181
butils.py
181
butils.py
|
@ -8,6 +8,11 @@ if "Font" in locals():
|
||||||
else:
|
else:
|
||||||
from .common import Font
|
from .common import Font
|
||||||
|
|
||||||
|
if "utils" in locals():
|
||||||
|
importlib.reload(utils)
|
||||||
|
else:
|
||||||
|
from .common import utils
|
||||||
|
|
||||||
def apply_all_transforms(obj):
|
def apply_all_transforms(obj):
|
||||||
mb = obj.matrix_basis
|
mb = obj.matrix_basis
|
||||||
if hasattr(obj.data, "transform"):
|
if hasattr(obj.data, "transform"):
|
||||||
|
@ -25,13 +30,10 @@ def get_parent_collection_names(collection, parent_names):
|
||||||
|
|
||||||
# Ensure it's a curve object
|
# Ensure it's a curve object
|
||||||
# TODO: no raising, please
|
# TODO: no raising, please
|
||||||
def get_curve_length(obj, num_samples = 100):
|
def get_curve_length(curve_obj, num_samples = 100):
|
||||||
total_length = 0
|
total_length = 0
|
||||||
|
|
||||||
if obj.type != 'CURVE':
|
curve = curve_obj.data
|
||||||
raise TypeError("The selected object is not a curve")
|
|
||||||
|
|
||||||
curve = obj.data
|
|
||||||
|
|
||||||
# Loop through all splines in the curve
|
# Loop through all splines in the curve
|
||||||
for spline in curve.splines:
|
for spline in curve.splines:
|
||||||
|
@ -39,6 +41,175 @@ def get_curve_length(obj, num_samples = 100):
|
||||||
|
|
||||||
return total_length
|
return total_length
|
||||||
|
|
||||||
|
def calc_point_on_bezier(bezier_point_1, bezier_point_2, t):
|
||||||
|
p1 = bezier_point_1.co
|
||||||
|
h1 = bezier_point_1.handle_right
|
||||||
|
p2 = bezier_point_2.co
|
||||||
|
h2 = bezier_point_2.handle_left
|
||||||
|
return ((1 - t)**3) * p1 + (3 * t * (1 - t)**2) * h1 + (3 * (t**2) * (1 - t)) * h2 + (t**3) * p2
|
||||||
|
|
||||||
|
def calc_tangent_on_bezier(bezier_point_1, bezier_point_2, t):
|
||||||
|
p1 = bezier_point_1.co
|
||||||
|
h1 = bezier_point_1.handle_right
|
||||||
|
p2 = bezier_point_2.co
|
||||||
|
h2 = bezier_point_2.handle_left
|
||||||
|
return (
|
||||||
|
(-3 * (1 - t)**2) * p1 + (-6 * t * (1 - t) + 3 * (1 - t)**2) * h1 +
|
||||||
|
(-3 * (t**2) + 6 * t * (1 - t)) * h2 + (3 * t**2) * p2
|
||||||
|
).normalized()
|
||||||
|
|
||||||
|
from math import radians, sqrt, pi, acos
|
||||||
|
|
||||||
|
def align_rotations_auto_pivot(mask, input_rotations, vectors, factors, local_main_axis):
|
||||||
|
output_rotations = [mathutils.Matrix().to_3x3() for _ in range(len(input_rotations))]
|
||||||
|
|
||||||
|
for i in mask:
|
||||||
|
vector = mathutils.Vector(vectors[i]).normalized()
|
||||||
|
input_rotation = mathutils.Euler(input_rotations[i])
|
||||||
|
|
||||||
|
if vector.length < 1e-6:
|
||||||
|
output_rotations[i] = input_rotation.to_matrix()
|
||||||
|
continue
|
||||||
|
|
||||||
|
old_rotation = input_rotation.to_matrix()
|
||||||
|
old_axis = (old_rotation @ local_main_axis).normalized()
|
||||||
|
new_axis = vector
|
||||||
|
# rotation_axis = (-(old_axis) + new_axis).normalized()
|
||||||
|
rotation_axis = old_axis.cross(new_axis).normalized()
|
||||||
|
|
||||||
|
if rotation_axis.length < 1e-6:
|
||||||
|
# Vectors are linearly dependent, fallback to another axis
|
||||||
|
rotation_axis = (old_axis + mathutils.Matrix().col[2]).normalized()
|
||||||
|
|
||||||
|
if rotation_axis.length < 1e-6:
|
||||||
|
# This is now guaranteed to not be zero
|
||||||
|
rotation_axis = (-(old_axis) + mathutils.Matrix().col[1]).normalized()
|
||||||
|
|
||||||
|
# full_angle = radians(sqrt((4 * pow(input_rotation.to_quaternion().dot(mathutils.Quaternion(vectors[i].normalized())), 2) - 3)))
|
||||||
|
# dot = old_axis.dot(new_axis)
|
||||||
|
# normalized_diff = (old_axis - new_axis).normalized()
|
||||||
|
# full_angle = acos(min((old_axis * new_axis + normalized_diff.dot(2)).length, 1))
|
||||||
|
full_angle = old_axis.angle(new_axis)
|
||||||
|
angle = factors[i] * full_angle
|
||||||
|
|
||||||
|
rotation = mathutils.Quaternion(rotation_axis, angle).to_matrix()
|
||||||
|
new_rotation_matrix = old_rotation @ rotation
|
||||||
|
output_rotations[i] = new_rotation_matrix
|
||||||
|
|
||||||
|
return [mat.to_4x4() for mat in output_rotations]
|
||||||
|
|
||||||
|
def calc_bezier_length(bezier_point_1, bezier_point_2, resolution=20):
|
||||||
|
step = 1/resolution
|
||||||
|
previous_p = bezier_point_1.co
|
||||||
|
length = 0
|
||||||
|
for i in range(0, resolution):
|
||||||
|
t = (i + 1) * step
|
||||||
|
p = calc_point_on_bezier(bezier_point_1, bezier_point_2, t)
|
||||||
|
length += (p - previous_p).length
|
||||||
|
previous_p = p
|
||||||
|
return length
|
||||||
|
|
||||||
|
def calc_point_on_bezier_spline(bezier_spline_obj,
|
||||||
|
distance,
|
||||||
|
output_tangent = False,
|
||||||
|
resolution_factor = 1.0):
|
||||||
|
# what's the point of just one point
|
||||||
|
# assert len(bezier_spline_obj.bezier_points) >= 2
|
||||||
|
# however, maybe let's have it not crash and do this
|
||||||
|
if len(bezier_spline_obj.bezier_points) < 1:
|
||||||
|
print("butils::calc_point_on_bezier_spline: whoops, no points. panicking. return 0,0,0")
|
||||||
|
if output_tangent:
|
||||||
|
return mathutils.Vector((0,0,0)), mathutils.Vector((1,0,0))
|
||||||
|
else:
|
||||||
|
return mathutils.Vector((0,0,0))
|
||||||
|
if len(bezier_spline_obj.bezier_points) == 1:
|
||||||
|
p = bezier_spline_obj.bezier_points[0]
|
||||||
|
travel = (p.handle_left - p.co).normalized() * distance
|
||||||
|
if output_tangent:
|
||||||
|
tangent = mathutils.Vector((1,0,0))
|
||||||
|
return travel, tangent
|
||||||
|
else:
|
||||||
|
return travel
|
||||||
|
|
||||||
|
if distance <= 0:
|
||||||
|
p = bezier_spline_obj.bezier_points[0]
|
||||||
|
travel = (p.co - p.handle_left).normalized() * distance
|
||||||
|
location = p.co + travel
|
||||||
|
if output_tangent:
|
||||||
|
p2 = bezier_spline_obj.bezier_points[1]
|
||||||
|
tangent = calc_tangent_on_bezier(p, p2, 0)
|
||||||
|
return location, tangent
|
||||||
|
else:
|
||||||
|
return location
|
||||||
|
|
||||||
|
beziers = []
|
||||||
|
lengths = []
|
||||||
|
total_length = 0
|
||||||
|
n_bezier_points = len(bezier_spline_obj.bezier_points)
|
||||||
|
for i in range(0, len(bezier_spline_obj.bezier_points) - 1):
|
||||||
|
bezier = [ bezier_spline_obj.bezier_points[i],
|
||||||
|
bezier_spline_obj.bezier_points[i + 1] ]
|
||||||
|
length = calc_bezier_length(bezier[0],
|
||||||
|
bezier[1],
|
||||||
|
int(bezier_spline_obj.resolution_u * resolution_factor))
|
||||||
|
total_length += length
|
||||||
|
beziers.append(bezier)
|
||||||
|
lengths.append(length)
|
||||||
|
# if total_length > distance:
|
||||||
|
# break
|
||||||
|
|
||||||
|
iterated_distance = 0
|
||||||
|
for i in range(0, len(beziers)):
|
||||||
|
if iterated_distance + lengths[i] > distance:
|
||||||
|
distance_on_bezier = (distance - iterated_distance)
|
||||||
|
d = distance_on_bezier / lengths[i]
|
||||||
|
print(f"i: {i}, d: {d}, distance_on_bezier: {distance_on_bezier}, distance: {distance}")
|
||||||
|
location = calc_point_on_bezier(beziers[i][0],
|
||||||
|
beziers[i][1],
|
||||||
|
d)
|
||||||
|
if output_tangent:
|
||||||
|
tangent = calc_tangent_on_bezier(beziers[i][0],
|
||||||
|
beziers[i][1],
|
||||||
|
d)
|
||||||
|
return location, tangent
|
||||||
|
else:
|
||||||
|
return location
|
||||||
|
iterated_distance += lengths[i]
|
||||||
|
# if we are here, the point is outside the spline
|
||||||
|
last_i = len(beziers) - 1
|
||||||
|
p = beziers[last_i][1]
|
||||||
|
travel = (p.handle_right - p.co).normalized() * (distance - total_length)
|
||||||
|
location = p.co + travel
|
||||||
|
if output_tangent:
|
||||||
|
tangent = calc_tangent_on_bezier(beziers[last_i][0],
|
||||||
|
p,
|
||||||
|
1)
|
||||||
|
return location, tangent
|
||||||
|
else:
|
||||||
|
return location
|
||||||
|
|
||||||
|
|
||||||
|
def calc_point_on_bezier_curve(bezier_curve_obj,
|
||||||
|
distance,
|
||||||
|
output_tangent = False,
|
||||||
|
resolution_factor = 1.0):
|
||||||
|
curve = bezier_curve_obj.data
|
||||||
|
|
||||||
|
# Loop through all splines in the curve
|
||||||
|
total_length = 0
|
||||||
|
for i, spline in enumerate(curve.splines):
|
||||||
|
resolution = int(spline.resolution_u * resolution_factor)
|
||||||
|
length = spline.calc_length(resolution=resolution)
|
||||||
|
if total_length + length > distance or i == len(curve.splines) - 1:
|
||||||
|
return calc_point_on_bezier_spline(spline,
|
||||||
|
(distance - total_length),
|
||||||
|
output_tangent,
|
||||||
|
resolution_factor)
|
||||||
|
total_length += length
|
||||||
|
|
||||||
|
# TODO: can this fail?
|
||||||
|
|
||||||
|
|
||||||
# def get_objects_by_name(name, startswith="", endswith=""):
|
# def get_objects_by_name(name, startswith="", endswith=""):
|
||||||
# return [obj for obj in bpy.context.scene.objects if obj.name.startswith(startswith) and if obj.name.endswith(endswith)]
|
# return [obj for obj in bpy.context.scene.objects if obj.name.startswith(startswith) and if obj.name.endswith(endswith)]
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
|
from mathutils import (
|
||||||
|
Vector,
|
||||||
|
)
|
||||||
|
|
||||||
def get_timestamp():
|
def get_timestamp():
|
||||||
return datetime.datetime \
|
return datetime.datetime \
|
||||||
|
@ -10,9 +13,32 @@ def get_timestamp():
|
||||||
def mapRange(in_value, in_min, in_max, out_min, out_max, clamp=False):
|
def mapRange(in_value, in_min, in_max, out_min, out_max, clamp=False):
|
||||||
output = out_min + ((out_max - out_min) / (in_max - in_min)) * (in_value - in_min)
|
output = out_min + ((out_max - out_min) / (in_max - in_min)) * (in_value - in_min)
|
||||||
if clamp:
|
if clamp:
|
||||||
if out_min < out_max:
|
if out_min < out_max:
|
||||||
return min(out_max, max(out_min, output))
|
return min(out_max, max(out_min, output))
|
||||||
else:
|
else:
|
||||||
return max(out_max, min(out_min, output))
|
return max(out_max, min(out_min, output))
|
||||||
else:
|
else:
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
# # Evaluate a bezier curve for the parameter 0<=t<=1 along its length
|
||||||
|
# def evaluateBezierPoint(p1, h1, h2, p2, t):
|
||||||
|
# return ((1 - t)**3) * p1 + (3 * t * (1 - t)**2) * h1 + (3 * (t**2) * (1 - t)) * h2 + (t**3) * p2
|
||||||
|
|
||||||
|
|
||||||
|
# # Evaluate the unit tangent on a bezier curve for t
|
||||||
|
# def evaluateBezierTangent(p1, h1, h2, p2, t):
|
||||||
|
# return (
|
||||||
|
# (-3 * (1 - t)**2) * p1 + (-6 * t * (1 - t) + 3 * (1 - t)**2) * h1 +
|
||||||
|
# (-3 * (t**2) + 6 * t * (1 - t)) * h2 + (3 * t**2) * p2
|
||||||
|
# ).normalized()
|
||||||
|
|
||||||
|
# def calculateBezierLength(p1, h1, h2, p2, resolution=20):
|
||||||
|
# step = 1/resolution
|
||||||
|
# previous_p = p1
|
||||||
|
# length = 0
|
||||||
|
# for i in range(0, resolution):
|
||||||
|
# t = (i + 1) * step
|
||||||
|
# p = evaluateBezierPoint(p1, h1, h2, p2, t)
|
||||||
|
# length += p.distance(previous_p)
|
||||||
|
# previous_p = p
|
||||||
|
# return length
|
||||||
|
|
Loading…
Reference in a new issue