Files
blender-easy-patch/generate_loop.py
小煜 ab91b120e6 feat: 初始化Easy Patch插件及依赖文件
- 添加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配置文件
2026-03-03 19:24:57 +08:00

195 lines
7.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from . import g
from .loop import Loop
from collections import defaultdict
# 因为要引用Loop patch,所以单独放了
def regenerate_loops(is_force_refresh=False):
boundaries = g.boundaries # 假设 'g' 有一个 'boundaries' 属性,它包含边界对象
graph = {}
# 构建无向图
for boundary in boundaries:
graph.setdefault(boundary.start_vertex, []).append(boundary.end_vertex)
graph.setdefault(boundary.end_vertex, []).append(boundary.start_vertex)
# 方向字典
boundary_direction = {
(boundary.start_vertex, boundary.end_vertex): (boundary, True)
for boundary in boundaries
}
boundary_direction.update(
{
(boundary.end_vertex, boundary.start_vertex): (boundary, False)
for boundary in boundaries
}
)
def dfs(node, start, path, visited, current_loops):
max_length = 6
if len(path) > max_length:
return
visited.add(node)
path.append(node)
for neighbor in graph[node]:
# 2条边在拓扑结构上是一摸一样的没办法通过拓扑办法识别最小圈
if neighbor == start and len(path) > 2:
# 找到一个环路创建对应的环路每条边是一个元组包含boundary和方向
loop = Loop()
for i in range(len(path) - 1):
start_vertex = path[i]
end_vertex = path[i + 1]
if (start_vertex, end_vertex) in boundary_direction:
boundary, direction = boundary_direction[
(start_vertex, end_vertex)
]
loop.add_edge(boundary, direction)
else:
boundary, direction = boundary_direction[
(end_vertex, start_vertex)
]
loop.add_edge(boundary, direction)
# 处理闭合边path[-1] 到 path[0]
start_vertex = path[-1]
end_vertex = path[0]
if (start_vertex, end_vertex) in boundary_direction:
boundary, direction = boundary_direction[(start_vertex, end_vertex)]
loop.add_edge(boundary, direction)
else:
boundary, direction = boundary_direction[(end_vertex, start_vertex)]
loop.add_edge(boundary, direction)
current_loops.append(loop)
elif neighbor not in visited:
dfs(neighbor, start, path, visited, current_loops)
path.pop()
visited.remove(node)
loops = [] # 所有环路
for node in graph:
current_loops = [] # 用来存储当前节点发现的环路
dfs(node, node, [], set(), current_loops)
loops.extend(current_loops) # 将当前节点的环路加入到结果中
# 去重环路
unique_loops = set(loops) # 使用 set 去重环路
# 删除长度超过6的
unique_loops = {loop for loop in unique_loops if len(loop.boundaries) <= 6}
# 如果一个环路,他有弦,则删掉
to_be_delete_loops = set()
# 获取连续1 2 3条boundary的映射并且要标记属于哪个loop的
start_v_and_end_v_to_loops_of_length = [
defaultdict(set) for _ in range(3)
] # 0是长度为1的1为长度为2的2为长度为3的元素为字典key是两个顶点value是相关的loop
for loop in unique_loops:
verts = loop.controll_verts
for vert_idx, vert in enumerate(verts):
for offset in range(1, 4): # 1~3
vert_1 = verts[vert_idx]
vert_2 = verts[(vert_idx + offset) % len(verts)]
sorted_start_v_and_end_v_tuple = tuple(
sorted([vert_1, vert_2], key=lambda v: v.index)
)
start_v_and_end_v_to_loops_of_length[offset - 1][
sorted_start_v_and_end_v_tuple
].add(loop)
# # debug形状
# print(len(start_v_and_end_v_to_loops_of_length))
# for i, start_v_and_end_v_to_loops in enumerate(
# start_v_and_end_v_to_loops_of_length
# ):
# print(f"长度{i+1}")
# for (vert_1, vert_2), loops in start_v_and_end_v_to_loops.items():
# print(len(loops))
for loop in unique_loops:
if loop.length <= 3: # 小于3不检查弦
continue
offset_range = loop.length - 3
loop_controll_verts = loop.controll_verts
for offset in range(1, offset_range + 1):
# 隔一个顶点就检查1条的隔两个顶点就检查1和2条的隔三个就检查123条的
# offset就是boundary的条数
# 遍历两两不相邻的顶点
for i in range(len(loop_controll_verts)):
for j in range(len(loop_controll_verts)):
# 确保 j 不等于 i并且 j 不是 i 的相邻边界
if (
i != j
and (j != (i + 1) % len(loop_controll_verts))
and (j != (i - 1) % len(loop_controll_verts))
):
vert_1 = loop_controll_verts[i]
vert_2 = loop_controll_verts[j]
sorted_start_v_and_end_v_tuple = tuple(
sorted([vert_1, vert_2], key=lambda v: v.index)
)
# 隔一个顶点就检查1条的2和3跳过
# 如果 i 和 j 隔了 1 个顶点,跳过 offset 为 1 和 2
distance = min(
abs(i - j), len(loop_controll_verts) - abs(i - j)
) # 0 1距离1
# print(loop.length, distance, offset)
if distance == 2 and (offset == 2 or offset == 3):
continue # 跳过 offset 为 1 或 2 的情况
# 如果 i 和 j 隔了 2 个顶点,跳过 offset 为 2
if distance == 3 and offset == 2:
continue # 跳过 offset 为 2 的情况
related_loops = start_v_and_end_v_to_loops_of_length[
offset - 1
].get(sorted_start_v_and_end_v_tuple)
if not related_loops:
continue
# 排除自身的影响,并且用-号而不用remove()这样搞不会导致start_v_and_end_v_to_loops_of_length被修改
remaining_loops = related_loops - {loop}
if remaining_loops:
to_be_delete_loops.add(loop)
# TODO:一旦被认为是弦则对该loop的循环就要退出了但是层数太多了
break
# # 从所有的loops中删除外层loop
unique_loops.difference_update(to_be_delete_loops)
# 防止重建网格
updated_loops = []
if not is_force_refresh:
loop_tobe_assign = set()
for loop in unique_loops:
if loop in g.loops:
continue
g.loops.append(loop)
updated_loops.append(loop)
else:
g.loops = unique_loops
updated_loops = unique_loops
# print(len(g.loops))
# for loop in g.loops:
# print("---")
# print(loop.shape)
# for b, d in loop.b_and_d_list:
# print(b.start_vertex.index, b.end_vertex.index, d)
for updated_loop in updated_loops:
updated_loop.recreate_pattern()
return updated_loops