diff --git a/application/SDF_Visualize/sdf_visualizer.cpp b/application/SDF_Visualize/sdf_visualizer.cpp new file mode 100644 index 0000000..cc94237 --- /dev/null +++ b/application/SDF_Visualize/sdf_visualizer.cpp @@ -0,0 +1,111 @@ +// sdf_visualizer.cpp +// ────────────────────────────────────────────────────────────────────────────── +// 依赖: +// prim_gen.hpp → baked_blobtree_t、aabb_t、primitive_t … +// base/subface.hpp → internal::get_eval_sdf_ptr、surface_type +// +// 文件格式(output_path): +// 行 0 : grid_res y_slice +// 行 1 : x0 x1 z0 z1 (世界坐标范围) +// 行 2 : num_subfaces +// 然后对每个 subface 重复: +// 行 : surface_type_int +// N 行 : N 个空格分隔的 float(iz 行,ix 列,N = grid_res+1) +// ────────────────────────────────────────────────────────────────────────────── + +#include "sdf_visualizer.hpp" +#include // internal::get_eval_sdf_ptr, surface_type + +#include +#include +#include +#include +#include +#include + +void dump_sdf_slice(const baked_blobtree_t& tree, + const std::string& output_path, + double y_slice, + int grid_res, + double extra_margin) +{ + // ── 0. 基本校验 ──────────────────────────────────────────────────────── + if (tree.subfaces.empty()) { + std::cerr << "[SDF_VIZ] No subfaces — nothing to dump.\n"; + return; + } + + // ── 1. 计算场景 AABB(含边距)────────────────────────────────────────── + aabb_t scene_aabb{}; + for (const auto& prim : tree.primitives) { scene_aabb.extend(prim->fetch_aabb()); } + if (scene_aabb.isEmpty()) { + std::cerr << "[SDF_VIZ] Scene AABB is invalid.\n"; + return; + } + const double base_margin = scene_aabb.sizes().maxCoeff() * extra_margin + 0.05; + scene_aabb.min().array() -= base_margin; + scene_aabb.max().array() += base_margin; + + const double x0 = scene_aabb.min().x(), x1 = scene_aabb.max().x(); + const double z0 = scene_aabb.min().z(), z1 = scene_aabb.max().z(); + const int N = grid_res + 1; // 每轴采样点数 + + const uint32_t ns = static_cast(tree.subfaces.size()); + std::cout << "[SDF_VIZ] Grid " << N << "×" << N << " | " << ns << " subface(s)" + << " | Y=" << y_slice << " | X[" << x0 << ", " << x1 << "]" + << " | Z[" << z0 << ", " << z1 << "]\n"; + + // ── 2. 对每个 subface 在网格上采样 SDF ──────────────────────────────── + // grids[s][iz * N + ix] = SDF 值(float 节省内存) + std::vector> grids(ns, std::vector(N * N, 0.0f)); + +for (uint32_t s = 0; s < ns; ++s) { + // 不再调用 .raw() 获取原始指针,而是直接使用包装器对象 + const auto& subface_wrapper = tree.subfaces[s]; // 使用引用,避免拷贝 + surface_type subface_type = tree.subface_types[s]; + + auto sdf_fn = internal::get_eval_sdf_ptr(subface_type); + if (!sdf_fn) { + std::cout << "[SDF_VIZ] subface[" << s << "] type=" << static_cast(subface_type) + << " → null eval_sdf, skipped.\n"; + continue; + } + + for (int iz = 0; iz < N; ++iz) { + const double z = z0 + (z1 - z0) * static_cast(iz) / grid_res; + for (int ix = 0; ix < N; ++ix) { + const double x = x0 + (x1 - x0) * static_cast(ix) / grid_res; + const Eigen::Vector3d pos(x, y_slice, z); + // 传递包装器对象,而不是原始指针 + grids[s][iz * N + ix] = static_cast(sdf_fn(subface_wrapper, pos)); + } + } + std::cout << "[SDF_VIZ] subface[" << s << "] type=" << static_cast(subface_type) << " ✓\n"; + } + + // ── 3. 写文件 ────────────────────────────────────────────────────────── + std::ofstream ofs(output_path); + if (!ofs) { + std::cerr << "[SDF_VIZ] Cannot open: " << output_path << "\n"; + return; + } + + // 文件头 + ofs << grid_res << ' ' << y_slice << '\n'; + ofs << x0 << ' ' << x1 << ' ' << z0 << ' ' << z1 << '\n'; + ofs << ns << '\n'; + + // 每个 subface 的数据块 + for (uint32_t s = 0; s < ns; ++s) { + ofs << static_cast(tree.subface_types[s]) << '\n'; + for (int iz = 0; iz < N; ++iz) { + for (int ix = 0; ix < N; ++ix) { + if (ix) ofs << ' '; + ofs << grids[s][iz * N + ix]; + } + ofs << '\n'; + } + } + + std::cout << "[SDF_VIZ] Written → " << output_path << '\n'; +} \ No newline at end of file diff --git a/application/SDF_Visualize/sdf_visualizer.hpp b/application/SDF_Visualize/sdf_visualizer.hpp new file mode 100644 index 0000000..e8442fd --- /dev/null +++ b/application/SDF_Visualize/sdf_visualizer.hpp @@ -0,0 +1,23 @@ +#pragma once +// sdf_visualizer.hpp +// ────────────────────────────────────────────────────────────────────────────── +// 将 baked_blobtree 在指定二维截面上的 SDF 分布写入文本文件, +// 供 visualize_sdf.py 读取并渲染热力图。 +// +// 使用方法(在 main.cpp 中): +// #include "sdf_visualizer.hpp" +// // bake 完成后、solve 之前: +// dump_sdf_slice(baked_blobtree, "sdf_slice.txt"); +// // 然后命令行运行:python visualize_sdf.py sdf_slice.txt +// ────────────────────────────────────────────────────────────────────────────── + +#include +#include "../../network_process/include/prim_gen.hpp" // baked_blobtree_t, aabb_t … + +// 在世界坐标 XZ 平面(y = y_slice)按 grid_res×grid_res 网格 +// 对每个 subface 采样 SDF,结果写入 output_path。 +void dump_sdf_slice(const baked_blobtree_t& tree, + const std::string& output_path = "sdf_slice.txt", + double y_slice = 0.0, + int grid_res = 256, + double extra_margin = 0.2); \ No newline at end of file diff --git a/application/SDF_Visualize/visualize_sdf.py b/application/SDF_Visualize/visualize_sdf.py new file mode 100644 index 0000000..5f1988a --- /dev/null +++ b/application/SDF_Visualize/visualize_sdf.py @@ -0,0 +1,334 @@ +#!/usr/bin/env python3 +""" +visualize_sdf.py +──────────────────────────────────────────────────────────────────────── +读取 dump_sdf_slice() 生成的文本文件,绘制: + · 各 subface 的 SDF 热力图(蓝=负/内部,红=正/外部,白=零面) + · 零等值线(石灰绿,代表算法"认定"的模型表面) + · 合成 SDF(max 合并,即 CSG 求交结果) + +用法: + pip install numpy matplotlib scipy + python visualize_sdf.py sdf_slice.txt [y_slice_for_xy_view] +──────────────────────────────────────────────────────────────────────── +""" + +import sys +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.colors as mcolors +import matplotlib.ticker as mticker +from pathlib import Path +from scipy.ndimage import gaussian_filter +from matplotlib.gridspec import GridSpec + +# ── surface_type 枚举(与 C++ 侧对齐)──────────────────────────────── +SURFACE_NAMES = { + 0: "Plane (cap)", + 1: "Sphere", + 2: "Cylinder", + 3: "Cone", + 4: "ExtrudePolyline\nSide Face", + 5: "ExtrudeHelixline\nSide Face", +} + +# 设置全局样式 +plt.rcParams.update({ + 'font.size': 9, + 'axes.titlesize': 10, + 'axes.labelsize': 9, + 'xtick.labelsize': 8, + 'ytick.labelsize': 8, + 'legend.fontsize': 8, + 'figure.titlesize': 12, + 'figure.autolayout': False, + 'savefig.bbox': 'tight', + 'savefig.pad_inches': 0.1, +}) + +# ───────────────────────────────────────────────────────────────────── +# 1. 读取数据 +# ───────────────────────────────────────────────────────────────────── +def load_sdf_data(path: str) -> dict: + """解析 dump_sdf_slice 输出文件,返回结构化字典。""" + lines = Path(path).read_text().splitlines() + idx = 0 + + # 行 0: grid_res y_slice + tok = lines[idx].split(); idx += 1 + grid_res = int(tok[0]) + y_slice = float(tok[1]) + + # 行 1: x0 x1 z0 z1 + tok = lines[idx].split(); idx += 1 + x0, x1, z0, z1 = float(tok[0]), float(tok[1]), float(tok[2]), float(tok[3]) + + # 行 2: num_subfaces + ns = int(lines[idx]); idx += 1 + + N = grid_res + 1 + subfaces = [] + for _ in range(ns): + stype = int(lines[idx]); idx += 1 + rows = [] + for _ in range(N): + rows.append(list(map(float, lines[idx].split()))) + idx += 1 + subfaces.append({"type": stype, "grid": np.array(rows, dtype=np.float64)}) + + return dict(grid_res=grid_res, y_slice=y_slice, + x0=x0, x1=x1, z0=z0, z1=z1, subfaces=subfaces) + + +# ───────────────────────────────────────────────────────────────────── +# 2. 绘制单个面板 +# ───────────────────────────────────────────────────────────────────── +def draw_panel(ax, XX, ZZ, grid: np.ndarray, title: str, y_slice: float, + cmap=None, show_neg_region=True, highlight_sign_flip=False): + """ + 在 ax 上绘制: + · 连续色带热力图(双斜率归一化,保证零值白色) + · 石灰绿零等值线(模型表面位置) + · 负值区边界(蓝色虚线,"算法认为的内部"边界) + """ + if cmap is None: + cmap = plt.cm.RdBu_r # 改为 RdBu_r,红色为正,蓝色为负,更直观 + + finite = grid[np.isfinite(grid)] + if finite.size == 0: + ax.set_title(title + "\n[no finite data]") + return + + # 以 98 分位数作为色域上限,避免极值压缩色彩 + vmax = float(np.percentile(np.abs(finite), 98)) + vmax = max(vmax, 1e-6) + norm = mcolors.TwoSlopeNorm(vmin=-vmax, vcenter=0.0, vmax=vmax) + + # 应用轻微高斯平滑,使热力图更清晰 + if grid.shape[0] > 10 and grid.shape[1] > 10: + grid_smoothed = gaussian_filter(grid, sigma=0.7, mode='nearest') + else: + grid_smoothed = grid + + # ── 填充等高线热力图 ── + cf = ax.contourf(XX, ZZ, grid_smoothed, levels=50, cmap=cmap, norm=norm, + alpha=0.85, extend='both') + + # 添加颜色条,但位置更紧凑 + cbar = plt.colorbar(cf, ax=ax, pad=0.01, fraction=0.045, shrink=0.9) + cbar.set_label("SDF", fontsize=7, labelpad=2) + cbar.ax.tick_params(labelsize=6, pad=1) + + # ── 零等值线(模型表面) ── + try: + cs0 = ax.contour(XX, ZZ, grid_smoothed, levels=[0.0], + colors=["lime"], linewidths=1.8, zorder=5, alpha=0.9) + if len(cs0.collections) > 0: + # 只在有零等值线的地方添加标签 + ax.clabel(cs0, fmt=" SDF=0 ", fontsize=7, inline=True, + colors="lime", zorder=6, inline_spacing=5) + except Exception: + pass + + # 高亮显示符号突变区域(如果启用) + if highlight_sign_flip and finite.size > 4: + from scipy.ndimage import generic_filter + def local_range(vals): + return vals.max() - vals.min() if vals.size > 0 else 0 + + # 检测局部范围内的剧烈符号变化 + local_range_grid = generic_filter(grid, local_range, size=3) + mean_range = float(np.percentile(local_range_grid, 85)) + sign_flip_mask = (local_range_grid > mean_range * 2.5) & np.isfinite(grid) + + if sign_flip_mask.any(): + # 用半透明黄色填充显示符号突变区域 + ax.contourf(XX, ZZ, sign_flip_mask.astype(float), + levels=[0.5, 1.5], colors=["yellow"], + alpha=0.25, zorder=4) + ax.contour(XX, ZZ, sign_flip_mask.astype(float), levels=[0.5], + colors=["yellow"], linewidths=1.0, linestyles="dotted", + zorder=5, alpha=0.7) + + # 添加图例说明 + sign_flip_pct = sign_flip_mask.sum() / sign_flip_mask.size * 100 + if sign_flip_pct > 0.5: # 仅在显著区域添加标注 + ax.text(0.02, 0.98, f"⚠ {sign_flip_pct:.1f}% sign-flip", + transform=ax.transAxes, fontsize=6, color="black", + bbox=dict(boxstyle="round,pad=0.2", fc="yellow", + alpha=0.7, ec="black", lw=0.5), + zorder=10, verticalalignment='top') + + # ── 统计信息标注 ── + neg_pct = np.sum(grid < 0) / grid.size * 100 + pos_pct = 100 - neg_pct + min_val, max_val = np.min(grid), np.max(grid) + + # 更简洁的统计信息 + info = f"Neg: {neg_pct:.1f}%\nPos: {pos_pct:.1f}%\nMin: {min_val:.2e}\nMax: {max_val:.2e}" + ax.text(0.02, 0.02, info, transform=ax.transAxes, + fontsize=6, color="white", zorder=7, + bbox=dict(boxstyle="round,pad=0.2", fc="black", alpha=0.6, + ec="gray", lw=0.5), + verticalalignment='bottom') + + # ── 坐标轴 ── + ax.set_xlabel("X", fontsize=8, labelpad=2) + ax.set_ylabel("Z", fontsize=8, labelpad=2) + ax.set_title(title, fontsize=9, pad=6, fontweight='medium') + ax.set_aspect("equal", adjustable="box") + ax.tick_params(labelsize=7, pad=2) + ax.xaxis.set_minor_locator(mticker.AutoMinorLocator(2)) + ax.yaxis.set_minor_locator(mticker.AutoMinorLocator(2)) + + # 更精细的网格 + ax.grid(True, which="major", color="gray", alpha=0.2, lw=0.3, linestyle='-') + ax.grid(True, which="minor", color="gray", alpha=0.1, lw=0.1, linestyle=':') + + +# ───────────────────────────────────────────────────────────────────── +# 3. 主绘图入口 - 重新设计布局 +# ───────────────────────────────────────────────────────────────────── +def plot_sdf_heatmap(data: dict, out_path: str): + subfaces = data["subfaces"] + ns = len(subfaces) + y_slice = data["y_slice"] + x0, x1, z0, z1 = data["x0"], data["x1"], data["z0"], data["z1"] + N = data["grid_res"] + 1 + + X = np.linspace(x0, x1, N) + Z = np.linspace(z0, z1, N) + XX, ZZ = np.meshgrid(X, Z) + + # 计算合成SDF + if ns > 0: + combined = subfaces[0]["grid"].copy() + for sf in subfaces[1:]: + combined = np.maximum(combined, sf["grid"]) + + # 智能布局计算 + if ns <= 1: + # 只有1个子面:一行两列 + fig = plt.figure(figsize=(12, 5.5)) + gs = GridSpec(1, 3, width_ratios=[1, 0.05, 1.2], wspace=0.15, hspace=0.1) + axes = [fig.add_subplot(gs[0, 0]), fig.add_subplot(gs[0, 2])] + elif ns == 2: + # 2个子面:一行三列 + fig = plt.figure(figsize=(15, 5)) + gs = GridSpec(1, 4, width_ratios=[1, 1, 0.05, 1.2], wspace=0.15, hspace=0.1) + axes = [fig.add_subplot(gs[0, 0]), fig.add_subplot(gs[0, 1]), fig.add_subplot(gs[0, 3])] + elif ns == 3: + # 3个子面:两行两列 + fig = plt.figure(figsize=(12, 9)) + gs = GridSpec(2, 3, width_ratios=[1, 1, 0.05], height_ratios=[1, 1], wspace=0.15, hspace=0.2) + axes = [ + fig.add_subplot(gs[0, 0]), fig.add_subplot(gs[0, 1]), + fig.add_subplot(gs[1, 0]), fig.add_subplot(gs[1, 1]), + fig.add_subplot(gs[0:2, 2]) # 合成图占两行 + ] + else: + # 更多子面:计算最优布局 + ncols = min(3, int(np.ceil(np.sqrt(ns + 1)))) + nrows = int(np.ceil((ns + 1) / ncols)) + fig_width = min(6 * ncols, 18) + fig_height = 5 * nrows + + fig, axes = plt.subplots(nrows, ncols, + figsize=(fig_width, fig_height), + squeeze=False) + axes = axes.flatten() + + cmap = plt.cm.RdBu_r + + # 绘制每个子面 + for s, sf in enumerate(subfaces): + if s < len(axes) - 1: # 最后一个位置留给合成图 + stype = sf["type"] + name = SURFACE_NAMES.get(stype, f"Type {stype}") + title = f"Subface {s}: {name}" + + # 对于第4种类型(ExtrudePolyline Side Face),启用符号突变检测 + highlight = (stype == 4) + + draw_panel(axes[s], XX, ZZ, sf["grid"], title, y_slice, + cmap, highlight_sign_flip=highlight) + + # 绘制合成面板 + if ns > 0 and ns < len(axes): + title_comb = "Combined SDF\nmax(subfaces) ≈ CSG Intersection" + draw_panel(axes[ns], XX, ZZ, combined, title_comb, y_slice, cmap, + highlight_sign_flip=True) + + # 标记隐藏多余子图 + for i in range(ns + 1, len(axes)): + axes[i].set_visible(False) + elif ns == 0: + axes[0].text(0.5, 0.5, "No subface data", + ha='center', va='center', transform=axes[0].transAxes) + axes[0].set_title("Empty Data") + for i in range(1, len(axes)): + axes[i].set_visible(False) + + # 添加全局标题 + fig.suptitle( + f"SDF Cross-Section Analysis - Y = {y_slice:.6f}\n" + "Blue = Inside Model (SDF < 0) | Red = Outside Model (SDF > 0) | " + "Lime Green = Model Surface (SDF = 0)", + fontsize=11, y=0.98, fontweight='bold' + ) + + # 添加图例说明 + fig.text(0.02, 0.02, + f"Data: {Path(out_path).stem}.txt | Grid: {data['grid_res']}×{data['grid_res']} | " + f"Subfaces: {ns} | X: [{x0:.3f}, {x1:.3f}] | Z: [{z0:.3f}, {z1:.3f}]", + fontsize=7, style='italic', alpha=0.7) + + # 保存图形 + fig.savefig(out_path, dpi=200, bbox_inches="tight", facecolor='white') + print(f"[SDF_VIZ] Heatmap saved → {out_path}") + + # 显示图形 + plt.tight_layout(rect=[0, 0.03, 1, 0.95]) # 为标题留出空间 + plt.show() + + +# ───────────────────────────────────────────────────────────────────── +# 4. CLI 入口 +# ───────────────────────────────────────────────────────────────────── +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: python visualize_sdf.py ") + print("Example: python visualize_sdf.py sdf_slice.txt") + sys.exit(1) + + data_path = sys.argv[1] + + if not Path(data_path).exists(): + print(f"[ERROR] File not found: {data_path}") + sys.exit(1) + + print(f"[SDF_VIZ] Loading {data_path} ...") + try: + data = load_sdf_data(data_path) + print(f"[SDF_VIZ] Grid: {data['grid_res']}×{data['grid_res']} | " + f"Y-slice: {data['y_slice']:.6f} | " + f"Subfaces: {len(data['subfaces'])}") + + # 生成输出路径 + stem = Path(data_path).stem + out_dir = Path(data_path).parent + out_path = str(out_dir / f"{stem}_heatmap.png") + + # 绘制热力图 + plot_sdf_heatmap(data, out_path) + + # 打印文件信息 + if Path(out_path).exists(): + file_size = Path(out_path).stat().st_size + print(f"[SDF_VIZ] Output: {out_path} ({file_size/1024:.1f} KB)") + + except Exception as e: + print(f"[ERROR] Failed to process {data_path}: {e}") + import traceback + traceback.print_exc() + sys.exit(1) \ No newline at end of file diff --git a/application/main.cpp b/application/main.cpp index 4c64870..e043938 100644 --- a/application/main.cpp +++ b/application/main.cpp @@ -4,6 +4,10 @@ #include #include +#include "SDF_Visualize/sdf_visualizer.hpp" +#include // 新增 +#include // 新增 + int main() { auto primitive_data_center = create_primitive_data_center(); @@ -11,16 +15,16 @@ int main() //auto sphere2 = create_primitive(primitive_data_center, PRIMITIVE_TYPE_SPHERE); //auto cylinder1 = create_primitive(primitive_data_center, PRIMITIVE_TYPE_CYLINDER); //auto cylinder2 = create_primitive(primitive_data_center, PRIMITIVE_TYPE_CYLINDER); - //auto polyline_extrude = create_primitive(primitive_data_center, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); - auto helixline_extrude = create_primitive(primitive_data_center, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); + auto polyline_extrude = create_primitive(primitive_data_center, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + //auto helixline_extrude = create_primitive(primitive_data_center, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); //primitive_apply_translation(cylinder1, {1.0, 0.0, 0.0}); //primitive_apply_translation(sphere1, {1.0, 0.0, 0.0}); - //primitive_apply_translation(polyline_extrude, {0.3, 0.0, 0.0}); + //primitive_apply_translation(polyline_extrude, {-2.0, 0.0, 0.0}); //primitive_apply_scale(cylinder1, {0.5, 1.0, 1.5}); //primitive_apply_scale(sphere2, {0.5, 1.0, 1.5}); - ////primitive_apply_scale(polyline_extrude, {1.3, 2.0, 0.6}); + //primitive_apply_scale(polyline_extrude, {1.3, 2.0, 0.6}); ////primitive_apply_scale(helixline_extrude, {0.7, 2.0, 1.0}); //double angle_x = 90.0 * pi / 180.0; // 角度转弧度 //primitive_apply_rotation(cylinder1, {sin(angle_x / 2.0), 0.0, 0.0, cos(angle_x / 2.0)}); @@ -28,9 +32,12 @@ int main() ////primitive_apply_rotation(polyline_extrude, {sin(angle_x / 3.0), 0.0, 0.0, cos(angle_x / 1.0)}); //std::cout << "primitive created..." << std::endl; + //double angle_x = 45.0 * pi / 180.0; // 角度转弧度 + //primitive_apply_rotation(polyline_extrude, {0.0, -sin(angle_x / 2.0), 0.0, cos(angle_x / 1.0)}); + //std::cout << "primitive created..." << std::endl; auto runtime_blobtree = create_blobtree(); - auto node_iter = blobtree_add_primitive_node(runtime_blobtree, helixline_extrude); + auto node_iter = blobtree_add_primitive_node(runtime_blobtree, polyline_extrude); //auto node_iter1 = blobtree_add_primitive_node(runtime_blobtree, cylinder1); //auto node_iter2 = blobtree_add_primitive_node(runtime_blobtree, sphere1); //auto node_iter3 = blobtree_add_operation_node(runtime_blobtree, node_iter1, node_iter2, INTERSECTION_OP); @@ -39,13 +46,46 @@ int main() //auto node_iter6 = blobtree_add_primitive_node(runtime_blobtree, cylinder2); //auto node_iter7 = blobtree_add_operation_node(runtime_blobtree, node_iter6, node_iter5, DIFFERENCE_OP); //auto node_iter8 = blobtree_add_primitive_node(runtime_blobtree, polyline_extrude); - //auto node_iter9 = blobtree_add_operation_node(runtime_blobtree, node_iter7, node_iter8, UNION_OP); + //auto node_iter9 = blobtree_add_operation_node(runtime_blobtree, node_iter1, node_iter8, UNION_OP); auto baked_blobtree = bake_blobtree(runtime_blobtree); destroy_blobtree(runtime_blobtree); std::cout << "blobtree created..." << std::endl; + + // 定义SDF数据文件的保存路径 + std::filesystem::path base_dir = "E:\\projects\\ImplicitSurfaceNetwork-xj\\application\\SDF_Visualize"; + std::filesystem::path file_path = base_dir / "sdf_slice.txt"; + + // 确保目录存在 + if (!std::filesystem::exists(base_dir)) { + std::filesystem::create_directories(base_dir); + std::cout << "已创建目录: " << base_dir.string() << std::endl; + } + // 打印文件存储路径 + std::string sdf_output_path = file_path.string(); + std::cout << "SDF数据将保存到: " << sdf_output_path << std::endl; + // 打印文件存储路径 + std::cout << "SDF数据将保存到: " << sdf_output_path << std::endl; + + dump_sdf_slice(*baked_blobtree, + /*output_path=*/sdf_output_path, + /*y_slice =*/0.0, + /*grid_res =*/256, + /*extra_margin=*/0.2); + + // 确认文件已生成 + std::ifstream test_file(sdf_output_path); + if (test_file.good()) { + std::cout << "SDF数据文件已成功生成: " << sdf_output_path << std::endl; + // 获取文件绝对路径 + std::filesystem::path abs_path = std::filesystem::absolute(sdf_output_path); + std::cout << " 绝对路径: " << abs_path.string() << std::endl; + } else { + std::cerr << "✗ 错误: SDF数据文件生成失败" << std::endl; + } + s_settings settings{}; settings.resolution = 48; // 72 settings.scene_aabb_margin = 1e-5; @@ -60,7 +100,7 @@ int main() //destroy_primitive(sphere1); //destroy_primitive(cylinder1); //destroy_primitive(polyline_extrude); - destroy_primitive(helixline_extrude); + //destroy_primitive(helixline_extrude); destroy_primitive_data_center(primitive_data_center); return 0; diff --git a/application/xmake.lua b/application/xmake.lua index 9aae8ad..3a28437 100644 --- a/application/xmake.lua +++ b/application/xmake.lua @@ -10,4 +10,6 @@ target("test_frontend") set_kind("binary") add_deps("frontend") add_files("main.cpp") + + add_files("**.cpp") target_end() \ No newline at end of file