Feature Classification#
Internal documentation for the feature classification rule system.
Note
Audience: Developers extending the library with new feature types.
This document covers the internal rule-based classification system. For using feature recognition, see Feature Recognition.
Feature Classification System#
This module provides a rule-based feature classification system for categorizing sheet metal features. The system is based on the “Characteristics Matrix” approach described in Yang Yang et al. (2021).
Overview#
The classification system uses three types of rules to categorize features:
Cut Feature Rules (Table 1): For holes, slots, notches, and corner reliefs
Composite Feature Rules (Table 2): For bends, jogs, lances, and complex features
Deform Feature Rules (Table 3): For emboss, louver, and deformation features
Each rule defines a set of geometric characteristics that must match for a feature to be classified as that type.
Core Components#
FeatureRuleRegistry#
The central registry containing all classification rules organized by category.
from volmdlr_tools.features.classification import FeatureRuleRegistry
Rule Categories#
Category |
Attribute |
Description |
|---|---|---|
Cut Features |
|
Holes, slots, notches, reliefs |
Composite Features |
|
Bends, jogs, lances |
Deform Features |
|
Emboss, louver |
Example: Accessing Rules#
from volmdlr_tools.features.classification import FeatureRuleRegistry
# List all cut feature rules
for rule in FeatureRuleRegistry.CUT_RULES:
print(f"{rule.name}: {rule.description}")
# List all composite feature rules
for rule in FeatureRuleRegistry.COMPOSITE_RULES:
print(f"{rule.name}: {rule.description}")
RuleMatcher#
The matching engine that compares extracted properties against rule definitions.
from volmdlr_tools.features.classification import RuleMatcher
Value Matching#
The matcher supports three types of value specifications:
Type |
Example |
Matches |
|---|---|---|
|
|
Any value |
Exact value |
|
Only the value 4 |
Range tuple |
|
Values 2, 3, 4, or 5 |
Open range |
|
Values up to 5 |
Open range |
|
Values 2 and above |
Methods#
matches_value(rule_value, actual_value) -> bool
Check if an actual value matches a rule specification.
matcher = RuleMatcher()
# Exact match
matcher.matches_value(4, 4) # True
matcher.matches_value(4, 5) # False
# Wildcard
matcher.matches_value(None, 42) # True (any value)
# Range match
matcher.matches_value((2, 5), 3) # True (in range)
matcher.matches_value((2, 5), 6) # False (out of range)
# Open range
matcher.matches_value((None, 5), 3) # True (unbounded lower)
matcher.matches_value((2, None), 10) # True (unbounded upper)
matches_rule(rule, properties) -> float
Check if properties match a rule. Returns a confidence score (0.0 to 1.0).
from volmdlr_tools.features.classification import RuleMatcher, FeatureRuleRegistry
matcher = RuleMatcher()
# Properties extracted from a thickness face chain
properties = {
"type_of_chain": "Closed",
"total_faces": 4,
"cylindrical_surfaces": 0,
"planar_surfaces": 4,
"parallel_planar_pairs": 2,
"different_radius_values": 0,
}
# Check against rectangular hole rule
rule = FeatureRuleRegistry.CUT_RULES[1] # RectangularHole
confidence = matcher.matches_rule(rule, properties)
print(f"Match confidence: {confidence:.2%}")
matches_deform_rule(rule, properties) -> bool
Check if properties match a deformation feature rule.
properties = {
"has_interconnected_faces": True,
"termination_criterion": 1,
"inner_loop_shape": "Circular",
}
for rule in FeatureRuleRegistry.DEFORM_RULES:
if matcher.matches_deform_rule(rule, properties):
print(f"Matched: {rule.name}")
Rule Dataclasses#
CutFeatureRule#
For cut features (Table 1 in Yang Yang et al.).
from volmdlr_tools.features.classification import CutFeatureRule
@dataclass(frozen=True)
class CutFeatureRule:
feature_class: type[Feature] # The feature class to instantiate
type_of_chain: str = None # "Closed" | "Open"
total_faces: RangeValue = None # Number of faces in chain
cylindrical_surfaces: RangeValue = None # Count of cylindrical faces
different_radius_values: RangeValue = None # Unique radius count
planar_surfaces: RangeValue = None # Count of planar faces
parallel_planar_pairs: RangeValue = None # Parallel face pairs
all_angles_concave: bool = None # All angles are concave?
is_fsf_neighbor: bool = None # Adjacent to fan-shaped face?
name: str = None # Rule identifier
description: str = None # Human-readable description
Example Cut Rules:
Rule |
Chain |
Faces |
Cylindrical |
Planar |
Parallel Pairs |
|---|---|---|---|---|---|
CircularHole |
Closed |
1-2 |
2 |
0 |
0 |
RectangularHole |
Closed |
4-8 |
0-4 |
4 |
2 |
Slot |
Closed |
4+ |
2 |
2 |
1 |
Notch |
Open |
1-3 |
- |
- |
- |
CornerRelief |
Open |
4+ |
- |
- |
- |
Relief |
Open |
3 |
- |
- |
- |
CompositeFeatureRule#
For bend/composite features (Table 2 in Yang Yang et al.).
from volmdlr_tools.features.classification import CompositeFeatureRule
@dataclass(frozen=True)
class CompositeFeatureRule:
feature_class: type[Feature] # The feature class
internal_chains: RangeValue = None # Internal chain count
boundary_subchains: RangeValue = None # Boundary subchain count
fan_shaped_faces: RangeValue = None # Fan-shaped face count
fan_shaped_pairs: RangeValue = None # Paired fan-shaped faces
share_common_face: bool = None # Faces share common neighbor?
opposite_normals: bool = None # Face normals are opposite?
d1_greater_d2: bool = None # Width comparison
name: str = None
description: str = None
Example Composite Rules:
Rule |
FSF Pairs |
Common Face |
Opposite Normals |
Pattern |
|---|---|---|---|---|
Bend |
1 |
True |
True |
[0, 1] |
Curl |
1 |
True |
True |
path_dist=3 |
Hem |
1 |
True |
True |
path_dist=5 |
Jog |
2 |
True |
True |
[0, 0, 1, 1] |
DeformFeatureRule#
For deformation features (Table 3 in Yang Yang et al.).
from volmdlr_tools.features.classification import DeformFeatureRule
@dataclass(frozen=True)
class DeformFeatureRule:
feature_class: type[Feature] # The feature class
has_interconnected_faces: bool = None # Has connected main faces?
termination_criterion: int = None # 1 or 2
inner_loop_shape: str = None # "Circular", "Rectangular", etc.
name: str = None
description: str = None
SheetMetalFeatureClassifier#
The high-level classifier that uses rules to categorize features.
from volmdlr_tools.features.extractors import SheetMetalFeatureClassifier
Usage#
from volmdlr_tools.features.extractors import SheetMetalFeatureClassifier
from volmdlr_tools.graph.faces import AttributedAdjacencyGraph
# Create classifier with AAG
classifier = SheetMetalFeatureClassifier(aag=my_aag)
# Classify a TFCGroup (thickness face chain group)
feature = classifier.classify(tfc_group)
if feature:
print(f"Classified as: {feature.__class__.__name__}")
else:
print("Could not classify feature")
Classification Workflow#
1. Extract Properties from TFCGroup#
# For cut features
properties = tfc_group.extract_cut_feature_properties()
# For composite features
properties = tfc_group.extract_composite_feature_properties()
2. Match Against Rules#
from volmdlr_tools.features.classification import RuleMatcher, FeatureRuleRegistry
matcher = RuleMatcher()
# Try cut rules first
best_match = None
best_confidence = 0.0
for rule in FeatureRuleRegistry.CUT_RULES:
confidence = matcher.matches_rule(rule, properties)
if confidence > best_confidence:
best_confidence = confidence
best_match = rule
if best_match and best_confidence > 0.5:
print(f"Best match: {best_match.name} ({best_confidence:.0%})")
3. Instantiate Feature#
if best_match:
feature = best_match.feature_class.from_faces_and_nodes(
faces=tfc_group.faces,
nodes=tfc_group.face_ids,
name=best_match.name
)
Extending the System#
Adding a New Feature Type#
Create the feature class in
volmdlr_tools/features/feature_types/sheet_metal/:
# In new_feature.py
from volmdlr_tools.features.feature_types.core import Feature
class MyNewFeature(Feature):
"""My custom sheet metal feature."""
@property
def category(self):
return FeatureCategory.CUT # or FORMING, etc.
Add a rule to
FeatureRuleRegistry:
# In registry.py
CUT_RULES = (
# ... existing rules ...
CutFeatureRule(
feature_class=MyNewFeature,
type_of_chain="Closed",
total_faces=(3, 5),
cylindrical_surfaces=1,
name="MyNewFeature",
description="Description of my new feature",
),
)
Position the rule by specificity (more specific rules first).
Confidence Scoring#
The RuleMatcher.matches_rule() method returns a confidence score:
1.0 (100%): All rule parameters matched
0.0 (0%): An exclusive parameter didn’t match
0.0 < x < 1.0: Partial match (ratio of matched parameters)
Exclusive Parameters (must match or confidence = 0):
type_of_chainall_angles_concaved1_greater_d2parallel_planar_pairstotal_facesfan_shaped_faces
FeatureCategory Enum#
from volmdlr_tools.features.constants import FeatureCategory
class FeatureCategory(Enum):
BLEND = "blend"
CAVITY = "cavity"
CUT = "cut"
FORMING = "forming"
UNKNOWN = "unknown"
See Also#
Sheet Metal Recognition - Complete sheet metal guide
Feature Recognition Module - General feature recognition
AAG (Attributed Adjacency Graph) - Graph representation
See Also#
Feature Processor - Using FeatureProcessor
Sheet Metal Features - Sheet metal features