904 lines
33 KiB
C++
904 lines
33 KiB
C++
#pragma once
|
||
|
||
// 基础OTK头文件 - 确保正确的包含顺序
|
||
#include <pfcGlobal.h>
|
||
#include <pfcSession.h>
|
||
#include <wfcSession.h>
|
||
#include <wfcGlobal.h>
|
||
#include <pfcModel.h>
|
||
#include <pfcExceptions.h>
|
||
#include <pfcExport.h>
|
||
#include <pfcFeature.h>
|
||
#include <pfcComponentFeat.h>
|
||
#include <pfcFeature_s.h>
|
||
#include <pfcSolid.h>
|
||
#include <wfcSolid.h>
|
||
#include <wfcSolidInstructions.h>
|
||
#include <pfcInterference.h>
|
||
#include <pfcSelect.h>
|
||
#include <pfcGeometry.h>
|
||
#include <pfcAssembly.h>
|
||
#include <string>
|
||
#include <vector>
|
||
#include <map>
|
||
#include <set>
|
||
#include <unordered_set>
|
||
#include <unordered_map>
|
||
#include <algorithm>
|
||
#include <cmath>
|
||
#include <utility>
|
||
#include <limits>
|
||
|
||
// Prevent Windows macro conflicts with std::min/max
|
||
#ifdef max
|
||
#undef max
|
||
#endif
|
||
#ifdef min
|
||
#undef min
|
||
#endif
|
||
|
||
// Creo状态信息结构
|
||
struct CreoStatus {
|
||
bool is_connected = false;
|
||
std::string version;
|
||
std::string build;
|
||
std::string working_directory;
|
||
int session_id = 0;
|
||
};
|
||
|
||
// 模型状态信息结构
|
||
struct ModelStatus {
|
||
bool has_model = false;
|
||
std::string name;
|
||
std::string filename;
|
||
std::string type;
|
||
std::string software; // 从OTK API获取,不设默认值
|
||
std::string version; // 从OTK API获取,不设默认值
|
||
std::string connection_time;
|
||
bool is_assembly = false;
|
||
int total_parts = 0;
|
||
int assembly_levels = 0;
|
||
std::string file_size;
|
||
std::string connection_status;
|
||
std::string open_time;
|
||
bool is_modified = false; // 模型是否已修改(未保存)
|
||
};
|
||
|
||
// 导出结果结构
|
||
struct ExportResult {
|
||
bool success = false;
|
||
std::string export_path;
|
||
std::string file_size;
|
||
std::string format;
|
||
std::string export_time;
|
||
std::string software;
|
||
std::string original_file;
|
||
std::string dirname;
|
||
std::string filename;
|
||
std::string error_message;
|
||
};
|
||
|
||
// 保存结果结构
|
||
struct SaveResult {
|
||
bool success = false;
|
||
std::string file_size;
|
||
std::string save_time;
|
||
std::string software;
|
||
std::string original_file;
|
||
std::string error_message;
|
||
};
|
||
|
||
// 关闭结果结构
|
||
struct CloseResult {
|
||
bool success = false;
|
||
std::string model_name;
|
||
bool was_modified = false;
|
||
std::string close_time;
|
||
std::string error_message;
|
||
};
|
||
|
||
// 打开结果结构
|
||
struct OpenResult {
|
||
bool success = false;
|
||
std::string model_name;
|
||
std::string model_type;
|
||
std::string file_path;
|
||
std::string file_size;
|
||
std::string open_time;
|
||
bool is_assembly = false;
|
||
int total_parts = 0;
|
||
bool model_in_session = false; // 验证模型是否真的在会话中
|
||
bool window_model_match = false; // 验证窗口是否正确关联模型
|
||
std::string error_message;
|
||
};
|
||
|
||
// 层级分析组件信息结构
|
||
struct ComponentInfo {
|
||
std::string id;
|
||
std::string name;
|
||
std::string type; // "assembly" 或 "part"
|
||
int level;
|
||
int children_count;
|
||
std::string path;
|
||
std::string file_size;
|
||
std::string deletion_safety; // "forbidden", "risky", "moderate"
|
||
bool is_visible;
|
||
std::string model_type; // "MDL_ASSEMBLY", "MDL_PART", "MDL_DRAWING" 等
|
||
std::string full_path; // 完整的组件路径
|
||
};
|
||
|
||
// 层级分析请求结构
|
||
struct HierarchyAnalysisRequest {
|
||
std::string software_type;
|
||
std::string project_name;
|
||
int max_depth;
|
||
bool include_geometry;
|
||
int target_level = -1; // 新增:指定返回的层级,-1表示返回所有
|
||
std::map<std::string, std::string> analysis_options;
|
||
};
|
||
|
||
// 删除建议结构
|
||
struct DeletionRecommendation {
|
||
std::string component_id;
|
||
std::string component_name;
|
||
int level;
|
||
std::string reason;
|
||
std::vector<std::string> risk_factors;
|
||
double confidence;
|
||
};
|
||
|
||
// 层级分析结果结构
|
||
struct HierarchyAnalysisResult {
|
||
bool success = false;
|
||
std::string message;
|
||
std::string project_name;
|
||
int total_levels;
|
||
int total_components;
|
||
std::vector<std::vector<ComponentInfo>> hierarchy;
|
||
std::vector<DeletionRecommendation> safe_deletions;
|
||
std::vector<DeletionRecommendation> risky_deletions;
|
||
std::string error_message;
|
||
};
|
||
|
||
// Shell Analysis Constants
|
||
namespace ShellAnalysisConstants {
|
||
// Confidence thresholds
|
||
constexpr double CONFIDENCE_STANDARD_PART = 0.99;
|
||
constexpr double CONFIDENCE_INTERNAL_CUT = 0.95;
|
||
constexpr double CONFIDENCE_INTERNAL_HOLE = 0.90;
|
||
constexpr double CONFIDENCE_INTERNAL_GENERIC = 0.80;
|
||
constexpr double CONFIDENCE_SHELL_FEATURE = 0.20;
|
||
constexpr double CONFIDENCE_EXTERNAL_FEATURE = 0.05;
|
||
|
||
// Performance thresholds
|
||
constexpr int LARGE_MODEL_THRESHOLD = 500;
|
||
constexpr int VERY_LARGE_MODEL_THRESHOLD = 1000;
|
||
constexpr double SAMPLING_RATIO_FAST = 0.1;
|
||
constexpr double SAMPLING_RATIO_STANDARD = 0.33;
|
||
|
||
// Geometry calculation weights
|
||
constexpr double VOLUME_WEIGHT = 0.5;
|
||
constexpr double SURFACE_WEIGHT = 0.3;
|
||
constexpr double BBOX_WEIGHT = 0.2;
|
||
|
||
// Batch processing
|
||
constexpr int BATCH_SIZE = 50;
|
||
constexpr int MAX_PARALLEL_FEATURES = 100;
|
||
|
||
// Cache settings
|
||
constexpr int CACHE_TTL_SECONDS = 600; // 10 minutes
|
||
constexpr int MAX_CACHE_SIZE = 1000;
|
||
}
|
||
|
||
// Analysis modes for large models
|
||
enum ShellAnalysisMode {
|
||
SHELL_ANALYSIS_FAST = 0, // Fast mode: sampling analysis
|
||
SHELL_ANALYSIS_STANDARD = 1, // Standard mode: normal analysis
|
||
SHELL_ANALYSIS_DETAILED = 2 // Detailed mode: full analysis
|
||
};
|
||
|
||
// Creo管理器类
|
||
class CreoManager {
|
||
public:
|
||
// 会话管理(避免重复代码)
|
||
struct SessionInfo {
|
||
pfcSession_ptr session;
|
||
wfcWSession_ptr wSession;
|
||
bool is_valid;
|
||
std::string version;
|
||
std::string build;
|
||
};
|
||
|
||
static CreoManager& Instance();
|
||
|
||
// 状态检测
|
||
CreoStatus GetCreoStatus();
|
||
ModelStatus GetModelStatus();
|
||
|
||
// 基础操作
|
||
bool ShowMessage(const std::string& message);
|
||
|
||
// 导出功能
|
||
ExportResult ExportModelToSTEP(const std::string& export_path, const std::string& geom_flags = "solids");
|
||
|
||
// 保存功能
|
||
SaveResult SaveModel();
|
||
|
||
// 关闭功能
|
||
CloseResult CloseModel(bool force_close = false);
|
||
|
||
// 打开功能
|
||
OpenResult OpenModel(const std::string& file_path, const std::string& open_mode = "active");
|
||
|
||
// 层级分析功能
|
||
HierarchyAnalysisResult AnalyzeModelHierarchy(const HierarchyAnalysisRequest& request);
|
||
|
||
// 层级删除功能
|
||
struct HierarchyDeleteResult {
|
||
bool success = false;
|
||
std::string message;
|
||
int original_levels;
|
||
int target_level;
|
||
int final_levels;
|
||
std::map<int, std::vector<std::string>> deleted_components;
|
||
int total_deleted;
|
||
int successful;
|
||
int failed;
|
||
std::string error_message;
|
||
};
|
||
|
||
HierarchyDeleteResult DeleteHierarchyComponents(const std::string& project_name, int target_level);
|
||
|
||
// 子装配体层级删除功能 - 以指定子装配体为顶层进行层级删除
|
||
HierarchyDeleteResult DeleteSubassemblyHierarchyComponents(const std::string& subassembly_path, int target_level);
|
||
|
||
// 薄壳化分析功能
|
||
struct ShellAnalysisRequest {
|
||
std::string software_type;
|
||
std::string project_name = "current_model";
|
||
std::string analysis_type = "surface_shell";
|
||
bool preserve_external_surfaces = true;
|
||
double min_wall_thickness = 1.0;
|
||
double confidence_threshold = 0.75;
|
||
};
|
||
|
||
struct FeatureDeletion {
|
||
int id;
|
||
std::string name;
|
||
std::string type;
|
||
std::string reason;
|
||
double confidence;
|
||
double volume_reduction; // Changed from int to double for better precision
|
||
std::string part_file;
|
||
std::string part_path;
|
||
std::string component_type = "FEATURE";
|
||
std::string volume_units = "percentage"; // Added to clarify units
|
||
int vote_count = 0; // Number of votes this component received in multi-directional projection analysis
|
||
};
|
||
|
||
struct EstimatedReduction {
|
||
std::string volume_reduction;
|
||
std::string file_size_reduction;
|
||
std::string performance_improvement;
|
||
};
|
||
|
||
struct HierarchyAnalysisInfo {
|
||
bool enabled = true;
|
||
int total_parts = 0;
|
||
int outer_parts = 0;
|
||
int internal_parts = 0;
|
||
int containment_relationships = 0;
|
||
std::map<std::string, std::string> performance_stats;
|
||
};
|
||
|
||
struct ShellAnalysisParameters {
|
||
bool preserve_external_surfaces = true;
|
||
double min_wall_thickness = 1.0;
|
||
double confidence_threshold = 0.7;
|
||
int total_features = 0;
|
||
int deletable_features = 0;
|
||
int preserved_features = 0;
|
||
bool assembly_analysis = false;
|
||
std::string analysis_strategy = "multi_directional_projection_analysis";
|
||
int surface_count = 0;
|
||
int shell_surfaces = 0;
|
||
int internal_surfaces = 0;
|
||
int shell_feature_whitelist = 0;
|
||
HierarchyAnalysisInfo hierarchy_analysis;
|
||
};
|
||
|
||
// New structure for enhanced shell analysis
|
||
struct ShellAnalysisItem {
|
||
std::string name;
|
||
std::string type;
|
||
int feature_id;
|
||
double confidence;
|
||
std::string recommendation;
|
||
std::string reason;
|
||
std::string path; // 组件完整路径
|
||
bool is_deletable = false;
|
||
double volume_percentage = 0.0; // Component volume percentage
|
||
};
|
||
|
||
struct ShellAnalysisResult {
|
||
bool success = false;
|
||
std::vector<FeatureDeletion> safe_deletions;
|
||
std::vector<FeatureDeletion> suggested_deletions;
|
||
std::vector<FeatureDeletion> preserve_list;
|
||
EstimatedReduction estimated_reduction;
|
||
ShellAnalysisParameters analysis_parameters;
|
||
std::string error_message;
|
||
|
||
// Enhanced fields for new algorithm
|
||
std::vector<ShellAnalysisItem> features;
|
||
int total_features_analyzed = 0;
|
||
int shell_features_count = 0;
|
||
int internal_features_count = 0;
|
||
int total_deletable = 0;
|
||
double deletion_percentage = 0.0;
|
||
std::string model_name;
|
||
|
||
// OBB optimization statistics
|
||
int obb_usage_count = 0;
|
||
int aabb_usage_count = 0;
|
||
double obb_usage_percentage = 0.0;
|
||
};
|
||
|
||
ShellAnalysisResult AnalyzeShellFeatures(const ShellAnalysisRequest& request);
|
||
ShellAnalysisResult AnalyzeShellFeaturesEnhanced(const ShellAnalysisRequest& request);
|
||
|
||
// 薄壳化分析辅助方法
|
||
std::string GetFeatureTypeName(pfcFeatureType feat_type);
|
||
bool IsExternalSurface(pfcFeature_ptr feature, bool preserve_external);
|
||
bool CheckWallThickness(pfcFeature_ptr feature, double min_thickness);
|
||
|
||
// 薄壳化算法辅助方法(基于真实OTK数据)
|
||
bool IsStandardPart(const std::string& part_name);
|
||
bool IsInternalPart(pfcFeature_ptr feature, pfcModel_ptr model);
|
||
|
||
// 智能边界检测算法
|
||
bool IsOnAssemblyBoundary(pfcFeature_ptr component, pfcOutline3D_ptr assembly_bbox, double tolerance);
|
||
double CalculateBoundaryOverlap(pfcOutline3D_ptr comp_bbox, pfcOutline3D_ptr assembly_bbox, double tolerance);
|
||
double GetOptimalTolerance(pfcOutline3D_ptr assembly_bbox);
|
||
|
||
// 真实几何计算方法
|
||
double CalculateFeatureVolumeImpact(pfcFeature_ptr feature, pfcSolid_ptr solid);
|
||
std::vector<double> CalculateBatchFeatureVolumeImpacts(
|
||
const std::vector<pfcFeature_ptr>& features,
|
||
pfcSolid_ptr solid,
|
||
int batch_size = 10);
|
||
|
||
// LOO (Leave-One-Out) attribution for accurate feature impact calculation
|
||
std::vector<double> CalculateLOOAttribution(
|
||
const std::vector<pfcFeature_ptr>& features,
|
||
pfcSolid_ptr solid,
|
||
int top_k = 10);
|
||
|
||
// Check feature proximity to external surface
|
||
double CheckFeatureProximityToSurface(
|
||
pfcFeature_ptr feature,
|
||
pfcSolid_ptr solid,
|
||
double min_thickness);
|
||
|
||
// Enhanced geometric boundary detection functions
|
||
bool AnalyzeFeatureGeometryEnhanced(
|
||
pfcFeature_ptr feature,
|
||
pfcSolid_ptr solid,
|
||
pfcOutline3D_ptr globalOutline,
|
||
double tolerance);
|
||
|
||
std::vector<pfcSurface_ptr> GetFeatureAffectedSurfaces(pfcFeature_ptr feature, pfcSolid_ptr solid);
|
||
bool IsSurfaceOnBoundary(pfcSurface_ptr surface, pfcSolid_ptr solid, pfcOutline3D_ptr globalOutline, double tolerance);
|
||
bool CheckSurfaceBoundaryByBounds(pfcSurface_ptr surface, pfcOutline3D_ptr globalOutline, double tolerance);
|
||
double CalculateDistanceToExternalSurface(pfcFeature_ptr feature, pfcSolid_ptr solid);
|
||
|
||
// Enhanced assembly component occlusion analysis
|
||
bool IsComponentOccludedByOthers(
|
||
pfcFeature_ptr component,
|
||
pfcOutline3D_ptr assembly_bbox,
|
||
const std::vector<std::pair<pfcFeature_ptr, std::string>>& all_components,
|
||
double tolerance);
|
||
double CalculateOcclusionRatio(pfcFeature_ptr target, pfcFeature_ptr occluder);
|
||
bool HasInterferenceWith(pfcFeature_ptr comp1, pfcFeature_ptr comp2);
|
||
|
||
// Estimate feature scale based on type and parameters
|
||
double EstimateFeatureScale(pfcFeature_ptr feature);
|
||
|
||
// 大模型优化方法
|
||
ShellAnalysisMode DetermineAnalysisMode(int feature_count);
|
||
std::vector<int> GetSamplingIndices(int total_features, ShellAnalysisMode mode);
|
||
|
||
// 优化的估算方法
|
||
EstimatedReduction CalculateRealisticReduction(
|
||
const std::vector<FeatureDeletion>& deletions,
|
||
double original_volume,
|
||
int total_features);
|
||
|
||
// 辅助功能
|
||
std::string GetCurrentTimeString();
|
||
std::string GetCurrentTimeStringISO();
|
||
std::string GetFileSize(const std::string& filepath);
|
||
int SafeCalculateAssemblyLevels(wfcWAssembly_ptr assembly);
|
||
|
||
// 会话信息获取
|
||
SessionInfo GetSessionInfo();
|
||
|
||
// Volume calculation
|
||
double GetComponentVolume(pfcSolid_ptr solid);
|
||
|
||
// 字符串转换辅助函数
|
||
std::string XStringToString(const xstring& xstr);
|
||
|
||
// 组件路径构建函数
|
||
std::string BuildComponentFullPath(wfcWComponentPath_ptr componentPath, const std::string& assemblyName);
|
||
|
||
// 文件大小统计
|
||
std::string GetModelFileSize(pfcModel_ptr model);
|
||
std::string CalculateAssemblyTotalSize(pfcModel_ptr model);
|
||
double ParseMBFromSizeString(const std::string& size_str);
|
||
|
||
// Component analysis helper methods
|
||
std::string EvaluateDeletionSafety(const ComponentInfo& component);
|
||
std::string GetComponentFileSize(wfcWComponentPath_ptr component_path);
|
||
std::string GetModelTypeString(pfcModelType model_type);
|
||
int CountChildComponents(wfcWComponentPath_ptr component_path);
|
||
|
||
private:
|
||
CreoManager(); // 需要自定义构造函数来设置配置
|
||
~CreoManager() = default;
|
||
CreoManager(const CreoManager&) = delete;
|
||
CreoManager& operator=(const CreoManager&) = delete;
|
||
|
||
// 辅助函数
|
||
xstring StringToXString(const std::string& str);
|
||
std::pair<std::string, std::string> ParseFilePath(const std::string& file_path);
|
||
|
||
// 层级分析私有方法 (新SOTA算法)
|
||
void AnalyzeAssemblyNode(wfcWAssembly_ptr assembly,
|
||
int level,
|
||
const std::string& parentName,
|
||
const std::string& currentPath,
|
||
HierarchyAnalysisResult& result,
|
||
int target_level = -1); // 新增参数
|
||
|
||
ComponentInfo CreateComponentFromFeature(pfcComponentFeat_ptr compFeat,
|
||
int level,
|
||
const std::string& parentName,
|
||
const std::string& currentPath,
|
||
pfcModel_ptr preloadedModel = nullptr);
|
||
|
||
pfcModel_ptr LoadComponentModel(pfcComponentFeat_ptr compFeat);
|
||
|
||
|
||
// 薄壳化分析递归方法
|
||
void CollectAllComponentsForShellAnalysis(wfcWAssembly_ptr assembly,
|
||
const std::string& parentPath,
|
||
std::vector<std::pair<pfcFeature_ptr, std::string>>& allComponents);
|
||
|
||
// Shell Analysis Cache
|
||
class ShellAnalysisCache {
|
||
private:
|
||
struct FeatureCacheEntry {
|
||
double volume_impact;
|
||
double surface_impact;
|
||
double confidence;
|
||
std::time_t timestamp;
|
||
std::string model_version; // Added for cache validation
|
||
int64_t model_modified_time; // Added for cache validation
|
||
};
|
||
|
||
std::map<std::string, FeatureCacheEntry> cache;
|
||
|
||
public:
|
||
bool GetCachedImpact(const std::string& key, double& volume, double& surface,
|
||
const std::string& current_version = "",
|
||
int64_t current_modified_time = 0) {
|
||
auto it = cache.find(key);
|
||
if (it != cache.end()) {
|
||
// Check both TTL and model state consistency
|
||
bool ttl_valid = (std::time(nullptr) - it->second.timestamp < ShellAnalysisConstants::CACHE_TTL_SECONDS);
|
||
bool version_valid = (current_version.empty() || it->second.model_version == current_version);
|
||
bool time_valid = (current_modified_time == 0 || it->second.model_modified_time == current_modified_time);
|
||
|
||
if (ttl_valid && version_valid && time_valid) {
|
||
volume = it->second.volume_impact;
|
||
surface = it->second.surface_impact;
|
||
return true;
|
||
}
|
||
// Invalid cache entry, remove it
|
||
cache.erase(it);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void SetCachedImpact(const std::string& key, double volume, double surface,
|
||
const std::string& model_version = "",
|
||
int64_t model_modified_time = 0) {
|
||
FeatureCacheEntry entry;
|
||
entry.volume_impact = volume;
|
||
entry.surface_impact = surface;
|
||
entry.timestamp = std::time(nullptr);
|
||
entry.model_version = model_version;
|
||
entry.model_modified_time = model_modified_time;
|
||
cache[key] = entry;
|
||
|
||
// Limit cache size
|
||
if (cache.size() > ShellAnalysisConstants::MAX_CACHE_SIZE) {
|
||
auto oldest = cache.begin();
|
||
for (auto it = cache.begin(); it != cache.end(); ++it) {
|
||
if (it->second.timestamp < oldest->second.timestamp) {
|
||
oldest = it;
|
||
}
|
||
}
|
||
cache.erase(oldest);
|
||
}
|
||
}
|
||
|
||
void ClearCache() {
|
||
cache.clear();
|
||
}
|
||
};
|
||
|
||
// Cache instance
|
||
ShellAnalysisCache shell_analysis_cache;
|
||
|
||
// 真实几何分析方法
|
||
bool AnalyzeFeatureGeometry(pfcFeature_ptr feature, pfcOutline3D_ptr globalOutline, double tolerance);
|
||
|
||
// Multi-directional extreme value projection algorithm structures and functions
|
||
struct Vector3D {
|
||
double x, y, z;
|
||
Vector3D() : x(0), y(0), z(0) {}
|
||
Vector3D(double x_, double y_, double z_) : x(x_), y(y_), z(z_) {}
|
||
|
||
Vector3D operator-(const Vector3D& other) const {
|
||
return Vector3D(x - other.x, y - other.y, z - other.z);
|
||
}
|
||
|
||
Vector3D operator+(const Vector3D& other) const {
|
||
return Vector3D(x + other.x, y + other.y, z + other.z);
|
||
}
|
||
|
||
Vector3D operator*(double scalar) const {
|
||
return Vector3D(x * scalar, y * scalar, z * scalar);
|
||
}
|
||
|
||
double dot(const Vector3D& other) const {
|
||
return x * other.x + y * other.y + z * other.z;
|
||
}
|
||
|
||
Vector3D cross(const Vector3D& other) const {
|
||
return Vector3D(
|
||
y * other.z - z * other.y,
|
||
z * other.x - x * other.z,
|
||
x * other.y - y * other.x
|
||
);
|
||
}
|
||
|
||
double length() const {
|
||
return sqrt(x * x + y * y + z * z);
|
||
}
|
||
|
||
Vector3D normalize() const {
|
||
double len = length();
|
||
if (len > 1e-10) {
|
||
return Vector3D(x / len, y / len, z / len);
|
||
}
|
||
return Vector3D(0, 0, 1);
|
||
}
|
||
};
|
||
|
||
struct AABB {
|
||
Vector3D minPoint, maxPoint;
|
||
|
||
AABB() : minPoint(1e9, 1e9, 1e9), maxPoint(-1e9, -1e9, -1e9) {}
|
||
|
||
AABB(const Vector3D& min_pt, const Vector3D& max_pt)
|
||
: minPoint(min_pt), maxPoint(max_pt) {}
|
||
|
||
void expand(const Vector3D& point) {
|
||
if (point.x < minPoint.x) minPoint.x = point.x;
|
||
if (point.y < minPoint.y) minPoint.y = point.y;
|
||
if (point.z < minPoint.z) minPoint.z = point.z;
|
||
if (point.x > maxPoint.x) maxPoint.x = point.x;
|
||
if (point.y > maxPoint.y) maxPoint.y = point.y;
|
||
if (point.z > maxPoint.z) maxPoint.z = point.z;
|
||
}
|
||
|
||
Vector3D diagonal() const {
|
||
return maxPoint - minPoint;
|
||
}
|
||
|
||
double getDiagonalLength() const {
|
||
return diagonal().length();
|
||
}
|
||
|
||
// Get all 8 corners of AABB
|
||
std::vector<Vector3D> getCorners() const {
|
||
std::vector<Vector3D> corners;
|
||
corners.reserve(8);
|
||
for (int i = 0; i < 8; i++) {
|
||
corners.push_back(Vector3D(
|
||
(i & 1) ? maxPoint.x : minPoint.x,
|
||
(i & 2) ? maxPoint.y : minPoint.y,
|
||
(i & 4) ? maxPoint.z : minPoint.z
|
||
));
|
||
}
|
||
return corners;
|
||
}
|
||
};
|
||
|
||
// Forward declarations
|
||
struct OBB;
|
||
|
||
// 2D Screen Grid for visibility calculation
|
||
struct ScreenGrid {
|
||
struct Cell {
|
||
double depth = std::numeric_limits<double>::max();
|
||
int componentId = -1;
|
||
};
|
||
|
||
Vector3D origin; // Grid origin point
|
||
Vector3D uAxis, vAxis; // Orthonormal basis vectors
|
||
double width, height; // Grid dimensions
|
||
int gridSizeU, gridSizeV;
|
||
std::vector<std::vector<Cell>> cells;
|
||
|
||
ScreenGrid() : gridSizeU(0), gridSizeV(0), width(0), height(0) {}
|
||
|
||
ScreenGrid(const AABB& sceneBounds, const Vector3D& viewDir, int gridSize = 96) {
|
||
gridSizeU = gridSize;
|
||
gridSizeV = gridSize;
|
||
|
||
// Build orthonormal basis
|
||
if (std::abs(viewDir.x) > 0.9) {
|
||
uAxis = Vector3D(0, 1, 0).cross(viewDir).normalize();
|
||
} else {
|
||
uAxis = Vector3D(1, 0, 0).cross(viewDir).normalize();
|
||
}
|
||
vAxis = viewDir.cross(uAxis).normalize();
|
||
|
||
// Calculate grid bounds
|
||
std::vector<Vector3D> corners = sceneBounds.getCorners();
|
||
double minU = std::numeric_limits<double>::max();
|
||
double maxU = std::numeric_limits<double>::lowest();
|
||
double minV = std::numeric_limits<double>::max();
|
||
double maxV = std::numeric_limits<double>::lowest();
|
||
|
||
for (const auto& corner : corners) {
|
||
double u = corner.dot(uAxis);
|
||
double v = corner.dot(vAxis);
|
||
minU = std::min(minU, u);
|
||
maxU = std::max(maxU, u);
|
||
minV = std::min(minV, v);
|
||
maxV = std::max(maxV, v);
|
||
}
|
||
|
||
// Set origin and dimensions
|
||
origin = Vector3D(minU, minV, 0);
|
||
width = maxU - minU;
|
||
height = maxV - minV;
|
||
|
||
// Initialize cells
|
||
cells.resize(gridSizeU, std::vector<Cell>(gridSizeV));
|
||
}
|
||
|
||
void updateCell(int u, int v, double depth, int id) {
|
||
if (u >= 0 && u < gridSizeU && v >= 0 && v < gridSizeV) {
|
||
if (depth < cells[u][v].depth) {
|
||
cells[u][v].depth = depth;
|
||
cells[u][v].componentId = id;
|
||
}
|
||
}
|
||
}
|
||
|
||
std::pair<int, int> worldToGrid(const Vector3D& point) const {
|
||
double u = point.dot(uAxis) - origin.x;
|
||
double v = point.dot(vAxis) - origin.y;
|
||
int gridU = static_cast<int>((u / width) * gridSizeU);
|
||
int gridV = static_cast<int>((v / height) * gridSizeV);
|
||
return {gridU, gridV};
|
||
}
|
||
|
||
void projectAABB(const AABB& box, const Vector3D& viewDir, double minDepth, int componentId) {
|
||
std::vector<Vector3D> corners = box.getCorners();
|
||
|
||
// Find UV bounds of projection
|
||
int minGridU = gridSizeU, maxGridU = -1;
|
||
int minGridV = gridSizeV, maxGridV = -1;
|
||
|
||
for (const auto& corner : corners) {
|
||
std::pair<int, int> gridPos = worldToGrid(corner);
|
||
int u = gridPos.first;
|
||
int v = gridPos.second;
|
||
minGridU = std::min(minGridU, std::max(0, u));
|
||
maxGridU = std::max(maxGridU, std::min(gridSizeU - 1, u));
|
||
minGridV = std::min(minGridV, std::max(0, v));
|
||
maxGridV = std::max(maxGridV, std::min(gridSizeV - 1, v));
|
||
}
|
||
|
||
// Update cells in bounding rectangle
|
||
for (int u = minGridU; u <= maxGridU; u++) {
|
||
for (int v = minGridV; v <= maxGridV; v++) {
|
||
updateCell(u, v, minDepth, componentId);
|
||
}
|
||
}
|
||
}
|
||
|
||
void projectOBB(const OBB& box, const Vector3D& viewDir, double minDepth, int componentId) {
|
||
std::vector<Vector3D> corners = box.getCorners();
|
||
|
||
// Find UV bounds of projection
|
||
int minGridU = gridSizeU, maxGridU = -1;
|
||
int minGridV = gridSizeV, maxGridV = -1;
|
||
|
||
for (const auto& corner : corners) {
|
||
std::pair<int, int> gridPos = worldToGrid(corner);
|
||
int u = gridPos.first;
|
||
int v = gridPos.second;
|
||
minGridU = std::min(minGridU, std::max(0, u));
|
||
maxGridU = std::max(maxGridU, std::min(gridSizeU - 1, u));
|
||
minGridV = std::min(minGridV, std::max(0, v));
|
||
maxGridV = std::max(maxGridV, std::min(gridSizeV - 1, v));
|
||
}
|
||
|
||
// Update cells in bounding rectangle
|
||
for (int u = minGridU; u <= maxGridU; u++) {
|
||
for (int v = minGridV; v <= maxGridV; v++) {
|
||
updateCell(u, v, minDepth, componentId);
|
||
}
|
||
}
|
||
}
|
||
|
||
std::unordered_map<int, int> getVisibilityCount() const {
|
||
std::unordered_map<int, int> counts;
|
||
for (const auto& row : cells) {
|
||
for (const auto& cell : row) {
|
||
if (cell.componentId >= 0) {
|
||
counts[cell.componentId]++;
|
||
}
|
||
}
|
||
}
|
||
return counts;
|
||
}
|
||
};
|
||
|
||
// Oriented Bounding Box for enhanced precision
|
||
struct OBB {
|
||
Vector3D center; // Center point in world space
|
||
Vector3D halfExtents; // Half extents in local space
|
||
Vector3D axes[3]; // Three orthogonal axes in world space
|
||
|
||
OBB() : center(0, 0, 0), halfExtents(0, 0, 0) {
|
||
axes[0] = Vector3D(1, 0, 0);
|
||
axes[1] = Vector3D(0, 1, 0);
|
||
axes[2] = Vector3D(0, 0, 1);
|
||
}
|
||
|
||
// Calculate support point projection for OBB in given direction
|
||
double getSupport(const Vector3D& direction) const {
|
||
// Transform world direction to OBB local space
|
||
Vector3D localDir(
|
||
direction.dot(axes[0]),
|
||
direction.dot(axes[1]),
|
||
direction.dot(axes[2])
|
||
);
|
||
|
||
// Calculate local space support point
|
||
Vector3D support;
|
||
support.x = (localDir.x >= 0) ? halfExtents.x : -halfExtents.x;
|
||
support.y = (localDir.y >= 0) ? halfExtents.y : -halfExtents.y;
|
||
support.z = (localDir.z >= 0) ? halfExtents.z : -halfExtents.z;
|
||
|
||
// Transform back to world space and calculate projection
|
||
Vector3D worldSupport = center +
|
||
axes[0] * support.x +
|
||
axes[1] * support.y +
|
||
axes[2] * support.z;
|
||
|
||
return worldSupport.dot(direction);
|
||
}
|
||
|
||
// Get all 8 corners of the OBB
|
||
std::vector<Vector3D> getCorners() const {
|
||
std::vector<Vector3D> corners;
|
||
corners.reserve(8);
|
||
|
||
for (int i = 0; i < 8; i++) {
|
||
Vector3D corner = center;
|
||
corner = corner + axes[0] * ((i & 1) ? halfExtents.x : -halfExtents.x);
|
||
corner = corner + axes[1] * ((i & 2) ? halfExtents.y : -halfExtents.y);
|
||
corner = corner + axes[2] * ((i & 4) ? halfExtents.z : -halfExtents.z);
|
||
corners.push_back(corner);
|
||
}
|
||
|
||
return corners;
|
||
}
|
||
};
|
||
|
||
struct ComponentItem {
|
||
pfcComponentFeat_ptr component;
|
||
pfcSolid_ptr solid;
|
||
pfcComponentPath_ptr path;
|
||
AABB worldAABB; // Fast rough filtering
|
||
OBB worldOBB; // Precise oriented bounding box
|
||
int featureId;
|
||
std::string name;
|
||
};
|
||
|
||
// Projection analysis result data structure
|
||
struct ProjectionAnalysisData {
|
||
std::unordered_map<int, int> visibilityVotes; // component ID -> visibility count
|
||
std::unordered_set<int> outerComponentIds; // outer component IDs
|
||
std::vector<ComponentItem> components; // all components with AABB data
|
||
AABB globalAABB; // global assembly bounding box
|
||
std::unordered_map<int, double> visibilityRatios; // component ID -> continuous visibility ratio
|
||
|
||
// OBB optimization statistics
|
||
int obb_usage_count = 0;
|
||
int aabb_usage_count = 0;
|
||
double obb_usage_percentage = 0.0;
|
||
};
|
||
|
||
// Component classification based on naming patterns and geometry
|
||
class ComponentClassifier {
|
||
public:
|
||
// Structure for specific internal models
|
||
struct SpecificInternalModel {
|
||
std::string name; // Model name (e.g., "12v4000g03_herhang.prt")
|
||
std::string pathSegment; // Path segment for validation (optional)
|
||
};
|
||
|
||
// Check if component name indicates a fastener
|
||
static bool IsFastener(const std::string& name);
|
||
|
||
// Check if component name indicates internal structure
|
||
static bool IsInternalStructure(const std::string& name);
|
||
|
||
// Check if component name indicates external shell
|
||
static bool IsExternalShell(const std::string& name);
|
||
|
||
// Check if component is likely internal based on geometry
|
||
static bool IsLikelyInternal(const AABB& compAABB, const AABB& globalAABB);
|
||
|
||
// Check if component is elongated/thin (benefits from OBB)
|
||
static bool IsElongatedPart(const std::string& name);
|
||
|
||
// Check if component is a specific internal model
|
||
static bool IsSpecificInternalModel(const std::string& name, const std::string& path);
|
||
|
||
// List of specific models to be marked as internal
|
||
static std::vector<SpecificInternalModel> specificInternalModels;
|
||
|
||
private:
|
||
static bool MatchesPattern(const std::string& text, const std::vector<std::string>& patterns);
|
||
};
|
||
|
||
// Multi-directional projection algorithm functions
|
||
std::pair<double, double> CalculateAABBProjectionRange(const AABB& aabb, const Vector3D& direction);
|
||
std::pair<double, double> CalculateOBBProjectionRange(const OBB& obb, const Vector3D& direction);
|
||
ProjectionAnalysisData PerformMultiDirectionalProjectionAnalysis(pfcAssembly_ptr assembly);
|
||
std::vector<ComponentItem> CollectAllComponents(pfcAssembly_ptr assembly);
|
||
std::vector<Vector3D> SampleDirections(int count = 96);
|
||
AABB TransformAABB(const AABB& localAABB, pfcTransform3D_ptr transform);
|
||
pfcTransform3D_ptr GetComponentWorldTransform(pfcComponentPath_ptr path);
|
||
double CalculateProjectionSupport(const AABB& aabb, const Vector3D& direction);
|
||
Vector3D PfcPointToVector3D(pfcPoint3D_ptr point);
|
||
AABB PfcOutlineToAABB(pfcOutline3D_ptr outline);
|
||
|
||
// OBB-related functions for enhanced precision
|
||
OBB ExtractOBBFromTransform(const AABB& localAABB, pfcTransform3D_ptr transform);
|
||
OBB ComputePCABasedOBB(pfcSolid_ptr solid, pfcTransform3D_ptr transform);
|
||
std::vector<Vector3D> ExtractSolidVertices(pfcSolid_ptr solid, int maxVertices = 1000);
|
||
OBB ComputeOBBFromVertices(const std::vector<Vector3D>& vertices);
|
||
std::vector<Vector3D> ComputePrincipalAxes(const std::vector<Vector3D>& vertices);
|
||
double CalculateOBBProjectionSupport(const OBB& obb, const Vector3D& direction);
|
||
double CalculateOBBThickness(const OBB& obb, const Vector3D& direction);
|
||
bool ShouldUseOBB(const ComponentItem& comp);
|
||
|
||
// Ray-based verification for shell analysis
|
||
bool IsComponentBlockedFromCenter(const Vector3D& globalCenter,
|
||
const ComponentItem& component,
|
||
const std::vector<ComponentItem>& allComponents);
|
||
};
|