volmdlr_tools.features.feature_types package#
Feature type classes representing different kinds of geometric features.
Subpackages#
- volmdlr_tools.features.feature_types.sheet_metal package
- Submodules
- volmdlr_tools.features.feature_types.sheet_metal.bends module
- volmdlr_tools.features.feature_types.sheet_metal.corners module
- volmdlr_tools.features.feature_types.sheet_metal.jogs module
- volmdlr_tools.features.feature_types.sheet_metal.notches module
- volmdlr_tools.features.feature_types.sheet_metal.reliefs module
- volmdlr_tools.features.feature_types.sheet_metal.lances module
- volmdlr_tools.features.feature_types.sheet_metal.louvres module
- volmdlr_tools.features.feature_types.sheet_metal.deformations module
- volmdlr_tools.features.feature_types.sheet_metal.unknown module
- Module contents
Submodules#
volmdlr_tools.features.feature_types.core module#
Core module for feature types.
- class volmdlr_tools.features.feature_types.core.Feature(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
ShapeBase class for all feature types.
- base_nodes: list[int]#
- category: FeatureCategory = 'unknown'#
- feature_type: FeatureType = 'unknown'#
- floor_node: int | None = None#
- classmethod from_faces_and_nodes(faces: list[Face], nodes: list[int], base_nodes: list[int] | None = None, name: str = '', *args, **kwargs) Feature#
Create a Feature from a list of faces and a list of nodes.
- groups_member_features: bool = False#
Whether this type’s
nodesare the union of member features’ own faces rather than faces it claims in its own right — a blend chain over its blends, a rib structure over its segments. Face-coverage reporting skips such types: counting both would report structural containment as two features competing for one face, and bury the real overlaps. A new type that groups others should say so here, or its members’ faces will all read as contested. Unlikefeature_typethis cannot be enforced in__init_subclass__:Falseis the right answer for almost every type, so a missing declaration is indistinguishable from a correct one.
- nodes: list[int]#
- topology: Literal['isolated', 'complex', 'splitting_adjacency', 'transition', 'unknown'] = 'unknown'#
- class volmdlr_tools.features.feature_types.core.InnerFeature(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
FeatureBase class for inner features (cavities, bosses) with geometric properties.
Inner features are connected to a face’s inner wire (cavities, bosses). Kept for backward compatibility with Cavity subtypes — will be inlined into Cavity hierarchy in a future refactor.
- property axis: Vector3D#
Get the axis direction vector of the feature.
- feature_type: FeatureType = 'unknown'#
- property length: float#
Get the length of the feature.
- property location: Point3D#
Get the center location of the feature based on its bounding box.
- property width: float#
Get the width of the feature.
- volmdlr_tools.features.feature_types.core.REPR_LENGTH_LIMIT = 500#
Longest string a feature’s repr may produce. A feature can span hundreds of faces and expose a dozen parameters, so the repr is capped: inspecting a whole part’s features must cost a predictable amount of an agent’s context. Set with headroom over the longest measured: 122 characters for the widest feature on the sheet-metal test part, and 361 for the most parameter-rich type there is (a countersink hole), so no recognised feature is truncated.
- volmdlr_tools.features.feature_types.core.REPR_NODES_LIMIT = 8#
How many face indices a repr lists before summarising the rest as a total.
- volmdlr_tools.features.feature_types.core.REPR_UNCOMPUTABLE = '<error>'#
Stands in for a parameter that could not be read at all: computing it raised. Deliberately distinct from
None, which since ADR 0001 is the answer a parameter itself gives when it has no value — including when the geometry does not support the measurement. Reaching this marker therefore means a property broke its own contract, and the repr says so rather than letting one unreadable parameter sink the whole survey.
- volmdlr_tools.features.feature_types.core.REPR_VALUE_LENGTH_LIMIT = 40#
Longest a single parameter value may print as.
- class volmdlr_tools.features.feature_types.core.RoundInnerFeature(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
InnerFeatureBase class for round (cylindrical) inner features.
Kept for backward compatibility with RoundCavity.
- property axis: Vector3D | None#
Get the axis direction vector of the round feature, or None without a cylindrical face.
- property diameter: float | None#
Get the diameter of the round feature, or None without a cylindrical face.
- feature_type: FeatureType = 'unknown'#
- property location: Point3D | None#
Get the center location of the round feature, or None without a cylindrical face.
- class volmdlr_tools.features.feature_types.core.SquareInnerFeature(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
InnerFeatureBase class for square/rectangular inner features.
Kept for backward compatibility with SquareCavity.
- property axis: Vector3D#
Get the axis direction vector of the square feature.
- static compute_square_geometry(faces: list[Face]) tuple[float, float, Vector3D, Point3D]#
Compute (length, width, axis, location) for a square/rectangular feature.
Shared helper so callers can branch on dimensions without instantiating a probe.
The dimensions come from pairs of opposite planar walls: two planar faces whose planes are parallel and separated along their common normal. Non-planar faces (e.g. rounded-corner cylinders) and coplanar face splits contribute to the centroid but never to the dimensions.
A shape can offer several opposite-wall pairs sharing one direction — a T-slot, a stepped pocket, a wall split at two different offsets. The widest pair in each direction wins, which is the cutout’s outer dimension along it, so the answer does not depend on the order faces are met in. The two widest directions become length and width.
The axis is the cross product of the two wall normals, which fixes the line the cutout runs along but not the arrow along it: the operand order depends on which wall pair is met first, and each plane normal comes from the underlying surface rather than from the material. The result is therefore passed through
canonical_direction(), so the same shape always yields the same vector. That sign is a convention: callers needing an oriented axis (against a stamping direction, say) must establish the orientation themselves.- Parameters:
faces – Non-empty list of Face objects (volmdlr Faces) forming the feature.
- Returns:
Tuple of (length, width, axis_vector, centroid_location); the axis is a unit vector with a canonical, non-oriented sign.
- Raises:
ValueError – If opposite walls cannot be found in two independent directions.
- feature_type: FeatureType = 'unknown'#
- property length: float#
Get the length of the square feature.
- property location: Point3D#
Get the center location of the square feature.
- property width: float#
Get the width of the square feature.
volmdlr_tools.features.feature_types.blends module#
Blending Features module.
- class volmdlr_tools.features.feature_types.blends.Blend(obj: TopoDS_Shape, nodes: list[int], blend_attribute: BlendAttribute | None, base_nodes: list[int] | None = None, name: str = '')#
Bases:
FeatureA class representing a blend.
- property confirmed: bool#
Whether the edge-blend pass could account for this blend’s boundary, not only its surface.
Trueonce that pass resolved the boundary into one of two recognized arrangements: one or two cross edges, which the blend curves around — matched by reading the curvature at the edge’s midpoint against the blend’s own radius — or exactly two terminating edges, which bound it against real neighbouring faces, as a fillet running between two end caps. AnUncertainblend never reaches either test, so it is never confirmed.Falsewhile the face still looks like a blend by its surface alone, its boundary unaccounted for. Such a face is a candidate: it is reported as a blend, and often is one, but a caller that must not act on a partly-traced feature should skip it.The flag is never re-evaluated. It is set during edge-blend recognition, before the later passes reclassify corner patches and rewrite the cross and terminating sets. So it records what that one pass could resolve at the time, and need not agree with the edge lists read off
blend_attributeafterwards: a blend can end up carrying one or two cross edges and still answerFalse, and aVertexcorner patch carries whichever verdict it was given while it was still taken for an edge blend. Read it as “the extractor got a coherent read of this blend’s edges”, not as a guarantee about the arrangement a caller finds now.A face recognition never accepted as a blend at all is likewise
False. There is nothing to distinguish here as there is forradiusandkind— an unconfirmed blend and an unrecognized face are both, plainly, not confirmed blends.- Returns:
True if the blend is confirmed, False if it is a candidate or was never recognized as a blend.
- Return type:
bool
- feature_type: FeatureType = 'blend'#
- classmethod from_node(aag: AttributedAdjacencyGraph, blend_node: int, name: str = '') Blend#
Build a single-node
Blendfrom blend_node in aag.
- property kind: BlendType | None#
Get the kind of blend recognition classified this one as.
One of
BlendType, decided for an edge blend by how many of its edges it meets a supporting face smoothly along (its spring edges):Ordinaryfor the two of a round running along an edge between two faces,Clifffor the one of a round tangent to a face on a single side and running out against a sharp edge on the other, andUncertainfor any other count — a face the surface test accepted as a blend but whose edges could not be resolved into either arrangement. AVertexblend is a corner patch rather than an edge round, recognized in a later pass: a curved face that at least three edge blends run into across cross edges, none of those neighbours being a corner patch itself. A face first taken for an edge blend is re-classified here, soVertexcan replace an earlierOrdinaryorCliff.Nonemeans the face carries no blend attribute — recognition never accepted it as a blend, so it was never classified at all. That is a different answer fromUncertain, which is a verdict recognition reached about a blend it did accept.- Returns:
Kind of the blend, or None if the face was never recognized as a blend.
- Return type:
BlendType | None
- property node_id: int#
Node ID of the blend.
- property radius: float | None#
Get the radius this blend was recognized by.
The value read at the mid-point of the first spring edge accepted for this blend: the reciprocal of the blend face’s dominant principal curvature there. For a corner (vertex) patch it is instead the radius of the tube the patch is swept from, falling back to the largest radius among the edge blends it joins when that tube cannot be read. Expressed in the model’s own length unit, so a metre-scale part reports a 3 mm round as
0.003.This is one sample, not a summary: a round whose radius varies along its length is reported here by whichever sample came first. Ask
widest_radiusfor how wide it ever gets.Nonemeans the face carries no blend attribute — recognition never accepted it as a blend, so no radius was ever read.0.0means the attribute is there but holds no radius, which is what an attribute rebuilt from its serialized form reports:radiusis listed inBlendAttribute._non_serializable_attributes, so it is never restored. Recognition itself never records0.0— a face whose spring edges cannot be read is refused an attribute altogether, and answersNonerather than zero.- Returns:
Radius of the blend, or None if the face was never recognized as a blend.
- Return type:
float | None
- topology: Literal['isolated', 'complex', 'splitting_adjacency', 'transition', 'unknown'] = 'transition'#
- property widest_radius: float | None#
Get the largest radius reached anywhere along this blend.
The maximum over the radii sampled at every spring edge accepted for this blend, each measured as described on
radiusand expressed in the same unit. A round holding one radius along its whole length reports the same value asradius; a variable-radius round reports the widest point it reaches, whichradiusalone cannot express. Recognition records a sample alongside every radius it reads, so the fallback toradiusinBlendAttribute.max_radiusis reached only by an attribute rebuilt from its serialized form, which carries neither.Noneand0.0mean here exactly what they mean forradius.- Returns:
Largest sampled radius of the blend, or None if the face was never recognized as a blend.
- Return type:
float | None
- class volmdlr_tools.features.feature_types.blends.BlendAttribute(confirmed: bool = False, length: float = 0.0, kind: BlendType = BlendType.Uncertain, cross_length: float = 0.0, isolable: bool = False, name: str = '')#
Bases:
DessiaObjectA class representing attributes of a blend.
- classmethod dict_to_object(dict_: dict[str, Any], **kwargs) SerializableObject#
Deserialize a dictionary to a BlendAttribute object.
- is_concave() bool#
Check if the blend has concave vexity.
- is_convex() bool#
Check if the blend has convex vexity.
- property is_isolated: bool#
Whether this blend has the isolated signature: two spring edges, two terminating edges and no cross edges.
This is the standalone fillet the isolated suppression recipe removes directly, derived from the recognized edge roles. It is the already-standalone state, independent of the broader
isolablecapability flag (which also marks reducible profile-corner rounds that carry cross edges to a corner blend).
- is_uncertain() bool#
Check if the blend has uncertain vexity.
- property max_radius: float#
Largest sampled radius along the blend (falls back to
radius).
- property owned_cross_edges_indices: list[int]#
Cross edges attributed to this blend for once-per-owner edge accounting.
A vertex blend records the edges it shares with its neighbouring edge blends as its own cross edges, but for any accounting purpose (aggregate counts, suppressibility invariants) those edges belong to the edge blends — a vertex blend owns none.
- radii: list[float]#
- to_dict(use_pointers: bool = True, memo=None, path: str = '#', id_method=True, id_memo=None, **kwargs) dict[str, Any]#
Convert the BlendAttribute object to a serializable dictionary.
- class volmdlr_tools.features.feature_types.blends.BlendChain(obj: TopoDS_Shape, nodes: list[int], cross_length: float, length: float, radius: float, base_nodes: list[int] | None = None, name: str = '')#
Bases:
FeatureA class representing a chain of blends.
- copy(deep: bool = True, memo: Any = None) BlendChain#
Copy of Shape.
- Returns:
return a copy the Shape.
- feature_type: FeatureType = 'blend_chain'#
- classmethod from_faces_and_nodes(faces: list[Face], nodes: list[int], name: str = '', *args, **kwargs) Feature#
Create a BlendChain from a list of faces and a list of nodes.
- Parameters:
faces – The faces to create the blend chain from.
nodes – The nodes to create the blend chain from.
name – The name of the blend chain.
- Returns:
The BlendChain.
- groups_member_features: bool = True#
Whether this type’s
nodesare the union of member features’ own faces rather than faces it claims in its own right — a blend chain over its blends, a rib structure over its segments. Face-coverage reporting skips such types: counting both would report structural containment as two features competing for one face, and bury the real overlaps. A new type that groups others should say so here, or its members’ faces will all read as contested. Unlikefeature_typethis cannot be enforced in__init_subclass__:Falseis the right answer for almost every type, so a missing declaration is indistinguishable from a correct one.
- property node_set: set[int]#
Face indices of this chain as a set, tolerating a scalar
nodesvalue.
- topology: Literal['isolated', 'complex', 'splitting_adjacency', 'transition', 'unknown'] = 'transition'#
- class volmdlr_tools.features.feature_types.blends.BlendType(*values)#
Bases:
EnumEnum representing different types of blends in a geometric model, including ordinary, vertex, and cliff.
- Cliff = 3#
- Ordinary = 1#
- Uncertain = 0#
- Vertex = 2#
- classmethod from_name(name: str) BlendType#
Resolve the member a caller named, refusing a name no member carries.
The name is the member’s own —
"Ordinary"— not the repr"BlendType.Ordinary".- Parameters:
name – Name of a member, as a caller would write it.
- Returns:
The named member.
- Return type:
- Raises:
ValueError – If no member carries that name.
volmdlr_tools.features.feature_types.cavities module#
Cavities Module.
- class volmdlr_tools.features.feature_types.cavities.Cavity(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
InnerFeatureBase class for representing cavities as shell objects.
A cavity is represented as a collection of faces that form a shell structure. This class serves as the foundation for more specific cavity types like round, square, rectangular, and slotted cavities.
- MIN_FACES_ROUND_HOLE = 2#
- MIN_FACES_SQUARE = 4#
- MIN_SLOT_END_CYLINDERS = 2#
- SLOT_WALL_DIRECTION_COUNT = 1#
- SQUARE_WALL_DIRECTION_COUNT = 2#
- cavity_axis() Vector3D | None#
Return the axis this cavity runs along, read from its own faces.
Deliberately not the inherited
axis, which for a plain cavity is the oriented bounding box’s smallest extent — the extrusion axis only when the cavity is shallower than it is wide.- Returns:
Unit axis with a canonical sign, or None when no axis emerges.
- property corner_radius: float | None#
Get the smallest corner rounding of the cutout, a sharp corner counting as 0.0.
Wall faces are the planar faces parallel to the cavity axis; two walls of different directions that touch meet in a sharp corner. Rounded corners are the cylindrical faces whose axis runs along the cavity axis and that stand against a wall — a cylinder touching no wall (e.g. a bore grouped into the same face set) is not a corner.
Walls are recognised to within
PERPENDICULAR_DOT_TOL, the same angular window (~0.08 deg) thatis_axis_parallel()uses, so a wall carrying real draft still reads as a wall.- Returns:
Minimum corner radius over all corners (0.0 when any corner is sharp), or None when it cannot be computed.
- property depth: float | None#
Get the extent of the cavity’s wall faces along its axis.
Runs from the entry plane to the floor for a blind cavity and to the exit for a through one — the same definition family as hole depth, which measures the hole’s walls and leaves its bottom out. A planar floor or cap (its normal along the axis) is therefore excluded, so a blind cavity’s depth stops at the floor rather than being set by it. Entry chamfers and drafted walls are walls, and count, as they do for a hole.
The axis comes from
cavity_axis(), i.e. from the faces themselves. UnlikeHole.depththis answers None rather than a number when the shape does not determine one.- Returns:
Depth along the cavity axis, or None when the axis cannot be determined or the cavity has no wall face.
- feature_type: FeatureType = 'cavity'#
- classmethod from_faces_and_nodes(faces: list[Face], nodes: list[int], name: str = '', *args, **kwargs) Cavity#
Create the appropriate cavity type from a list of faces.
Analyzes the surface types of the provided faces and creates the corresponding specialized cavity instance (RoundCavity, SquareCavity, RectangularCavity, or SlottedCavity).
Walls made of planes and cylinders are told apart by the number of distinct wall-plane directions: one direction (two parallel walls plus end cylinders) is a slot, two perpendicular-ish directions are a square/rectangular cutout whose corners may be sharp or rounded.
Floors and lids are excluded before counting. Counting them too would add a direction that only blind cutouts have, so a blind slot would read as two directions and a blind rounded rectangle as three, and neither would ever classify.
- Parameters:
faces – List of faces that define the cavity geometry.
nodes – List of nodes that define the cavity geometry.
name – Optional name for the cavity, defaults to empty string.
- Returns:
The matching Cavity subclass, or a generic Cavity when the faces match no specialised shape.
- topology: Literal['isolated', 'complex', 'splitting_adjacency', 'transition', 'unknown'] = 'isolated'#
- class volmdlr_tools.features.feature_types.cavities.EndCircle(center: Point3D, radius: float)#
Bases:
objectOne cylindrical end cap of a slot, as a circle on the slot’s mid-plane.
The project glossary (
CONTEXT.md) calls this an “end circle”: the centre and radius of a cylindrical face closing off one extremity of a slotted cutout.- center: Point3D#
- radius: float#
- class volmdlr_tools.features.feature_types.cavities.RectangularCavity(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
SquareCavityRepresents a rectangular cavity with unequal width and length.
Inherits all functionality from SquareCavity but represents cavities where the width and length dimensions differ beyond the tolerance threshold (1e-6).
- feature_type: FeatureType = 'rectangular_cavity'#
- class volmdlr_tools.features.feature_types.cavities.RoundCavity(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
RoundInnerFeature,CavityRepresents a circular/cylindrical cavity.
A round cavity is defined by one or two cylindrical surfaces and has properties like diameter, axis direction, and center location derived from the cylindrical geometry.
- feature_type: FeatureType = 'round_cavity'#
- class volmdlr_tools.features.feature_types.cavities.SlottedCavity(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
CavityRepresents a slotted cavity (obround/elongated cavity).
A slotted cavity consists of a combination of cylindrical surfaces and planar faces, typically forming a slot shape with rounded ends. Common in mechanical design for allowing adjustment along one axis.
A plain slot has two side walls and two end caps, but a keyhole slot carries more end caps of differing radii, so the face count is not fixed.
Two contracts live side by side here. The cached dimensions (
width,axis,location) raise when the slot has too few end caps to have them. The measured parameters (length,center_distance,slot_length,end_circles) answer None — or an empty list — instead, so a caller can read them across a mixed batch of cavities without guarding each one.- property axis: Vector3D#
Get the axis direction vector of the slotted cavity.
- Returns:
Unit vector representing the cavity axis direction.
- property center_distance: float | None#
Get the distance between the centers of the two end circles.
The project glossary (
CONTEXT.md) calls this the “center distance” (entraxe).slot_lengthis this plus both end radii.- Returns:
Center-to-center distance of the slot end caps, or None when fewer than two end circles can be found.
- property end_circles: list[EndCircle]#
Get the end circle of each cylindrical end cap of the slot.
The cylinder axis origin returned by OCP sits at an arbitrary height along the axis, so each center is re-projected onto its axis at the level of the slot’s mid-thickness. That level is read from the faces’ own bounding box rather than from
location, so an end circle can be had without the slot’s cached properties (and their stricter requirements) being computed.- Returns:
One
EndCircleper cylindrical face; empty when the slot exposes none.
- feature_type: FeatureType = 'slotted_cavity'#
- property length: float | None#
Get the bounding extent of the slot along its long direction.
The project glossary (
CONTEXT.md) reserves “length” for the bounding extent, and keeps the design quantity slot rules are stated against under its own name,slot_length. The two coincide on a clean slot; the extent is the one that still answers when a boundary is irregular, and it is a measurement of the faces rather than of the end circles.- Returns:
Extent between the outer ends of the slot, or None when fewer than two end circles can be found.
- property location: Point3D#
Get the center location of the slotted cavity.
- Returns:
Center point of the cavity.
- property slot_length: float | None#
Get the canonical slot length: center distance plus both end radii.
The project glossary (
CONTEXT.md) calls this the “slot length” (longueur totale) — the quantity design rules on slots are stated against. Distinct fromlength, defined on this class, which is the bounding extent between the end faces.- Returns:
Overall slot length along its long direction, or None when fewer than two end circles can be found.
- property width: float#
Get the width of the slotted cavity.
- Returns:
Width dimension of the cavity.
- class volmdlr_tools.features.feature_types.cavities.SquareCavity(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
SquareInnerFeature,CavityRepresents a square cavity with equal width and length.
A square cavity is defined by four or more planar wall faces spanning two directions, with corners that may be sharp or rounded by cylindrical corner faces. The width and length are approximately equal (within tolerance of 1e-6).
- feature_type: FeatureType = 'square_cavity'#
- volmdlr_tools.features.feature_types.cavities.cavity_axis_from_faces(faces: list[Face]) Vector3D | None#
Determine the axis a cutout runs along, from its faces alone.
Every wall of a prismatic cutout is parallel to its axis, and every cap across it is perpendicular to the walls. Two readings of that:
When cylindrical faces are present and share one direction — end caps of a slot, rounded corners of a rectangle, the bore of a round cavity — that direction is the axis.
Otherwise the axis is perpendicular to the wall normals, so each pair of non-parallel plane directions offers a candidate cross product. The candidate that the most planar faces are perpendicular to is the axis: on a blind box the four walls outvote the single floor.
This is what lets a blind cutout be measured and classified. Reading the axis off the oriented bounding box instead would give the box’s smallest extent, which is only the axis when the cavity is shallower than it is wide.
- Parameters:
faces – The cavity’s faces.
- Returns:
Unit axis with a canonical sign, or None when no axis emerges.
volmdlr_tools.features.feature_types.sharp_edges module#
Sheet metal emboss module.
- class volmdlr_tools.features.feature_types.sharp_edges.SharpEdge(obj: TopoDS_Shape, node: int, name: str = '')#
Bases:
EdgeEmboss feature type.
volmdlr_tools.features.feature_types.ribs module#
Rib feature type.
A rib is a thin protruding wall added to a (usually thin-shell) part to increase stiffness — see Lai et al. (2017), Recognition and decomposition of rib features in thin-shell plastic parts for finite element analysis.
In Attributed-Adjacency-Graph (AAG) terms a single rib segment is made of:
end faces (
end_face_nodes) — the broad parallel plates that define the wall: one or more coplanar faces per side (end_face_sidesrecords the two-side partition), anti-parallel normals across the wall, separated bythickness;shell faces (
shell_face_nodes) — the rib’s exposed faces (top and end caps), joined to the end faces through convex edges;base faces (
base_face_nodes) — the surrounding part walls the rib stands on, joined to the end faces through concave edges. These belong to the part rather than the rib and are kept only as references;fillet faces (
fillet_nodes) — optional blend faces between the above.
nodes holds the rib’s own faces (end + shell + fillet), mirroring how
Extrusion keeps its
base face out of nodes.
- class volmdlr_tools.features.feature_types.ribs.Rib(obj: TopoDS_Shape, nodes: list[int], end_face_sides: list[list[int]], shell_face_nodes: list[int], base_face_nodes: list[int] | None = None, fillet_nodes: list[int] | None = None, thickness: float | None = None, name: str = '')#
Bases:
FeatureA single rib segment: a thin protruding wall on a part.
The wall’s two-side partition (
end_face_sides) is the stored truth;end_face_nodes(their union) andbase_face_nodes(an alias of the inheritedbase_nodes) are derived read-only views, so each fact lives in exactly one place.- Parameters:
obj – The
TopoDS_Shape(shell) representing the rib’s own faces.nodes – AAG face indices of the rib’s own faces (end + shell + fillet).
end_face_sides – The two-side partition of the wall’s end faces — one list of AAG face indices per wall side, each non-empty, disjoint.
shell_face_nodes – The rib’s exposed faces (top / end caps).
base_face_nodes – Surrounding part walls the rib stands on (references; stored as the Feature-level
base_nodes).fillet_nodes – Blend faces between rib faces, if any.
thickness – Distance between the two end faces, in the model’s units.
name – Optional name for the feature.
- property base_face_nodes: list[int]#
Alias of the Feature-level
base_nodes, in the rib vocabulary.
- category: FeatureCategory = 'addition'#
- property end_face_nodes: list[int]#
The wall’s end faces, both sides together (union of
end_face_sides).
- feature_type: FeatureType = 'rib'#
- classmethod from_faces_and_nodes(faces: list[Face], nodes: list[int], shell_face_nodes: list[int], end_face_nodes: list[int] | None = None, end_face_sides: list[list[int]] | None = None, base_face_nodes: list[int] | None = None, fillet_nodes: list[int] | None = None, thickness: float | None = None, name: str = '') Rib#
Create a Rib from a list of faces and the recognized face roles.
The wall may be given either as the two-side partition (
end_face_sides, required for merged ribs with more than two end faces) or as a plain two-faceend_face_nodes(one face per side). When both are given they must agree.- Parameters:
faces – The rib’s own faces (end + shell + fillet) as volmdlr
Faceor rawTopoDS_Faceobjects; used to build the shell.nodes – AAG face indices matching
faces.shell_face_nodes – The rib’s exposed faces (top / end caps).
end_face_nodes – The wall’s end faces for the classic two-face rib; the partition is derived (one face per side).
end_face_sides – The two-side partition of the wall’s end faces.
base_face_nodes – Surrounding part walls (references).
fillet_nodes – Blend faces between rib faces, if any.
thickness – Distance between the two end faces, in the model’s units.
name – Optional name for the feature.
- Returns:
A Rib instance.
- Raises:
ValueError – If neither wall spelling is given, if a non-two-face
end_face_nodescomes without its partition, or if the two spellings disagree.
- topology: Literal['isolated', 'complex', 'splitting_adjacency', 'transition', 'unknown'] = 'complex'#
- class volmdlr_tools.features.feature_types.ribs.RibStructure(obj: TopoDS_Shape, nodes: list[int], segment_nodes: list[list[int]], name: str = '')#
Bases:
FeatureA set of rib segments connected at junctions (T-, X-, … connections).
A rib structure groups individual
Ribsegments whose own faces meet at a junction.segment_nodesrecords each member segment’s own face indices;nodesis their union (the structure’s own faces).- Parameters:
obj – The
TopoDS_Shape(shell) of all the structure’s faces.nodes – AAG face indices of all the structure’s own faces.
segment_nodes – One list of own-face indices per member rib segment.
name – Optional name for the feature.
- category: FeatureCategory = 'addition'#
- copy(deep: bool = True, memo: Any = None) RibStructure#
Copy the RibStructure, preserving its member segments.
- feature_type: FeatureType = 'rib_structure'#
- classmethod from_segments(segments: list[Rib], faces: list[Face], name: str = '') RibStructure#
Create a RibStructure from its member rib segments.
- Parameters:
segments – The connected
Ribsegments forming the structure.faces – The own faces of all segments (volmdlr
Faceor rawTopoDS_Face), used to build the structure’s shell.name – Optional name for the feature.
- Returns:
A RibStructure instance.
- groups_member_features: bool = True#
Whether this type’s
nodesare the union of member features’ own faces rather than faces it claims in its own right — a blend chain over its blends, a rib structure over its segments. Face-coverage reporting skips such types: counting both would report structural containment as two features competing for one face, and bury the real overlaps. A new type that groups others should say so here, or its members’ faces will all read as contested. Unlikefeature_typethis cannot be enforced in__init_subclass__:Falseis the right answer for almost every type, so a missing declaration is indistinguishable from a correct one.
- topology: Literal['isolated', 'complex', 'splitting_adjacency', 'transition', 'unknown'] = 'complex'#
Module contents#
Features types module.
- class volmdlr_tools.features.feature_types.Blend(obj: TopoDS_Shape, nodes: list[int], blend_attribute: BlendAttribute | None, base_nodes: list[int] | None = None, name: str = '')#
Bases:
FeatureA class representing a blend.
- property confirmed: bool#
Whether the edge-blend pass could account for this blend’s boundary, not only its surface.
Trueonce that pass resolved the boundary into one of two recognized arrangements: one or two cross edges, which the blend curves around — matched by reading the curvature at the edge’s midpoint against the blend’s own radius — or exactly two terminating edges, which bound it against real neighbouring faces, as a fillet running between two end caps. AnUncertainblend never reaches either test, so it is never confirmed.Falsewhile the face still looks like a blend by its surface alone, its boundary unaccounted for. Such a face is a candidate: it is reported as a blend, and often is one, but a caller that must not act on a partly-traced feature should skip it.The flag is never re-evaluated. It is set during edge-blend recognition, before the later passes reclassify corner patches and rewrite the cross and terminating sets. So it records what that one pass could resolve at the time, and need not agree with the edge lists read off
blend_attributeafterwards: a blend can end up carrying one or two cross edges and still answerFalse, and aVertexcorner patch carries whichever verdict it was given while it was still taken for an edge blend. Read it as “the extractor got a coherent read of this blend’s edges”, not as a guarantee about the arrangement a caller finds now.A face recognition never accepted as a blend at all is likewise
False. There is nothing to distinguish here as there is forradiusandkind— an unconfirmed blend and an unrecognized face are both, plainly, not confirmed blends.- Returns:
True if the blend is confirmed, False if it is a candidate or was never recognized as a blend.
- Return type:
bool
- feature_type: FeatureType = 'blend'#
- classmethod from_node(aag: AttributedAdjacencyGraph, blend_node: int, name: str = '') Blend#
Build a single-node
Blendfrom blend_node in aag.
- property kind: BlendType | None#
Get the kind of blend recognition classified this one as.
One of
BlendType, decided for an edge blend by how many of its edges it meets a supporting face smoothly along (its spring edges):Ordinaryfor the two of a round running along an edge between two faces,Clifffor the one of a round tangent to a face on a single side and running out against a sharp edge on the other, andUncertainfor any other count — a face the surface test accepted as a blend but whose edges could not be resolved into either arrangement. AVertexblend is a corner patch rather than an edge round, recognized in a later pass: a curved face that at least three edge blends run into across cross edges, none of those neighbours being a corner patch itself. A face first taken for an edge blend is re-classified here, soVertexcan replace an earlierOrdinaryorCliff.Nonemeans the face carries no blend attribute — recognition never accepted it as a blend, so it was never classified at all. That is a different answer fromUncertain, which is a verdict recognition reached about a blend it did accept.- Returns:
Kind of the blend, or None if the face was never recognized as a blend.
- Return type:
BlendType | None
- property node_id: int#
Node ID of the blend.
- property radius: float | None#
Get the radius this blend was recognized by.
The value read at the mid-point of the first spring edge accepted for this blend: the reciprocal of the blend face’s dominant principal curvature there. For a corner (vertex) patch it is instead the radius of the tube the patch is swept from, falling back to the largest radius among the edge blends it joins when that tube cannot be read. Expressed in the model’s own length unit, so a metre-scale part reports a 3 mm round as
0.003.This is one sample, not a summary: a round whose radius varies along its length is reported here by whichever sample came first. Ask
widest_radiusfor how wide it ever gets.Nonemeans the face carries no blend attribute — recognition never accepted it as a blend, so no radius was ever read.0.0means the attribute is there but holds no radius, which is what an attribute rebuilt from its serialized form reports:radiusis listed inBlendAttribute._non_serializable_attributes, so it is never restored. Recognition itself never records0.0— a face whose spring edges cannot be read is refused an attribute altogether, and answersNonerather than zero.- Returns:
Radius of the blend, or None if the face was never recognized as a blend.
- Return type:
float | None
- topology: Literal['isolated', 'complex', 'splitting_adjacency', 'transition', 'unknown'] = 'transition'#
- property widest_radius: float | None#
Get the largest radius reached anywhere along this blend.
The maximum over the radii sampled at every spring edge accepted for this blend, each measured as described on
radiusand expressed in the same unit. A round holding one radius along its whole length reports the same value asradius; a variable-radius round reports the widest point it reaches, whichradiusalone cannot express. Recognition records a sample alongside every radius it reads, so the fallback toradiusinBlendAttribute.max_radiusis reached only by an attribute rebuilt from its serialized form, which carries neither.Noneand0.0mean here exactly what they mean forradius.- Returns:
Largest sampled radius of the blend, or None if the face was never recognized as a blend.
- Return type:
float | None
- class volmdlr_tools.features.feature_types.BlendChain(obj: TopoDS_Shape, nodes: list[int], cross_length: float, length: float, radius: float, base_nodes: list[int] | None = None, name: str = '')#
Bases:
FeatureA class representing a chain of blends.
- copy(deep: bool = True, memo: Any = None) BlendChain#
Copy of Shape.
- Returns:
return a copy the Shape.
- feature_type: FeatureType = 'blend_chain'#
- classmethod from_faces_and_nodes(faces: list[Face], nodes: list[int], name: str = '', *args, **kwargs) Feature#
Create a BlendChain from a list of faces and a list of nodes.
- Parameters:
faces – The faces to create the blend chain from.
nodes – The nodes to create the blend chain from.
name – The name of the blend chain.
- Returns:
The BlendChain.
- groups_member_features: bool = True#
Whether this type’s
nodesare the union of member features’ own faces rather than faces it claims in its own right — a blend chain over its blends, a rib structure over its segments. Face-coverage reporting skips such types: counting both would report structural containment as two features competing for one face, and bury the real overlaps. A new type that groups others should say so here, or its members’ faces will all read as contested. Unlikefeature_typethis cannot be enforced in__init_subclass__:Falseis the right answer for almost every type, so a missing declaration is indistinguishable from a correct one.
- property node_set: set[int]#
Face indices of this chain as a set, tolerating a scalar
nodesvalue.
- topology: Literal['isolated', 'complex', 'splitting_adjacency', 'transition', 'unknown'] = 'transition'#
- class volmdlr_tools.features.feature_types.BlindSlot(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, floor_node: int | None = None, name: str = '')#
Bases:
SlotA slot that is closed on both ends.
A blind slot is fully enclosed within the part, accessible only from the top (cutting direction).
- feature_type: FeatureType = 'blind_slot'#
- open_ends: OpenEndsCount = 0#
- class volmdlr_tools.features.feature_types.BlindStep(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, floor_node: int | None = None, name: str = '')#
Bases:
StepA blind step - a step that forms a closed cycle.
A blind step is similar to a step but forms a cycle in the AAG, indicating it wraps around or is fully bounded. Shares the same geometric properties (depth, axis, location) as Step.
- category: FeatureCategory = 'cut'#
- feature_type: FeatureType = 'blind_step'#
- class volmdlr_tools.features.feature_types.Cavity(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
InnerFeatureBase class for representing cavities as shell objects.
A cavity is represented as a collection of faces that form a shell structure. This class serves as the foundation for more specific cavity types like round, square, rectangular, and slotted cavities.
- MIN_FACES_ROUND_HOLE = 2#
- MIN_FACES_SQUARE = 4#
- MIN_SLOT_END_CYLINDERS = 2#
- SLOT_WALL_DIRECTION_COUNT = 1#
- SQUARE_WALL_DIRECTION_COUNT = 2#
- cavity_axis() Vector3D | None#
Return the axis this cavity runs along, read from its own faces.
Deliberately not the inherited
axis, which for a plain cavity is the oriented bounding box’s smallest extent — the extrusion axis only when the cavity is shallower than it is wide.- Returns:
Unit axis with a canonical sign, or None when no axis emerges.
- property corner_radius: float | None#
Get the smallest corner rounding of the cutout, a sharp corner counting as 0.0.
Wall faces are the planar faces parallel to the cavity axis; two walls of different directions that touch meet in a sharp corner. Rounded corners are the cylindrical faces whose axis runs along the cavity axis and that stand against a wall — a cylinder touching no wall (e.g. a bore grouped into the same face set) is not a corner.
Walls are recognised to within
PERPENDICULAR_DOT_TOL, the same angular window (~0.08 deg) thatis_axis_parallel()uses, so a wall carrying real draft still reads as a wall.- Returns:
Minimum corner radius over all corners (0.0 when any corner is sharp), or None when it cannot be computed.
- property depth: float | None#
Get the extent of the cavity’s wall faces along its axis.
Runs from the entry plane to the floor for a blind cavity and to the exit for a through one — the same definition family as hole depth, which measures the hole’s walls and leaves its bottom out. A planar floor or cap (its normal along the axis) is therefore excluded, so a blind cavity’s depth stops at the floor rather than being set by it. Entry chamfers and drafted walls are walls, and count, as they do for a hole.
The axis comes from
cavity_axis(), i.e. from the faces themselves. UnlikeHole.depththis answers None rather than a number when the shape does not determine one.- Returns:
Depth along the cavity axis, or None when the axis cannot be determined or the cavity has no wall face.
- feature_type: FeatureType = 'cavity'#
- classmethod from_faces_and_nodes(faces: list[Face], nodes: list[int], name: str = '', *args, **kwargs) Cavity#
Create the appropriate cavity type from a list of faces.
Analyzes the surface types of the provided faces and creates the corresponding specialized cavity instance (RoundCavity, SquareCavity, RectangularCavity, or SlottedCavity).
Walls made of planes and cylinders are told apart by the number of distinct wall-plane directions: one direction (two parallel walls plus end cylinders) is a slot, two perpendicular-ish directions are a square/rectangular cutout whose corners may be sharp or rounded.
Floors and lids are excluded before counting. Counting them too would add a direction that only blind cutouts have, so a blind slot would read as two directions and a blind rounded rectangle as three, and neither would ever classify.
- Parameters:
faces – List of faces that define the cavity geometry.
nodes – List of nodes that define the cavity geometry.
name – Optional name for the cavity, defaults to empty string.
- Returns:
The matching Cavity subclass, or a generic Cavity when the faces match no specialised shape.
- topology: Literal['isolated', 'complex', 'splitting_adjacency', 'transition', 'unknown'] = 'isolated'#
- class volmdlr_tools.features.feature_types.Chamfer(obj: TopoDS_Shape, node_id: int, chamfer_attribute: ChamferAttribute, base_nodes: list[int] | None = None, name: str = '')#
Bases:
FeatureA single chamfer face feature.
- Parameters:
obj – The TopoDS_Shape of the chamfer face.
node_id – The face ID in the AAG.
chamfer_attribute – Geometric attributes of the chamfer.
name – Optional name.
- category: FeatureCategory = 'blend'#
- feature_type: FeatureType = 'chamfer'#
- topology: Literal['isolated', 'complex', 'splitting_adjacency', 'transition', 'unknown'] = 'transition'#
- class volmdlr_tools.features.feature_types.ChamferAttribute(surface_type: str = 'Plane', under_face_ids: tuple[int, int] = (0, 0), under_edge_ids: dict[int, list[int]] | None = None, offset_values: tuple[float, float] = (0.0, 0.0), chamfer_angle: float = 0.0, area: float = 0.0, width: float = 0.0, aspect_ratio: float = 0.0, width_under_ratio: float = 0.0, confirmed: bool = False, name: str = '')#
Bases:
DessiaObjectGeometric attributes of a chamfer face.
- Parameters:
surface_type – Surface type of the chamfer face (“Plane” or “ConicalSurface”).
under_face_ids – The two face IDs that the chamfer bridges.
under_edge_ids – Edge indices per under-face, keyed by face ID.
offset_values – Chamfer offset distances (V1, V2) along the two under-faces.
chamfer_angle – Angle of chamfer relative to first under-face, in radians.
area – Surface area of the chamfer face.
width – Minimum width of the chamfer face.
aspect_ratio – Length-to-width ratio.
width_under_ratio – Ratio of chamfer width to combined under-edge length.
confirmed – Whether the chamfer has been confirmed by filtering.
- classmethod dict_to_object(dict_: dict[str, Any], **kwargs) SerializableObject#
Deserialize a dictionary to a ChamferAttribute object.
- property is_symmetric: bool#
Return True if the chamfer offsets are approximately equal (45-degree chamfer).
- to_dict(use_pointers: bool = True, memo=None, path: str = '#', id_method=True, id_memo=None, **kwargs) dict[str, Any]#
Convert the ChamferAttribute to a serializable dictionary.
- class volmdlr_tools.features.feature_types.ChamferChain(obj: TopoDS_Shape, nodes: list[int], offset_values: tuple[float, float] = (0.0, 0.0), length: float = 0.0, base_nodes: list[int] | None = None, name: str = '')#
Bases:
FeatureA chain of connected chamfer faces with compatible offsets.
- Parameters:
obj – The TopoDS_Shape (Shell) of the chain.
nodes – List of face IDs in the chain.
offset_values – Common offset values (V1, V2) for the chain.
length – Total length of the chain along the under-edges.
name – Optional name.
- category: FeatureCategory = 'blend'#
- copy(deep: bool = True, memo: Any = None) ChamferChain#
Copy the ChamferChain, preserving all attributes.
- feature_type: FeatureType = 'chamfer_chain'#
- classmethod from_faces_and_nodes(faces: list[Face], nodes: list[int], name: str = '', *args, **kwargs) Feature#
Create a ChamferChain from a list of faces and node IDs.
- Parameters:
faces – The faces that make up the chain.
nodes – The face IDs corresponding to the faces.
name – Optional name.
- Returns:
A ChamferChain instance.
- groups_member_features: bool = True#
Whether this type’s
nodesare the union of member features’ own faces rather than faces it claims in its own right — a blend chain over its blends, a rib structure over its segments. Face-coverage reporting skips such types: counting both would report structural containment as two features competing for one face, and bury the real overlaps. A new type that groups others should say so here, or its members’ faces will all read as contested. Unlikefeature_typethis cannot be enforced in__init_subclass__:Falseis the right answer for almost every type, so a missing declaration is indistinguishable from a correct one.
- topology: Literal['isolated', 'complex', 'splitting_adjacency', 'transition', 'unknown'] = 'transition'#
- class volmdlr_tools.features.feature_types.CounterboreHole(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
MultiRadiusHoleRepresents a counterbore hole (stepped hole).
A counterbore hole has two or more cylindrical sections with different diameters, typically used to allow a bolt head to sit flush with the surface.
The counterbore consists of: - Major section: larger diameter cylinder at the entry (for bolt head) - Minor section: smaller diameter cylinder (the bore itself) - Transition face: annular planar face connecting the two sections
- property feature_type: FeatureType#
Counterbore holes are classified as
FeatureType.COUNTERBORE.
- class volmdlr_tools.features.feature_types.CounterdrillHole(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
HoleRepresents a counterdrill hole.
A counterdrill hole has three sections: - Entry section: small cylindrical pilot/guide at the surface - Conical section: tapered transition - Bore section: main cylindrical hole (larger than entry)
This is essentially a combination of counterbore and countersink features.
- property bore_diameter: float | None#
Get the diameter of the main bore cylindrical section.
- Returns:
Diameter of the bore.
- property bore_radius: float | None#
Get the radius of the main bore cylindrical section.
- Returns:
Radius of the bore.
- property cone_angle: float | None#
Get the full angle of the transition cone in radians.
- Returns:
Full cone angle (2 * semi-angle) in radians.
- property cone_angle_degrees: float | None#
Get the full angle of the transition cone in degrees.
- Returns:
Full cone angle in degrees.
- property cone_semi_angle: float | None#
Get the semi-angle of the transition cone between pilot and bore, in radians.
A blind counterdrill also carries a drill-point bottom cone; the one reported here is the transition cone, selected as
intermediate_cone_angledescribes.- Returns:
Semi-angle of the transition cone in radians.
- property diameter: float | None#
Primary bore diameter — the main cylindrical section (not the entry pilot).
- property entry_diameter: float | None#
Get the diameter of the entry (pilot) cylindrical section.
- Returns:
Diameter of the entry section.
- property entry_radius: float | None#
Get the radius of the entry (pilot) cylindrical section.
- Returns:
Radius of the entry section.
- property feature_type: FeatureType#
Counterdrill holes are classified as
FeatureType.COUNTERDRILL.A counterdrill is its own geometry, not a countersink: it adds a cylindrical pilot at the surface ahead of the cone. Reporting it as a countersink left consumers that branch on the type tag unable to tell the two apart, and so unable to reproduce the pilot.
- property intermediate_cone_angle: float | None#
Get the included angle of the transition cone between pilot and bore, in radians.
A blind counterdrill can also carry a drill-point bottom cone; that cone closes onto the axis (its smallest boundary radius is zero) while the transition cone spans between the two cylinder radii, so only cones whose boundary radii stay strictly positive qualify.
This is the drawing’s name for the same quantity
cone_anglereports; both select the transition cone.- Returns:
Included angle of the transition cone in radians, or None if no such cone is found.
- property radius: float | None#
Primary bore radius — the main cylindrical section (not the entry pilot).
- class volmdlr_tools.features.feature_types.CountersinkHole(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
HoleRepresents a countersink hole.
A countersink hole has a conical entry section combined with a cylindrical bore, typically used for flat-head screws.
The countersink consists of: - Conical section: tapered entry at the surface - Bore section: cylindrical hole below the cone
- property bore_diameter: float | None#
Get the diameter of the cylindrical bore section.
- Returns:
Diameter of the bore.
- property bore_radius: float | None#
Get the radius of the cylindrical bore section.
- Returns:
Radius of the bore.
- property cone_angle: float | None#
Get the full cone angle in radians.
- Returns:
Full cone angle (2 * semi-angle) in radians.
- property cone_angle_degrees: float | None#
Get the full cone angle in degrees.
- Returns:
Full cone angle in degrees.
- property cone_semi_angle: float | None#
Get the semi-angle of the conical section in radians.
- Returns:
Semi-angle of the cone in radians.
- property countersink_angle: float | None#
Get the countersink angle: the included angle of the entry cone, in radians.
This is the angle a drawing callout states (e.g. a 90-degree countersink), the canonical name for it in the project glossary (
CONTEXT.md). It is an alias ofcone_angle, not an independent measurement.- Returns:
Included angle of the entry cone in radians, or None if no entry cone is found.
- property countersink_radius: float | None#
Outer radius of the conical entry (at the part surface).
- property diameter: float | None#
Primary bore diameter — the cylindrical section beneath the conical entry.
- property feature_type: FeatureType#
Countersink holes are classified as
FeatureType.COUNTERSINK.
- property radius: float | None#
Primary bore radius — the cylindrical section beneath the conical entry.
- class volmdlr_tools.features.feature_types.Cylinder(obj: TopoDS_Shape, nodes: list[int], is_internal: bool = False, base_nodes: list[int] | None = None, name: str = '')#
Bases:
RevolutionFeatureA class representing a cylindrical feature (shaft/pin/boss or bore).
Can represent both external cylinders (protrusions) and internal cylinders (bores). The is_internal property determines the feature category: - External (is_internal=False): ADDITION category - Internal (is_internal=True): CUT category
- property axis: Vector3D#
Get the axis direction vector of the cylinder.
Lazily calculates the axis from the cylindrical surface if not already computed.
- Returns:
Unit vector representing the cylinder axis direction.
- Return type:
Vector3D
- property diameter: float#
Get the diameter of the cylinder.
- Returns:
Diameter of the cylinder (2 * radius).
- Return type:
float
- feature_type: FeatureType = 'cylinder'#
- get_axis_info() tuple[Vector3D, Point3D] | None#
Return
(axis_direction, location).
- property length: float#
Get the length of the cylinder.
Lazily calculates the length from the V-parameter range if not already computed.
- Returns:
Length of the cylinder along its axis.
- Return type:
float
- property location: Point3D#
Get the center location of the cylinder axis.
Lazily calculates the center from the cylindrical surface axis if not already computed.
- Returns:
A point on the cylinder axis.
- Return type:
Point3D
- property radius: float#
Get the radius of the cylinder.
Lazily calculates the radius from the cylindrical surface if not already computed.
- Returns:
Radius of the cylinder.
- Return type:
float
- class volmdlr_tools.features.feature_types.EndCircle(center: Point3D, radius: float)#
Bases:
objectOne cylindrical end cap of a slot, as a circle on the slot’s mid-plane.
The project glossary (
CONTEXT.md) calls this an “end circle”: the centre and radius of a cylindrical face closing off one extremity of a slotted cutout.- center: Point3D#
- radius: float#
- class volmdlr_tools.features.feature_types.Extrusion(obj: TopoDS_Shape, nodes: list[int], extrusion_params: ExtrusionParameters, base_node: int | None = None, base_plane: volmdlr.Frame3D | None = None, heuristic_source: str = '', extrusion_shape: ExtrusionShape | None = None, end_nodes: list[int] | None = None, side_nodes: list[int] | None = None, name: str = '')#
Bases:
FeatureMaterial addition feature confirmed as an extrusion shape.
Represents bosses, pads, and other additive extrusion features regardless of the discovery mechanism (split adjacency, boss splitting, defeaturing).
- Parameters:
obj – The TopoDS_Shape object representing the feature.
nodes – The face indices that compose the feature.
extrusion_params – Geometric parameters of the extrusion.
base_node – Original AAG face index of the base face.
base_plane – Construction plane (populated when base_node is set).
heuristic_source – Provenance label for debugging/traceability.
extrusion_shape – The ExtrusionShape recognizer result, if available.
name – Optional name for the feature.
- category: FeatureCategory = 'addition'#
- feature_type: FeatureType = 'extrusion'#
- classmethod from_faces_and_nodes(faces: list[Face], nodes: list[int], extrusion_params: ExtrusionParameters, base_node: int | None = None, base_plane: volmdlr.Frame3D | None = None, heuristic_source: str = '', extrusion_shape: ExtrusionShape | None = None, end_nodes: list[int] | None = None, side_nodes: list[int] | None = None, name: str = '') Extrusion#
Create an Extrusion from a list of faces and nodes.
- Parameters:
faces – List of Face objects composing the feature.
nodes – List of face indices in the AAG.
extrusion_params – Geometric parameters of the extrusion.
base_node – Original AAG face index of the base face.
base_plane – Construction plane.
heuristic_source – Provenance label.
extrusion_shape – The ExtrusionShape recognizer result, if available.
end_nodes – End-cap (profile) face indices, in the same space as nodes.
side_nodes – Side (lateral) face indices, in the same space as nodes.
name – Optional name for the feature.
- Returns:
An Extrusion instance.
- class volmdlr_tools.features.feature_types.ExtrusionParameters(axis: Vector3D, height: float, draft_angle: float = 0.0, extrusion_type: str = 'PRISMATIC')#
Bases:
objectGeometric parameters describing an extrusion operation.
- Parameters:
axis – Unit vector representing the extrusion direction.
height – Extrusion height (positive value).
draft_angle – Draft angle in radians (0 for prismatic).
extrusion_type – “PRISMATIC” or “DRAFTED”.
- axis: Vector3D#
- draft_angle: float = 0.0#
- extrusion_type: str = 'PRISMATIC'#
- classmethod from_extrusion_shape(extrusion_shape: ExtrusionShape) ExtrusionParameters#
Create ExtrusionParameters from an ExtrusionShape recognizer result.
- Parameters:
extrusion_shape – An ExtrusionShape instance from the recognizer.
- Returns:
ExtrusionParameters with axis, height, draft_angle, and type.
- height: float#
- property is_prismatic: bool#
Return True if this is a prismatic extrusion (no draft).
- class volmdlr_tools.features.feature_types.Feature(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
ShapeBase class for all feature types.
- base_nodes: list[int]#
- category: FeatureCategory = 'unknown'#
- feature_type: FeatureType = 'unknown'#
- floor_node: int | None = None#
- classmethod from_faces_and_nodes(faces: list[Face], nodes: list[int], base_nodes: list[int] | None = None, name: str = '', *args, **kwargs) Feature#
Create a Feature from a list of faces and a list of nodes.
- groups_member_features: bool = False#
Whether this type’s
nodesare the union of member features’ own faces rather than faces it claims in its own right — a blend chain over its blends, a rib structure over its segments. Face-coverage reporting skips such types: counting both would report structural containment as two features competing for one face, and bury the real overlaps. A new type that groups others should say so here, or its members’ faces will all read as contested. Unlikefeature_typethis cannot be enforced in__init_subclass__:Falseis the right answer for almost every type, so a missing declaration is indistinguishable from a correct one.
- nodes: list[int]#
- topology: Literal['isolated', 'complex', 'splitting_adjacency', 'transition', 'unknown'] = 'unknown'#
- class volmdlr_tools.features.feature_types.FeatureCategory(*values)#
Bases:
str,EnumHigh-level category of manufacturing features.
- Variables:
CUT – Material removal features (slots, notches, holes).
ADDITION – Material addition features (bosses, shafts, extrusions).
FORMING – Deformation features (bends, jogs).
BLEND – Fillet/chamfer features.
CAVITY – Pocket/cavity features.
UNKNOWN – Unclassified features.
- ADDITION = 'addition'#
- BLEND = 'blend'#
- CAVITY = 'cavity'#
- CUT = 'cut'#
- FORMING = 'forming'#
- UNKNOWN = 'unknown'#
- class volmdlr_tools.features.feature_types.Hole(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
FeatureBase class for representing hole features.
A hole is a cylindrical or conical cut feature typically created by drilling, boring, reaming, or similar machining operations. This class represents the geometric feature rather than the machining operation.
- Variables:
category – Feature category (CUT for material removal).
base_nodes – List of face indices that form the base/entry faces.
- property access_direction: Vector3D | None#
Get the tool feed direction vector for the hole.
Unit vector pointing from
origin(the entry on the axis) into the bore, i.e. the direction the tool travels into the material.origin + depth * access_directionlands at the end of the hole inside the volume.Note:
access_directionis collinear withaxisbut its sign is independent — the axis direction is whatever OCP chose for the cylindrical surface’s coordinate system, whileaccess_directionis anchored to the geometry of the entry.- Returns:
Unit vector or None if not computed.
- property axis: Vector3D#
Get the axis direction vector of the hole.
Lazily calculates the axis from the cylindrical surface if not already computed.
- Returns:
Unit vector representing the hole axis direction.
- Return type:
Vector3D
- category: FeatureCategory = 'cut'#
- property depth: float#
Get the depth of the hole.
Lazily calculates the depth from the bounding box if not already computed.
- Returns:
Depth of the hole along its axis.
- Return type:
float
- property diameter: float | None#
Get the diameter of the hole.
- Returns:
Diameter of the hole (2 * radius).
- Return type:
float
- property entry_blend_radius: float | None#
Minor radius of the entry-side fillet, or None if absent / not computed.
- property extrusion_height: float#
Get the extrusion height of the hole (alias for depth).
This follows the terminology from Sunil et al. (2010).
- Returns:
The extrusion height (same as depth).
- property face_type_counts: dict | None#
Get the face-type classification counts from coaxial grouping analysis.
- Returns:
Dict with cylindrical_count, conical_count, planar_count, annular_count, distinct_radii, cone_radii. None if not computed.
- property feature_type: FeatureType#
Cut-feature classification for this hole.
Subclasses override to declare their specific
FeatureType. The base default routes single-radius holes (Hole,RoundHole) throughis_blind.
- property floor_blend_radius: float | None#
Minor radius of the floor-side fillet, or None if absent / not computed.
- classmethod from_faces_and_nodes(faces: list[Face], nodes: list[int], name: str = '', has_entry_cone: bool = False, has_intermediate_cone: bool = False, distinct_radii_count: int = 0) Hole#
Create the appropriate hole type from a list of faces.
Analyze the surface types of the provided faces and create the corresponding specialized hole instance (RoundHole, CounterboreHole, CountersinkHole, etc.).
Classification logic (based on cone position, not just cone presence): - CounterdrillHole: entry cylinder + intermediate cone + bore cylinder - CountersinkHole: entry cone (adjacent to base face) + cylindrical bore - SteppedHole: 3+ distinct cylindrical radii (N-level stepped hole) - CounterboreHole: 2 distinct cylindrical radii (no entry/intermediate cone) - RoundHole: single radius cylindrical (may have bottom cone = drill point) - TaperHole: conical only (no cylindrical)
Note: Bottom cones (drill points at the end of blind holes) do NOT affect classification. They are distinguished from entry cones (countersinks) by not being adjacent to base faces.
- Parameters:
faces – List of faces that define the hole geometry.
nodes – List of nodes that define the hole geometry.
name – Optional name for the hole, defaults to empty string.
has_entry_cone – True if hole has a cone adjacent to base face (countersink).
has_intermediate_cone – True if hole has a cone between cylinders of different radii (counterdrill).
distinct_radii_count – Number of distinct cylindrical radii (from enrichment). 0 means not computed.
- Returns:
Appropriate Hole subclass instance based on face geometry.
- property is_blind: bool#
Check if the hole is blind (closed at one end).
A blind hole has only one entry/base face, meaning it doesn’t pass through the material. Through holes have base faces on both ends.
- Returns:
True if the hole is blind, False if it passes through.
- Return type:
bool
- property location: Point3D#
Get the center location of the hole.
Lazily calculates the center from the cylindrical surface axis if not already computed.
- Returns:
Center point of the hole.
- Return type:
Point3D
- property origin: Point3D | None#
Get the entry point of the hole on its axis.
originis the point on the axis line at the entry-side end of the bore — i.e. where the tool first meets the part. It is distinct fromlocation, which is the OCP-chosen anchor of the cylindrical surface’s coordinate system and lies somewhere on the (infinite) axis line, not necessarily at the entry.For through holes, the entry side is determined by
access_direction. Becauseaccess_directionpoints from the entry into the bore, the entry sits on the side opposite to whereaccess_directionpoints: ifaxis · access_direction < 0, the entry is the end with the largest axial projection; otherwise it is the end with the smallest. Whenaccess_directionis unavailable, the convention is to return the end with the largest axial projection.origin,depth, andaccess_directiontogether satisfy the invariantorigin + depth * access_direction= end of the hole inside the volume (bottom for blind, far base for through).- Returns:
Point3Don the hole axis at the entry, orNoneif no projection data is available.
- property radius: float | None#
Get the primary bore radius of the hole.
For a simple
Holethis is the radius of the single cylindrical surface. Subclasses override this property to return the radius that is semantically meaningful as the “bore” for that hole variant:MultiRadiusHole/ counterbore →minor_radius(narrowest bore, i.e. the through-drill diameter).CountersinkHole→bore_radius(the cylindrical section beneath the conical entry, i.e. the drill-bit diameter).CounterdrillHole→bore_radius(the main cylindrical bore, not the pilot/guide cylinder at the entry).
In all cases the returned value is the radius you would use to select a drill bit or a fastener for the feature.
- Returns:
Primary bore radius.
- Return type:
float
- property revolution_frame: Frame3D#
Frame3D placing
revolution_profilein 3D for the revolution.
- property revolution_profile: Contour2D#
Closed 2D half-section that, when revolved around
axis, reproduces the hole.
- revolved_solid(angle: float = 6.283185307179586) Solid#
Reconstruct the hole’s negative volume as a revolved solid.
- set_cached_properties(radius: float | None = None, axis: Vector3D | None = None, location: Point3D | None = None, face_type_counts: dict | None = None, access_direction: tuple[float, float, float] | None = None, entry_blend_radius: float | None = None, floor_blend_radius: float | None = None) None#
Set pre-computed geometric properties to avoid redundant OCP calls.
Called by extractors that have already computed these values during the extraction process.
- Parameters:
radius – The bore radius (smallest cylindrical radius).
axis – The hole axis direction vector.
location – The hole center location.
face_type_counts – Dict with surface type counts from coaxial analysis.
access_direction – Unit vector for tool approach direction.
entry_blend_radius – Minor radius of the entry-side fillet, if any.
floor_blend_radius – Minor radius of the floor-side fillet, if any.
- topology: Literal['isolated', 'complex', 'splitting_adjacency', 'transition', 'unknown'] = 'isolated'#
- class volmdlr_tools.features.feature_types.MultiRadiusHole(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
HoleBase class for holes with multiple distinct cylindrical radii (counterbore, stepped).
- property access_direction: Vector3D | None#
Tool-feed direction, derived from the radius profile.
Overrides the extractor-cached value to keep
origin,access_direction, andstepsin sync: all three reflect the side where the largest-radius cylinder is. Returns the axis unit vector pointing from the entry into the bore (i.e. opposite to the side where the largest-radius cylinder sits along the axis).
- property counterbore_depth: float | None#
Depth of the counterbore recess (larger cylindrical section).
- property diameter: float | None#
Get bore diameter (smallest) for base class compatibility.
- property major_diameter: float | None#
Get the largest diameter.
- property major_radius: float | None#
Get the largest radius.
- property minor_diameter: float | None#
Get the smallest diameter.
- property minor_radius: float | None#
Get the smallest radius.
- property radius: float#
Primary bore radius — the narrow drill-bit diameter (minor_radius).
- property steps: list[HoleStep]#
List of cylindrical sections, ordered entry → bore.
Each
HoleStepcarries the section’s radius, the point on the axis at its entry-side end, and its axial length. The first element is the entry (largest radius for counterbore / stepped holes); the last is the bore (smallest radius).- Returns:
List of
HoleStep. Empty if the hole has no cylindrical faces.
- class volmdlr_tools.features.feature_types.OpenPocket(obj: TopoDS_Shape, nodes: list[int], floor_node: int | None = None, open_sides: int = 1, base_nodes: list[int] | None = None, name: str = '')#
Bases:
PocketA pocket that is open on one or more sides to the part boundary.
An open pocket differs from a closed pocket in that one or more wall faces touch the part boundary, allowing access from the side rather than only from the top.
- Attributes:
open_sides: Number of sides open to the boundary.
- feature_type: FeatureType = 'open_pocket'#
- topology: Literal['isolated', 'complex', 'splitting_adjacency', 'transition', 'unknown'] = 'complex'#
- class volmdlr_tools.features.feature_types.OpenSlot(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, floor_node: int | None = None, name: str = '')#
Bases:
SlotA slot that is open on one side to the part boundary.
An open slot is accessible from one end, with the other end closed by a wall face.
- feature_type: FeatureType = 'open_slot'#
- open_ends: OpenEndsCount = 1#
- class volmdlr_tools.features.feature_types.Pocket(obj: TopoDS_Shape, nodes: list[int], floor_node: int | None = None, base_nodes: list[int] | None = None, name: str = '')#
Bases:
FeatureBase class representing a pocket feature (material removal).
A pocket is a depression or cavity in a part, characterized by a floor face and surrounding wall faces. The edges between the floor and walls are concave.
- Attributes:
category: Feature category (CUT for material removal). floor_node: Node index of the floor face.
- property axis: Vector3D#
Get the axis direction vector of the pocket.
- Returns:
Unit vector representing the pocket axis direction.
- Return type:
Vector3D
- category: FeatureCategory = 'cut'#
- property depth: float#
Get the depth of the pocket.
Lazily calculates the depth from the bounding box if not already computed.
- Returns:
Depth of the pocket along its axis.
- Return type:
float
- feature_type: FeatureType = 'pocket'#
- classmethod from_faces_and_nodes(faces: list[Face], nodes: list[int], floor_node: int | None = None, name: str = '', **kwargs) Pocket#
Create a Pocket (or subclass) from a list of faces.
- Parameters:
faces – List of faces that define the pocket geometry.
nodes – List of nodes that define the pocket geometry.
floor_node – Index of the floor face. Must be present in nodes if provided.
name – Optional name for the pocket, defaults to empty string.
kwargs – Additional keyword arguments forwarded to the constructor (e.g. open_sides).
- Returns:
Pocket instance (or subclass instance when called on a subclass).
- Raises:
ValueError – If floor_node is provided but not found in nodes.
- property location: Point3D#
Get the center location of the pocket.
- Returns:
Center point of the pocket’s bounding box.
- Return type:
Point3D
- property wall_nodes: list[int]#
Get the wall face nodes.
- Returns:
List of face indices that form the walls (all nodes except floor).
- Return type:
list[int]
- class volmdlr_tools.features.feature_types.RectangularCavity(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
SquareCavityRepresents a rectangular cavity with unequal width and length.
Inherits all functionality from SquareCavity but represents cavities where the width and length dimensions differ beyond the tolerance threshold (1e-6).
- feature_type: FeatureType = 'rectangular_cavity'#
- class volmdlr_tools.features.feature_types.RevolutionFeature(obj: TopoDS_Shape, nodes: list[int], is_internal: bool = False, base_nodes: list[int] | None = None, name: str = '')#
Bases:
FeatureBase class for revolution features (Cone, Cylinder, Torus).
Provides shared infrastructure: -
is_internal/categoryproperties -base_nodes,cap_faces,support_faces,isolated_faceslists -from_faces_and_nodesclassmethod - Abstractget_axis_info()for polymorphic axis access- cap_faces: list[int]#
- property category: FeatureCategory#
CUT if internal, ADDITION if external.
- feature_type: FeatureType = 'unknown'#
- classmethod from_faces_and_nodes(faces: list[Face], nodes: list[int], is_internal: bool = False, name: str = '', cap_faces: list[int] | None = None, support_faces: list[int] | None = None, isolated_faces: list[int] | None = None) RevolutionFeature#
Create an instance from faces, setting optional face lists.
- abstractmethod get_axis_info() tuple[Vector3D, Point3D] | None#
Return
(axis_direction, axis_point)for this feature, or None.
- property is_internal: bool#
Return True if this is an internal feature (bore/countersink/fillet).
- isolated_faces: list[int]#
- support_faces: list[int]#
- class volmdlr_tools.features.feature_types.Rib(obj: TopoDS_Shape, nodes: list[int], end_face_sides: list[list[int]], shell_face_nodes: list[int], base_face_nodes: list[int] | None = None, fillet_nodes: list[int] | None = None, thickness: float | None = None, name: str = '')#
Bases:
FeatureA single rib segment: a thin protruding wall on a part.
The wall’s two-side partition (
end_face_sides) is the stored truth;end_face_nodes(their union) andbase_face_nodes(an alias of the inheritedbase_nodes) are derived read-only views, so each fact lives in exactly one place.- Parameters:
obj – The
TopoDS_Shape(shell) representing the rib’s own faces.nodes – AAG face indices of the rib’s own faces (end + shell + fillet).
end_face_sides – The two-side partition of the wall’s end faces — one list of AAG face indices per wall side, each non-empty, disjoint.
shell_face_nodes – The rib’s exposed faces (top / end caps).
base_face_nodes – Surrounding part walls the rib stands on (references; stored as the Feature-level
base_nodes).fillet_nodes – Blend faces between rib faces, if any.
thickness – Distance between the two end faces, in the model’s units.
name – Optional name for the feature.
- property base_face_nodes: list[int]#
Alias of the Feature-level
base_nodes, in the rib vocabulary.
- category: FeatureCategory = 'addition'#
- property end_face_nodes: list[int]#
The wall’s end faces, both sides together (union of
end_face_sides).
- feature_type: FeatureType = 'rib'#
- fillet_nodes: list[int]#
- classmethod from_faces_and_nodes(faces: list[Face], nodes: list[int], shell_face_nodes: list[int], end_face_nodes: list[int] | None = None, end_face_sides: list[list[int]] | None = None, base_face_nodes: list[int] | None = None, fillet_nodes: list[int] | None = None, thickness: float | None = None, name: str = '') Rib#
Create a Rib from a list of faces and the recognized face roles.
The wall may be given either as the two-side partition (
end_face_sides, required for merged ribs with more than two end faces) or as a plain two-faceend_face_nodes(one face per side). When both are given they must agree.- Parameters:
faces – The rib’s own faces (end + shell + fillet) as volmdlr
Faceor rawTopoDS_Faceobjects; used to build the shell.nodes – AAG face indices matching
faces.shell_face_nodes – The rib’s exposed faces (top / end caps).
end_face_nodes – The wall’s end faces for the classic two-face rib; the partition is derived (one face per side).
end_face_sides – The two-side partition of the wall’s end faces.
base_face_nodes – Surrounding part walls (references).
fillet_nodes – Blend faces between rib faces, if any.
thickness – Distance between the two end faces, in the model’s units.
name – Optional name for the feature.
- Returns:
A Rib instance.
- Raises:
ValueError – If neither wall spelling is given, if a non-two-face
end_face_nodescomes without its partition, or if the two spellings disagree.
- topology: Literal['isolated', 'complex', 'splitting_adjacency', 'transition', 'unknown'] = 'complex'#
- class volmdlr_tools.features.feature_types.RibStructure(obj: TopoDS_Shape, nodes: list[int], segment_nodes: list[list[int]], name: str = '')#
Bases:
FeatureA set of rib segments connected at junctions (T-, X-, … connections).
A rib structure groups individual
Ribsegments whose own faces meet at a junction.segment_nodesrecords each member segment’s own face indices;nodesis their union (the structure’s own faces).- Parameters:
obj – The
TopoDS_Shape(shell) of all the structure’s faces.nodes – AAG face indices of all the structure’s own faces.
segment_nodes – One list of own-face indices per member rib segment.
name – Optional name for the feature.
- category: FeatureCategory = 'addition'#
- copy(deep: bool = True, memo: Any = None) RibStructure#
Copy the RibStructure, preserving its member segments.
- feature_type: FeatureType = 'rib_structure'#
- classmethod from_segments(segments: list[Rib], faces: list[Face], name: str = '') RibStructure#
Create a RibStructure from its member rib segments.
- Parameters:
segments – The connected
Ribsegments forming the structure.faces – The own faces of all segments (volmdlr
Faceor rawTopoDS_Face), used to build the structure’s shell.name – Optional name for the feature.
- Returns:
A RibStructure instance.
- groups_member_features: bool = True#
Whether this type’s
nodesare the union of member features’ own faces rather than faces it claims in its own right — a blend chain over its blends, a rib structure over its segments. Face-coverage reporting skips such types: counting both would report structural containment as two features competing for one face, and bury the real overlaps. A new type that groups others should say so here, or its members’ faces will all read as contested. Unlikefeature_typethis cannot be enforced in__init_subclass__:Falseis the right answer for almost every type, so a missing declaration is indistinguishable from a correct one.
- topology: Literal['isolated', 'complex', 'splitting_adjacency', 'transition', 'unknown'] = 'complex'#
- class volmdlr_tools.features.feature_types.RoundCavity(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
RoundInnerFeature,CavityRepresents a circular/cylindrical cavity.
A round cavity is defined by one or two cylindrical surfaces and has properties like diameter, axis direction, and center location derived from the cylindrical geometry.
- feature_type: FeatureType = 'round_cavity'#
- class volmdlr_tools.features.feature_types.RoundHole(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
HoleRepresents a simple cylindrical hole.
A round hole is defined by one or more cylindrical surfaces and optionally a planar bottom face for blind holes. This is the most common hole type.
- class volmdlr_tools.features.feature_types.SharpEdge(obj: TopoDS_Shape, node: int, name: str = '')#
Bases:
EdgeEmboss feature type.
- class volmdlr_tools.features.feature_types.Slot(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, floor_node: int | None = None, name: str = '')#
Bases:
FeatureBase class representing a slot feature (material removal).
A slot is an elongated groove or channel in a part, characterized by a linear chain of faces connected by concave edges. The typical pattern is floor-wall-floor or a U-shaped cross-section.
- Attributes:
category: Feature category (CUT for material removal). open_ends: Number of open ends; see OpenEndsCount.
- property axis: Vector3D#
Get the axis direction vector of the slot (along its length).
- Returns:
Unit vector representing the slot axis direction.
- Return type:
Vector3D
- category: FeatureCategory = 'cut'#
- property depth: float#
Get the depth of the slot.
- Returns:
Depth dimension of the slot.
- Return type:
float
- feature_type: FeatureType = 'slot'#
- classmethod from_faces_and_nodes(faces: list[Face], nodes: list[int], base_nodes: list[int] | None = None, floor_node: int | None = None, name: str = '', *args, **kwargs) Slot#
Create a Slot from a list of faces.
- Parameters:
faces – List of faces that define the slot geometry.
nodes – List of nodes that define the slot geometry.
base_nodes – Face indices of the base/entry faces for the slot.
floor_node – Node index of the floor face.
name – Optional name for the slot, defaults to empty string.
- Returns:
Slot instance.
- property length: float#
Get the length of the slot.
- Returns:
Length dimension of the slot.
- Return type:
float
- property location: Point3D#
Get the center location of the slot.
- Returns:
Center point of the slot’s bounding box.
- Return type:
Point3D
- open_ends: OpenEndsCount = 0#
- topology: Literal['isolated', 'complex', 'splitting_adjacency', 'transition', 'unknown'] = 'complex'#
- property width: float#
Get the width of the slot.
- Returns:
Width dimension of the slot.
- Return type:
float
- class volmdlr_tools.features.feature_types.SlottedCavity(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
CavityRepresents a slotted cavity (obround/elongated cavity).
A slotted cavity consists of a combination of cylindrical surfaces and planar faces, typically forming a slot shape with rounded ends. Common in mechanical design for allowing adjustment along one axis.
A plain slot has two side walls and two end caps, but a keyhole slot carries more end caps of differing radii, so the face count is not fixed.
Two contracts live side by side here. The cached dimensions (
width,axis,location) raise when the slot has too few end caps to have them. The measured parameters (length,center_distance,slot_length,end_circles) answer None — or an empty list — instead, so a caller can read them across a mixed batch of cavities without guarding each one.- property axis: Vector3D#
Get the axis direction vector of the slotted cavity.
- Returns:
Unit vector representing the cavity axis direction.
- property center_distance: float | None#
Get the distance between the centers of the two end circles.
The project glossary (
CONTEXT.md) calls this the “center distance” (entraxe).slot_lengthis this plus both end radii.- Returns:
Center-to-center distance of the slot end caps, or None when fewer than two end circles can be found.
- property end_circles: list[EndCircle]#
Get the end circle of each cylindrical end cap of the slot.
The cylinder axis origin returned by OCP sits at an arbitrary height along the axis, so each center is re-projected onto its axis at the level of the slot’s mid-thickness. That level is read from the faces’ own bounding box rather than from
location, so an end circle can be had without the slot’s cached properties (and their stricter requirements) being computed.- Returns:
One
EndCircleper cylindrical face; empty when the slot exposes none.
- feature_type: FeatureType = 'slotted_cavity'#
- property length: float | None#
Get the bounding extent of the slot along its long direction.
The project glossary (
CONTEXT.md) reserves “length” for the bounding extent, and keeps the design quantity slot rules are stated against under its own name,slot_length. The two coincide on a clean slot; the extent is the one that still answers when a boundary is irregular, and it is a measurement of the faces rather than of the end circles.- Returns:
Extent between the outer ends of the slot, or None when fewer than two end circles can be found.
- property location: Point3D#
Get the center location of the slotted cavity.
- Returns:
Center point of the cavity.
- property slot_length: float | None#
Get the canonical slot length: center distance plus both end radii.
The project glossary (
CONTEXT.md) calls this the “slot length” (longueur totale) — the quantity design rules on slots are stated against. Distinct fromlength, defined on this class, which is the bounding extent between the end faces.- Returns:
Overall slot length along its long direction, or None when fewer than two end circles can be found.
- property width: float#
Get the width of the slotted cavity.
- Returns:
Width dimension of the cavity.
- class volmdlr_tools.features.feature_types.SquareCavity(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
SquareInnerFeature,CavityRepresents a square cavity with equal width and length.
A square cavity is defined by four or more planar wall faces spanning two directions, with corners that may be sharp or rounded by cylindrical corner faces. The width and length are approximately equal (within tolerance of 1e-6).
- feature_type: FeatureType = 'square_cavity'#
- class volmdlr_tools.features.feature_types.Step(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, floor_node: int | None = None, name: str = '')#
Bases:
FeatureA step feature - the simplest form of slot/pocket.
A step consists of exactly 2 faces connected by a single concave edge. This represents a simple ledge or shelf in the part geometry.
Based on Joshi & Chang: “A 2-face pattern with a concave edge”
- property axis: Vector3D#
Get the axis direction vector of the step.
- Returns:
Unit vector representing the step axis direction.
- Return type:
Vector3D
- category: FeatureCategory = 'cut'#
- property depth: float#
Get the depth (height) of the step.
- Returns:
Depth dimension of the step.
- Return type:
float
- feature_type: FeatureType = 'step'#
- classmethod from_faces_and_nodes(faces: list[Face], nodes: list[int], base_nodes: list[int] | None = None, floor_node: int | None = None, name: str = '', *args, **kwargs) Step#
Create a Step from a list of faces.
- Parameters:
faces – List of exactly 2 faces that define the step geometry.
nodes – List of 2 nodes that define the step geometry.
base_nodes – Face indices of the base/entry faces for the step.
name – Optional name for the step, defaults to empty string.
- Returns:
Step instance.
- property location: Point3D#
Get the center location of the step.
- Returns:
Center point of the step’s bounding box.
- Return type:
Point3D
- topology: Literal['isolated', 'complex', 'splitting_adjacency', 'transition', 'unknown'] = 'complex'#
- class volmdlr_tools.features.feature_types.SteppedHole(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
MultiRadiusHoleRepresents an N-level stepped hole (3+ distinct diameters).
A stepped hole has three or more cylindrical sections with distinct diameters, connected through annular planar transition faces. This generalizes CounterboreHole (which is a 2-level stepped hole).
Per Sunil et al. (2010), a stepped hole is “a series of coaxial holes, one inside the other, such as a counter bore or a counter sunk hole.”
- property feature_type: FeatureType#
Stepped holes are classified as
FeatureType.STEPPED_HOLE.
- property step_count: int#
Number of distinct diameter levels.
- property step_radii: list[float]#
Distinct radii in entry → bore order (largest first).
- Returns:
List of radii. Empty if no cylindrical faces are present.
- class volmdlr_tools.features.feature_types.TaperHole(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, name: str = '')#
Bases:
HoleRepresents a tapered/conical hole.
A taper hole is purely conical, often used for tapered pins or special fastening applications.
- property feature_type: FeatureType#
Taper holes are classified as
FeatureType.TAPER_HOLE.
- property taper_angle: float | None#
Get the taper angle: the included angle of the hole’s cone, in radians.
When several conical faces are present (e.g. a drill-point bottom cone under the tapered wall), the wall is the cone with the widest opening.
- Returns:
Included angle of the tapered wall in radians, or None if no conical face is found.
- class volmdlr_tools.features.feature_types.ThroughSlot(obj: TopoDS_Shape, nodes: list[int], base_nodes: list[int] | None = None, floor_node: int | None = None, name: str = '')#
Bases:
SlotA slot that passes completely through the part.
A through slot is open on both ends, allowing passage from one side of the part to the other.
- feature_type: FeatureType = 'through_slot'#
- open_ends: OpenEndsCount = 2#
- class volmdlr_tools.features.feature_types.Torus(obj: TopoDS_Shape, nodes: list[int], is_internal: bool = False, base_nodes: list[int] | None = None, name: str = '')#
Bases:
RevolutionFeatureA class representing a toroidal feature (rounded edge or fillet).
Can represent both external tori (rounded edges) and internal tori (fillets). The is_internal property determines the feature category: - External (is_internal=False): ADDITION category - Internal (is_internal=True): CUT category
- property axis: Vector3D#
Get the axis direction vector of the torus.
The axis is perpendicular to the plane of the torus (the major circle plane).
- Returns:
Unit vector representing the torus axis direction.
- Return type:
Vector3D
- property center: Point3D#
Get the center location of the torus.
This is the center of the major circle (the torus axis passes through this point).
- Returns:
Center point of the torus.
- Return type:
Point3D
- feature_type: FeatureType = 'torus'#
- get_axis_info() tuple[Vector3D, Point3D] | None#
Return
(axis_direction, center).
- property major_radius: float#
Get the major radius of the torus.
The major radius is the distance from the torus center to the tube center.
- Returns:
Major radius.
- Return type:
float
- property minor_radius: float#
Get the minor radius of the torus.
The minor radius is the radius of the tube cross-section.
- Returns:
Minor radius.
- Return type:
float
- property overall_diameter: float | None#
Get the overall outer diameter of the torus.
- Returns:
Overall diameter (2 * (major_radius + minor_radius)), or None if unavailable.
- Return type:
float | None
- property tube_diameter: float | None#
Get the tube diameter of the torus.
- Returns:
Tube diameter (2 * minor_radius), or None if unavailable.
- Return type:
float | None
- class volmdlr_tools.features.feature_types.UnknownFeature(obj: TopoDS_Shape, nodes: list[int], category: FeatureCategory = FeatureCategory.UNKNOWN, heuristic_source: str = '', seed_nodes: list[int] | None = None, name: str = '')#
Bases:
FeatureA detected feature that has not been classified into a specific type.
Used as the “we found something but don’t know what it is yet” placeholder for detector hits that didn’t match a specific feature class.
- Parameters:
obj – The TopoDS_Shape object representing the feature.
nodes – The face indices that compose the feature.
category – CUT (material removal) or ADDITION (material addition).
heuristic_source – Which algorithm detected this feature (for traceability).
seed_nodes – Optional seed face indices used during detection.
name – Optional name for the feature.
- copy(deep: bool = True, memo: Any = None) UnknownFeature#
Copy the UnknownFeature, preserving all attributes.
- Returns:
A copy of this UnknownFeature.
- feature_type: FeatureType = 'unknown'#
- classmethod from_faces_and_nodes(faces: list[Face], nodes: list[int], category: FeatureCategory = FeatureCategory.UNKNOWN, heuristic_source: str = '', seed_nodes: list[int] | None = None, name: str = '', *args, **kwargs) UnknownFeature#
Create an UnknownFeature from a list of faces and nodes.
- Parameters:
faces – List of Face objects composing the feature.
nodes – List of face indices in the AAG.
category – CUT or ADDITION.
heuristic_source – Which algorithm detected this feature.
seed_nodes – Optional seed face indices.
name – Optional name for the feature.
- Returns:
An UnknownFeature instance.