feat: 添加子装配体Shrinkwrap支持 - 新增component_path参数支持对子装配体进行Shrinkwrap - 添加FindComponentByPath方法按路径查找子装配体 - 向后兼容,不传component_path时行为不变

This commit is contained in:
sladro 2025-12-08 10:25:22 +08:00
parent a1e0237c9e
commit 04edf96378
3 changed files with 153 additions and 2 deletions

View File

@ -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);

View File

@ -1,4 +1,5 @@
#include "ShrinkwrapManager.h"
#include <wfcAssembly.h>
#include <iostream>
#include <sstream>
#include <algorithm>
@ -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<std::string> 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.";

View File

@ -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:
// 单例模式