# 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