diff --git a/CreoManager.cpp b/CreoManager.cpp index 602f8ce..53e5af5 100644 --- a/CreoManager.cpp +++ b/CreoManager.cpp @@ -28,6 +28,13 @@ #endif #include #include + +// Shell Analysis Algorithm Constants +const int SHELL_ANALYSIS_NUM_DIRECTIONS = 96; +const double SHELL_ANALYSIS_MIN_VISIBILITY_RATIO = 0.05; +const int SHELL_ANALYSIS_MIN_VOTES = 2; +const double SHELL_ANALYSIS_HIGH_VISIBILITY_THRESHOLD = 0.25; +const double SHELL_ANALYSIS_MEDIUM_VISIBILITY_THRESHOLD = 0.08; #include #include #include @@ -1595,7 +1602,7 @@ CreoManager::ShellAnalysisResult CreoManager::AnalyzeShellFeaturesEnhanced(const const std::unordered_map& visibilityVotes = analysisData.visibilityVotes; const std::vector& allComponents = analysisData.components; const AABB& globalAABB = analysisData.globalAABB; - const int numDirections = 96; // Keep this for visibility ratio calculations + const int numDirections = SHELL_ANALYSIS_NUM_DIRECTIONS; // Build component AABB mapping for quick lookup std::unordered_map componentAABBs; @@ -1689,14 +1696,14 @@ CreoManager::ShellAnalysisResult CreoManager::AnalyzeShellFeaturesEnhanced(const } // Determine confidence based on visibility ratio - if (visibilityRatio >= 0.25) { + if (visibilityRatio >= SHELL_ANALYSIS_HIGH_VISIBILITY_THRESHOLD) { // Highly visible component - clearly on outer surface item.confidence = 0.1; // Very low deletion confidence item.recommendation = "KEEP"; item.reason = "Component highly visible from multiple directions (visibility: " + std::to_string((int)(visibilityRatio * 100)) + "%)"; result.shell_features_count++; - } else if (visibilityRatio >= 0.08) { + } else if (visibilityRatio >= SHELL_ANALYSIS_MEDIUM_VISIBILITY_THRESHOLD) { // Partially visible component - likely on outer surface or important structure item.confidence = 0.4; // Medium deletion confidence item.recommendation = "REVIEW"; @@ -2724,151 +2731,122 @@ std::vector CreoManager::CollectAllComponents(pfcAss return components; } +std::pair CreoManager::CalculateAABBProjectionRange(const AABB& aabb, const Vector3D& direction) { + Vector3D corners[8] = { + {aabb.minPoint.x, aabb.minPoint.y, aabb.minPoint.z}, + {aabb.maxPoint.x, aabb.minPoint.y, aabb.minPoint.z}, + {aabb.minPoint.x, aabb.maxPoint.y, aabb.minPoint.z}, + {aabb.maxPoint.x, aabb.maxPoint.y, aabb.minPoint.z}, + {aabb.minPoint.x, aabb.minPoint.y, aabb.maxPoint.z}, + {aabb.maxPoint.x, aabb.minPoint.y, aabb.maxPoint.z}, + {aabb.minPoint.x, aabb.maxPoint.y, aabb.maxPoint.z}, + {aabb.maxPoint.x, aabb.maxPoint.y, aabb.maxPoint.z} + }; + + double minProj = corners[0].dot(direction); + double maxProj = minProj; + + for (int i = 1; i < 8; i++) { + double proj = corners[i].dot(direction); + minProj = std::min(minProj, proj); + maxProj = std::max(maxProj, proj); + } + + return {minProj, maxProj}; +} + +std::pair CreoManager::CalculateOBBProjectionRange(const OBB& obb, const Vector3D& direction) { + std::vector corners = obb.getCorners(); + double minProj = corners[0].dot(direction); + double maxProj = minProj; + + for (size_t i = 1; i < corners.size(); i++) { + double proj = corners[i].dot(direction); + minProj = std::min(minProj, proj); + maxProj = std::max(maxProj, proj); + } + + return {minProj, maxProj}; +} + // Main multi-directional extreme value projection analysis with depth window and voting CreoManager::ProjectionAnalysisData CreoManager::PerformMultiDirectionalProjectionAnalysis(pfcAssembly_ptr assembly) { ProjectionAnalysisData result; - if (!assembly) return result; + // Step 1: Collect all components + result.components = CollectAllComponents(assembly); - try { - // Step 1: Collect all components - result.components = CollectAllComponents(assembly); - if (result.components.empty()) return result; + // Step 2: Calculate global assembly AABB + for (const ComponentItem& comp : result.components) { + result.globalAABB.expand(comp.worldAABB.minPoint); + result.globalAABB.expand(comp.worldAABB.maxPoint); + } - // Step 2: Calculate global assembly AABB + // Step 3: Sample directions (good coverage) + const int numDirections = SHELL_ANALYSIS_NUM_DIRECTIONS; + std::vector directions = SampleDirections(numDirections); + + // Voting map: component ID -> number of directions where it's visible + result.visibilityVotes.reserve(result.components.size()); + + + // Step 4: For each direction, determine visible components using occlusion detection + for (const Vector3D& direction : directions) { + // Structure to hold projection data + struct ProjectionData { + double minProj; // Minimum projection value + double maxProj; // Maximum projection value + int featureId; + }; + + std::vector projections; + projections.reserve(result.components.size()); + + // Calculate projection ranges for all components for (const ComponentItem& comp : result.components) { - result.globalAABB.expand(comp.worldAABB.minPoint); - result.globalAABB.expand(comp.worldAABB.maxPoint); + bool useOBB = ShouldUseOBB(comp); + double minProj, maxProj; + + if (useOBB) { + auto range = CalculateOBBProjectionRange(comp.worldOBB, direction); + minProj = range.first; + maxProj = range.second; + } else { + auto range = CalculateAABBProjectionRange(comp.worldAABB, direction); + minProj = range.first; + maxProj = range.second; + } + + projections.push_back({minProj, maxProj, comp.featureId}); } - // Step 3: Sample directions (96 directions for good coverage) - const int numDirections = 96; - std::vector directions = SampleDirections(numDirections); + // Sort by maximum projection value (farthest first) + std::sort(projections.begin(), projections.end(), + [](const ProjectionData& a, const ProjectionData& b) { + return a.maxProj > b.maxProj; + }); - // Voting map: component ID -> number of directions where it's visible - result.visibilityVotes.reserve(result.components.size()); + // Occlusion detection: only unoccluded components are visible + double occlusionBoundary = std::numeric_limits::max(); - // OBB usage statistics for performance analysis - int totalOBBUsage = 0; - int totalAABBUsage = 0; - - // Step 4: For each direction, determine visible components using depth window - for (const Vector3D& direction : directions) { - // Structure to hold projection data - struct ProjectionData { - double support; // Projection support value - double thickness; // Component thickness in this direction - int featureId; - }; - - std::vector projections; - projections.reserve(result.components.size()); - - // Calculate projections and thickness for all components - for (const ComponentItem& comp : result.components) { - // Smart selection: use OBB for elongated parts, AABB for regular parts - bool useOBB = ShouldUseOBB(comp); - double support; - - if (useOBB) { - support = CalculateOBBProjectionSupport(comp.worldOBB, direction); - totalOBBUsage++; - } else { - support = CalculateProjectionSupport(comp.worldAABB, direction); - totalAABBUsage++; - } - - // Calculate thickness consistently with support calculation - double thickness; - if (useOBB) { - thickness = CalculateOBBThickness(comp.worldOBB, direction); - } else { - // AABB thickness calculation (original method) - Vector3D minSupport, maxSupport; - minSupport.x = (direction.x < 0) ? comp.worldAABB.maxPoint.x : comp.worldAABB.minPoint.x; - minSupport.y = (direction.y < 0) ? comp.worldAABB.maxPoint.y : comp.worldAABB.minPoint.y; - minSupport.z = (direction.z < 0) ? comp.worldAABB.maxPoint.z : comp.worldAABB.minPoint.z; - maxSupport.x = (direction.x >= 0) ? comp.worldAABB.maxPoint.x : comp.worldAABB.minPoint.x; - maxSupport.y = (direction.y >= 0) ? comp.worldAABB.maxPoint.y : comp.worldAABB.minPoint.y; - maxSupport.z = (direction.z >= 0) ? comp.worldAABB.maxPoint.z : comp.worldAABB.minPoint.z; - thickness = std::abs((maxSupport - minSupport).dot(direction)); - } - - projections.push_back({support, thickness, comp.featureId}); - } - - // Sort by support value (highest first) - std::sort(projections.begin(), projections.end(), - [](const ProjectionData& a, const ProjectionData& b) { - return a.support > b.support; - }); - - if (projections.empty()) continue; - - double bestSupport = projections.front().support; - - // Calculate median thickness for adaptive window - std::vector thicknesses; - thicknesses.reserve(projections.size()); - for (const auto& p : projections) { - thicknesses.push_back(p.thickness); - } - std::nth_element(thicknesses.begin(), - thicknesses.begin() + thicknesses.size() / 2, - thicknesses.end()); - double medianThickness = thicknesses[thicknesses.size() / 2]; - - // Adaptive depth window (max of absolute and relative) - double assemblyDiagonal = result.globalAABB.getDiagonalLength(); - double absoluteWindow = std::max(1e-6, 0.002 * assemblyDiagonal); // 0.2% of diagonal - double relativeWindow = 0.15 * medianThickness; // 15% of median thickness - double depthWindow = std::max(absoluteWindow, relativeWindow); - - // Top-K fallback to ensure minimum visible components - int topK = std::min(12, std::max(3, (int)std::sqrt(result.components.size()))); - - // Mark visible components (within window OR in top-K) - int rank = 0; - for (const auto& proj : projections) { - if ((proj.support >= bestSupport - depthWindow) || (rank < topK)) { - result.visibilityVotes[proj.featureId]++; - rank++; - } else { - break; // Components further back are not visible - } + for (const auto& proj : projections) { + // If component's farthest point is in front of occlusion boundary, it's visible + if (proj.maxProj < occlusionBoundary) { + result.visibilityVotes[proj.featureId]++; + // Update occlusion boundary to this component's nearest point + occlusionBoundary = proj.minProj; } } + } - // Step 5: Determine outer components based on voting threshold - double minVisibilityRatio = 0.08; // At least 8% of directions - int minVotes = std::max(3, (int)(minVisibilityRatio * numDirections)); + // Step 5: Determine outer components based on voting threshold + double minVisibilityRatio = SHELL_ANALYSIS_MIN_VISIBILITY_RATIO; + int minVotes = std::max(SHELL_ANALYSIS_MIN_VOTES, (int)(minVisibilityRatio * numDirections)); - for (const auto& kvp : result.visibilityVotes) { - if (kvp.second >= minVotes) { - result.outerComponentIds.insert(kvp.first); - } + for (const auto& kvp : result.visibilityVotes) { + if (kvp.second >= minVotes) { + result.outerComponentIds.insert(kvp.first); } - - // Step 6: Apply safety check - ensure at least some components are marked as outer - if (result.outerComponentIds.empty() && !result.components.empty()) { - // Fallback: mark components on assembly boundary as outer - double assemblyDiagonal = result.globalAABB.getDiagonalLength(); - for (const ComponentItem& comp : result.components) { - // Check if component AABB touches assembly boundary - double boundaryTolerance = assemblyDiagonal * 0.005; // 0.5% tolerance for tighter boundary detection - if (abs(comp.worldAABB.minPoint.x - result.globalAABB.minPoint.x) <= boundaryTolerance || - abs(comp.worldAABB.maxPoint.x - result.globalAABB.maxPoint.x) <= boundaryTolerance || - abs(comp.worldAABB.minPoint.y - result.globalAABB.minPoint.y) <= boundaryTolerance || - abs(comp.worldAABB.maxPoint.y - result.globalAABB.maxPoint.y) <= boundaryTolerance || - abs(comp.worldAABB.minPoint.z - result.globalAABB.minPoint.z) <= boundaryTolerance || - abs(comp.worldAABB.maxPoint.z - result.globalAABB.maxPoint.z) <= boundaryTolerance) { - result.outerComponentIds.insert(comp.featureId); - } - } - } - - } catch (...) { - // Analysis failed, return empty set (conservative: don't delete anything) } return result; diff --git a/CreoManager.h b/CreoManager.h index 4c8a964..8a97f00 100644 --- a/CreoManager.h +++ b/CreoManager.h @@ -26,6 +26,7 @@ #include #include #include +#include // Creo状态信息结构 struct CreoStatus { @@ -631,6 +632,22 @@ private: return worldSupport.dot(direction); } + + // Get all 8 corners of the OBB + std::vector getCorners() const { + std::vector 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 { @@ -679,6 +696,8 @@ private: }; // Multi-directional projection algorithm functions + std::pair CalculateAABBProjectionRange(const AABB& aabb, const Vector3D& direction); + std::pair CalculateOBBProjectionRange(const OBB& obb, const Vector3D& direction); ProjectionAnalysisData PerformMultiDirectionalProjectionAnalysis(pfcAssembly_ptr assembly); std::vector CollectAllComponents(pfcAssembly_ptr assembly); std::vector SampleDirections(int count = 96);