165 lines
5.6 KiB
C++
165 lines
5.6 KiB
C++
#include "pch.h"
|
|
|
|
#include "Shrinkwrap90Manager.h"
|
|
|
|
#include "Creo90Manager.h"
|
|
|
|
#include "CreoOtk.h"
|
|
|
|
#include <filesystem>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <thread>
|
|
#include <chrono>
|
|
|
|
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<char>(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<double>(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);
|
|
}
|