808 lines
31 KiB
C++
808 lines
31 KiB
C++
#include "ShrinkwrapManager.h"
|
||
#include <wfcAssembly.h>
|
||
#include <iostream>
|
||
#include <sstream>
|
||
#include <algorithm>
|
||
#include <chrono>
|
||
#include <iomanip>
|
||
#include <fstream>
|
||
#include <cmath>
|
||
#define NOMINMAX
|
||
#include <windows.h>
|
||
// 取消Windows API宏定义,避免与OTK方法冲突
|
||
#ifdef GetCurrentDirectory
|
||
#undef GetCurrentDirectory
|
||
#endif
|
||
#include <functional>
|
||
#include <numeric>
|
||
#include <io.h> // For _access on Windows
|
||
|
||
ShrinkwrapManager* ShrinkwrapManager::instance = nullptr;
|
||
|
||
ShrinkwrapManager& ShrinkwrapManager::Instance() {
|
||
if (instance == nullptr) {
|
||
instance = new ShrinkwrapManager();
|
||
}
|
||
return *instance;
|
||
}
|
||
|
||
SessionInfo ShrinkwrapManager::GetSessionInfo() {
|
||
SessionInfo info;
|
||
info.is_valid = false;
|
||
|
||
try {
|
||
info.session = pfcGetProESession();
|
||
if (info.session) {
|
||
info.wSession = wfcWSession::cast(info.session);
|
||
if (info.wSession) {
|
||
info.is_valid = true;
|
||
try {
|
||
xstring version_xstr = pfcGetProEVersion();
|
||
if (version_xstr != xstringnil && !version_xstr.IsEmpty()) {
|
||
info.version = StringToStdString(version_xstr);
|
||
} else {
|
||
info.version = "Unknown";
|
||
}
|
||
} catch (...) {
|
||
info.version = "Unknown";
|
||
}
|
||
}
|
||
}
|
||
} catch (...) {
|
||
info.is_valid = false;
|
||
}
|
||
|
||
return info;
|
||
}
|
||
|
||
std::string ShrinkwrapManager::StringToStdString(const xstring& xstr) {
|
||
try {
|
||
if (xstr.IsNull()) {
|
||
return "";
|
||
}
|
||
|
||
std::wstring wstr(xstr);
|
||
if (wstr.empty()) {
|
||
return "";
|
||
}
|
||
|
||
std::string result(wstr.begin(), wstr.end());
|
||
return result;
|
||
} catch (...) {
|
||
return "";
|
||
}
|
||
}
|
||
|
||
xstring ShrinkwrapManager::StringToXString(const std::string& str) {
|
||
try {
|
||
std::wstring wstr(str.begin(), str.end());
|
||
return xstring(wstr.c_str());
|
||
} catch (...) {
|
||
return xstringnil;
|
||
}
|
||
}
|
||
|
||
std::string ShrinkwrapManager::GetCurrentTimeString() {
|
||
auto now = std::chrono::system_clock::now();
|
||
auto time_t = std::chrono::system_clock::to_time_t(now);
|
||
|
||
std::stringstream ss;
|
||
ss << std::put_time(std::localtime(&time_t), "%Y-%m-%dT%H:%M:%S");
|
||
return ss.str();
|
||
}
|
||
|
||
std::string ShrinkwrapManager::GenerateSafePartName(pfcModel_ptr source_model) {
|
||
if (!source_model) {
|
||
// Fallback to timestamp-based name
|
||
auto now = std::chrono::system_clock::now();
|
||
auto time_t = std::chrono::system_clock::to_time_t(now);
|
||
std::stringstream ss;
|
||
ss << "shrinkwrap_" << std::put_time(std::localtime(&time_t), "%Y%m%d_%H%M%S");
|
||
return ss.str();
|
||
}
|
||
|
||
try {
|
||
// Get source model name
|
||
xstring name_xstr = source_model->GetFileName();
|
||
std::string base_name = StringToStdString(name_xstr);
|
||
|
||
// Remove extension
|
||
size_t dot_pos = base_name.find_last_of(".");
|
||
if (dot_pos != std::string::npos) {
|
||
base_name = base_name.substr(0, dot_pos);
|
||
}
|
||
|
||
// Clean non-ASCII characters, keep only alphanumeric and underscore
|
||
std::string safe_name;
|
||
safe_name.reserve(base_name.length());
|
||
|
||
for (char c : base_name) {
|
||
if (std::isalnum(static_cast<unsigned char>(c)) || c == '_') {
|
||
safe_name += c;
|
||
} else if (!safe_name.empty() && safe_name.back() != '_') {
|
||
safe_name += '_';
|
||
}
|
||
}
|
||
|
||
// Remove trailing underscores
|
||
while (!safe_name.empty() && safe_name.back() == '_') {
|
||
safe_name.pop_back();
|
||
}
|
||
|
||
// Ensure name is not empty
|
||
if (safe_name.empty()) {
|
||
safe_name = "model";
|
||
}
|
||
|
||
// Limit length (Creo has 31 character limit, reserve space for suffix)
|
||
if (safe_name.length() > 20) {
|
||
safe_name = safe_name.substr(0, 20);
|
||
}
|
||
|
||
// Add shrinkwrap suffix
|
||
return safe_name + "_shrink";
|
||
|
||
} catch (...) {
|
||
// Fallback to timestamp-based name
|
||
auto now = std::chrono::system_clock::now();
|
||
auto time_t = std::chrono::system_clock::to_time_t(now);
|
||
std::stringstream ss;
|
||
ss << "shrinkwrap_" << std::put_time(std::localtime(&time_t), "%Y%m%d_%H%M%S");
|
||
return ss.str();
|
||
}
|
||
}
|
||
|
||
std::string ShrinkwrapManager::CalculateFileSize(const std::string& file_path) {
|
||
try {
|
||
HANDLE hFile = CreateFileA(file_path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||
if (hFile != INVALID_HANDLE_VALUE) {
|
||
LARGE_INTEGER fileSize;
|
||
if (GetFileSizeEx(hFile, &fileSize)) {
|
||
CloseHandle(hFile);
|
||
double size_mb = static_cast<double>(fileSize.QuadPart) / (1024.0 * 1024.0);
|
||
|
||
std::stringstream ss;
|
||
ss << std::fixed << std::setprecision(1) << size_mb << "MB";
|
||
return ss.str();
|
||
}
|
||
CloseHandle(hFile);
|
||
}
|
||
} catch (...) {
|
||
// File access error
|
||
}
|
||
|
||
return "0.0MB";
|
||
}
|
||
|
||
bool ShrinkwrapManager::ValidateRequest(const ShrinkwrapShellRequest& request, std::string& error_message) {
|
||
if (request.software_type != "creo") {
|
||
error_message = "Invalid software_type, must be 'creo'";
|
||
return false;
|
||
}
|
||
|
||
if (request.quality < 1 || request.quality > 10) {
|
||
error_message = "Quality must be between 1 and 10";
|
||
return false;
|
||
}
|
||
|
||
if (request.small_surface_percentage < 0.0 || request.small_surface_percentage > 100.0) {
|
||
error_message = "Small surface percentage must be between 0.0 and 100.0";
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
bool ShrinkwrapManager::IsCreoSessionAvailable() {
|
||
SessionInfo session_info = GetSessionInfo();
|
||
return session_info.is_valid;
|
||
}
|
||
|
||
std::string ShrinkwrapManager::GetSupportedFormats() {
|
||
return "Assembly models only - outputs Part (.prt) files";
|
||
}
|
||
|
||
void ShrinkwrapManager::ApplyPresetParameters(ShrinkwrapShellRequest& request) {
|
||
if (request.preset == "fast") {
|
||
// Fast档:最快速生成,最保守设置
|
||
request.quality = 3;
|
||
request.chord_height = 0.3;
|
||
request.ignore_small_surfaces = false; // 关闭小面过滤
|
||
request.small_surface_percentage = 0.0; // 不过滤
|
||
request.fill_holes = false; // 不填孔
|
||
request.output_type = "solid_surface";
|
||
|
||
} else if (request.preset == "balanced") {
|
||
// Balanced档:平衡设置,适合大多数情况
|
||
request.quality = 5;
|
||
request.chord_height = 0.15;
|
||
request.ignore_small_surfaces = false; // 仍然关闭小面过滤避免卡死
|
||
request.small_surface_percentage = 0.0;
|
||
request.fill_holes = false; // 暂时不填孔
|
||
request.output_type = "solid_surface";
|
||
|
||
} else if (request.preset == "tight") {
|
||
// Tight档:相对高精度,但仍然保守
|
||
request.quality = 5; // 限制在5以内避免卡死
|
||
request.chord_height = 0.1;
|
||
request.ignore_small_surfaces = false; // 不过滤小面
|
||
request.small_surface_percentage = 0.0;
|
||
request.fill_holes = false; // 暂时不填孔,等稳定后再开启
|
||
request.output_type = "solid_surface";
|
||
}
|
||
// 空字符串或其他值保持默认参数
|
||
}
|
||
|
||
|
||
double ShrinkwrapManager::CalculateModelVolume(pfcModel_ptr model) {
|
||
if (!model) return 0.0;
|
||
|
||
try {
|
||
pfcSolid_ptr solid = pfcSolid::cast(model);
|
||
if (solid) {
|
||
pfcMassProperty_ptr mass_props = solid->GetMassProperty(nullptr);
|
||
if (mass_props) {
|
||
return mass_props->GetVolume();
|
||
}
|
||
}
|
||
} catch (...) {
|
||
// Volume calculation failed
|
||
}
|
||
|
||
return 0.0;
|
||
}
|
||
|
||
bool ShrinkwrapManager::FileExists(const std::string& filepath) {
|
||
// Use _access for Windows platform
|
||
return (_access(filepath.c_str(), 0) == 0);
|
||
}
|
||
|
||
std::string ShrinkwrapManager::GenerateUniqueFilePath(const std::string& base_path, const std::string& file_name) {
|
||
// Construct full path
|
||
std::string full_path = base_path + "\\" + file_name;
|
||
|
||
// Return original path if file doesn't exist
|
||
if (!FileExists(full_path)) {
|
||
return full_path;
|
||
}
|
||
|
||
// Extract name and extension
|
||
size_t dot_pos = file_name.find_last_of('.');
|
||
std::string name_without_ext = (dot_pos != std::string::npos) ? file_name.substr(0, dot_pos) : file_name;
|
||
std::string extension = (dot_pos != std::string::npos) ? file_name.substr(dot_pos) : "";
|
||
|
||
// Try different suffixes
|
||
int counter = 1;
|
||
const int max_attempts = 999; // Limit maximum attempts
|
||
|
||
while (counter <= max_attempts) {
|
||
std::string new_file_name = name_without_ext + "_" + std::to_string(counter) + extension;
|
||
full_path = base_path + "\\" + new_file_name;
|
||
|
||
if (!FileExists(full_path)) {
|
||
return full_path;
|
||
}
|
||
counter++;
|
||
}
|
||
|
||
// Use timestamp as fallback if all attempts exhausted
|
||
auto now = std::chrono::system_clock::now();
|
||
auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
|
||
std::string timestamp_file_name = name_without_ext + "_" + std::to_string(timestamp) + extension;
|
||
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, std::string& error_detail) {
|
||
if (!source_model) {
|
||
error_detail = "Source model is null";
|
||
return nullptr;
|
||
}
|
||
|
||
SessionInfo session_info = GetSessionInfo();
|
||
if (!session_info.is_valid) {
|
||
error_detail = "Creo session is not valid";
|
||
return nullptr;
|
||
}
|
||
|
||
try {
|
||
// Step 1: Cast to Solid
|
||
pfcSolid_ptr source_solid = nullptr;
|
||
try {
|
||
source_solid = pfcSolid::cast(source_model);
|
||
} catch (...) {
|
||
error_detail = "Exception when casting source model to Solid";
|
||
return nullptr;
|
||
}
|
||
if (!source_solid) {
|
||
error_detail = "Failed to cast source model to Solid. Model type may not support Shrinkwrap.";
|
||
return nullptr;
|
||
}
|
||
|
||
// Step 2: Generate safe Part name
|
||
std::string base_name;
|
||
try {
|
||
base_name = GenerateSafePartName(source_model);
|
||
} catch (...) {
|
||
error_detail = "Exception when generating safe part name";
|
||
return nullptr;
|
||
}
|
||
|
||
// Step 3: Ensure unique name by checking existing models
|
||
std::string unique_filename = base_name;
|
||
int counter = 1;
|
||
xstring test_name = StringToXString(unique_filename);
|
||
|
||
while (true) {
|
||
try {
|
||
pfcModel_ptr existing_model = session_info.session->GetModel(test_name, pfcMDL_PART);
|
||
if (existing_model) {
|
||
unique_filename = base_name + "_" + std::to_string(counter);
|
||
test_name = StringToXString(unique_filename);
|
||
counter++;
|
||
} else {
|
||
break;
|
||
}
|
||
} catch (...) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Step 4: Create output Part with unique name
|
||
// Get working directory first to check for existing files
|
||
std::string working_dir;
|
||
try {
|
||
xstring working_dir_xstr = session_info.session->GetCurrentDirectory();
|
||
working_dir = StringToStdString(working_dir_xstr);
|
||
} catch (...) {
|
||
working_dir = "";
|
||
}
|
||
|
||
// Generate unique filename by checking both session and disk
|
||
int name_counter = 0;
|
||
const int max_name_attempts = 100;
|
||
std::string final_filename = base_name;
|
||
|
||
while (name_counter < max_name_attempts) {
|
||
if (name_counter > 0) {
|
||
final_filename = base_name + "_" + std::to_string(name_counter);
|
||
}
|
||
|
||
// Ensure filename is not too long (Creo limit is 31 characters)
|
||
if (final_filename.length() > 31) {
|
||
final_filename = final_filename.substr(0, 25) + "_" + std::to_string(name_counter);
|
||
}
|
||
|
||
// Check if file exists on disk
|
||
std::string file_path = working_dir + "\\" + final_filename + ".prt";
|
||
bool file_exists_on_disk = FileExists(file_path);
|
||
|
||
// Check if model exists in session
|
||
bool model_exists_in_session = false;
|
||
try {
|
||
xstring check_name = StringToXString(final_filename);
|
||
pfcModel_ptr existing_model = session_info.session->GetModel(check_name, pfcMDL_PART);
|
||
if (existing_model) {
|
||
model_exists_in_session = true;
|
||
// Try to erase from session
|
||
try {
|
||
existing_model->Erase();
|
||
model_exists_in_session = false;
|
||
} catch (...) {
|
||
// Erase failed
|
||
}
|
||
}
|
||
} catch (...) {
|
||
// GetModel throws exception if model doesn't exist
|
||
}
|
||
|
||
if (!file_exists_on_disk && !model_exists_in_session) {
|
||
break; // Found a unique name
|
||
}
|
||
|
||
name_counter++;
|
||
}
|
||
|
||
if (name_counter >= max_name_attempts) {
|
||
error_detail = "Could not find unique filename after " + std::to_string(max_name_attempts) + " attempts";
|
||
return nullptr;
|
||
}
|
||
|
||
xstring output_name = StringToXString(final_filename);
|
||
pfcPart_ptr output_part = nullptr;
|
||
|
||
try {
|
||
output_part = session_info.session->CreatePart(output_name);
|
||
} catch (...) {
|
||
error_detail = "Exception when creating output Part: " + final_filename + ". Working dir: " + working_dir;
|
||
return nullptr;
|
||
}
|
||
if (!output_part) {
|
||
error_detail = "Failed to create output Part: " + final_filename;
|
||
return nullptr;
|
||
}
|
||
|
||
pfcModel_ptr output_model = pfcModel::cast(output_part);
|
||
if (!output_model) {
|
||
error_detail = "Failed to cast output Part to Model";
|
||
return nullptr;
|
||
}
|
||
|
||
// Step 5: Create Shrinkwrap instructions
|
||
pfcShrinkwrapSurfaceSubsetInstructions_ptr instructions = nullptr;
|
||
try {
|
||
instructions = pfcShrinkwrapSurfaceSubsetInstructions::Create(output_model);
|
||
} catch (...) {
|
||
error_detail = "Exception when creating ShrinkwrapSurfaceSubsetInstructions";
|
||
return nullptr;
|
||
}
|
||
if (!instructions) {
|
||
error_detail = "Failed to create ShrinkwrapSurfaceSubsetInstructions";
|
||
return nullptr;
|
||
}
|
||
|
||
// Step 6: Set parameters
|
||
try {
|
||
instructions->SetQuality(request.quality);
|
||
instructions->SetAutoHoleFilling(request.fill_holes);
|
||
instructions->SetIgnoreSmallSurfaces(request.ignore_small_surfaces);
|
||
instructions->SetSmallSurfPercentage(request.small_surface_percentage);
|
||
instructions->SetIgnoreQuilts(request.ignore_quilts);
|
||
instructions->SetIgnoreSkeleton(request.ignore_skeleton);
|
||
instructions->SetAssignMassProperties(request.assign_mass_properties);
|
||
} catch (...) {
|
||
error_detail = "Exception when setting Shrinkwrap parameters";
|
||
return nullptr;
|
||
}
|
||
|
||
// Step 7: Cast to export instructions
|
||
pfcShrinkwrapExportInstructions_ptr export_instructions = pfcShrinkwrapExportInstructions::cast(instructions);
|
||
if (!export_instructions) {
|
||
error_detail = "Failed to cast instructions to ShrinkwrapExportInstructions";
|
||
return nullptr;
|
||
}
|
||
|
||
// Step 8: Execute ExportShrinkwrap
|
||
try {
|
||
source_solid->ExportShrinkwrap(export_instructions);
|
||
} catch (...) {
|
||
error_detail = "Exception during ExportShrinkwrap execution - model may be too complex";
|
||
return nullptr;
|
||
}
|
||
|
||
try {
|
||
if (output_model) {
|
||
// Get current working directory
|
||
xstring current_dir_xstr = session_info.session->GetCurrentDirectory();
|
||
std::string save_path = StringToStdString(current_dir_xstr);
|
||
|
||
// Get model file name with extension
|
||
xstring model_name_xstr = output_model->GetFileName();
|
||
std::string file_name = StringToStdString(model_name_xstr);
|
||
|
||
// Add .prt extension if not present
|
||
if (file_name.find(".prt") == std::string::npos &&
|
||
file_name.find(".PRT") == std::string::npos) {
|
||
file_name += ".prt";
|
||
}
|
||
|
||
// Check for existing file and generate unique path
|
||
std::string unique_file_path = GenerateUniqueFilePath(save_path, file_name);
|
||
|
||
// Extract new file name if path was modified
|
||
size_t last_slash = unique_file_path.find_last_of('\\');
|
||
if (last_slash != std::string::npos) {
|
||
std::string new_file_name = unique_file_path.substr(last_slash + 1);
|
||
|
||
// Remove extension to get model name
|
||
size_t dot_pos = new_file_name.find_last_of('.');
|
||
if (dot_pos != std::string::npos) {
|
||
new_file_name = new_file_name.substr(0, dot_pos);
|
||
}
|
||
|
||
// Rename model if name changed
|
||
std::string original_name = StringToStdString(model_name_xstr);
|
||
size_t orig_dot_pos = original_name.find_last_of('.');
|
||
if (orig_dot_pos != std::string::npos) {
|
||
original_name = original_name.substr(0, orig_dot_pos);
|
||
}
|
||
|
||
if (new_file_name != original_name) {
|
||
xstring new_name_xstr = StringToXString(new_file_name);
|
||
output_model->Rename(new_name_xstr);
|
||
}
|
||
}
|
||
|
||
// Save model to disk
|
||
output_model->Save();
|
||
}
|
||
} catch (...) {
|
||
// Save failed but return model anyway
|
||
// Model exists in session even if save failed
|
||
}
|
||
|
||
return pfcModel::cast(output_part);
|
||
|
||
} catch (const pfcXToolkitError& e) {
|
||
error_detail = "OTK Toolkit Error during ExportShrinkwrap";
|
||
return nullptr;
|
||
} catch (const pfcXBadArgument& e) {
|
||
error_detail = "Bad Argument Error: Invalid parameter passed to Shrinkwrap API";
|
||
return nullptr;
|
||
} catch (const pfcXToolkitOutOfMemory& e) {
|
||
error_detail = "Out of Memory Error during Shrinkwrap operation";
|
||
return nullptr;
|
||
} catch (const pfcXToolkitGeneralError& e) {
|
||
error_detail = "General OTK Error during ExportShrinkwrap - model may be too complex or have invalid geometry";
|
||
return nullptr;
|
||
} catch (const std::bad_alloc& e) {
|
||
error_detail = "System memory allocation failed";
|
||
return nullptr;
|
||
} catch (const std::exception& e) {
|
||
error_detail = std::string("Standard exception: ") + e.what();
|
||
return nullptr;
|
||
} catch (...) {
|
||
error_detail = "Unknown exception during Shrinkwrap operation";
|
||
return nullptr;
|
||
}
|
||
}
|
||
|
||
|
||
ShrinkwrapShellResult ShrinkwrapManager::ExecuteShrinkwrapShell(const ShrinkwrapShellRequest& request) {
|
||
ShrinkwrapShellResult result;
|
||
|
||
// Apply preset parameters (if specified)
|
||
ShrinkwrapShellRequest processed_request = request;
|
||
if (!processed_request.preset.empty()) {
|
||
ApplyPresetParameters(processed_request);
|
||
}
|
||
|
||
// Validate request
|
||
std::string validation_error;
|
||
if (!ValidateRequest(processed_request, validation_error)) {
|
||
result.success = false;
|
||
result.error_message = validation_error;
|
||
return result;
|
||
}
|
||
|
||
// Check Creo session
|
||
SessionInfo session_info = GetSessionInfo();
|
||
if (!session_info.is_valid) {
|
||
result.success = false;
|
||
result.error_message = "Creo session not available";
|
||
return result;
|
||
}
|
||
|
||
try {
|
||
// Get current model
|
||
pfcModel_ptr current_model = session_info.session->GetCurrentModel();
|
||
if (!current_model) {
|
||
result.success = false;
|
||
result.error_message = "No current model loaded";
|
||
return result;
|
||
}
|
||
|
||
// Check model type - Shrinkwrap works for both assembly and part
|
||
if (current_model->GetType() != pfcMDL_ASSEMBLY && current_model->GetType() != pfcMDL_PART) {
|
||
result.success = false;
|
||
result.error_message = "Shrinkwrap shell export requires a part or assembly model. Current model type is not supported.";
|
||
return result;
|
||
}
|
||
|
||
// 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;
|
||
std::string shrinkwrap_error_detail;
|
||
try {
|
||
shrinkwrap_model = ExecuteOTKShrinkwrap(target_model, processed_request, shrinkwrap_error_detail);
|
||
} catch (const pfcXToolkitError& e) {
|
||
result.success = false;
|
||
result.error_message = "OTK Toolkit Error: Creo operation failed. This may indicate model corruption or invalid geometry.";
|
||
return result;
|
||
} catch (const pfcXBadArgument& e) {
|
||
result.success = false;
|
||
result.error_message = "Invalid Parameters: One or more shrinkwrap parameters are invalid. Please check quality, chord_height and other settings.";
|
||
return result;
|
||
} catch (const pfcXToolkitOutOfMemory& e) {
|
||
result.success = false;
|
||
result.error_message = "Memory Error: Insufficient memory to process the model. Try reducing quality or using a simpler preset.";
|
||
return result;
|
||
} catch (const std::bad_alloc& e) {
|
||
result.success = false;
|
||
result.error_message = "System Memory Error: Out of memory. Please close other applications and try again.";
|
||
return result;
|
||
} catch (...) {
|
||
result.success = false;
|
||
result.error_message = "Unknown Error: An unexpected error occurred during shrinkwrap processing.";
|
||
return result;
|
||
}
|
||
|
||
if (!shrinkwrap_model) {
|
||
result.success = false;
|
||
result.error_message = "Shrinkwrap Processing Failed: " + shrinkwrap_error_detail;
|
||
return result;
|
||
}
|
||
|
||
// Get output file information
|
||
try {
|
||
xstring current_dir = session_info.session->GetCurrentDirectory();
|
||
std::string actual_save_path = StringToStdString(current_dir);
|
||
|
||
xstring model_name = shrinkwrap_model->GetFileName();
|
||
std::string actual_filename = StringToStdString(model_name);
|
||
|
||
// Set result parameters
|
||
result.parameters.output_file_path = actual_save_path + "\\" + actual_filename;
|
||
result.parameters.output_file_size = CalculateFileSize(result.parameters.output_file_path);
|
||
result.parameters.shrinkwrap_time = GetCurrentTimeString();
|
||
result.parameters.method = "creo_native_shrinkwrap";
|
||
result.parameters.quality = processed_request.quality;
|
||
result.parameters.fill_holes = processed_request.fill_holes;
|
||
result.parameters.ignore_small_surfaces = processed_request.ignore_small_surfaces;
|
||
result.parameters.small_surface_percentage = processed_request.small_surface_percentage;
|
||
result.parameters.ignore_quilts = processed_request.ignore_quilts;
|
||
result.parameters.ignore_skeleton = processed_request.ignore_skeleton;
|
||
result.parameters.assign_mass_properties = processed_request.assign_mass_properties;
|
||
result.parameters.preset_used = processed_request.preset;
|
||
|
||
result.success = true;
|
||
result.message = "Shrinkwrap shell export completed successfully";
|
||
} catch (...) {
|
||
// Failed to get file info, but shrinkwrap succeeded
|
||
result.parameters.method = "creo_native_shrinkwrap";
|
||
result.parameters.quality = processed_request.quality;
|
||
result.success = true;
|
||
result.message = "Shrinkwrap completed but file info unavailable";
|
||
}
|
||
|
||
} catch (const std::exception& e) {
|
||
result.success = false;
|
||
result.error_message = "Standard error: " + std::string(e.what());
|
||
} catch (...) {
|
||
result.success = false;
|
||
result.error_message = "Unknown error during shrinkwrap operation";
|
||
}
|
||
|
||
return result;
|
||
} |