# utils/html_generator.py from pathlib import Path from typing import Dict, Any, Optional from datetime import datetime from utils.logger import get_logger logger = get_logger(__name__) try: import orjson def _json_dumps(obj: Any) -> bytes: return orjson.dumps(obj, option=orjson.OPT_SERIALIZE_NUMPY | orjson.OPT_NON_STR_KEYS) def _json_dumps_str(obj: Any) -> str: return orjson.dumps(obj, option=orjson.OPT_SERIALIZE_NUMPY | orjson.OPT_NON_STR_KEYS).decode("utf-8") _JSON_FAST = True except ImportError: import json def _json_dumps(obj: Any) -> bytes: return json.dumps(obj, ensure_ascii=False).encode("utf-8") def _json_dumps_str(obj: Any) -> str: return json.dumps(obj, ensure_ascii=False) _JSON_FAST = False class HTMLGenerator: """HTML文件生成器 — 数据分离架构,Three.js 0.170 + PBR渲染""" def __init__(self, output_dir: str = "./html_output"): self.output_dir = Path(output_dir) self.output_dir.mkdir(exist_ok=True) def generate_3d_viewer_html(self, stp_filename: str, data_filename: str) -> str: """生成3D可视化HTML页面 — 通过fetch异步加载companion JSON数据""" cavity_html = self._build_cavity_info_panel_template() html_content = f""" 3D模具几何可视化 - {stp_filename}
加载几何数据...

📐 {stp_filename}

顶点-
三角面-
点云-
体积-
{cavity_html}
""" return html_content def _build_cavity_info_panel_template(self) -> str: """构建型腔信息面板 — 由JS动态填充,这里放置容器""" return """

🔧 关键工艺参数

模具参数
收缩率-
拔模角-
分型线长度-
几何特性
产品体积-
产品重量-
壁厚范围-
制造要求
模仁材料-
硬度-
表面光洁度-
预估周期-
""" def generate_3d_viewer_data( self, geometry_data: Dict[str, Any], cavity_data: Optional[Dict[str, Any]] = None, pointcloud_data: Optional[Dict[str, Any]] = None, lod_data: Optional[Dict[str, Any]] = None, ) -> Dict[str, Any]: """生成companion JSON数据文件内容,支持多级LOD""" pc = dict(pointcloud_data) if pointcloud_data else {} if lod_data and lod_data.get("lods"): pc["lods"] = lod_data["lods"] data = { "version": "4.0.0", "generated_at": datetime.now().isoformat(), "geometry": geometry_data, "cavity": cavity_data, "pointcloud": pc if pc else pointcloud_data, } return data def save_html_file(self, html_content: str, filename: str) -> str: """保存HTML文件到磁盘""" try: file_path = self.output_dir / filename file_path.write_text(html_content, encoding='utf-8') logger.info(f"HTML文件保存成功: {file_path}") return str(file_path) except Exception as e: logger.error(f"保存HTML文件失败: {e}") raise def save_data_file(self, data_content: Dict[str, Any], filename: str) -> str: """保存JSON数据文件到磁盘 — 使用orjson高速序列化""" try: file_path = self.output_dir / filename file_path.write_bytes(_json_dumps(data_content)) logger.info(f"数据文件保存成功: {file_path} (orjson={_JSON_FAST})") return str(file_path) except Exception as e: logger.error(f"保存数据文件失败: {e}") raise def generate_and_save_visualization( self, geometry_data: Dict[str, Any], stp_filename: str, cavity_data: Optional[Dict[str, Any]] = None, pointcloud_data: Optional[Dict[str, Any]] = None, suffix: Optional[str] = None, lod_data: Optional[Dict[str, Any]] = None, ) -> str: """生成并保存可视化HTML + companion JSON数据文件。返回HTML文件路径(向后兼容)""" try: base_stem = Path(stp_filename).stem.replace(" ", "_") ts = datetime.now().strftime("%Y%m%d_%H%M%S") suffix_part = f"_{suffix}" if suffix else "" base_name = f"mold_{base_stem}{suffix_part}_{ts}" html_filename = f"{base_name}.html" data_filename = f"{base_name}_data.json" data_content = self.generate_3d_viewer_data( geometry_data, cavity_data, pointcloud_data, lod_data=lod_data ) self.save_data_file(data_content, data_filename) html_content = self.generate_3d_viewer_html(stp_filename, data_filename) html_file_path = self.save_html_file(html_content, html_filename) return html_file_path except Exception as e: logger.error(f"生成可视化文件失败: {e}") raise