146 lines
4.6 KiB
Python
146 lines
4.6 KiB
Python
|
#!/usr/bin/env python3
|
|||
|
# -*- coding: utf-8 -*-
|
|||
|
"""
|
|||
|
SUW Core - Geometry Utils Module
|
|||
|
拆分自: suw_impl.py (Line 606-732)
|
|||
|
用途: 3D几何类(Point3d、Vector3d、Transformation)和材质类型常量
|
|||
|
版本: 1.0.0
|
|||
|
作者: SUWood Team
|
|||
|
"""
|
|||
|
|
|||
|
import re
|
|||
|
import math
|
|||
|
from typing import Dict, Optional
|
|||
|
|
|||
|
# ==================== 几何类扩展 ====================
|
|||
|
|
|||
|
|
|||
|
class Point3d:
|
|||
|
"""3D点类 - 对应Ruby的Geom::Point3d"""
|
|||
|
|
|||
|
def __init__(self, x: float = 0.0, y: float = 0.0, z: float = 0.0):
|
|||
|
self.x = x
|
|||
|
self.y = y
|
|||
|
self.z = z
|
|||
|
|
|||
|
@classmethod
|
|||
|
def parse(cls, value: str):
|
|||
|
"""从字符串解析3D点"""
|
|||
|
if not value or value.strip() == "":
|
|||
|
return None
|
|||
|
|
|||
|
# 解析格式: "(x,y,z)" 或 "x,y,z"
|
|||
|
clean_value = re.sub(r'[()]*', '', value)
|
|||
|
xyz = [float(axis.strip()) for axis in clean_value.split(',')]
|
|||
|
|
|||
|
# 转换mm为内部单位(假设输入是mm)
|
|||
|
return cls(xyz[0] * 0.001, xyz[1] * 0.001, xyz[2] * 0.001)
|
|||
|
|
|||
|
def to_s(self, unit: str = "mm", digits: int = -1) -> str:
|
|||
|
"""转换为字符串"""
|
|||
|
if unit == "cm":
|
|||
|
x_val = self.x * 100 # 内部单位转换为cm
|
|||
|
y_val = self.y * 100
|
|||
|
z_val = self.z * 100
|
|||
|
return f"({x_val:.3f}, {y_val:.3f}, {z_val:.3f})"
|
|||
|
else: # mm
|
|||
|
x_val = self.x * 1000 # 内部单位转换为mm
|
|||
|
y_val = self.y * 1000
|
|||
|
z_val = self.z * 1000
|
|||
|
|
|||
|
if digits == -1:
|
|||
|
return f"({x_val}, {y_val}, {z_val})"
|
|||
|
else:
|
|||
|
return f"({x_val:.{digits}f}, {y_val:.{digits}f}, {z_val:.{digits}f})"
|
|||
|
|
|||
|
def __str__(self):
|
|||
|
return self.to_s()
|
|||
|
|
|||
|
def __repr__(self):
|
|||
|
return f"Point3d({self.x}, {self.y}, {self.z})"
|
|||
|
|
|||
|
|
|||
|
class Vector3d:
|
|||
|
"""3D向量类 - 对应Ruby的Geom::Vector3d"""
|
|||
|
|
|||
|
def __init__(self, x: float = 0.0, y: float = 0.0, z: float = 0.0):
|
|||
|
self.x = x
|
|||
|
self.y = y
|
|||
|
self.z = z
|
|||
|
|
|||
|
@classmethod
|
|||
|
def parse(cls, value: str):
|
|||
|
"""从字符串解析3D向量"""
|
|||
|
if not value or value.strip() == "":
|
|||
|
return None
|
|||
|
|
|||
|
clean_value = re.sub(r'[()]*', '', value)
|
|||
|
xyz = [float(axis.strip()) for axis in clean_value.split(',')]
|
|||
|
|
|||
|
return cls(xyz[0] * 0.001, xyz[1] * 0.001, xyz[2] * 0.001)
|
|||
|
|
|||
|
def to_s(self, unit: str = "mm") -> str:
|
|||
|
"""转换为字符串"""
|
|||
|
if unit == "cm":
|
|||
|
x_val = self.x * 100 # 内部单位转换为cm
|
|||
|
y_val = self.y * 100
|
|||
|
z_val = self.z * 100
|
|||
|
return f"({x_val:.3f}, {y_val:.3f}, {z_val:.3f})"
|
|||
|
elif unit == "in":
|
|||
|
return f"({self.x}, {self.y}, {self.z})"
|
|||
|
else: # mm
|
|||
|
x_val = self.x * 1000 # 内部单位转换为mm
|
|||
|
y_val = self.y * 1000
|
|||
|
z_val = self.z * 1000
|
|||
|
return f"({x_val}, {y_val}, {z_val})"
|
|||
|
|
|||
|
def normalize(self):
|
|||
|
"""归一化向量"""
|
|||
|
length = math.sqrt(self.x**2 + self.y**2 + self.z**2)
|
|||
|
if length > 0:
|
|||
|
return Vector3d(self.x/length, self.y/length, self.z/length)
|
|||
|
return Vector3d(0, 0, 0)
|
|||
|
|
|||
|
def __str__(self):
|
|||
|
return self.to_s()
|
|||
|
|
|||
|
|
|||
|
class Transformation:
|
|||
|
"""变换矩阵类 - 对应Ruby的Geom::Transformation"""
|
|||
|
|
|||
|
def __init__(self, origin: Point3d = None, x_axis: Vector3d = None,
|
|||
|
y_axis: Vector3d = None, z_axis: Vector3d = None):
|
|||
|
self.origin = origin or Point3d(0, 0, 0)
|
|||
|
self.x_axis = x_axis or Vector3d(1, 0, 0)
|
|||
|
self.y_axis = y_axis or Vector3d(0, 1, 0)
|
|||
|
self.z_axis = z_axis or Vector3d(0, 0, 1)
|
|||
|
|
|||
|
@classmethod
|
|||
|
def parse(cls, data: Dict[str, str]):
|
|||
|
"""从字典解析变换"""
|
|||
|
origin = Point3d.parse(data.get("o"))
|
|||
|
x_axis = Vector3d.parse(data.get("x"))
|
|||
|
y_axis = Vector3d.parse(data.get("y"))
|
|||
|
z_axis = Vector3d.parse(data.get("z"))
|
|||
|
|
|||
|
return cls(origin, x_axis, y_axis, z_axis)
|
|||
|
|
|||
|
def store(self, data: Dict[str, str]):
|
|||
|
"""存储变换到字典"""
|
|||
|
data["o"] = self.origin.to_s("mm")
|
|||
|
data["x"] = self.x_axis.to_s("in")
|
|||
|
data["y"] = self.y_axis.to_s("in")
|
|||
|
data["z"] = self.z_axis.to_s("in")
|
|||
|
|
|||
|
|
|||
|
# ==================== 材质类型常量 ====================
|
|||
|
|
|||
|
# 基础材质类型(从suw_impl.py Line 725-727拆分)
|
|||
|
MAT_TYPE_NORMAL = 0 # 普通材质
|
|||
|
MAT_TYPE_OBVERSE = 1 # 正面材质
|
|||
|
MAT_TYPE_NATURE = 2 # 自然材质
|
|||
|
|
|||
|
# 扩展材质类型(为兼容性添加)
|
|||
|
MAT_TYPE_REVERSE = 3 # 反面材质
|
|||
|
MAT_TYPE_THIN = 4 # 薄材质
|