diff --git a/butils.py b/butils.py index 7dd696f..1982546 100644 --- a/butils.py +++ b/butils.py @@ -3,6 +3,7 @@ import mathutils import queue import importlib import os +import re # import time # for debugging performance # then import dependencies for our addon @@ -559,6 +560,7 @@ def set_text_on_curve(text_properties): if advance == next_line_advance: # self.report({'INFO'}, f"would like to add new line for {text_properties.text} please") print(f"would like to add new line for {text_properties.text} please") + # TODO: add a new line advance = next_line_advance continue is_command = False @@ -643,3 +645,251 @@ def set_text_on_curve(text_properties): # elapsedtime = endtime - starttime return True + +# blender bound_box vertices +# +# 3------7. +# |`. | `. +y +# | `2------6 -z | +# | | | | `. | +# 0---|--4. | `+--- +x +# `. | `.| +# `1------5 + +def add_metrics_obj_from_bound_box(glyph, bound_box=None): + mesh = bpy.data.meshes.new(f"{glyph.name}_metrics") # add the new mesh + obj = bpy.data.objects.new(mesh.name, mesh) + col = glyph.users_collection[0] + col.objects.link(obj) + # bpy.context.view_layer.objects.active = obj + obj.parent = glyph + + if type(bound_box) == type(None): + bound_box = glyph.bound_box + + verts = [bound_box[0], + bound_box[1], + bound_box[2], + bound_box[3], + bound_box[4], + bound_box[5], + bound_box[6], + bound_box[7], + ] + edges = [[0,1],[1,2],[2,3],[3,0], + [0,4],[1,5],[2,6],[3,7], + [4,5],[5,6],[6,7],[7,4], + ] + faces = [] + + mesh.from_pydata(verts, edges, faces) + +def is_mesh(o): + return type(o.data) == bpy.types.Mesh + +def is_metrics_object(o): + return (re.match("[\w]*_metrics$", o.name) != None or re.match("[\w]*_metrics.[\d]{3}$", o.name) != None) and is_mesh(o) + +def is_glyph(o): + try: + return type(o.parent) is not type(None) \ + and "glyphs" in o.parent.name \ + and is_mesh(o) \ + and not is_metrics_object(o) + except ReferenceError as e: + return False +# duplicate +# def remove_metrics_from_selection(): + # for o in bpy.context.selected_objects: + # is_possibly_glyph = is_mesh(o) + # if is_possibly_glyph: + # metrics = [] + # for c in o.children: + # if is_metrics_object(c): + # metrics.append(c) + # completely_delete_objs(metrics) + +def get_max_bound_box(bb_1, bb_2 = None): + if type(bb_2) == type(None): + bb_2 = bb_1 + x_max = max(bb_1[4][0], bb_2[4][0]) + x_min = min(bb_1[0][0], bb_2[0][0]) + y_max = max(bb_1[3][1], bb_2[3][1]) + y_min = min(bb_1[0][1], bb_2[0][1]) + z_max = max(bb_1[1][2], bb_2[1][2]) + z_min = min(bb_1[0][2], bb_2[0][2]) + return [ + mathutils.Vector((x_min, y_min, z_min)), + mathutils.Vector((x_min, y_min, z_max)), + mathutils.Vector((x_min, y_max, z_max)), + mathutils.Vector((x_min, y_max, z_min)), + + mathutils.Vector((x_max, y_min, z_min)), + mathutils.Vector((x_max, y_min, z_max)), + mathutils.Vector((x_max, y_max, z_max)), + mathutils.Vector((x_max, y_max, z_min)), + ] + +# blender bound_box vertices +# +# 3------7. +# |`. | `. +y +# | `2------6 | +# | | | | | +# 0---|--4. | +--- +x +# `. | `.| `. +# `1------5 `+z + +# why not [ [0] * 3 ] * 8 +# https://stackoverflow.com/questions/2397141/how-to-initialize-a-two-dimensional-array-list-of-lists-if-not-using-numpy-in +def bound_box_as_array(bound_box): + array = [ [0] * 3 for i in range(8) ] + for i in range(0, len(bound_box)): + for j in range(0, len(bound_box[i])): + array[i][j] = bound_box[i][j] + return array + +## +# @brief get_metrics_bound_box +# generates a metrics bounding box +# where x-width comes from bb +# and y-height + z-depth from bb_uebermetrics +# +# @param bb +# @param bb_uebermetrics +# +# @return metrics +def get_metrics_bound_box(bb, bb_uebermetrics): + metrics = [[0] * 3] * 8 + # hurrays + if type(bb_uebermetrics) == bpy.types.bpy_prop_array: + metrics = bound_box_as_array(bb_uebermetrics) + else: + metrics = bb_uebermetrics.copy() + metrics[0][0] = bb[0][0] + metrics[1][0] = bb[1][0] + metrics[2][0] = bb[2][0] + metrics[3][0] = bb[3][0] + metrics[4][0] = bb[4][0] + metrics[5][0] = bb[5][0] + metrics[6][0] = bb[6][0] + metrics[7][0] = bb[7][0] + return metrics + +def add_default_metrics_to_objects(objects=None, overwrite_existing=False): + if type(objects) == type(None): + objects=bpy.context.selected_objects + targets = [] + reference_bound_box = None + for o in objects: + is_possibly_glyph = is_glyph(o) + if is_possibly_glyph: + metrics = [] + for c in o.children: + if is_metrics_object(c): + metrics.append(c) + + if len(metrics) == 0: + targets.append(o) + reference_bound_box = get_max_bound_box(o.bound_box, reference_bound_box) + elif len(metrics) >= 0 and overwrite_existing: + completely_delete_objs(metrics) + targets.append(o) + reference_bound_box = get_max_bound_box(o.bound_box, reference_bound_box) + else: + for m in metrics: + reference_bound_box = get_max_bound_box(m.bound_box, reference_bound_box) + for t in targets: + bound_box = get_metrics_bound_box(t.bound_box, reference_bound_box) + add_metrics_obj_from_bound_box(t, bound_box) + +def remove_metrics_from_objects(objects=None): + if type(objects) == type(None): + objects=bpy.context.selected_objects + metrics = [] + for o in objects: + for c in o.children: + if is_metrics_object(c): + metrics.append(c) + completely_delete_objs(metrics) + +def align_metrics_of_objects_to_active_object(objects=None): + if type(objects) == type(None): + objects=bpy.context.selected_objects + if len(objects) == 0: + return "no objects selected" + + # define the reference_bound_box + reference_bound_box = None + if type(bpy.context.active_object) == type(None): + return "no active_object, but align_to_active_object is True" + for c in bpy.context.active_object.children: + if is_metrics_object(c): + reference_bound_box = bound_box_as_array(c.bound_box) + break + if type(reference_bound_box) == type(None): + if not is_mesh(bpy.context.active_object): + return "active_object is not a mesh and does not have a metrics child" + reference_bound_box = bound_box_as_array(bpy.context.active_object.bound_box) + + # do it + for o in objects: + is_possibly_glyph = is_glyph(o) + if is_possibly_glyph: + metrics = [] + for c in o.children: + if is_metrics_object(c): + metrics.append(c) + + bb = None + if len(metrics) == 0: + bb = get_metrics_bound_box(o.bound_box, + reference_bound_box) + else: + bb = get_metrics_bound_box(metrics[0].bound_box, + reference_bound_box) + if len(metrics) > 0: + completely_delete_objs(metrics) + + add_metrics_obj_from_bound_box(o, bb) + return "" + +def align_metrics_of_objects(objects=None): + if type(objects) == type(None): + objects=bpy.context.selected_objects + if len(objects) == 0: + return "no objects selected" + + targets = [] + reference_bound_box = None + for o in objects: + is_possibly_glyph = is_glyph(o) + if is_possibly_glyph: + metrics = [] + for c in o.children: + if is_metrics_object(c): + metrics.append(c) + + if len(metrics) == 0: + reference_bound_box = get_max_bound_box(o.bound_box, + reference_bound_box) + elif len(metrics) > 0: + reference_bound_box = get_max_bound_box(metrics[0].bound_box, + reference_bound_box) + targets.append(o) + for t in targets: + metrics = [] + for c in t.children: + if is_metrics_object(c): + metrics.append(c) + bound_box = None + if len(metrics) == 0: + bound_box = get_metrics_bound_box(t.bound_box, + reference_bound_box) + else: + bound_box = get_metrics_bound_box(metrics[0].bound_box, + reference_bound_box) + completely_delete_objs(metrics) + + add_metrics_obj_from_bound_box(t, bound_box) + return ""