- 添加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配置文件
195 lines
7.6 KiB
Python
195 lines
7.6 KiB
Python
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
|