- 添加Blender插件核心文件:__init__.py、ui.py、property.py、preference.py - 添加插件工具模块:g.py、loop.py、generate_loop.py、const.py、op.py - 添加翻译工具:utils/trans.py - 添加PuLP线性规划库及其依赖文件,包括CBC求解器二进制文件 - 添加.gitignore和VSCode配置文件
786 lines
24 KiB
Python
786 lines
24 KiB
Python
from __future__ import annotations
|
||
import bpy
|
||
import blf
|
||
import gpu
|
||
import bmesh
|
||
from bpy_extras import view3d_utils
|
||
from . import g
|
||
from gpu_extras.batch import batch_for_shader
|
||
from gpu_extras.presets import draw_circle_2d
|
||
from .utils import functions
|
||
import random
|
||
from .utils.trans import t
|
||
import mathutils
|
||
import itertools
|
||
import traceback
|
||
import time
|
||
|
||
from .utils import functions
|
||
from . import const
|
||
|
||
if bpy.app.version[0] == 3 or bpy.app.version[0] == 4:
|
||
import bgl
|
||
|
||
def draw_desc(self, context):
|
||
prefs = functions.get_preferences()
|
||
|
||
if prefs.desc_position_y == 100 or prefs.desc_position_x == 100:
|
||
return
|
||
|
||
font_id = 0
|
||
|
||
region = context.region
|
||
width, height = region.width, region.height
|
||
|
||
blf.color(font_id, 1, 1, 1, 0.6)
|
||
|
||
font_size = self.get_font_size()
|
||
self.set_font_size(font_size)
|
||
# base_x, base_y = width / 15, height * 7 / 8
|
||
base_x = width * prefs.desc_position_x / 100
|
||
base_y = height * (100 - prefs.desc_position_y) / 100
|
||
|
||
def position_generator(base_x, base_y, line_height):
|
||
while True:
|
||
blf.position(font_id, base_x, base_y, 0)
|
||
yield
|
||
base_y -= line_height
|
||
|
||
pos_gen = position_generator(base_x, base_y, font_size)
|
||
|
||
if g.mode == g.Mode.NORMAL:
|
||
next(pos_gen)
|
||
blf.draw(font_id, f"{t('current_status')}: {t('NORMAL')}")
|
||
|
||
next(pos_gen)
|
||
next(pos_gen)
|
||
blf.draw(font_id, f"{t('click_drag_to_draw_line')}")
|
||
|
||
next(pos_gen)
|
||
blf.draw(font_id, f"{t('hold_ctrl')} - {t('snap_vert')}")
|
||
next(pos_gen)
|
||
blf.draw(
|
||
font_id, f"{t('hold_ctrl')}+{t('hold_shift')} - {t('snap_exist_path')}"
|
||
)
|
||
next(pos_gen)
|
||
blf.draw(
|
||
font_id,
|
||
f"M {t('key')} - {t('draw_on_axis_of_symmetry')}{'('+t('persist')+')' if g.is_drawing_along_mirror_persist else ''}: {t('enable') if g.is_drawing_along_mirror else t('disable')}",
|
||
)
|
||
|
||
next(pos_gen)
|
||
blf.draw(
|
||
font_id,
|
||
f"B {t('key')} - {t('skip_no_solution_pattern')}: {t('enable') if g.is_auto_search_pattern else t('disable')}",
|
||
)
|
||
|
||
next(pos_gen)
|
||
blf.draw(
|
||
font_id,
|
||
f"S {t('key')} - {t('snap_surface')}: {t('enable') if g.is_snap_surface else t('disable')}",
|
||
)
|
||
|
||
if g.mode == g.Mode.NORMAL:
|
||
next(pos_gen)
|
||
blf.draw(
|
||
font_id,
|
||
f"V {t('key')} - {t('snap_opposite')}: {t('enable') if g.is_snap_opposite else t('disable')}",
|
||
)
|
||
|
||
if g.hovering_boundary:
|
||
next(pos_gen)
|
||
blf.draw(font_id, f"X {t('key')} - {t('delete_highlight_edge')}")
|
||
|
||
if g.hovering_loop:
|
||
loop = g.hovering_loop
|
||
next(pos_gen)
|
||
next(pos_gen)
|
||
blf.draw(font_id, f"Ctrl+{t('scroll')} - {t('change_segment')}")
|
||
next(pos_gen)
|
||
blf.draw(font_id, f"Shift+{t('scroll')} - {t('change_padding')}")
|
||
next(pos_gen)
|
||
blf.draw(font_id, f"T {t('key')} - {t('switch_pattern')}")
|
||
next(pos_gen)
|
||
blf.draw(font_id, f"R {t('key')} - {t('rotate_pattern')}")
|
||
|
||
if g.hovering_loop.pattern:
|
||
next(pos_gen)
|
||
blf.draw(
|
||
font_id,
|
||
f"Shift + 1,2,3 - {t('smooth_mesh')}",
|
||
)
|
||
next(pos_gen)
|
||
blf.draw(
|
||
font_id,
|
||
f"i {t('key')} - {t('faces_normal_toward_viewport')}",
|
||
)
|
||
|
||
if g.hovering_loop.pattern:
|
||
pattern = loop.pattern
|
||
next(pos_gen)
|
||
next(pos_gen)
|
||
blf.draw(
|
||
font_id,
|
||
f"{t('solver_solution')}: {[int(i) for i in pattern.solution]}",
|
||
)
|
||
next(pos_gen)
|
||
blf.draw(font_id, f"{t('current_para')}:")
|
||
next(pos_gen)
|
||
blf.draw(font_id, f"{t('pattern')}: {pattern.name}")
|
||
next(pos_gen)
|
||
blf.draw(
|
||
font_id,
|
||
f"{t('rotation')}: {pattern.rotation}",
|
||
)
|
||
else:
|
||
next(pos_gen)
|
||
next(pos_gen)
|
||
blf.color(font_id, 1, 0.5, 0.5, 0.6)
|
||
blf.draw(
|
||
font_id,
|
||
f"{t('solver_solution')}: {loop.solver_msg}",
|
||
)
|
||
|
||
#
|
||
if (
|
||
any([i for i in loop.solver_constraint_list if i is not None])
|
||
or loop.solver_constraint_rotation is not None
|
||
or loop.solver_constraint_pattern
|
||
):
|
||
blf.color(font_id, 1, 0.5, 0.5, 0.6)
|
||
|
||
next(pos_gen)
|
||
next(pos_gen)
|
||
blf.draw(
|
||
font_id,
|
||
f"{t('solver_constraint')}:",
|
||
)
|
||
|
||
next(pos_gen)
|
||
blf.draw(
|
||
font_id,
|
||
f"{t('pattern')}: {loop.solver_constraint_pattern.name}",
|
||
)
|
||
next(pos_gen)
|
||
blf.draw(
|
||
font_id,
|
||
f"{t('rotation')}: {loop.solver_constraint_rotation}",
|
||
)
|
||
next(pos_gen)
|
||
blf.draw(
|
||
font_id,
|
||
f"{t('var_constraint')}: {[i if i is not None else '' for i in loop.solver_constraint_list]}",
|
||
)
|
||
|
||
next(pos_gen)
|
||
blf.draw(
|
||
font_id,
|
||
f"C {t('key')} {t('clear_constraint')}",
|
||
)
|
||
next(pos_gen)
|
||
blf.draw(
|
||
font_id,
|
||
f"Shift + C {t('only_clear_var')}",
|
||
)
|
||
next(pos_gen)
|
||
blf.draw(
|
||
font_id,
|
||
f"Ctrl + C {t('only_clear_rotation')}",
|
||
)
|
||
|
||
elif g.mode == g.Mode.DRAWING:
|
||
next(pos_gen)
|
||
blf.draw(font_id, f"{t('current_status')}: {t('DRAWING')}")
|
||
next(pos_gen)
|
||
next(pos_gen)
|
||
|
||
next(pos_gen)
|
||
blf.draw(font_id, f"{t('right_click')}: {t('cancel_this_draw')}")
|
||
|
||
next(pos_gen)
|
||
blf.draw(font_id, f"{t('left_click_release')}: {t('apply_this_draw')}")
|
||
|
||
next(pos_gen)
|
||
blf.draw(font_id, f"{t('scroll')} - {t('change_segment')}")
|
||
|
||
elif g.mode == g.Mode.MERGE_BOUNDARY:
|
||
next(pos_gen)
|
||
blf.draw(font_id, f"{t('current_status')}: {t('MERGE_BOUNDARY')}")
|
||
next(pos_gen)
|
||
next(pos_gen)
|
||
|
||
blf.color(font_id, 1, 1, 1, 0.6)
|
||
next(pos_gen)
|
||
next(pos_gen)
|
||
next(pos_gen)
|
||
blf.draw(font_id, f"{t('enter')} - {t('apply_mesh')}")
|
||
next(pos_gen)
|
||
blf.draw(font_id, f"ESC - {t('quit')}")
|
||
|
||
|
||
def draw_snap_vertex(self, context):
|
||
if (
|
||
not g.drawing_verts_snap_vertex
|
||
or g.drawing_verts_snap_vertex not in g.obj_bm.verts
|
||
):
|
||
return
|
||
|
||
location_2d = view3d_utils.location_3d_to_region_2d(
|
||
context.region,
|
||
context.region_data,
|
||
g.obj.matrix_world @ g.drawing_verts_snap_vertex.co,
|
||
)
|
||
if not location_2d:
|
||
return
|
||
|
||
if location_2d:
|
||
font_size = self.get_font_size() // 2
|
||
self.set_line_width(1)
|
||
draw_circle_2d(location_2d, (1, 1, 1, 1), font_size)
|
||
|
||
|
||
def draw_hovering_loop_3d(self):
|
||
if g.mode != g.Mode.NORMAL:
|
||
return
|
||
|
||
hovering_loop = g.hovering_loop
|
||
if not hovering_loop:
|
||
return
|
||
|
||
verts = hovering_loop.verts
|
||
mid_co = sum((g.obj.matrix_world @ v.co for v in verts), mathutils.Vector()) / len(
|
||
verts
|
||
)
|
||
|
||
coords = [mid_co] + [g.obj.matrix_world @ v.co for v in verts]
|
||
indices = []
|
||
|
||
for i in range(len(verts)):
|
||
if i == len(verts) - 1:
|
||
indices.append((0, i + 1, 1))
|
||
else:
|
||
indices.append((0, i + 1, i + 2))
|
||
|
||
shader = self.shader_3d_uniform_color
|
||
gpu.state.blend_set("ALPHA")
|
||
batch = batch_for_shader(shader, "TRIS", {"pos": coords}, indices=indices)
|
||
shader.bind()
|
||
shader.uniform_float("color", (0, 0.6, 0, 0.3))
|
||
batch.draw(shader)
|
||
|
||
|
||
def draw_hovering_boundary(self):
|
||
if g.mode != g.Mode.NORMAL:
|
||
return
|
||
|
||
hovering_boundary = g.hovering_boundary
|
||
if not hovering_boundary:
|
||
return
|
||
|
||
path = [g.obj.matrix_world @ v.co for v in hovering_boundary.verts]
|
||
line_co_pairs = []
|
||
for i in range(len(path) - 1):
|
||
line_co_pairs.append((path[i], path[i + 1]))
|
||
draw_lines_3d(self, line_co_pairs, (1, 1, 1, 1), 5)
|
||
|
||
|
||
def draw_loop(self):
|
||
for loop in g.loops:
|
||
for idx, (boundary, is_forward) in enumerate(loop.b_and_d_list):
|
||
|
||
region = bpy.context.region
|
||
rv3d = bpy.context.region_data
|
||
coord_2d = view3d_utils.location_3d_to_region_2d(
|
||
region, rv3d, boundary.mid_co
|
||
)
|
||
if coord_2d is not None:
|
||
text = str(idx)
|
||
if not is_forward:
|
||
text += " R"
|
||
|
||
# 绘制索引
|
||
blf.size(0, 50)
|
||
blf.position(0, coord_2d[0], coord_2d[1], 0)
|
||
blf.color(0, 0, 1, 0, 1)
|
||
blf.draw(0, text)
|
||
|
||
|
||
def draw_temp_patch_boundary(self):
|
||
boundary = g.drawing_path_boundary
|
||
if not boundary:
|
||
return
|
||
|
||
# draw path
|
||
vertex_co_along_path, path = boundary.get_actual_verts_co_and_path()
|
||
|
||
shader = self.shader_3d_uniform_color
|
||
gpu.state.blend_set("ALPHA")
|
||
self.set_line_width(5)
|
||
batch = batch_for_shader(shader, "LINE_STRIP", {"pos": path})
|
||
shader.bind()
|
||
shader.uniform_float("color", (0.0, 0.0, 1, 0.5))
|
||
batch.draw(shader)
|
||
|
||
# draw vertex
|
||
# vertex_co_along_path = patch_boundary.vertices_co_along_path()
|
||
for vertex_co in vertex_co_along_path:
|
||
# TODO: vulkun 用这个shader的时候,gpu.state.point_size_set 失效了,等待修复或别的方案。因为新shader POINT_UNIFORM_COLOR,只能画2d顶点
|
||
batch = batch_for_shader(shader, "POINTS", {"pos": [vertex_co]})
|
||
shader.uniform_float("color", (1, 1, 1, 1))
|
||
batch.draw(shader)
|
||
|
||
|
||
def draw_boundaries_3d(self):
|
||
if not g.boundaries:
|
||
return
|
||
|
||
# edges
|
||
if g.is_redraw_boundary or not g.last_boundary_data:
|
||
draw_boundary_data_list = []
|
||
for boundary in g.boundaries:
|
||
line_color = functions.hsv_to_rgb(
|
||
functions.stable_random_from_obj(boundary), 1, 1, 1
|
||
)
|
||
boundaries_verts_co = boundary.vertices_co
|
||
boundaries_verts_co = [g.obj.matrix_world @ i for i in boundaries_verts_co]
|
||
|
||
line_co_pairs = []
|
||
vert_coords = boundaries_verts_co
|
||
for i in range(len(boundaries_verts_co) - 1):
|
||
line_co_pair = (boundaries_verts_co[i], boundaries_verts_co[i + 1])
|
||
|
||
line_co_pairs.append(line_co_pair)
|
||
|
||
draw_boundary_data_list.append((line_co_pairs, line_color, vert_coords))
|
||
|
||
draw_lines_3d(self, line_co_pairs, line_color, line_width=2.5)
|
||
draw_points_3d(self, vert_coords, (1, 1, 1, 1), point_size=5)
|
||
|
||
g.last_boundary_data = draw_boundary_data_list
|
||
g.is_redraw_boundary = False
|
||
else:
|
||
for boundary_data in g.last_boundary_data:
|
||
line_co_pairs, line_color, vert_coords = boundary_data
|
||
draw_lines_3d(self, line_co_pairs, line_color, line_width=2.5)
|
||
draw_points_3d(self, vert_coords, (1, 1, 1, 1), point_size=5)
|
||
|
||
|
||
def draw_boundaries_text_2d(self):
|
||
font_id = 0
|
||
font_size = self.get_font_size()
|
||
|
||
context = bpy.context
|
||
rv3d = context.region_data
|
||
region = context.region
|
||
|
||
# 绘制边数
|
||
for boundary in g.boundaries:
|
||
coord_2d = view3d_utils.location_3d_to_region_2d(region, rv3d, boundary.mid_co)
|
||
if coord_2d is None:
|
||
return
|
||
|
||
x, y = coord_2d
|
||
blf.position(font_id, x, y, 0)
|
||
blf.color(font_id, 1, 1, 1, 1)
|
||
self.set_font_size(font_size)
|
||
blf.draw(font_id, f"{boundary.length}")
|
||
|
||
# 绘制建议文本
|
||
for loop in g.loops:
|
||
for i, boundary in enumerate(loop.boundaries):
|
||
boundary_mid_co_2d = view3d_utils.location_3d_to_region_2d(
|
||
region, rv3d, boundary.mid_co
|
||
)
|
||
loop_mid_co_2d = view3d_utils.location_3d_to_region_2d(
|
||
region, rv3d, loop.mid_co
|
||
)
|
||
|
||
offset = mathutils.Vector((0, 0))
|
||
if (
|
||
boundary_mid_co_2d.x <= loop_mid_co_2d.x
|
||
and boundary_mid_co_2d.y <= loop_mid_co_2d.y
|
||
):
|
||
result = mathutils.Vector((1, 1)) # a在b的左下角
|
||
elif (
|
||
boundary_mid_co_2d.x >= loop_mid_co_2d.x
|
||
and boundary_mid_co_2d.y <= loop_mid_co_2d.y
|
||
):
|
||
result = mathutils.Vector((-3, 1)) # a在b的右下角
|
||
elif (
|
||
boundary_mid_co_2d.x <= loop_mid_co_2d.x
|
||
and boundary_mid_co_2d.y >= loop_mid_co_2d.y
|
||
):
|
||
result = mathutils.Vector((1, -1)) # a在b的左上角
|
||
elif (
|
||
boundary_mid_co_2d.x >= loop_mid_co_2d.x
|
||
and boundary_mid_co_2d.y >= loop_mid_co_2d.y
|
||
):
|
||
result = mathutils.Vector((-3, -1)) # a在b的右上角
|
||
|
||
offset_value = 20
|
||
offset = result * offset_value
|
||
|
||
# 建议文本
|
||
suggest_text = None
|
||
if loop.suggest_solution:
|
||
pos_value = loop.suggest_solution[i]
|
||
neg_value = loop.suggest_solution[i + len(loop.boundaries)]
|
||
if pos_value > 0:
|
||
suggest_text = f"+{int(pos_value)}"
|
||
if neg_value > 0:
|
||
suggest_text = f"{-int(neg_value)}"
|
||
|
||
suggest_text_co_3d = boundary.mid_co
|
||
suggest_text_co_2d = view3d_utils.location_3d_to_region_2d(
|
||
region, rv3d, suggest_text_co_3d
|
||
)
|
||
if not suggest_text_co_2d:
|
||
continue
|
||
suggest_text_co_2d = suggest_text_co_2d + offset
|
||
|
||
constraint_text = None
|
||
constraint_value = loop.solver_constraint_list[i]
|
||
if loop.solver_constraint_list[i] is not None:
|
||
constraint_text = f"{t('padding')}:{int(constraint_value)}"
|
||
draw_patch_boundary_text(
|
||
self,
|
||
suggest_text_co_2d,
|
||
suggest_text=suggest_text,
|
||
constraint_text=constraint_text,
|
||
)
|
||
|
||
|
||
def draw_patch_pattern(self):
|
||
font_id = 0
|
||
|
||
if g.is_redraw_pattern:
|
||
for loop in g.loops:
|
||
pattern = loop.pattern
|
||
if not loop.pattern:
|
||
continue
|
||
|
||
color = (1, 1, 1, 0.5)
|
||
verts = pattern.interior_verts
|
||
edges = pattern.interior_edges
|
||
if verts:
|
||
pass
|
||
# 减轻压力
|
||
# draw_verts_2d(self, verts, color, 5)
|
||
|
||
line_co_pairs = []
|
||
for edge in edges:
|
||
line_co_pair = (
|
||
g.obj.matrix_world @ edge.verts[0].co,
|
||
g.obj.matrix_world @ edge.verts[1].co,
|
||
)
|
||
line_co_pairs.append(line_co_pair)
|
||
|
||
if line_co_pairs:
|
||
draw_lines_3d(self, line_co_pairs, color, line_width=2)
|
||
|
||
|
||
def draw_lines_3d(self, line_co_pairs, color, line_width=2):
|
||
if not line_co_pairs:
|
||
return
|
||
|
||
line_co_pair_flatten = list(itertools.chain(*line_co_pairs))
|
||
|
||
shader = self.shader_3d_uniform_color
|
||
gpu.state.blend_set("ALPHA")
|
||
self.set_line_width(line_width)
|
||
batch = batch_for_shader(shader, "LINES", {"pos": line_co_pair_flatten})
|
||
shader.bind()
|
||
shader.uniform_float("color", color)
|
||
batch.draw(shader)
|
||
|
||
|
||
def draw_points_3d(self, vert_coords, color, point_size=3):
|
||
if not vert_coords:
|
||
return
|
||
|
||
if bpy.app.version < (4, 5, 0):
|
||
shader = self.shader_3d_uniform_color
|
||
gpu.state.blend_set("ALPHA")
|
||
self.set_point_size(point_size)
|
||
batch = batch_for_shader(shader, "POINTS", {"pos": vert_coords})
|
||
shader.bind()
|
||
shader.uniform_float("color", color)
|
||
batch.draw(shader)
|
||
else:
|
||
shader = self.shader_point_uniform_color
|
||
batch = batch_for_shader(shader, "POINTS", {"pos": vert_coords})
|
||
shader.uniform_float("color", color)
|
||
gpu.state.point_size_set(point_size)
|
||
batch.draw(shader)
|
||
|
||
|
||
def draw_edges_2d(self, edges, color):
|
||
if not edges:
|
||
return
|
||
|
||
edges_co = [
|
||
tuple(g.obj.matrix_world @ vert.co for vert in edge.verts) for edge in edges
|
||
]
|
||
context = bpy.context
|
||
region = context.region
|
||
rv3d = context.region_data
|
||
|
||
coords_3d = edges_co
|
||
coords_2d = [
|
||
functions.coords_3d_to_2d(region, rv3d, sublist) for sublist in coords_3d
|
||
]
|
||
if None in coords_2d:
|
||
return
|
||
|
||
coords_2d = list(itertools.chain.from_iterable(coords_2d))
|
||
|
||
shader = self.shader_2d_uniform_color
|
||
batch = batch_for_shader(shader, "LINES", {"pos": coords_2d})
|
||
shader.bind()
|
||
shader.uniform_float("color", color)
|
||
batch.draw(shader)
|
||
|
||
|
||
def draw_path_2d(self, path, color, line_width=1):
|
||
if not path:
|
||
return
|
||
|
||
edges_co = [(path[i], path[i + 1]) for i in range(len(path) - 1)]
|
||
|
||
context = bpy.context
|
||
region = context.region
|
||
rv3d = context.region_data
|
||
|
||
coords_3d = edges_co
|
||
coords_2d = [
|
||
[
|
||
(view3d_utils.location_3d_to_region_2d(region, rv3d, item) or (None, None))
|
||
for item in sublist
|
||
if view3d_utils.location_3d_to_region_2d(region, rv3d, item) is not None
|
||
]
|
||
for sublist in coords_3d
|
||
]
|
||
if None in coords_2d:
|
||
return
|
||
|
||
coords_2d = list(itertools.chain.from_iterable(coords_2d))
|
||
|
||
self.set_line_width(line_width)
|
||
shader = self.shader_2d_uniform_color
|
||
batch = batch_for_shader(shader, "LINES", {"pos": coords_2d})
|
||
shader.bind()
|
||
shader.uniform_float("color", color)
|
||
batch.draw(shader)
|
||
|
||
|
||
def draw_patch_boundary_text(self, coord_2d, suggest_text, constraint_text):
|
||
|
||
font_id = 0
|
||
font_size = self.get_font_size()
|
||
|
||
# text
|
||
if coord_2d is None:
|
||
return
|
||
x, y = coord_2d
|
||
|
||
self.set_font_size(font_size * (2 / 3))
|
||
offset = font_size
|
||
if suggest_text is not None:
|
||
blf.position(font_id, x, y - offset, 0)
|
||
blf.color(font_id, 0, 1, 0, 1)
|
||
blf.draw(font_id, str(suggest_text))
|
||
offset += font_size
|
||
|
||
if constraint_text is not None:
|
||
blf.position(font_id, x, y, 0)
|
||
blf.color(font_id, 1, 0, 0, 1)
|
||
blf.draw(font_id, str(constraint_text))
|
||
|
||
self.set_font_size(font_size)
|
||
|
||
|
||
class Draw:
|
||
def __init__(self):
|
||
self.title_handler = None
|
||
self.temp_handler = None
|
||
self.snap_vertex_handler = None
|
||
|
||
self._draw_2d_handler = None
|
||
self._draw_3d_handler = None
|
||
|
||
def get_font_size(self):
|
||
region = bpy.context.region
|
||
width, height = region.width, region.height
|
||
font_size = max(10, min(width, height) // 50)
|
||
return font_size
|
||
|
||
def draw_2d(self, context):
|
||
if repr(self.op_instance).endswith("invalid>"):
|
||
self.clear()
|
||
return
|
||
|
||
if g.is_changing_mesh:
|
||
return
|
||
|
||
if not g.is_running:
|
||
self.clear()
|
||
return
|
||
|
||
try:
|
||
start_time = time.time()
|
||
# draw_hovering_boundary_2d(self)
|
||
draw_hovering_boundary_2d_time = time.time() - start_time
|
||
|
||
draw_desc(self, context)
|
||
|
||
start_time = time.time()
|
||
draw_snap_vertex(self, context)
|
||
draw_snap_vertex_time = time.time() - start_time
|
||
|
||
start_time = time.time()
|
||
# draw_boundaries(self)
|
||
draw_boundaries_time = time.time() - start_time
|
||
|
||
draw_boundaries_text_2d(self)
|
||
|
||
start_time = time.time()
|
||
# draw_patch_pattern(self)
|
||
draw_patch_pattern_time = time.time() - start_time
|
||
|
||
# last_echo_time = g.last_echo_time if g.last_echo_time else 0
|
||
# if time.time() - last_echo_time > 0.1:
|
||
# print(
|
||
# f"{draw_hovering_boundary_2d_time:.3f}",
|
||
# f"{draw_snap_vertex_time:.3f}",
|
||
# f"{draw_boundaries_time:.3f}",
|
||
# f"{draw_patch_pattern_time:.3f}",
|
||
# )
|
||
# g.last_echo_time = time.time()
|
||
except Exception as e:
|
||
self.clear()
|
||
self.process_exception(e)
|
||
|
||
# draw_loop(self) # debug
|
||
|
||
def draw_3d(self, context):
|
||
if repr(self.op_instance).endswith("invalid>"):
|
||
self.clear()
|
||
return
|
||
|
||
if g.is_changing_mesh:
|
||
return
|
||
|
||
if not g.is_running:
|
||
self.clear()
|
||
return
|
||
|
||
try:
|
||
draw_hovering_boundary(self)
|
||
|
||
draw_hovering_loop_3d(self)
|
||
draw_temp_patch_boundary(self)
|
||
|
||
draw_patch_pattern(self)
|
||
draw_boundaries_3d(self)
|
||
|
||
except Exception as e:
|
||
self.clear()
|
||
self.process_exception(e)
|
||
# raise e
|
||
|
||
def process_exception(self, e):
|
||
error_info = traceback.format_exc()
|
||
functions.log(error_info)
|
||
|
||
# tab退出时,bm删除,这里执行循环很快,还来不及清空,会抛出ReferenceError,这个情况不向ui抛出错误
|
||
if isinstance(e, ReferenceError):
|
||
error_info = traceback.format_exc()
|
||
|
||
pass
|
||
else:
|
||
g.running_exception = e
|
||
# raise e
|
||
pass
|
||
|
||
def set_line_width(self, line_width):
|
||
if bpy.app.version[0] == 3:
|
||
# 3.x
|
||
bgl.glLineWidth(line_width)
|
||
else:
|
||
# 4.x 5.x
|
||
gpu.state.line_width_set(line_width)
|
||
|
||
def set_point_size(self, point_size):
|
||
if bpy.app.version[0] == 3:
|
||
# 3.x
|
||
bgl.glPointSize(point_size)
|
||
else:
|
||
# 4.x 5.x
|
||
gpu.state.point_size_set(point_size)
|
||
|
||
def set_font_size(self, font_size):
|
||
font_id = 0
|
||
if bpy.app.version[0] == 3:
|
||
# # 3.x got 3
|
||
blf.size(font_id, font_size, 72)
|
||
else:
|
||
# 4.0 got 2
|
||
blf.size(font_id, font_size)
|
||
|
||
def start(self, op_instance):
|
||
functions.log("start draw")
|
||
|
||
self.op_instance = op_instance
|
||
|
||
if bpy.app.version[0] == 3:
|
||
if bpy.app.version[0] >= 6:
|
||
# 3.6
|
||
self.shader_3d_uniform_color = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||
self.shader_2d_uniform_color = gpu.shader.from_builtin(
|
||
"2D_UNIFORM_COLOR"
|
||
)
|
||
else:
|
||
# 3.3
|
||
self.shader_3d_uniform_color = gpu.shader.from_builtin(
|
||
"3D_UNIFORM_COLOR"
|
||
)
|
||
self.shader_2d_uniform_color = gpu.shader.from_builtin(
|
||
"2D_UNIFORM_COLOR"
|
||
)
|
||
|
||
else:
|
||
# 4.0 5.x
|
||
self.shader_3d_uniform_color = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||
self.shader_2d_uniform_color = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||
|
||
if bpy.app.version >= (4, 5, 0):
|
||
self.shader_point_uniform_color = gpu.shader.from_builtin("POINT_UNIFORM_COLOR")
|
||
|
||
if not self._draw_2d_handler:
|
||
self._draw_2d_handler = bpy.types.SpaceView3D.draw_handler_add(
|
||
self.draw_2d,
|
||
(bpy.context,),
|
||
"WINDOW",
|
||
"POST_PIXEL",
|
||
)
|
||
if not self._draw_3d_handler:
|
||
self._draw_3d_handler = bpy.types.SpaceView3D.draw_handler_add(
|
||
self.draw_3d,
|
||
(bpy.context,),
|
||
"WINDOW",
|
||
"POST_VIEW",
|
||
)
|
||
|
||
def clear(self):
|
||
functions.log("darw cleared")
|
||
if self._draw_2d_handler:
|
||
bpy.types.SpaceView3D.draw_handler_remove(self._draw_2d_handler, "WINDOW")
|
||
self._draw_2d_handler = None
|
||
|
||
if self._draw_3d_handler:
|
||
bpy.types.SpaceView3D.draw_handler_remove(self._draw_3d_handler, "WINDOW")
|
||
self._draw_3d_handler = None
|
||
|
||
|
||
draw = Draw()
|