CreoOtkPluging/ShrinkwrapManager.cpp

690 lines
27 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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) {
if (!source_model) return nullptr;
SessionInfo session_info = GetSessionInfo();
if (!session_info.is_valid) return nullptr;
try {
pfcSolid_ptr source_solid = pfcSolid::cast(source_model);
if (!source_solid) return nullptr;
// Generate safe Part name based on source model
std::string base_name = GenerateSafePartName(source_model);
// Ensure unique name by checking existing models
std::string unique_filename = base_name;
int counter = 1;
xstring test_name = StringToXString(unique_filename);
// Check if name already exists, add counter if necessary
while (true) {
try {
pfcModel_ptr existing_model = session_info.session->GetModel(test_name, pfcMDL_PART);
if (existing_model) {
// Name exists, try next counter
unique_filename = base_name + "_" + std::to_string(counter);
test_name = StringToXString(unique_filename);
counter++;
} else {
// Name doesn't exist, can use it
break;
}
} catch (...) {
// GetModel throws exception if model doesn't exist, name is available
break;
}
}
xstring output_name = StringToXString(unique_filename);
pfcPart_ptr output_part = session_info.session->CreatePart(output_name);
if (!output_part) return nullptr;
pfcModel_ptr output_model = pfcModel::cast(output_part);
if (!output_model) return nullptr;
pfcShrinkwrapSurfaceSubsetInstructions_ptr instructions =
pfcShrinkwrapSurfaceSubsetInstructions::Create(output_model);
if (!instructions) return nullptr;
// 使用用户提交的参数设置
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);
pfcShrinkwrapExportInstructions_ptr export_instructions = pfcShrinkwrapExportInstructions::cast(instructions);
if (export_instructions) {
source_solid->ExportShrinkwrap(export_instructions);
}
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) {
// OTK工具包错误
return nullptr;
} catch (const pfcXBadArgument& e) {
// 参数错误
return nullptr;
} catch (const pfcXToolkitOutOfMemory& e) {
// OTK内存不足错误
return nullptr;
} catch (const std::bad_alloc& e) {
// 标准库内存错误
return nullptr;
} catch (const std::exception& e) {
// 标准异常
return nullptr;
} catch (...) {
// 未知异常
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;
try {
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.";
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: Unable to generate shrinkwrap model. Check if the current model is valid and not corrupted.";
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;
}