diff --git a/code/pre_processing/FeatureSample/FeatureSampleConfig.cpp b/code/pre_processing/FeatureSample/FeatureSampleConfig.cpp new file mode 100644 index 0000000..1eb82c9 --- /dev/null +++ b/code/pre_processing/FeatureSample/FeatureSampleConfig.cpp @@ -0,0 +1,94 @@ +#include "FeatureSampleConfig.h" +#include + +namespace MeshLib { + +FeatureSampleConfig FeatureSampleConfig::FromCommandLine(int argc, char** argv) { + cxxopts::Options options("FeatureSample", "Feature sampling for 3D meshes"); + + options.add_options() + ("i,input", "input mesh (obj/off format)", cxxopts::value()) + ("f,feature", "input feature file (fea format)", cxxopts::value()) + ("p,pointcloud", "input pointcloud", cxxopts::value()) + ("pf", "face of input pointcloud", cxxopts::value()) + ("o,output", "output mesh/points", cxxopts::value()) + ("k,mask", "output mask file", cxxopts::value()) + ("fs", "feature samples", cxxopts::value()) + ("ns", "non-feature samples", cxxopts::value()) + ("m,mode", "processing mode", cxxopts::value()) + ("c,color", "use coloring", cxxopts::value()) + ("mp", "max patches per cluster", cxxopts::value()) + ("cot", "use cotangent weight", cxxopts::value()) + ("s,sigma", "position noise sigma", cxxopts::value()) + ("sn", "normal noise sigma", cxxopts::value()) + ("csg", "generate csg tree", cxxopts::value()) + ("convex", "is first layer convex", cxxopts::value()) + ("r,repairturn", "repair turn vertex", cxxopts::value()) + ("verbose", "verbose level", cxxopts::value()) + ("strict", "strict mode") + ("repairtree", "repair tree feature") + ("h,help", "print help"); + + auto result = options.parse(argc, argv); + + if (result.count("help")) { + std::cout << options.help() << std::endl; + exit(0); + } + + FeatureSampleConfig config; + + // 检查必需参数 + if (!result.count("m") || !result.count("i") || !result.count("o")) { + throw std::runtime_error("Missing required parameters"); + } + + // 设置文件路径 + config.files.input_mesh = result["i"].as(); + config.files.output_file = result["o"].as(); + if (result.count("f")) config.files.input_feature = result["f"].as(); + if (result.count("p")) config.files.input_pointcloud = result["p"].as(); + if (result.count("pf")) config.files.input_pointcloud_face = result["pf"].as(); + if (result.count("k")) config.files.output_mask = result["k"].as(); + + // 设置采样参数 + if (result.count("fs")) config.sampling.feature_samples = result["fs"].as(); + if (result.count("ns")) config.sampling.nonfeature_samples = result["ns"].as(); + if (result.count("s")) config.sampling.sigma = result["s"].as(); + if (result.count("sn")) config.sampling.sigma_normal = result["sn"].as(); + if (result.count("cot")) config.sampling.use_cotweight = result["cot"].as(); + + // 设置处理选项 + config.processing.mode = result["m"].as(); + if (result.count("c")) config.processing.use_color = result["c"].as(); + if (result.count("mp")) config.processing.max_patches = result["mp"].as(); + if (result.count("csg")) config.processing.generate_csg = result["csg"].as(); + if (result.count("convex")) config.processing.is_convex = result["convex"].as(); + if (result.count("r")) config.processing.repair_turn = result["r"].as(); + if (result.count("verbose")) config.processing.verbose = result["verbose"].as(); + if (result.count("strict")) config.processing.strict_mode = true; + if (result.count("repairtree")) config.processing.repair_tree = true; + + return config; +} + +bool FeatureSampleConfig::Validate() const { + // 检查文件路径 + if (files.input_mesh.empty() || files.output_file.empty()) { + return false; + } + + // 检查采样参数 + if (sampling.feature_samples < 0 || sampling.nonfeature_samples < 0) { + return false; + } + + // 检查处理模式 + if (processing.mode != 0 && processing.mode != 1) { + return false; + } + + return true; +} + +} // namespace MeshLib \ No newline at end of file diff --git a/code/pre_processing/FeatureSample/FeatureSampleConfig.h b/code/pre_processing/FeatureSample/FeatureSampleConfig.h new file mode 100644 index 0000000..be3befb --- /dev/null +++ b/code/pre_processing/FeatureSample/FeatureSampleConfig.h @@ -0,0 +1,48 @@ +#pragma once +#include +#include "cxxopts.hpp" + +namespace MeshLib { + +class FeatureSampleConfig { +public: + // 文件路径 + struct FilePaths { + std::string input_mesh; // 输入网格文件 + std::string input_feature; // 输入特征文件 + std::string input_pointcloud; // 输入点云文件 + std::string input_pointcloud_face; // 输入点云面片文件 + std::string output_file; // 输出文件 + std::string output_mask; // 输出掩码文件 + } files; + + // 采样参数 + struct SamplingParams { + int feature_samples = 10000; // 特征边采样点数 + int nonfeature_samples = 40000; // 非特征面采样点数 + double sigma = 0.0; // 位置噪声标准差 + double sigma_normal = 0.0; // 法向噪声标准差 + bool use_cotweight = false; // 是否使用余切权重 + } sampling; + + // 处理选项 + struct ProcessingOptions { + int mode = 1; // 处理模式 (0:标准化, 1:特征采样) + bool use_color = false; // 是否使用颜色 + int max_patches = -1; // 每个颜色簇的最大面片数 + bool generate_csg = false; // 是否生成CSG树 + bool is_convex = false; // 第一层是否为凸的 + bool repair_turn = true; // 是否修复转角顶点 + bool repair_tree = false; // 是否修复树特征 + bool strict_mode = false; // 是否使用严格模式 + int verbose = 0; // 详细输出级别 + } processing; + + // 从命令行参数解析配置 + static FeatureSampleConfig FromCommandLine(int argc, char** argv); + + // 验证配置是否有效 + bool Validate() const; +}; + +} // namespace MeshLib \ No newline at end of file diff --git a/code/pre_processing/FeatureSample/Source.cpp b/code/pre_processing/FeatureSample/Source.cpp index 40066b6..669e082 100644 --- a/code/pre_processing/FeatureSample/Source.cpp +++ b/code/pre_processing/FeatureSample/Source.cpp @@ -10,6 +10,7 @@ #include "helper.h" #include "happly.h" #include "Tree.h" +#include "FeatureSampleConfig.h" using namespace MeshLib; @@ -120,57 +121,28 @@ int main(int argc, char** argv) { try { - cxxopts::Options options("FeaturedModelPointSample", "Point Sampling program for featured CAD models (author: Haoxiang Guo, Email: guohaoxiangxiang@gmail.com)"); - options - .positional_help("[optional args]") - .show_positional_help() - .allow_unrecognised_options() - .add_options() - ("i,input", "input mesh (obj/off format)", cxxopts::value()) - ("f,feature", "input feature file (fea format)", cxxopts::value()) - ("p,pointcloud", "input pointcloud", cxxopts::value()) - ("pf", "face of input pointcloud", cxxopts::value()) - ("o,output", "output mesh/points (obj/off/points/xyz format)", cxxopts::value()) - ("k,mask", "output mask file (txt format)", cxxopts::value()) - ("fs", "number of samples on feature edges(default: 10000)", cxxopts::value()) - ("ns", "number of samples on non-feature faces(default: 40000)", cxxopts::value()) - ("m,mode", "processing mode: 0 for normalization and 1 for feature sample", cxxopts::value()) - ("c,color", "whether coloring is used, 0: not used, 1: used, default: 0", cxxopts::value()) - ("mp", "maximum number of patches in each colored cluster, only work for csg, default -1(no upper bound)", cxxopts::value()) - ("cot", "whether cotangent weight is used for sampling, 0: not used, 1: used, default: 0", cxxopts::value()) - ("s,sigma", "sigma for noisy points position, default 0.0", cxxopts::value()) - ("sn", "sigma for noisy points normal in degrees, default 0.0", cxxopts::value()) - ("csg", "whether generating csg tree for model, default: 0", cxxopts::value()) - ("convex", "whether the first layer is convex, default: 0", cxxopts::value()) - ("r,repairturn", "whether the turn vertex are repaired, default: 1", cxxopts::value()) - ("verbose", "verbose setting, default: 0", cxxopts::value()) - ("strict", "treat all edges as either strictly convex or concave") - ("repairtree", "repair tree feature") - ("h,help", "print help"); + // 解析命令行参数到配置类 + auto config = FeatureSampleConfig::FromCommandLine(argc, argv); - auto result = options.parse(argc, argv); - if (result.count("help")) - { - std::cout << options.help({ "", "Group" }) << std::endl; - exit(0); + // 验证配置 + if (!config.Validate()) { + throw std::runtime_error("Invalid configuration"); } int n_nonfeature_sample = 50000; int n_feature_sample = 0; int min_sample_perpatch = 50; double sigma = -1.0; double sigma_n = -1.0; - assert(result.count("m")); - int processing_mode = result["m"].as(); - assert(result.count("i") && result.count("o")); - auto& inputfile = result["i"].as(); - auto& outputfile = result["o"].as(); + int processing_mode = config.processing.mode; + auto& inputfile = config.files.input_mesh; + auto& outputfile = config.files.output_file; //output pts by colors int last_dot = (int)outputfile.find_last_of("."); auto output_prefix = outputfile.substr(0, last_dot); int flag_csg = 0; bool flag_sample_pts = true; //simply generate mask and csg tree of a given point cloud - if (result.count("p")) + if (config.files.input_pointcloud.empty()) { flag_sample_pts = false; } @@ -183,13 +155,13 @@ int main(int argc, char** argv) mesh.load_off(inputfile.c_str()); std::cout << "verts: " << mesh.get_vertices_list()->size() << " face: " << mesh.get_faces_list()->size() << std::endl; bool flag_verbose = false; - if (result.count("verbose")) + if (config.processing.verbose) { - flag_verbose = (bool)result["verbose"].as(); + flag_verbose = (bool)config.processing.verbose; } bool flag_strict = false; - if (result.count("strict")) + if (config.processing.strict_mode) { flag_strict = true; } @@ -234,30 +206,30 @@ int main(int argc, char** argv) //mask: //feature: 0 //non feature: 1,2,3...indicating coloring - assert(result.count("f") && result.count("k")); - auto& inputfeaturefile = result["f"].as(); - auto& outputmaskfile = result["k"].as(); - if (result.count("fs")) - n_feature_sample = result["fs"].as(); - if (result.count("ns")) - n_nonfeature_sample = result["ns"].as(); - if (result.count("s")) - sigma = result["s"].as(); - if (result.count("sn")) - sigma_n = result["sn"].as(); + assert(!config.files.input_feature.empty() && !config.files.output_mask.empty()); + auto& inputfeaturefile = config.files.input_feature; + auto& outputmaskfile = config.files.output_mask; + if (config.sampling.feature_samples) + n_feature_sample = config.sampling.feature_samples; + if (config.sampling.nonfeature_samples) + n_nonfeature_sample = config.sampling.nonfeature_samples; + if (config.sampling.sigma) + sigma = config.sampling.sigma; + if (config.sampling.sigma_normal) + sigma_n = config.sampling.sigma_normal; bool flag_repair_turn_features = true; - if (result.count("r")) - flag_repair_turn_features = result["r"].as(); + if (config.processing.repair_turn) + flag_repair_turn_features = true; bool flag_repair_tree_features = false; - if (result.count("repairtree")) + if (config.processing.repair_tree) { flag_repair_tree_features = true; } - if (result.count("csg")) - flag_csg = result["csg"].as(); + if (config.processing.generate_csg) + flag_csg = true; bool flag_skip_hanging_features = false; //not skipping hanging features std::vector sample_mask; @@ -267,13 +239,13 @@ int main(int argc, char** argv) std::vector sample_pts_tris; //used for assign labels of sample pts if (!flag_sample_pts) { - auto& inputpcfile = result["p"].as(); + auto& inputpcfile = config.files.input_pointcloud; load_xyz_file(inputpcfile.c_str(), sample_pts, sample_pt_normals); sample_pts_tris.resize(sample_pts.size(), 0); - if (result.count("pf")) + if (!config.files.input_pointcloud_face.empty()) { - auto& inputpffile = result["pf"].as(); + auto& inputpffile = config.files.input_pointcloud_face; std::ifstream ifs(inputpffile); for (size_t ii = 0; ii < sample_pts.size(); ii++) @@ -635,14 +607,14 @@ int main(int argc, char** argv) int n_color = cluster_id; //coloring bool flag_coloring = false; - if (result.count("c")) - flag_coloring = result["c"].as(); + if (config.processing.use_color) + flag_coloring = true; bool flag_first_convex = false; - if (result.count("convex")) - flag_first_convex = result["convex"].as(); + if (config.processing.is_convex) + flag_first_convex = true; if (flag_coloring && !flag_csg) { @@ -979,8 +951,8 @@ int main(int argc, char** argv) if (flag_coloring) { int max_patch_per_cluster = -1; - if (result.count("mp")) - max_patch_per_cluster = result["mp"].as(); + if (config.processing.max_patches) + max_patch_per_cluster = config.processing.max_patches; std::vector cluster_color(connectivity_v.size(), -1); n_color = tree_coloring(tree, connectivity_v, cluster_color, 0, max_patch_per_cluster) + 1; for (size_t i = 0; i < face_clusters.size(); i++) @@ -1002,7 +974,7 @@ int main(int argc, char** argv) //sampling on triangles std::vector tri_mean_curvature_normalize(tri_verts.size(), 0.0); - if (result.count("cot")) + if (config.sampling.use_cotweight) { //compute tri_mean_curvature std::vector vert_curvature;