feat: 添加子装配体Shrinkwrap支持 - 新增component_path参数支持对子装配体进行Shrinkwrap - 添加FindComponentByPath方法按路径查找子装配体 - 向后兼容,不传component_path时行为不变
This commit is contained in:
parent
a1e0237c9e
commit
04edf96378
@ -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);
|
||||
|
||||
@ -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.";
|
||||
|
||||
@ -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:
|
||||
// 单例模式
|
||||
|
||||
Loading…
Reference in New Issue
Block a user