feat: enhance shell analysis with two-step verification and remove conflicting mechanisms
- 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 <noreply@anthropic.com>
This commit is contained in:
parent
92cd47549f
commit
9b2e60249c
149
CreoManager.cpp
149
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<Vector3D> 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<std::string> 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<ComponentItem>& 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<double>::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
|
||||
|
||||
@ -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<ComponentItem>& allComponents);
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user