import numpy as np
import os
import re
import logging
import shutil
import math
from pathlib import Path
from typing import List, Dict, Tuple, Optional
import datetime
class RockMassOrientationAnalyzer:
"""完整修正版的岩体多面体方位角分析与旋转程序"""
def __init__(self, base_path: str = r"E:\河南五岳抽水储能电站"):
self.base_path = Path(base_path)
self.setup_logging()
# 坐标系定义
self.north = 0.0 # 北向
self.east = 90.0 # 东向
self.south = 180.0 # 南向
self.west = 270.0 # 西向
# 容差设置
self.distance_tolerance = 1e-8
self.angle_tolerance = 1e-6
# 旋转映射关系
self.alpha_to_gamma = {
90: 0, 120: 30, 150: 60, 180: 90, 210: 120, 240: 150,
270: 180, 300: 210, 330: 240, 0: 270, 30: 300, 60: 330
}
self.beta_to_theta = {
90: 0, 60: 30, 30: 60, 0: 90
}
# 处理结果统计
self.results = {
'total_files': 0,
'success': 0,
'skip': 0,
'error': 0,
'details': []
}
def setup_logging(self):
"""设置日志系统"""
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('rock_mass_analysis.log', encoding='utf-8'),
logging.StreamHandler()
]
)
self.logger = logging.getLogger(__name__)
def scan_directory_structure(self) -> Dict:
"""第1步:扫描三级目录结构"""
self.logger.info("开始扫描目录结构...")
structure = {
'level1_folders': [],
'total_files': 0
}
try:
# 扫描第一级文件夹
for level1_dir in sorted(self.base_path.iterdir()):
if not level1_dir.is_dir():
continue
level1_info = self.parse_level1_directory(level1_dir.name)
if not level1_info:
self.logger.warning(f"跳过无效的第一级文件夹: {level1_dir.name}")
continue
level1_data = {
'path': level1_dir,
'name': level1_dir.name,
'info': level1_info,
'level2_folders': []
}
# 扫描第二级文件夹
for level2_dir in sorted(level1_dir.iterdir()):
if not level2_dir.is_dir():
continue
level2_info = self.parse_level2_directory(level2_dir.name)
if not level2_info:
self.logger.warning(f"跳过无效的第二级文件夹: {level2_dir.name}")
continue
level2_data = {
'path': level2_dir,
'name': level2_dir.name,
'info': level2_info,
'level3_folders': []
}
# 扫描第三级文件夹
for level3_dir in sorted(level2_dir.iterdir()):
if not level3_dir.is_dir():
continue
model_file = level3_dir / "model_domain.dat"
if model_file.exists():
level3_data = {
'path': level3_dir,
'name': level3_dir.name,
'model_file': model_file,
'relative_path': f"{level1_dir.name}/{level2_dir.name}/{level3_dir.name}"
}
level2_data['level3_folders'].append(level3_data)
structure['total_files'] += 1
if level2_data['level3_folders']:
level1_data['level2_folders'].append(level2_data)
if level1_data['level2_folders']:
structure['level1_folders'].append(level1_data)
self.logger.info(f"扫描完成: 找到 {len(structure['level1_folders'])} 个第一级文件夹, {structure['total_files']} 个数据文件")
return structure
except Exception as e:
self.logger.error(f"扫描目录结构失败: {e}")
return structure
def parse_level1_directory(self, dir_name: str) -> Optional[Dict]:
"""解析第一级文件夹名称"""
pattern = r'(\d+)-([\d.]+)m×([\d.]+)m×([\d.]+)m'
match = re.match(pattern, dir_name)
if match:
return {
'sequence': int(match.group(1)),
'width': float(match.group(2)), # 宽 (X轴)
'length': float(match.group(3)), # 长 (Y轴)
'height': float(match.group(4)) # 高 (Z轴)
}
return None
def parse_level2_directory(self, dir_name: str) -> Optional[Dict]:
"""解析第二级文件夹名称"""
patterns = [
r'(\d+)-α=(\d+)°-β=(\d+)°',
r'α=(\d+)°-β=(\d+)°',
r'(\d+).*?α\s*=\s*(\d+).*?β\s*=\s*(\d+)'
]
for pattern in patterns:
match = re.search(pattern, dir_name)
if match:
groups = match.groups()
try:
if len(groups) == 3:
return {
'sequence': int(groups[0]),
'target_alpha': int(groups[1]),
'target_beta': int(groups[2])
}
elif len(groups) == 2:
seq_match = re.search(r'^(\d+)', dir_name)
sequence = int(seq_match.group(1)) if seq_match else 0
return {
'sequence': sequence,
'target_alpha': int(groups[0]),
'target_beta': int(groups[1])
}
except (ValueError, IndexError) as e:
self.logger.warning(f"解析文件夹名 '{dir_name}' 时出错: {e}")
continue
self.logger.warning(f"无法解析第二级文件夹名称: {dir_name}")
return None
def interactive_selection(self, structure: Dict) -> List[Dict]:
"""第2步:用户交互选择处理范围"""
print("\n" + "=" * 80)
print("岩体多面体方位角分析与旋转程序")
print("=" * 80)
if not structure['level1_folders']:
print("未找到可处理的文件夹")
return []
# 显示统计信息
print("扫描结果:")
print(f"- 第一级文件夹: {len(structure['level1_folders'])} 个")
print(f"- 数据文件总数: {structure['total_files']} 个")
# 显示可用的第一级文件夹
print(f"\n可用的第一级文件夹:")
for i, level1 in enumerate(structure['level1_folders'], 1):
level2_count = len(level1['level2_folders'])
level3_count = sum(len(l2['level3_folders']) for l2 in level1['level2_folders'])
print(f"{i:2d}. {level1['name']} "
f"({level2_count}个方向配置, {level3_count}个数据文件)")
# 选择处理范围
try:
choice = input("\n请选择处理范围:\n"
"1. 输入文件夹序号(逗号分隔, 如: 1,3,5)\n"
"2. 输入 'all' 处理所有文件夹\n"
"3. 输入 'quit' 退出程序\n"
"请选择: ").strip()
if choice.lower() == 'quit':
print("程序退出")
return []
elif choice.lower() == 'all':
selected_indices = list(range(len(structure['level1_folders'])))
print(f"选择了所有 {len(selected_indices)} 个文件夹")
else:
selected_indices = []
for part in choice.split(','):
part = part.strip()
if part.isdigit():
idx = int(part) - 1
if 0 <= idx < len(structure['level1_folders']):
selected_indices.append(idx)
if not selected_indices:
print("输入无效,将处理所有文件夹")
selected_indices = list(range(len(structure['level1_folders'])))
else:
print(f"选择了 {len(selected_indices)} 个文件夹")
except Exception as e:
print(f"输入错误: {e},将处理所有文件夹")
selected_indices = list(range(len(structure['level1_folders'])))
# 收集选中的文件信息
selected_files = []
for idx in selected_indices:
level1 = structure['level1_folders'][idx]
for level2 in level1['level2_folders']:
for level3 in level2['level3_folders']:
file_info = {
'level1_path': level1['path'],
'level1_name': level1['name'],
'level1_info': level1['info'],
'level2_path': level2['path'],
'level2_name': level2['name'],
'level2_info': level2['info'],
'level3_path': level3['path'],
'level3_name': level3['name'],
'model_file': level3['model_file'],
'relative_path': level3['relative_path'],
'target_alpha': level2['info']['target_alpha'],
'target_beta': level2['info']['target_beta']
}
selected_files.append(file_info)
print(f"选择了 {len(selected_files)} 个文件进行处理")
return selected_files
def read_model_domain_file(self, file_path: Path) -> Dict:
"""第3-4步:读取model_domain.dat文件并解析"""
self.logger.info(f"读取文件: {file_path}")
try:
with open(file_path, 'r', encoding='utf-8') as f:
lines = [line.strip() for line in f.readlines() if line.strip()]
data = {
'file_path': file_path,
'original_lines': lines, # 保存原始行内容
'points': [],
'faces': {}
}
if len(lines) < 22:
raise ValueError("文件格式不完整")
# 解析顶点坐标(第12-19行)
vertex_start = 11
points = []
for i in range(vertex_start, min(vertex_start + 8, len(lines))):
parts = lines[i].split()
if len(parts) >= 3:
try:
x, y, z = map(float, parts[:3])
points.append(np.array([x, y, z]))
except ValueError:
continue
if len(points) != 8:
raise ValueError(f"顶点数量不正确: {len(points)},期望8个")
data['points'] = points
# 解析面信息(第6-11行)
face_point_indices = []
face_data_start = 5
for i in range(face_data_start, min(face_data_start + 6, len(lines))):
try:
indices = list(map(int, lines[i].split()))
if len(indices) >= 4:
face_point_indices.append(indices)
except (ValueError, IndexError):
break
if len(face_point_indices) != 6:
raise ValueError(f"面数量不正确: {len(face_point_indices)},期望6个")
# 识别面的几何属性
data['faces'] = self.identify_faces(points, face_point_indices)
self.logger.info(f"成功读取: {len(points)}个顶点, {len(face_point_indices)}个面")
return data
except Exception as e:
raise ValueError(f"读取文件失败: {e}")
def identify_faces(self, points: List[np.ndarray], face_point_indices: List[List[int]]) -> Dict:
"""识别面的几何属性"""
faces = {}
# 根据立方体几何面识别规则
face_definitions = {
1: {"name": "左面", "expected_nodes": [1, 2, 4, 3]},
2: {"name": "右面", "expected_nodes": [5, 6, 8, 7]},
3: {"name": "下面", "expected_nodes": [1, 2, 6, 5]},
4: {"name": "上面", "expected_nodes": [3, 4, 8, 7]},
5: {"name": "前面", "expected_nodes": [1, 5, 7, 3]},
6: {"name": "后面", "expected_nodes": [2, 6, 8, 4]}
}
for i, indices in enumerate(face_point_indices, 1):
if i not in face_definitions:
continue
# 验证索引有效性
valid_indices = []
for idx in indices:
if 1 <= idx <= len(points):
valid_indices.append(idx)
if len(valid_indices) < 3:
continue
# 获取面的顶点
face_points = []
for idx in valid_indices:
face_points.append(points[idx - 1])
face_array = np.array(face_points)
# 计算面的中心点
center = np.mean(face_array, axis=0)
# 计算法向量
normal = self.calculate_face_normal(face_array)
face_info = face_definitions[i]
faces[i] = {
'indices': valid_indices,
'points': face_array,
'center': center,
'normal': normal,
'type': face_info["name"],
'expected_nodes': face_info["expected_nodes"]
}
return faces
def calculate_face_normal(self, face_points: np.ndarray) -> np.ndarray:
"""计算面的法向量"""
if len(face_points) < 3:
return np.array([0, 0, 1])
v1 = face_points[1] - face_points[0]
v2 = face_points[2] - face_points[0]
normal = np.cross(v1, v2)
norm = np.linalg.norm(normal)
if norm > self.distance_tolerance:
return normal / norm
return np.array([0, 0, 1])
def calculate_epsilon_angle(self, data: Dict) -> float:
"""计算Ԑ角度(L12向量方位角)"""
try:
faces = data['faces']
# 使用L12向量(左面→右面)
if 1 in faces and 2 in faces:
face1 = faces[1] # 左面
face2 = faces[2] # 右面
L12_vector = face2['center'] - face1['center']
# 计算在水平面的投影
projection = np.array([L12_vector[0], L12_vector[1], 0])
return self.calculate_azimuth_from_vector(projection)
return 90.0 # 默认东向
except Exception as e:
self.logger.error(f"计算Ԑ角度失败: {e}")
return 90.0
def calculate_phi_angle(self, data: Dict) -> float:
"""计算φ角度(L34在YOZ平面与Y轴夹角)"""
try:
faces = data['faces']
if 3 in faces and 4 in faces:
face3 = faces[3] # 下面
face4 = faces[4] # 上面
L34_vector = face4['center'] - face3['center']
# 计算在YOZ平面的投影
projection_yz = np.array([0, L34_vector[1], L34_vector[2]])
projection_length = np.linalg.norm(projection_yz)
if projection_length < self.distance_tolerance:
return 90.0
# 计算与Y轴的夹角
y_axis = np.array([0, 1, 0])
dot_product = np.dot(projection_yz, y_axis)
dot_product = max(min(dot_product, projection_length), -projection_length)
cos_angle = dot_product / projection_length
cos_angle = max(min(cos_angle, 1.0), -1.0)
angle_rad = math.acos(cos_angle)
angle_deg = math.degrees(angle_rad)
# φ角度范围是0-90°
phi_angle = min(angle_deg, 180 - angle_deg)
return max(0, min(phi_angle, 90))
return 90.0
except Exception as e:
self.logger.error(f"计算φ角度失败: {e}")
return 90.0
def calculate_azimuth_from_vector(self, vector: np.ndarray) -> float:
"""从向量计算方位角(直接映射法)"""
try:
x, y = vector[0], vector[1]
# 处理零向量
if abs(x) < self.distance_tolerance and abs(y) < self.distance_tolerance:
return 90.0 # 默认东向
# 直接映射法
if abs(x) < self.distance_tolerance: # 主要在Y轴方向
if y > 0:
return 90.0 # 东向
else:
return 270.0 # 西向
if abs(y) < self.distance_tolerance: # 主要在X轴方向
if x > 0:
return 180.0 # 南向
else:
return 0.0 # 北向
# 斜向向量使用标准计算
math_angle_rad = math.atan2(y, x)
math_angle_deg = math.degrees(math_angle_rad)
azimuth = (180 - math_angle_deg) % 360
return azimuth
except Exception as e:
self.logger.error(f"计算方位角失败: {e}")
return 90.0
def check_initial_conditions(self, data: Dict, file_info: Dict) -> Tuple[bool, str]:
"""第5-7步:检查初始条件"""
try:
points = data['points']
faces = data['faces']
# 检查中心点是否在原点
center = np.mean(points, axis=0)
center_distance = np.linalg.norm(center)
if center_distance > self.distance_tolerance:
return False, f"初始中心点不在原点: 距离={center_distance:.6f}"
# 检查尺寸匹配
x_coords = [p[0] for p in points]
y_coords = [p[1] for p in points]
z_coords = [p[2] for p in points]
actual_width = max(x_coords) - min(x_coords)
actual_length = max(y_coords) - min(y_coords)
actual_height = max(z_coords) - min(z_coords)
target_width = file_info['level1_info']['width']
target_length = file_info['level1_info']['length']
target_height = file_info['level1_info']['height']
if (abs(actual_width - target_width) > self.distance_tolerance or
abs(actual_length - target_length) > self.distance_tolerance or
abs(actual_height - target_height) > self.distance_tolerance):
return False, f"尺寸不匹配: 实际({actual_width:.3f}x{actual_length:.3f}x{actual_height:.3f}) 目标({target_width:.3f}x{target_length:.3f}x{target_height:.3f})"
# 检查当前方位角
current_alpha = self.calculate_epsilon_angle(data)
current_beta = self.calculate_phi_angle(data)
# 检查是否为默认方向 (90°, 90°)
alpha_diff = abs(current_alpha - 90)
beta_diff = abs(current_beta - 90)
if alpha_diff > self.angle_tolerance or beta_diff > self.angle_tolerance:
return False, f"非初始方位: 当前(α={current_alpha:.1f}°, β={current_beta:.1f}°) 期望(90°, 90°)"
# 检查是否需要旋转
target_alpha = file_info['target_alpha']
target_beta = file_info['target_beta']
if abs(target_alpha - 90) < self.angle_tolerance and abs(target_beta - 90) < self.angle_tolerance:
return False, "目标方位为默认方向,跳过旋转"
return True, "初始条件检查通过"
except Exception as e:
return False, f"初始条件检查失败: {e}"
def rotate_points(self, points: List[np.ndarray], gamma: float, theta: float) -> List[np.ndarray]:
"""执行旋转操作 - 增强调试版本"""
try:
self.logger.info(f"开始旋转: γ={gamma}°, θ={theta}°")
# 记录旋转前状态
original_center = np.mean(points, axis=0)
self.logger.info(f"旋转前中心点: ({original_center[0]:.6f}, {original_center[1]:.6f}, {original_center[2]:.6f})")
# 水平旋转(绕Z轴)
points_after_horizontal = self.rotate_around_z(points, gamma)
center_horizontal = np.mean(points_after_horizontal, axis=0)
self.logger.info(f"水平旋转后中心点: ({center_horizontal[0]:.6f}, {center_horizontal[1]:.6f}, {center_horizontal[2]:.6f})")
# 垂直旋转(绕L65轴)
points_after_vertical = self.rotate_around_L65(points_after_horizontal, theta)
center_vertical = np.mean(points_after_vertical, axis=0)
self.logger.info(f"垂直旋转后中心点: ({center_vertical[0]:.6f}, {center_vertical[1]:.6f}, {center_vertical[2]:.6f})")
# 验证旋转效果
total_movement = np.linalg.norm(center_vertical - original_center)
self.logger.info(f"总移动距离: {total_movement:.6f}")
# 验证点数量
if len(points_after_vertical) != len(points):
raise ValueError(f"旋转后点数不匹配: {len(points_after_vertical)} vs {len(points)}")
self.logger.info("旋转完成")
return points_after_vertical
except Exception as e:
self.logger.error(f"旋转失败: {e}")
return points
def rotate_around_z(self, points: List[np.ndarray], gamma: float) -> List[np.ndarray]:
"""绕Z轴旋转(水平旋转)"""
try:
if abs(gamma) < self.angle_tolerance:
self.logger.info("水平旋转角度为0,跳过水平旋转")
return points
gamma_rad = math.radians(gamma)
cos_g = math.cos(gamma_rad)
sin_g = math.sin(gamma_rad)
rotation_matrix = np.array([
[cos_g, -sin_g, 0],
[sin_g, cos_g, 0],
[0, 0, 1]
])
rotated_points = []
for i, point in enumerate(points):
rotated_point = rotation_matrix @ point
rotated_points.append(rotated_point)
# 记录显著移动
movement = np.linalg.norm(rotated_point - point)
if movement > 0.001:
self.logger.info(f"节点{i+1} 水平旋转移动: {movement:.6f}")
self.logger.info(f"水平旋转完成: γ={gamma}°")
return rotated_points
except Exception as e:
self.logger.error(f"绕Z轴旋转失败: {e}")
return points
def rotate_around_L65(self, points: List[np.ndarray], theta: float) -> List[np.ndarray]:
"""绕L65轴旋转(垂直旋转)- 修正:L65方向为面6→面5"""
try:
if abs(theta) < self.angle_tolerance:
self.logger.info("垂直旋转角度为0,跳过垂直旋转")
return points
# 计算L65轴(面6→面5)
faces = self.get_faces_from_points(points)
if 5 not in faces or 6 not in faces:
self.logger.warning("无法计算L65轴,返回原坐标")
return points
face5_center = faces[5]['center'] # 前面中心
face6_center = faces[6]['center'] # 后面中心
# L65轴方向:面6→面5(后面→前面)
L65_axis = face5_center - face6_center
axis_length = np.linalg.norm(L65_axis)
if axis_length < self.distance_tolerance:
self.logger.warning("L65轴长度为零,返回原坐标")
return points
L65_axis = L65_axis / axis_length # 单位化
self.logger.info(f"L65轴方向: ({L65_axis[0]:.6f}, {L65_axis[1]:.6f}, {L65_axis[2]:.6f})")
self.logger.info(f"垂直旋转角度: θ={theta}°")
# 使用罗德里格斯旋转公式
theta_rad = math.radians(theta)
cos_t = math.cos(theta_rad)
sin_t = math.sin(theta_rad)
# 旋转矩阵
K = np.array([
[0, -L65_axis[2], L65_axis[1]],
[L65_axis[2], 0, -L65_axis[0]],
[-L65_axis[1], L65_axis[0], 0]
])
I = np.eye(3)
rotation_matrix = I + sin_t * K + (1 - cos_t) * (K @ K)
rotated_points = []
for i, point in enumerate(points):
rotated_point = rotation_matrix @ point
rotated_points.append(rotated_point)
# 记录每个点的移动
movement = np.linalg.norm(rotated_point - point)
if movement > 0.001: # 只记录显著移动
self.logger.info(f"节点{i+1} 垂直旋转移动: {movement:.6f}")
# 验证旋转效果
original_center = np.mean(points, axis=0)
rotated_center = np.mean(rotated_points, axis=0)
center_movement = np.linalg.norm(rotated_center - original_center)
self.logger.info(f"垂直旋转中心点移动: {center_movement:.6f}")
self.logger.info("垂直旋转完成")
return rotated_points
except Exception as e:
self.logger.error(f"绕L65轴旋转失败: {e}")
return points
def get_faces_from_points(self, points: List[np.ndarray]) -> Dict:
"""从点集重新计算面信息"""
faces = {}
# 定义面的节点索引(根据您的文件格式)
face_indices = {
1: [0, 1, 3, 2], # 左面: 节点1,2,4,3
2: [4, 5, 7, 6], # 右面: 节点5,6,8,7
3: [0, 1, 5, 4], # 下面: 节点1,2,6,5
4: [2, 3, 7, 6], # 上面: 节点3,4,8,7
5: [0, 4, 6, 2], # 前面: 节点1,5,7,3
6: [1, 5, 7, 3] # 后面: 节点2,6,8,4
}
for face_id, indices in face_indices.items():
try:
face_points = []
for idx in indices:
if 0 <= idx < len(points):
face_points.append(points[idx])
if len(face_points) >= 3:
face_array = np.array(face_points)
center = np.mean(face_array, axis=0)
# 定义面类型
face_types = {
1: "左面", 2: "右面", 3: "下面",
4: "上面", 5: "前面", 6: "后面"
}
faces[face_id] = {
'indices': [idx + 1 for idx in indices], # 转换为1-based索引
'points': face_array,
'center': center,
'type': face_types.get(face_id, f"面{face_id}")
}
except Exception as e:
self.logger.warning(f"计算面{face_id}信息失败: {e}")
continue
return faces
def update_model_domain_file(self, original_data: Dict, rotated_points: List[np.ndarray], file_path: Path):
"""更新model_domain.dat文件 - 增强验证版本"""
try:
# 创建备份
backup_path = file_path.with_suffix('.dat.backup')
shutil.copy2(file_path, backup_path)
self.logger.info(f"创建备份: {backup_path}")
# 使用保存的原始行内容
lines = original_data['original_lines'].copy()
# 验证旋转后的坐标
if len(rotated_points) != 8:
raise ValueError(f"旋转后点数不正确: {len(rotated_points)},期望8个")
# 记录旋转前后的坐标变化(用于调试)
original_points = original_data['points']
self.logger.info("坐标变化验证:")
for i in range(min(8, len(rotated_points))):
original = original_points[i]
rotated = rotated_points[i]
distance = np.linalg.norm(rotated - original)
self.logger.info(f"节点{i+1}: 原始({original[0]:.6f}, {original[1]:.6f}, {original[2]:.6f}) "
f"旋转后({rotated[0]:.6f}, {rotated[1]:.6f}, {rotated[2]:.6f}) "
f"移动距离: {distance:.6f}")
# 替换顶点坐标(第12-19行)
vertex_start = 11
replaced_count = 0
for i in range(min(8, len(rotated_points))):
line_idx = vertex_start + i
if line_idx < len(lines):
point = rotated_points[i]
# 保持原始格式
old_line = lines[line_idx]
new_line = f" {point[0]:11.6f} {point[1]:11.6f} {point[2]:11.6f}"
lines[line_idx] = new_line
replaced_count += 1
# 记录替换详情
self.logger.info(f"替换行{line_idx+1}: {old_line.strip()} -> {new_line.strip()}")
if replaced_count != 8:
raise ValueError(f"坐标替换数量不正确: {replaced_count},期望8个")
# 保存更新后的文件
with open(file_path, 'w', encoding='utf-8') as f:
f.write('\n'.join(lines) + '\n')
# 验证文件是否保存成功
if file_path.exists():
file_size = file_path.stat().st_size
self.logger.info(f"文件更新成功: {file_path} (大小: {file_size} 字节)")
else:
raise ValueError("文件保存失败")
except Exception as e:
raise ValueError(f"更新文件失败: {e}")
def process_single_file(self, file_info: Dict) -> Dict:
"""处理单个文件"""
result = {
'file_info': file_info,
'status': 'pending',
'message': '',
'current_alpha': 0,
'current_beta': 0,
'target_alpha': file_info['target_alpha'],
'target_beta': file_info['target_beta'],
'gamma': 0,
'theta': 0,
'rotation_applied': False
}
try:
# 读取文件
data = self.read_model_domain_file(file_info['model_file'])
# 检查初始条件
conditions_ok, message = self.check_initial_conditions(data, file_info)
if not conditions_ok:
result['status'] = 'skip' if "跳过" in message else 'error'
result['message'] = message
return result
# 计算旋转角度
gamma = self.alpha_to_gamma.get(file_info['target_alpha'], 0)
theta = self.beta_to_theta.get(file_info['target_beta'], 0)
result['gamma'] = gamma
result['theta'] = theta
# 执行旋转
rotated_points = self.rotate_points(data['points'], gamma, theta)
# 验证旋转结果
if len(rotated_points) != len(data['points']):
raise ValueError(f"旋转后点数不匹配: {len(rotated_points)} vs {len(data['points'])}")
# 更新文件
self.update_model_domain_file(data, rotated_points, file_info['model_file'])
result['status'] = 'success'
result['message'] = f"旋转成功: γ={gamma}°, θ={theta}°"
result['rotation_applied'] = True
except Exception as e:
result['status'] = 'error'
result['message'] = f"处理失败: {e}"
return result
def generate_report(self):
"""生成处理报告"""
report_file = "rotation_analysis_report.txt"
with open(report_file, 'w', encoding='utf-8') as f:
f.write("岩体多面体方位角分析与旋转处理报告\n")
f.write("=" * 70 + "\n")
f.write(f"生成时间: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"基础路径: {self.base_path}\n")
f.write(f"处理文件总数: {self.results['total_files']}\n")
f.write(f"成功处理: {self.results['success']}\n")
f.write(f"跳过处理: {self.results['skip']}\n")
f.write(f"处理失败: {self.results['error']}\n")
f.write("\n详细处理结果:\n")
f.write("-" * 70 + "\n")
for result in self.results['details']:
f.write(f"\n文件: {result['file_info']['relative_path']}\n")
f.write(f"状态: {result['status']}\n")
f.write(f"信息: {result['message']}\n")
if result['status'] != 'error':
f.write(f"目标方向: α={result['target_alpha']}°, β={result['target_beta']}°\n")
if result['rotation_applied']:
f.write(f"旋转角度: γ={result['gamma']}°, θ={result['theta']}°\n")
self.logger.info(f"处理报告已生成: {report_file}")
def run(self):
"""主运行流程"""
self.logger.info("启动岩体多面体方位角分析与旋转程序")
try:
# 扫描目录结构
structure = self.scan_directory_structure()
if not structure['level1_folders']:
self.logger.error("未找到可处理的文件夹")
return
# 用户交互选择
selected_files = self.interactive_selection(structure)
if not selected_files:
self.logger.info("未选择任何文件")
return
self.results['total_files'] = len(selected_files)
# 处理文件
self.logger.info(f"开始处理 {len(selected_files)} 个文件...")
for i, file_info in enumerate(selected_files, 1):
self.logger.info(f"[{i}/{len(selected_files)}] 处理: {file_info['relative_path']}")
result = self.process_single_file(file_info)
self.results['details'].append(result)
# 更新统计
if result['status'] == 'success':
self.results['success'] += 1
elif result['status'] == 'skip':
self.results['skip'] += 1
else:
self.results['error'] += 1
self.logger.info(f"处理结果: {result['status']} - {result['message']}")
# 生成报告
self.generate_report()
# 显示完成信息
print(f"\n处理完成!")
print(f"总处理文件数: {self.results['total_files']}")
print(f"成功旋转: {self.results['success']}")
print(f"跳过处理: {self.results['skip']}")
print(f"处理失败: {self.results['error']}")
print(f"详细报告见: rotation_analysis_report.txt")
except Exception as e:
self.logger.error(f"程序执行失败: {e}")
raise
def main():
"""主程序入口"""
try:
analyzer = RockMassOrientationAnalyzer()
analyzer.run()
except KeyboardInterrupt:
print("\n程序被用户中断")
except Exception as e:
print(f"程序执行错误: {e}")
logging.error(f"程序执行错误: {e}")
if __name__ == "__main__":
main()
最新发布