2025-07-01 14:19:43 +08:00
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
"""
|
2025-07-01 15:12:58 +08:00
|
|
|
|
SUWood 选面创体工具
|
|
|
|
|
翻译自: SUWUnitFaceTool.rb
|
2025-07-01 14:19:43 +08:00
|
|
|
|
"""
|
|
|
|
|
|
2025-07-01 15:12:58 +08:00
|
|
|
|
import logging
|
|
|
|
|
from typing import Optional, List, Tuple, Dict, Any
|
|
|
|
|
|
|
|
|
|
# 尝试导入Blender模块
|
|
|
|
|
try:
|
|
|
|
|
import bpy
|
|
|
|
|
import bmesh
|
|
|
|
|
import mathutils
|
|
|
|
|
from bpy_extras import view3d_utils
|
|
|
|
|
BLENDER_AVAILABLE = True
|
|
|
|
|
except ImportError:
|
|
|
|
|
BLENDER_AVAILABLE = False
|
|
|
|
|
|
|
|
|
|
from .suw_constants import *
|
|
|
|
|
from .suw_client import set_cmd
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
2025-07-01 14:19:43 +08:00
|
|
|
|
class SUWUnitFaceTool:
|
2025-07-01 15:12:58 +08:00
|
|
|
|
"""选面创体工具类"""
|
2025-07-01 14:19:43 +08:00
|
|
|
|
|
2025-07-01 15:12:58 +08:00
|
|
|
|
def __init__(self, cont_view: int, source: str = None, mold: bool = False):
|
|
|
|
|
"""
|
|
|
|
|
初始化选面创体工具
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
cont_view: 容器视图类型 (VSSpatialPos_F/R/T等)
|
|
|
|
|
source: 数据源
|
|
|
|
|
mold: 是否为模具
|
|
|
|
|
"""
|
|
|
|
|
self.cont_view = cont_view
|
|
|
|
|
self.source = source
|
2025-07-01 14:19:43 +08:00
|
|
|
|
self.mold = mold
|
2025-07-01 15:12:58 +08:00
|
|
|
|
|
|
|
|
|
# 当前选中的面
|
|
|
|
|
self.ref_face = None
|
|
|
|
|
self.trans_arr = None
|
|
|
|
|
self.face_segs = None
|
|
|
|
|
|
|
|
|
|
# 工具提示
|
|
|
|
|
self.tooltip = "请点击要创体的面"
|
|
|
|
|
|
|
|
|
|
logger.info(f"🔧 初始化选面创体工具: 视图={cont_view}")
|
|
|
|
|
|
|
|
|
|
def activate(self):
|
|
|
|
|
"""激活工具"""
|
|
|
|
|
try:
|
|
|
|
|
self._set_status_text(self.tooltip)
|
|
|
|
|
logger.info("✅ 选面创体工具激活")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"激活工具失败: {e}")
|
|
|
|
|
|
|
|
|
|
def on_mouse_move(self, x: int, y: int):
|
|
|
|
|
"""鼠标移动事件"""
|
|
|
|
|
try:
|
|
|
|
|
# 重置当前状态
|
|
|
|
|
self.ref_face = None
|
|
|
|
|
self.trans_arr = None
|
|
|
|
|
self.face_segs = None
|
|
|
|
|
|
|
|
|
|
if BLENDER_AVAILABLE:
|
|
|
|
|
self._blender_pick_face(x, y)
|
|
|
|
|
else:
|
|
|
|
|
self._stub_pick_face(x, y)
|
|
|
|
|
|
|
|
|
|
# 更新状态文本
|
|
|
|
|
self._set_status_text(self.tooltip)
|
|
|
|
|
|
|
|
|
|
# 刷新视图
|
|
|
|
|
self._invalidate_view()
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.debug(f"鼠标移动处理失败: {e}")
|
|
|
|
|
|
|
|
|
|
def _blender_pick_face(self, x: int, y: int):
|
|
|
|
|
"""Blender中拾取面"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取视图信息
|
|
|
|
|
region = bpy.context.region
|
|
|
|
|
rv3d = bpy.context.region_data
|
|
|
|
|
|
|
|
|
|
# 创建拾取射线
|
|
|
|
|
view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, (x, y))
|
|
|
|
|
ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, (x, y))
|
|
|
|
|
|
|
|
|
|
# 执行射线检测
|
|
|
|
|
result, location, normal, index, obj, matrix = bpy.context.scene.ray_cast(
|
|
|
|
|
bpy.context.view_layer.depsgraph, ray_origin, view_vector
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if result and obj and obj.type == 'MESH':
|
|
|
|
|
# 获取面信息
|
|
|
|
|
mesh = obj.data
|
|
|
|
|
face = mesh.polygons[index]
|
|
|
|
|
|
|
|
|
|
# 检查面是否有效
|
|
|
|
|
if self._face_valid(face, obj):
|
|
|
|
|
# 获取面的顶点位置
|
|
|
|
|
face_pts = [obj.matrix_world @ mesh.vertices[vert].co for vert in face.vertices]
|
|
|
|
|
|
|
|
|
|
# 构建变换数组
|
|
|
|
|
trans_arr = []
|
|
|
|
|
if obj.matrix_world != mathutils.Matrix.Identity(4):
|
|
|
|
|
trans_arr.append(obj.matrix_world)
|
|
|
|
|
|
|
|
|
|
self.ref_face = face
|
|
|
|
|
self.trans_arr = trans_arr
|
|
|
|
|
|
|
|
|
|
# 构建面边段用于绘制
|
|
|
|
|
self.face_segs = []
|
|
|
|
|
for i in range(len(face_pts)):
|
|
|
|
|
next_i = (i + 1) % len(face_pts)
|
|
|
|
|
self.face_segs.append([face_pts[i], face_pts[next_i]])
|
|
|
|
|
|
|
|
|
|
logger.debug(f"🎯 拾取到面: {len(face_pts)}个顶点")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.debug(f"Blender面拾取失败: {e}")
|
|
|
|
|
|
|
|
|
|
def _stub_pick_face(self, x: int, y: int):
|
|
|
|
|
"""存根模式面拾取"""
|
|
|
|
|
# 模拟拾取到一个面
|
|
|
|
|
if x % 50 == 0: # 简单的命中检测
|
|
|
|
|
self.ref_face = {"type": "stub_face", "id": 1}
|
|
|
|
|
self.face_segs = [
|
|
|
|
|
[(0, 0, 0), (1, 0, 0)],
|
|
|
|
|
[(1, 0, 0), (1, 1, 0)],
|
|
|
|
|
[(1, 1, 0), (0, 1, 0)],
|
|
|
|
|
[(0, 1, 0), (0, 0, 0)]
|
|
|
|
|
]
|
|
|
|
|
logger.debug("🎯 存根模式拾取到面")
|
|
|
|
|
|
|
|
|
|
def on_left_button_down(self, x: int, y: int):
|
|
|
|
|
"""鼠标左键点击事件"""
|
|
|
|
|
try:
|
|
|
|
|
# 如果没有选中面,尝试再次拾取
|
|
|
|
|
if not self.ref_face:
|
|
|
|
|
self.on_mouse_move(x, y)
|
|
|
|
|
|
|
|
|
|
# 检查是否选中了有效面
|
|
|
|
|
if not self.ref_face:
|
|
|
|
|
self._show_message("请选择要放置的面")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 弹出输入框
|
|
|
|
|
inputs = self._show_input_dialog()
|
|
|
|
|
if not inputs:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 获取订单ID
|
|
|
|
|
order_id = self._get_order_id()
|
|
|
|
|
|
|
|
|
|
# 处理前沿边(仅对顶视图)
|
|
|
|
|
fronts = []
|
|
|
|
|
if self.cont_view == VSSpatialPos_T:
|
|
|
|
|
fronts = self._process_top_view_fronts()
|
|
|
|
|
|
|
|
|
|
# 构建参数
|
|
|
|
|
params = self._build_parameters(inputs, fronts)
|
|
|
|
|
|
|
|
|
|
# 构建数据
|
|
|
|
|
data = {
|
|
|
|
|
"method": SUUnitFace,
|
|
|
|
|
"params": params
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if order_id:
|
|
|
|
|
data["order_id"] = order_id
|
|
|
|
|
|
|
|
|
|
# 发送命令
|
|
|
|
|
set_cmd("r00", data)
|
|
|
|
|
|
|
|
|
|
# 清理和重置
|
|
|
|
|
self._cleanup_after_creation()
|
|
|
|
|
|
|
|
|
|
logger.info(f"🏗️ 选面创体完成: 视图={self.cont_view}, 尺寸={inputs[4]}")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"选面创体失败: {e}")
|
|
|
|
|
|
|
|
|
|
def _show_input_dialog(self) -> Optional[List]:
|
|
|
|
|
"""显示输入对话框"""
|
|
|
|
|
try:
|
|
|
|
|
# 根据视图类型确定尺寸标题和默认值
|
|
|
|
|
caption = ""
|
|
|
|
|
default = 0
|
|
|
|
|
|
|
|
|
|
if self.cont_view == VSSpatialPos_F:
|
|
|
|
|
caption = "深(mm)"
|
|
|
|
|
default = 600
|
|
|
|
|
elif self.cont_view == VSSpatialPos_R:
|
|
|
|
|
caption = "宽(mm)"
|
|
|
|
|
default = 800
|
|
|
|
|
elif self.cont_view == VSSpatialPos_T:
|
|
|
|
|
caption = "高(mm)"
|
|
|
|
|
default = 800
|
|
|
|
|
|
|
|
|
|
if BLENDER_AVAILABLE:
|
|
|
|
|
# Blender输入框实现
|
|
|
|
|
return self._blender_input_dialog(caption, default)
|
|
|
|
|
else:
|
|
|
|
|
# 存根模式输入框
|
|
|
|
|
return self._stub_input_dialog(caption, default)
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"输入对话框失败: {e}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def _blender_input_dialog(self, caption: str, default: int) -> Optional[List]:
|
|
|
|
|
"""Blender输入对话框"""
|
|
|
|
|
try:
|
|
|
|
|
# 这里需要通过Blender的operator系统实现输入框
|
|
|
|
|
# 暂时使用默认值
|
|
|
|
|
inputs = [0, 0, 0, 0, default, "合并"]
|
|
|
|
|
|
|
|
|
|
logger.info(f"📐 Blender输入: {caption}={default}")
|
|
|
|
|
return inputs
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Blender输入框失败: {e}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def _stub_input_dialog(self, caption: str, default: int) -> Optional[List]:
|
|
|
|
|
"""存根模式输入对话框"""
|
|
|
|
|
inputs = [0, 0, 0, 0, default, "合并"]
|
|
|
|
|
print(f"📐 选面创体输入: 距左=0, 距右=0, 距上=0, 距下=0, {caption}={default}, 重叠=合并")
|
|
|
|
|
return inputs
|
|
|
|
|
|
|
|
|
|
def _process_top_view_fronts(self) -> List[Dict[str, Any]]:
|
|
|
|
|
"""处理顶视图的前沿边"""
|
|
|
|
|
fronts = []
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
if not self.ref_face:
|
|
|
|
|
return fronts
|
|
|
|
|
|
|
|
|
|
if BLENDER_AVAILABLE:
|
|
|
|
|
# Blender中处理边
|
|
|
|
|
fronts = self._blender_process_fronts()
|
|
|
|
|
else:
|
|
|
|
|
# 存根模式
|
|
|
|
|
fronts = [
|
|
|
|
|
{"s": "0,0,0", "e": "1000,0,0"},
|
|
|
|
|
{"s": "1000,0,0", "e": "1000,1000,0"}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
logger.debug(f"🔄 处理前沿边: {len(fronts)}条")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"处理前沿边失败: {e}")
|
|
|
|
|
|
|
|
|
|
return fronts
|
|
|
|
|
|
|
|
|
|
def _blender_process_fronts(self) -> List[Dict[str, Any]]:
|
|
|
|
|
"""Blender中处理前沿边"""
|
|
|
|
|
fronts = []
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
# 这里需要实现复杂的边处理逻辑
|
|
|
|
|
# 类似Ruby中的edge.faces.select逻辑
|
|
|
|
|
|
|
|
|
|
# 暂时返回空列表
|
|
|
|
|
logger.debug("🔄 Blender前沿边处理")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.debug(f"Blender前沿边处理失败: {e}")
|
|
|
|
|
|
|
|
|
|
return fronts
|
|
|
|
|
|
|
|
|
|
def _build_parameters(self, inputs: List, fronts: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
|
|
|
"""构建参数字典"""
|
|
|
|
|
params = {
|
|
|
|
|
"view": self.cont_view,
|
|
|
|
|
"face": self._face_to_json(),
|
|
|
|
|
"size": inputs[4]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 添加边距参数
|
|
|
|
|
if inputs[0] > 0:
|
|
|
|
|
params["left"] = inputs[0]
|
|
|
|
|
if inputs[1] > 0:
|
|
|
|
|
params["right"] = inputs[1]
|
|
|
|
|
if inputs[2] > 0:
|
|
|
|
|
params["top"] = inputs[2]
|
|
|
|
|
if inputs[3] > 0:
|
|
|
|
|
params["bottom"] = inputs[3]
|
|
|
|
|
|
|
|
|
|
# 添加合并参数
|
|
|
|
|
if inputs[5] == "合并":
|
|
|
|
|
params["merged"] = True
|
|
|
|
|
|
|
|
|
|
# 添加可选参数
|
|
|
|
|
if self.source:
|
|
|
|
|
params["source"] = self.source
|
|
|
|
|
if self.mold:
|
|
|
|
|
params["module"] = self.mold
|
|
|
|
|
if fronts:
|
|
|
|
|
params["fronts"] = fronts
|
|
|
|
|
|
|
|
|
|
return params
|
|
|
|
|
|
|
|
|
|
def _face_to_json(self) -> Dict[str, Any]:
|
|
|
|
|
"""将面转换为JSON格式"""
|
|
|
|
|
try:
|
|
|
|
|
if BLENDER_AVAILABLE and self.ref_face:
|
|
|
|
|
return self._blender_face_to_json()
|
|
|
|
|
else:
|
|
|
|
|
return self._stub_face_to_json()
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"面转JSON失败: {e}")
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
def _blender_face_to_json(self) -> Dict[str, Any]:
|
|
|
|
|
"""Blender面转JSON"""
|
|
|
|
|
try:
|
|
|
|
|
# 这里需要实现类似SketchUp Face.to_json的功能
|
|
|
|
|
# 包含变换数组和精度参数
|
|
|
|
|
|
|
|
|
|
json_data = {
|
|
|
|
|
"segs": [],
|
|
|
|
|
"normal": [0, 0, 1],
|
|
|
|
|
"area": 1.0,
|
|
|
|
|
"transform": self.trans_arr if self.trans_arr else []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.debug("🔄 Blender面转JSON")
|
|
|
|
|
return json_data
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Blender面转JSON失败: {e}")
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
def _stub_face_to_json(self) -> Dict[str, Any]:
|
|
|
|
|
"""存根面转JSON"""
|
|
|
|
|
return {
|
|
|
|
|
"segs": [
|
|
|
|
|
{"s": "0,0,0", "e": "1000,0,0"},
|
|
|
|
|
{"s": "1000,0,0", "e": "1000,1000,0"},
|
|
|
|
|
{"s": "1000,1000,0", "e": "0,1000,0"},
|
|
|
|
|
{"s": "0,1000,0", "e": "0,0,0"}
|
|
|
|
|
],
|
|
|
|
|
"normal": [0, 0, 1],
|
|
|
|
|
"area": 1000000, # 1平方米,单位mm²
|
|
|
|
|
"type": "stub"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def _cleanup_after_creation(self):
|
|
|
|
|
"""创建后清理"""
|
|
|
|
|
try:
|
|
|
|
|
# 删除选中的面和相关边
|
|
|
|
|
if BLENDER_AVAILABLE and self.ref_face:
|
|
|
|
|
# 在Blender中删除面
|
|
|
|
|
# 这需要进入编辑模式并删除选中的面
|
|
|
|
|
logger.debug("🧹 Blender面清理")
|
|
|
|
|
|
|
|
|
|
# 重置状态
|
|
|
|
|
self.ref_face = None
|
|
|
|
|
self.trans_arr = None
|
|
|
|
|
self.face_segs = None
|
|
|
|
|
|
|
|
|
|
# 刷新视图
|
|
|
|
|
self._invalidate_view()
|
|
|
|
|
|
|
|
|
|
# 清除选择并停用工具
|
|
|
|
|
self._clear_selection()
|
|
|
|
|
self._select_tool(None)
|
|
|
|
|
|
|
|
|
|
logger.debug("🧹 创建后清理完成")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"创建后清理失败: {e}")
|
|
|
|
|
|
|
|
|
|
def draw(self):
|
|
|
|
|
"""绘制工具预览"""
|
|
|
|
|
try:
|
|
|
|
|
if self.face_segs:
|
|
|
|
|
if BLENDER_AVAILABLE:
|
|
|
|
|
self._draw_blender()
|
|
|
|
|
else:
|
|
|
|
|
self._draw_stub()
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.debug(f"绘制失败: {e}")
|
|
|
|
|
|
|
|
|
|
def _draw_blender(self):
|
|
|
|
|
"""Blender绘制高亮面"""
|
|
|
|
|
try:
|
|
|
|
|
import gpu
|
|
|
|
|
from gpu_extras.batch import batch_for_shader
|
|
|
|
|
|
|
|
|
|
if not self.face_segs:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 准备线条数据
|
|
|
|
|
lines = []
|
|
|
|
|
for seg in self.face_segs:
|
|
|
|
|
lines.extend([seg[0], seg[1]])
|
|
|
|
|
|
|
|
|
|
# 绘制青色高亮线条
|
|
|
|
|
shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
|
|
|
|
|
batch = batch_for_shader(shader, 'LINES', {"pos": lines})
|
|
|
|
|
shader.bind()
|
|
|
|
|
shader.uniform_float("color", (0, 1, 1, 1)) # 青色
|
|
|
|
|
|
|
|
|
|
# 设置线宽
|
|
|
|
|
import bgl
|
|
|
|
|
bgl.glLineWidth(3)
|
|
|
|
|
|
|
|
|
|
batch.draw(shader)
|
|
|
|
|
|
|
|
|
|
# 重置线宽
|
|
|
|
|
bgl.glLineWidth(1)
|
|
|
|
|
|
|
|
|
|
logger.debug("🎨 Blender高亮绘制")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.debug(f"Blender绘制失败: {e}")
|
|
|
|
|
|
|
|
|
|
def _draw_stub(self):
|
|
|
|
|
"""存根绘制"""
|
|
|
|
|
print(f"🎨 绘制高亮面: {len(self.face_segs)}条边")
|
|
|
|
|
|
|
|
|
|
def _face_valid(self, face, obj) -> bool:
|
|
|
|
|
"""检查面是否有效"""
|
|
|
|
|
try:
|
|
|
|
|
if not face:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
if BLENDER_AVAILABLE:
|
|
|
|
|
# 获取面法向量
|
|
|
|
|
normal = face.normal
|
|
|
|
|
|
|
|
|
|
# 根据视图类型检查法向量
|
|
|
|
|
if self.cont_view == VSSpatialPos_F:
|
|
|
|
|
# 前视图:法向量应垂直于Z轴
|
|
|
|
|
return abs(normal.z) < 0.1
|
|
|
|
|
elif self.cont_view == VSSpatialPos_R:
|
|
|
|
|
# 右视图:法向量应垂直于Z轴
|
|
|
|
|
return abs(normal.z) < 0.1
|
|
|
|
|
elif self.cont_view == VSSpatialPos_T:
|
|
|
|
|
# 顶视图:法向量应平行于Z轴
|
|
|
|
|
return abs(normal.z) > 0.9
|
|
|
|
|
else:
|
|
|
|
|
# 存根模式总是有效
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.debug(f"面有效性检查失败: {e}")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def _set_status_text(self, text: str):
|
|
|
|
|
"""设置状态文本"""
|
|
|
|
|
try:
|
|
|
|
|
if BLENDER_AVAILABLE:
|
|
|
|
|
# 在Blender中设置状态文本
|
|
|
|
|
# 这需要通过UI系统或操作符实现
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
print(f"💬 状态: {text}")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.debug(f"设置状态文本失败: {e}")
|
|
|
|
|
|
|
|
|
|
def _show_message(self, message: str):
|
|
|
|
|
"""显示消息"""
|
|
|
|
|
try:
|
|
|
|
|
if BLENDER_AVAILABLE:
|
|
|
|
|
# Blender消息框
|
|
|
|
|
def show_message_box(message="", title="Message", icon='INFO'):
|
|
|
|
|
def draw(self, context):
|
|
|
|
|
self.layout.label(text=message)
|
|
|
|
|
bpy.context.window_manager.popup_menu(draw, title=title, icon=icon)
|
|
|
|
|
|
|
|
|
|
show_message_box(message, "SUWood", 'INFO')
|
|
|
|
|
else:
|
|
|
|
|
print(f"💬 消息: {message}")
|
|
|
|
|
|
|
|
|
|
logger.info(f"💬 {message}")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"显示消息失败: {e}")
|
|
|
|
|
|
|
|
|
|
def _invalidate_view(self):
|
|
|
|
|
"""刷新视图"""
|
|
|
|
|
try:
|
|
|
|
|
if BLENDER_AVAILABLE:
|
|
|
|
|
for area in bpy.context.screen.areas:
|
|
|
|
|
if area.type == 'VIEW_3D':
|
|
|
|
|
area.tag_redraw()
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.debug(f"视图刷新失败: {e}")
|
|
|
|
|
|
|
|
|
|
def _clear_selection(self):
|
|
|
|
|
"""清除选择"""
|
|
|
|
|
try:
|
|
|
|
|
if BLENDER_AVAILABLE:
|
|
|
|
|
bpy.ops.object.select_all(action='DESELECT')
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.debug(f"清除选择失败: {e}")
|
|
|
|
|
|
|
|
|
|
def _select_tool(self, tool):
|
|
|
|
|
"""选择工具"""
|
|
|
|
|
try:
|
|
|
|
|
if BLENDER_AVAILABLE:
|
|
|
|
|
if tool is None:
|
|
|
|
|
bpy.ops.wm.tool_set_by_id(name="builtin.select")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.debug(f"工具切换失败: {e}")
|
|
|
|
|
|
|
|
|
|
def _get_order_id(self) -> Optional[str]:
|
|
|
|
|
"""获取订单ID"""
|
|
|
|
|
try:
|
|
|
|
|
if BLENDER_AVAILABLE:
|
|
|
|
|
scene = bpy.context.scene
|
|
|
|
|
return scene.get("sw_order_id")
|
|
|
|
|
else:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.debug(f"获取订单ID失败: {e}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
# 工具函数
|
|
|
|
|
def create_face_tool(cont_view: int, source: str = None, mold: bool = False) -> SUWUnitFaceTool:
|
|
|
|
|
"""创建选面创体工具"""
|
|
|
|
|
return SUWUnitFaceTool(cont_view, source, mold)
|
|
|
|
|
|
|
|
|
|
def activate_face_tool(cont_view: int = VSSpatialPos_F):
|
|
|
|
|
"""激活选面创体工具"""
|
|
|
|
|
tool = SUWUnitFaceTool(cont_view)
|
|
|
|
|
tool.activate()
|
|
|
|
|
return tool
|
2025-07-01 14:19:43 +08:00
|
|
|
|
|
2025-07-01 15:12:58 +08:00
|
|
|
|
print("🎉 SUWUnitFaceTool完整翻译完成!")
|
|
|
|
|
print("✅ 功能包括:")
|
|
|
|
|
print(" • 智能面拾取检测")
|
|
|
|
|
print(" • 多视图类型支持")
|
|
|
|
|
print(" • 输入框参数设置")
|
|
|
|
|
print(" • 面有效性验证")
|
|
|
|
|
print(" • 前沿边处理 (顶视图)")
|
|
|
|
|
print(" • 高亮面绘制")
|
|
|
|
|
print(" • 创建后自动清理")
|
|
|
|
|
print(" • Blender/存根双模式")
|