#include "pch.h" #include "Shrinkwrap90Manager.h" #include "Creo90Manager.h" #include "CreoOtk.h" #include #include #include #include #include namespace { std::string EnsurePrtExtension(std::string path) { std::filesystem::path p(path); if (!p.has_extension()) { p.replace_extension(".prt"); } return p.string(); } } // namespace ShrinkwrapShellResult Shrinkwrap90Manager::ExportShell(const ShrinkwrapShellRequest& req) { if (req.output_file_path.empty()) { throw std::runtime_error("output_file_path is required"); } if (req.quality < 1 || req.quality > 10) { throw std::runtime_error("quality must be in [1,10]"); } if (req.small_surface_percentage < 0.0) { throw std::runtime_error("small_surface_percentage must be >= 0"); } Creo90Manager creo; pfcSession_ptr session = creo.GetSession(); if (!session) { throw std::runtime_error("Creo session not available"); } pfcSolid_ptr source = creo.GetCurrentSolid(); if (!source) { throw std::runtime_error("current model is not a solid (part/assembly) or no model is active"); } std::string outPathS = EnsurePrtExtension(req.output_file_path); std::filesystem::path outPath(outPathS); std::string stem = outPath.stem().string(); std::string modelName = SanitizeCreoModelName(stem); if (modelName.empty()) modelName = "shrinkwrap"; pfcPart_ptr outputPart = session->CreatePart(modelName.c_str()); pfcModel_ptr outputModel = pfcModel::cast(outputPart); pfcShrinkwrapModelExportInstructions_ptr modelInstr; if (req.method == 1) { // Faceted Solid // Note: Faceted Solid export is handled via pfcShrinkwrapFacetedPartInstructions // But user wants "Faceted Solid" creation which usually creates a new model (like Merged Solid). // However, OTK distinguishes between "Export to file" (STL/VRML/FacetedPart) and "Create feature/model". // pfcShrinkwrapMergedSolidInstructions creates a new model. // pfcShrinkwrapSurfaceSubsetInstructions creates a new model. // pfcShrinkwrapFacetedPartInstructions exports to a file directly OR creates a model? // Let's check pfcShrinkwrapFacetedPartInstructions::Create signature. // Create(pfcModel_ptr OutputModel, xbool Lightweight) // So it exports to 'OutputModel'. pfcShrinkwrapFacetedPartInstructions_ptr fInstr = pfcShrinkwrapFacetedPartInstructions::Create(outputModel, xfalse); modelInstr = pfcShrinkwrapModelExportInstructions::cast(fInstr); } else if (req.method == 2) { // Merged Solid pfcShrinkwrapMergedSolidInstructions_ptr mInstr = pfcShrinkwrapMergedSolidInstructions::Create(outputModel); modelInstr = pfcShrinkwrapModelExportInstructions::cast(mInstr); } else { // Surface Subset (Default = 0) pfcShrinkwrapSurfaceSubsetInstructions_ptr sInstr = pfcShrinkwrapSurfaceSubsetInstructions::Create(outputModel); modelInstr = pfcShrinkwrapModelExportInstructions::cast(sInstr); } // Common options modelInstr->SetQuality(req.quality); modelInstr->SetAutoHoleFilling(req.fill_holes ? xtrue : xfalse); modelInstr->SetIgnoreSmallSurfaces(req.ignore_small_surfaces ? xtrue : xfalse); if (req.ignore_small_surfaces) { modelInstr->SetSmallSurfPercentage(req.small_surface_percentage); } modelInstr->SetIgnoreQuilts(req.ignore_quilts ? xtrue : xfalse); modelInstr->SetIgnoreSkeleton(req.ignore_skeleton ? xtrue : xfalse); modelInstr->SetAssignMassProperties(req.assign_mass_properties ? xtrue : xfalse); pfcShrinkwrapExportInstructions_ptr exportInstr = pfcShrinkwrapExportInstructions::cast(modelInstr); source->ExportShrinkwrap(exportInstr); std::string dir = outPath.parent_path().string(); pfcModelDescriptor_ptr descr = pfcModelDescriptor::Create(pfcMDL_PART, modelName.c_str(), nullptr); descr->SetPath(dir.c_str()); outputPart->Backup(descr); std::filesystem::path actualOutPath = outPath.parent_path() / (modelName + ".prt"); // Ensure file is written std::this_thread::sleep_for(std::chrono::milliseconds(200)); ShrinkwrapShellResult res; res.output_path = actualOutPath.string(); std::error_code ec; uintmax_t bytes = std::filesystem::file_size(actualOutPath, ec); if (ec || bytes == 0) { // Retry std::this_thread::sleep_for(std::chrono::milliseconds(500)); bytes = std::filesystem::file_size(actualOutPath, ec); } if (ec) bytes = 0; res.file_size = FormatFileSize(bytes); res.execution_time = ""; // 由上层填充 return res; } std::string Shrinkwrap90Manager::SanitizeCreoModelName(const std::string& fileStem) { std::string out; out.reserve(fileStem.size()); for (unsigned char uc : fileStem) { char c = static_cast(uc); if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '-') { out.push_back(c); } else if (c == ' ') { out.push_back('_'); } } if (!out.empty() && (out[0] >= '0' && out[0] <= '9')) { out.insert(out.begin(), '_'); } if (out.size() > 31) { out.resize(31); } return out; } std::string Shrinkwrap90Manager::FormatFileSize(uintmax_t bytes) { std::ostringstream oss; double mb = static_cast(bytes) / (1024.0 * 1024.0); oss.setf(std::ios::fixed); oss.precision(1); oss << mb << " MB"; return oss.str(); } std::string Shrinkwrap90Manager::FormatHhMmSs(long long totalSeconds) { if (totalSeconds < 0) totalSeconds = 0; long long h = totalSeconds / 3600; long long m = (totalSeconds % 3600) / 60; long long s = totalSeconds % 60; char buf[32]; sprintf_s(buf, "%02lld:%02lld:%02lld", h, m, s); return std::string(buf); }