From 9b2e60249c1ba5db43b29cf245b9557ec9b3c42e Mon Sep 17 00:00:00 2001 From: sladro Date: Sat, 20 Sep 2025 08:48:12 +0800 Subject: [PATCH] feat: enhance shell analysis with two-step verification and remove conflicting mechanisms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Implemented ray-based verification as second step after 2D projection - Each direction now votes 0 or 1 based on both visibility and ray test - Removed same-path component sharing mechanism for independent evaluation - Removed IsLikelyInternal geometric rules that conflicted with visibility detection - Components are now evaluated purely based on visual evidence This ensures more accurate shell detection by preventing: - 0-vote components being marked as shell due to path sharing - 6-vote visible components being marked as internal due to geometry 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- CreoManager.cpp | 149 +++++++++++++++++++++++++++++++++++++----------- CreoManager.h | 5 ++ 2 files changed, 122 insertions(+), 32 deletions(-) diff --git a/CreoManager.cpp b/CreoManager.cpp index cbf160f..d6150fa 100644 --- a/CreoManager.cpp +++ b/CreoManager.cpp @@ -1732,18 +1732,7 @@ CreoManager::ShellAnalysisResult CreoManager::AnalyzeShellFeaturesEnhanced(const bool is_outer_component = (outerComponentIds.find(item.feature_id) != outerComponentIds.end()); // Apply rule-based adjustments - // Get component's AABB for geometric rules - AABB compAABB; - auto aabbIter = componentAABBs.find(item.feature_id); - if (aabbIter != componentAABBs.end()) { - compAABB = aabbIter->second; - - // Check geometric rules - if (ComponentClassifier::IsLikelyInternal(compAABB, globalAABB)) { - item.confidence = std::min(0.95, item.confidence + 0.2); // Increase deletion confidence - item.reason += " [Geometry: Deep internal]"; - } - } + // Geometric rules removed - rely on visibility-based detection instead // Apply naming pattern rules - use unified component name std::string unifiedName = comp_name; @@ -2796,6 +2785,13 @@ CreoManager::ProjectionAnalysisData CreoManager::PerformMultiDirectionalProjecti result.globalAABB.expand(comp.worldAABB.maxPoint); } + // Calculate global center for ray verification + Vector3D globalCenter = Vector3D( + (result.globalAABB.minPoint.x + result.globalAABB.maxPoint.x) * 0.5, + (result.globalAABB.minPoint.y + result.globalAABB.maxPoint.y) * 0.5, + (result.globalAABB.minPoint.z + result.globalAABB.maxPoint.z) * 0.5 + ); + // Step 3: Sample directions (good coverage) const int numDirections = SHELL_ANALYSIS_NUM_DIRECTIONS; std::vector directions = SampleDirections(numDirections); @@ -2894,7 +2890,7 @@ CreoManager::ProjectionAnalysisData CreoManager::PerformMultiDirectionalProjecti auto visibilityCounts = grid.getVisibilityCount(); int totalCells = grid.gridSizeU * grid.gridSizeV; - // Update visibility votes based on grid visibility + // Update visibility votes based on grid visibility with ray verification for (const auto& kvp : visibilityCounts) { int componentId = kvp.first; int cellCount = kvp.second; @@ -2907,9 +2903,23 @@ CreoManager::ProjectionAnalysisData CreoManager::PerformMultiDirectionalProjecti result.visibilityRatios[componentId] = std::max(result.visibilityRatios[componentId], visibilityRatio); } - // Vote if component meets minimum visibility threshold for this direction + // Two-step verification: projection visibility + ray verification if (visibilityRatio >= SHELL_ANALYSIS_MIN_VISIBLE_RATIO_PER_DIR) { - result.visibilityVotes[componentId]++; + // Find the component for ray verification + const ComponentItem* targetComponent = nullptr; + for (const auto& comp : result.components) { + if (comp.featureId == componentId) { + targetComponent = ∁ + break; + } + } + + // Perform ray verification from global center + if (targetComponent && !IsComponentBlockedFromCenter(globalCenter, *targetComponent, result.components)) { + // Component passed both 2D projection and ray verification + result.visibilityVotes[componentId]++; + } + // If blocked by ray test, this direction votes 0 (no increment) // Debug output for specific component for (const auto& comp : result.components) { @@ -2991,23 +3001,7 @@ CreoManager::ProjectionAnalysisData CreoManager::PerformMultiDirectionalProjecti } } - // Step 6: Post-process - if any component with a path is visible, mark all same-path components as visible - std::set visiblePaths; - for (int visibleFeatureId : result.outerComponentIds) { - auto pathIter = featureIdToPath.find(visibleFeatureId); - if (pathIter != featureIdToPath.end()) { - visiblePaths.insert(pathIter->second); - } - } - - // Add all components with visible paths to outer component set - for (const auto& pathEntry : featureIdToPath) { - int featureId = pathEntry.first; - const std::string& path = pathEntry.second; - if (visiblePaths.find(path) != visiblePaths.end()) { - result.outerComponentIds.insert(featureId); - } - } + // Step 6 removed: Same-path sharing mechanism deleted to ensure independent component evaluation return result; } @@ -3486,6 +3480,97 @@ bool CreoManager::ShouldUseOBB(const ComponentItem& comp) { return aspectRatio > 2.5 || ComponentClassifier::IsElongatedPart(comp.name); } +// Ray-based verification for shell analysis +bool CreoManager::IsComponentBlockedFromCenter(const Vector3D& globalCenter, + const ComponentItem& component, + const std::vector& allComponents) { + // Calculate component center + Vector3D componentCenter = Vector3D( + (component.worldAABB.minPoint.x + component.worldAABB.maxPoint.x) * 0.5, + (component.worldAABB.minPoint.y + component.worldAABB.maxPoint.y) * 0.5, + (component.worldAABB.minPoint.z + component.worldAABB.maxPoint.z) * 0.5 + ); + + // Calculate ray direction (from global center through component center) + Vector3D rayDirection = (componentCenter - globalCenter).normalize(); + + // Start ray from component center (skip checking between global center and component) + // We want to check if ray is blocked AFTER passing through the component + Vector3D rayStart = componentCenter; + + // Check intersection with all other components + for (const ComponentItem& otherComp : allComponents) { + // Skip self + if (otherComp.featureId == component.featureId) { + continue; + } + + // Calculate other component center + Vector3D otherCenter = Vector3D( + (otherComp.worldAABB.minPoint.x + otherComp.worldAABB.maxPoint.x) * 0.5, + (otherComp.worldAABB.minPoint.y + otherComp.worldAABB.maxPoint.y) * 0.5, + (otherComp.worldAABB.minPoint.z + otherComp.worldAABB.maxPoint.z) * 0.5 + ); + + // Check if other component is in the outward direction + Vector3D toOther = otherCenter - rayStart; + double dotProduct = toOther.dot(rayDirection); + + // If other component is not in the ray direction, skip + if (dotProduct <= 0) { + continue; + } + + // Simple AABB ray intersection test + // Check if ray from component center outward intersects other component's AABB + double tMin = 0.0; + double tMax = std::numeric_limits::max(); + + for (int i = 0; i < 3; i++) { + double origin = (i == 0) ? rayStart.x : (i == 1) ? rayStart.y : rayStart.z; + double dir = (i == 0) ? rayDirection.x : (i == 1) ? rayDirection.y : rayDirection.z; + double minVal = (i == 0) ? otherComp.worldAABB.minPoint.x : + (i == 1) ? otherComp.worldAABB.minPoint.y : + otherComp.worldAABB.minPoint.z; + double maxVal = (i == 0) ? otherComp.worldAABB.maxPoint.x : + (i == 1) ? otherComp.worldAABB.maxPoint.y : + otherComp.worldAABB.maxPoint.z; + + if (std::abs(dir) < 1e-10) { + // Ray is parallel to slab + if (origin < minVal || origin > maxVal) { + // Ray is outside slab + tMin = tMax + 1; // Force no intersection + break; + } + } else { + // Compute intersection t values + double t1 = (minVal - origin) / dir; + double t2 = (maxVal - origin) / dir; + + if (t1 > t2) { + std::swap(t1, t2); + } + + tMin = std::max(tMin, t1); + tMax = std::min(tMax, t2); + + if (tMin > tMax) { + // No intersection + break; + } + } + } + + // If ray intersects this component's AABB, the component is blocked + if (tMin <= tMax && tMin >= 0) { + return true; // Component is blocked + } + } + + return false; // Component is not blocked (is outer shell) +} + // ComponentClassifier extension for elongated parts bool CreoManager::ComponentClassifier::IsElongatedPart(const std::string& name) { // Common elongated part naming patterns diff --git a/CreoManager.h b/CreoManager.h index 04cb6d0..162430f 100644 --- a/CreoManager.h +++ b/CreoManager.h @@ -875,4 +875,9 @@ private: 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& allComponents); };