Example Scripts#

This section includes example scripts demonstrating how to use volmdlr_tools.

Assembly Graph#

These scripts demonstrate how to use the GraphAssembly class to create and manipulate graphs from CAD assembly.

Main Features#

This script demonstrates the main features of the GraphAssembly class.

Graph Assembly Example#
 1"""Script to showcase the volmdlr_tools.graph.assembly module."""
 2
 3import math
 4from pathlib import Path
 5from time import perf_counter
 6
 7from volmdlr.model import VolumeModel
 8
 9from volmdlr_tools.graph.assembly import GraphAssembly
10
11STEP_FILE = Path(__file__).absolute().parent.parent.parent.parent / "data" / "step" / "test.stp"
12
13# DEFINING OBJECTS
14# Define a VolumeModel from a STEP file
15volume_model = VolumeModel.from_step(str(STEP_FILE))
16
17# Plot the VolumeModel
18volume_model.display_3d()
19
20# Define a GraphAssembly from the VolumeModel
21graph = GraphAssembly.from_volume_model(volume_model)
22
23# Plot the GraphAssembly
24graph.plot_graph().plot()
25
26# Print the GraphAssembly
27graph.print_tree()
28
29# SETTING METADATA
30# Set bounding box attribute for Solid nodes
31graph.set_bounding_box_attribute()
32
33t_start = perf_counter()
34# Set the minimum distance between Solid nodes
35graph.set_distance_attribute()
36t_end = perf_counter()
37exact_distances = {
38    edge: graph[edge].get("distance")
39    for edge in graph.edges
40    if graph[edge[0]]["class"] == "Solid" and graph[edge[1]]["class"] == "Solid"
41}
42print(f"Time to calculate all distances using exact method: {t_end - t_start} s")
43
44t_start = perf_counter()
45# Set the approximated minimum distance between Solid nodes
46graph.set_distance_attribute(exact=False)
47t_end = perf_counter()
48print(f"Time to calculate all distances using approximated method: {t_end - t_start} s")
49for edge in graph.edges:
50    if graph[edge[0]]["class"] == "Solid" and graph[edge[1]]["class"] == "Solid":
51        print("graph[edge].get(distance): ", graph[edge].get("distance"))
52        print("exact_distances[edge]: ", exact_distances[edge])
53        assert math.isclose(graph[edge].get("distance"), exact_distances[edge], abs_tol=0.005)
54
55# Set the connection between solids nodes
56graph.set_connection_geometry()
57
58# EXTRACTING DATA
59# Extract all the solids near the solid named "PLATE"
60solids = graph.get_close_solids(solid_node_id=1, maximum_distance=0.1)
61
62# SERIALIZE
63graph.save_export_to_file(selector="json", filepath="graph.json")
64
65# DESERIALIZE
66graph2 = GraphAssembly.from_json("graph.json")
67# assert graph == graph2

Rules Check Showcases#

These scripts demonstrate how to use the GraphAssembly class to check rules on the assembly graph.

Example of Position Rule Check#
 1"""Showcase of position rule checking using graph."""
 2
 3from pathlib import Path
 4
 5from volmdlr.model import VolumeModel
 6
 7from volmdlr_tools.graph.assembly import GraphAssembly
 8
 9STEP_FILE = Path(__file__).parent.parent.parent.parent.parent / "data" / "step" / "test.stp"
10
11# DEFINING OBJECTS
12volume_model = VolumeModel.from_step(str(STEP_FILE))
13# volume_model.display_3d()
14graph = GraphAssembly.from_volume_model(volume_model)
15# graph.plot()
16
17
18# RULE: Check that all the nuts are under the plate
19# Note: The plate is Solid named "PLATE" and the nuts are Solids named "NUT"
20TOLERANCE = 1e-6
21
22# Set the bounding box attribute for all the nodes that are Solid
23graph.set_nodes_attribute(
24    attribute="bounding_box",
25    attribute_function=lambda node: graph.get_shape(node).bounding_box,
26    filter_function=lambda node: graph[node]["class"] == "Solid",
27)
28
29# Retrieve the minimum altitude of the plate
30plate_id = next(iter(graph.get_nodes(filter_function=lambda node: graph[node]["name"] == "PLATE")))
31plate_min_altitude = graph[plate_id]["bounding_box"].zmin
32
33# Get the nodes with name "NUT" that are above the plate minimum altitude
34incorrect_nodes = graph.get_nodes(
35    filter_function=lambda node: graph[node]["name"] == "NUT"
36    and graph[node]["class"] == "Solid"
37    and graph[node]["bounding_box"].zmax - TOLERANCE > plate_min_altitude
38)
39
40if len(incorrect_nodes) == 0:
41    print("Rule #1: All the nuts are under the plate.")
42else:
43    print(f"Rule #1: The following nodes are above the plate: {incorrect_nodes.keys()}")
44    primitives = [graph.get_shape(node) for node in incorrect_nodes] + [graph.get_shape(plate_id)]
45    VolumeModel(primitives=primitives).display_3d()
Example of Distance Rule Check#
 1"""Showcase of distance rule checking using graph."""
 2
 3from pathlib import Path
 4
 5from volmdlr.model import VolumeModel
 6
 7from volmdlr_tools.graph.assembly import GraphAssembly
 8
 9STEP_FILE = Path(__file__).parent.parent.parent.parent.parent / "data" / "step" / "test.stp"
10
11# DEFINING OBJECTS
12volume_model = VolumeModel.from_step(str(STEP_FILE))
13# volume_model.display_3d()
14graph = GraphAssembly.from_volume_model(volume_model)
15# graph.plot()
16
17
18# RULE: Check if the bolts are at a minimum distance between each other
19# Note: The bolts are Solids named "BOLT"
20MIN_DISTANCE = 0.01
21
22# Create an edge between all the bolts
23graph.create_edges(filter_function=lambda edge: graph[edge[0]]["name"] == "BOLT" and graph[edge[1]]["name"] == "BOLT")
24
25# Set the distance attribute between all the bolts
26graph.set_edges_attribute(
27    attribute="distance",
28    attribute_function=lambda edge: graph.get_shape(edge[0]).distance(graph.get_shape(edge[1])),
29    filter_function=lambda edge: graph[edge[0]]["name"] == "BOLT" and graph[edge[1]]["name"] == "BOLT",
30)
31
32# Get the edges with distance less than 0.1
33incorrect_edges = graph.get_edges(
34    filter_function=lambda edge: graph[edge].get("distance") is not None  # the distance attribute is set
35    and graph[edge]["distance"] < MIN_DISTANCE  # the distance is less than MIN_DISTANCE
36    and graph[edge[0]]["name"] == "BOLT"  # the first node is a BOLT
37    and graph[edge[1]]["name"] == "BOLT"  # the second node is a BOLT
38)
39
40# Result
41if len(incorrect_edges) == 0:
42    print("Rule #2: All the bolts are at a minimum distance between each other.")
43else:
44    print(f"Rule #2: The following bolts are at a distance less than {MIN_DISTANCE} to another bolt: {incorrect_edges}")
45    primitives = [graph.get_shape(edge[0]) for edge in incorrect_edges] + [
46        graph.get_shape(edge[1]) for edge in incorrect_edges
47    ]
48    VolumeModel(primitives=primitives).display_3d()
Example of Connection Rule Check#
 1"""Showcase of connection rule checking using graph."""
 2
 3from pathlib import Path
 4
 5from volmdlr.model import VolumeModel
 6
 7from volmdlr_tools.graph.assembly import GraphAssembly
 8
 9STEP_FILE = Path(__file__).parent.parent.parent.parent.parent / "data" / "step" / "test.stp"
10
11# DEFINING OBJECTS
12volume_model = VolumeModel.from_step(str(STEP_FILE))
13# volume_model.display_3d()
14graph = GraphAssembly.from_volume_model(volume_model)
15# graph.plot()
16
17
18# RULE: Check that the rod is only connected to other solid with "Cylindrical" connection
19# Note: The rod is a unique Solid named "ROD"
20
21# Set the connection geometry attribute for all the edges between solids
22graph.set_connection_geometry()
23
24# Extract the edges with connection to the rod
25edges = graph.get_edges(
26    filter_function=lambda edge: (graph[edge[0]]["name"] == "ROD" or graph[edge[1]]["name"] == "ROD")
27    and graph[edge].get("connection") is not None
28)
29
30# Identify the edges that are not cylindrical connection
31for edge, attribute in edges.items():
32    shape1 = graph.get_shape(edge[0])
33    shape2 = graph.get_shape(edge[1])
34    connection_faces = [face for contact_pairs in attribute["connection"].contact_faces for face in contact_pairs]
35    vm = VolumeModel([shape1, shape2, *connection_faces]).display_3d()
36    for face in connection_faces:
37        print(f"{face.__class__.__name__} connection between {graph[edge[0]]['name']} and {graph[edge[1]]['name']}")
Example of Proximity Rule Check#
 1"""Showcase of proximity rule checking using graph."""
 2
 3from pathlib import Path
 4
 5from volmdlr.model import VolumeModel
 6
 7from volmdlr_tools.graph.assembly import GraphAssembly
 8
 9STEP_FILE = Path(__file__).parent.parent.parent.parent.parent / "data" / "step" / "test.stp"
10
11# DEFINING OBJECTS
12volume_model = VolumeModel.from_step(str(STEP_FILE))
13# volume_model.display_3d()
14graph = GraphAssembly.from_volume_model(volume_model)
15# graph.plot()
16
17
18# RULE: The ROD should not have more than 4 solids at a given distance, and the plate should not be part of it
19# Note: The rod is a unique Solid named "ROD" and the plate is a Solid named "PLATE"
20PROXIMITY_DISTANCE = 0.01
21
22# Set the distance attribute for all the edges between solids
23graph.set_distance_attribute()
24
25# Extract the solid closer than PROXIMITY_DISTANCE to the rod
26edges = graph.get_edges(
27    filter_function=lambda edge: (graph[edge[0]]["name"] == "ROD" or graph[edge[1]]["name"] == "ROD")
28    and graph[edge].get("distance") is not None
29    and graph[edge]["distance"] < PROXIMITY_DISTANCE
30)
31
32# Create a subgraph with the rod and the solids at a given distance
33rod_proximity_graph = graph.subgraph(lambda node: node in [edge[0] for edge in edges] + [edge[1] for edge in edges])
34
35# Check if the rod is connected to more than 4 solids
36print(f"Rule #1: The rod is close to {rod_proximity_graph.n_nodes - 1} solids.")
37print(
38    f"Rule #2: The plate is near the rod: {len(rod_proximity_graph.get_nodes(filter_function=lambda node: graph[node]['name'] == 'PLATE')) > 0}"
39)

Feature Recognition#

This script demonstrates how to use the FeaturesRecognizer class to recognize features such as cavities and blends.

Feature Recognition Example#
 1"""Script to showcase the feature recognition module."""
 2
 3from pathlib import Path
 4
 5from volmdlr.model import VolumeModel
 6
 7from volmdlr_tools import features_recognition
 8from volmdlr_tools.features.feature_types import Blend, BlendChain, Cavity
 9
10folder = Path(__file__).resolve().parent.parent / "data" / "step"
11
12# STEP_FILE = folder / "test_faces.step"
13# STEP_FILE = folder / "nist_ctc_01_asme1_nx1980_rd-ap242e3.stp"
14STEP_FILE = folder / "nist_ctc_02_asme1_nx1980_rc-ap242e3.stp"
15# STEP_FILE = folder / "nist_ctc_03_asme1_nx1980_rc-ap242e3.stp"
16# STEP_FILE = folder / "nist_ctc_04_asme1_nx1980_rd-ap242e3.stp"
17# STEP_FILE = folder / "nist_ctc_05_asme1_nx1980_rd-ap242e3.stp"
18
19
20volume_model = VolumeModel.from_step(str(STEP_FILE))
21volume_model.display_3d(show_curves=True)
22
23shape = volume_model.primitives[0]
24
25# Create a FeaturesRecognizer object with the shape
26features_processor = features_recognition.FeatureProcessor(shape=shape)
27
28# --- Blends Recognition ---
29# Choose the maximum allowable radius of a blend face.
30features_processor.extract_blends(max_radius=0.05)
31
32# Get blending faces.
33features_processor.show_features(feature_type=Blend)
34
35features_processor.show_features(feature_type=BlendChain)
36
37
38# --- Cavitivies Recognition ---
39features_processor.extract_cavities()
40cavitivies_features = features_processor.cavities
41features_processor.show_features(feature_type=Cavity)
42
43# --- Sharp edges Recognition ---
44features_processor.extract_sharp_edges()
45# sharp_edges_feature = features_processor.sharp_edges
46# Get sharp edges.
47sharp_edges = features_processor.sharp_edges
48for edge in sharp_edges:
49    edge.color = (1.0, 0.0, 0.0)
50vol_model = VolumeModel([shape, *sharp_edges])
51vol_model.display_3d(show_curves=True)
52
53features_processor.check_platform()
54# blends_features.check_platform()
55# sharp_edges_feature.check_platform()