From 04edf9637801fcb60bb95a2c3fb7778f0a48305f Mon Sep 17 00:00:00 2001 From: sladro Date: Mon, 8 Dec 2025 10:25:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=AD=90=E8=A3=85?= =?UTF-8?q?=E9=85=8D=E4=BD=93Shrinkwrap=E6=94=AF=E6=8C=81=20-=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9Ecomponent=5Fpath=E5=8F=82=E6=95=B0=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=AF=B9=E5=AD=90=E8=A3=85=E9=85=8D=E4=BD=93=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?Shrinkwrap=20-=20=E6=B7=BB=E5=8A=A0FindComponentByPath=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E6=8C=89=E8=B7=AF=E5=BE=84=E6=9F=A5=E6=89=BE=E5=AD=90?= =?UTF-8?q?=E8=A3=85=E9=85=8D=E4=BD=93=20-=20=E5=90=91=E5=90=8E=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=EF=BC=8C=E4=B8=8D=E4=BC=A0component=5Fpath=E6=97=B6?= =?UTF-8?q?=E8=A1=8C=E4=B8=BA=E4=B8=8D=E5=8F=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ShellExportHandler.cpp | 3 + ShrinkwrapManager.cpp | 148 ++++++++++++++++++++++++++++++++++++++++- ShrinkwrapManager.h | 4 ++ 3 files changed, 153 insertions(+), 2 deletions(-) diff --git a/ShellExportHandler.cpp b/ShellExportHandler.cpp index 7c720a7..231978b 100644 --- a/ShellExportHandler.cpp +++ b/ShellExportHandler.cpp @@ -158,6 +158,9 @@ ShrinkwrapShellRequest ShellExportHandler::ParseShrinkwrapRequest(const std::str request.method = method; } + // 解析 component_path 参数(可选,用于指定子装配体) + request.component_path = ExtractJsonValue(json_body, "component_path"); + request.quality = ExtractJsonIntValue(json_body, "quality", 8); request.chord_height = ExtractJsonDoubleValue(json_body, "chord_height", 0.1); request.fill_holes = ExtractJsonBoolValue(json_body, "fill_holes", true); diff --git a/ShrinkwrapManager.cpp b/ShrinkwrapManager.cpp index f045304..b7d595e 100644 --- a/ShrinkwrapManager.cpp +++ b/ShrinkwrapManager.cpp @@ -1,4 +1,5 @@ #include "ShrinkwrapManager.h" +#include #include #include #include @@ -291,6 +292,115 @@ std::string ShrinkwrapManager::GenerateUniqueFilePath(const std::string& base_pa return base_path + "\\" + timestamp_file_name; } +pfcModel_ptr ShrinkwrapManager::FindComponentByPath(wfcWAssembly_ptr assembly, const std::string& component_path, const SessionInfo& session_info) { + if (!assembly || component_path.empty()) return nullptr; + + try { + // Parse path: split by '/' or '\' + std::vector path_segments; + std::string path = component_path; + std::replace(path.begin(), path.end(), '\\', '/'); + + std::istringstream iss(path); + std::string segment; + while (std::getline(iss, segment, '/')) { + if (!segment.empty()) { + path_segments.push_back(segment); + } + } + + if (path_segments.empty()) return nullptr; + + // Get current assembly name to check if first segment is the top-level + xstring asm_name_xstr = pfcModel::cast(assembly)->GetFileName(); + std::string asm_name = StringToStdString(asm_name_xstr); + + // If first segment matches top-level assembly name, skip it + size_t start_index = 0; + if (!path_segments.empty() && !asm_name.empty()) { + // Case-insensitive comparison + std::string seg_lower = path_segments[0]; + std::string asm_lower = asm_name; + std::transform(seg_lower.begin(), seg_lower.end(), seg_lower.begin(), ::tolower); + std::transform(asm_lower.begin(), asm_lower.end(), asm_lower.begin(), ::tolower); + if (seg_lower == asm_lower) { + start_index = 1; + } + } + + // If nothing left after skipping top-level, return the assembly itself + if (start_index >= path_segments.size()) { + return pfcModel::cast(assembly); + } + + // Recursively search for target component + wfcWAssembly_ptr current_asm = assembly; + + for (size_t i = start_index; i < path_segments.size(); i++) { + std::string target_name = path_segments[i]; + std::string target_lower = target_name; + std::transform(target_lower.begin(), target_lower.end(), target_lower.begin(), ::tolower); + + pfcFeatures_ptr features = current_asm->ListFeaturesByType(xfalse, pfcFEATTYPE_COMPONENT); + if (!features) return nullptr; + + bool found = false; + int features_count = features->getarraysize(); + + for (int j = 0; j < features_count; j++) { + try { + pfcFeature_ptr feature = features->get(j); + if (!feature) continue; + + pfcComponentFeat_ptr comp_feat = pfcComponentFeat::cast(feature); + if (!comp_feat) continue; + + auto model_descr = comp_feat->GetModelDescr(); + if (!model_descr) continue; + + xstring comp_name_xstr = model_descr->GetFileName(); + std::string comp_name = StringToStdString(comp_name_xstr); + std::string comp_lower = comp_name; + std::transform(comp_lower.begin(), comp_lower.end(), comp_lower.begin(), ::tolower); + + if (comp_lower == target_lower) { + // Found matching component + pfcModel_ptr comp_model = session_info.session->GetModelFromDescr(model_descr); + if (!comp_model) { + comp_model = session_info.session->RetrieveModel(model_descr); + } + + if (!comp_model) continue; + + // If this is the last segment, return the model + if (i == path_segments.size() - 1) { + return comp_model; + } + + // Otherwise, continue searching in this sub-assembly + if (comp_model->GetType() == pfcMDL_ASSEMBLY) { + current_asm = wfcWAssembly::cast(comp_model); + if (current_asm) { + found = true; + break; + } + } + } + } catch (...) { + continue; + } + } + + if (!found) return nullptr; + } + + return nullptr; + + } catch (...) { + return nullptr; + } +} + pfcModel_ptr ShrinkwrapManager::ExecuteOTKShrinkwrap(pfcModel_ptr source_model, const ShrinkwrapShellRequest& request) { if (!source_model) return nullptr; @@ -470,10 +580,44 @@ ShrinkwrapShellResult ShrinkwrapManager::ExecuteShrinkwrapShell(const Shrinkwrap return result; } - // Execute Shrinkwrap directly on top-level model + // Determine target model for shrinkwrap + pfcModel_ptr target_model = current_model; + + // If component_path is specified, find the sub-assembly + if (!processed_request.component_path.empty()) { + if (current_model->GetType() != pfcMDL_ASSEMBLY) { + result.success = false; + result.error_message = "component_path can only be used when current model is an assembly."; + return result; + } + + wfcWAssembly_ptr current_assembly = wfcWAssembly::cast(current_model); + if (!current_assembly) { + result.success = false; + result.error_message = "Failed to cast current model to assembly."; + return result; + } + + // Parse component path and find target component + target_model = FindComponentByPath(current_assembly, processed_request.component_path, session_info); + if (!target_model) { + result.success = false; + result.error_message = "Component not found at path: " + processed_request.component_path; + return result; + } + + // Verify target is a solid (part or assembly) + if (target_model->GetType() != pfcMDL_ASSEMBLY && target_model->GetType() != pfcMDL_PART) { + result.success = false; + result.error_message = "Target component must be a part or assembly for shrinkwrap."; + return result; + } + } + + // Execute Shrinkwrap on target model pfcModel_ptr shrinkwrap_model = nullptr; try { - shrinkwrap_model = ExecuteOTKShrinkwrap(current_model, processed_request); + shrinkwrap_model = ExecuteOTKShrinkwrap(target_model, processed_request); } catch (const pfcXToolkitError& e) { result.success = false; result.error_message = "OTK Toolkit Error: Creo operation failed. This may indicate model corruption or invalid geometry."; diff --git a/ShrinkwrapManager.h b/ShrinkwrapManager.h index 7d12b9f..c4e467e 100644 --- a/ShrinkwrapManager.h +++ b/ShrinkwrapManager.h @@ -22,6 +22,7 @@ struct ShrinkwrapShellRequest { std::string software_type = "creo"; std::string project_name = "Shrinkwrap Shell Export"; std::string method = "outer_shell"; + std::string component_path = ""; // 可选:子装配体路径,如 "TopAsm.asm/SubAsm.asm",为空则使用当前模型 int quality = 8; // 1-10, 质量等级 double chord_height = 0.1; // 弦高控制三角化误差(mm), 0.05-0.2 bool fill_holes = true; // 自动填孔 @@ -93,6 +94,9 @@ private: // File system operations bool FileExists(const std::string& filepath); std::string GenerateUniqueFilePath(const std::string& base_path, const std::string& file_name); + + // Component path parsing and finding + pfcModel_ptr FindComponentByPath(wfcWAssembly_ptr assembly, const std::string& component_path, const SessionInfo& session_info); public: // 单例模式