Source code for pyrobopath.process.utilities

import numpy as np
import networkx as nx
from typing import List

from pyrobopath.toolpath import Toolpath
from .dependency_graph import DependencyGraph


[docs] def create_dependency_graph_by_z(toolpath: Toolpath) -> DependencyGraph: """ Create a layered dependency graph from a toolpath based on contour Z-heights. Constructs a `DependencyGraph` where each contour is a node, and dependencies are defined by the relative Z-height of the contours. Specifically, a contour is dependent on all contours in the layer directly below it. Parameters ---------- toolpath : Toolpath A toolpath composed of multiple contours to be layered and ordered. Returns ------- DependencyGraph A dependency graph with inter-layer dependencies based on Z-height. """ contour_z = [] for contour in toolpath.contours: z_values = np.array(contour.path)[:, 2] z_values = set(z_values) contour_z.append(z_values.pop()) dg = DependencyGraph() unique_z = sorted(set(contour_z)) # connect start node ind = [i for i, x in enumerate(contour_z) if x == unique_z[0]] for first_layer_node in ind: dg.add_node(first_layer_node) for a, b in zip(unique_z[:-1], unique_z[1:]): ind_a = [i for i, x in enumerate(contour_z) if x == a] ind_b = [i for i, x in enumerate(contour_z) if x == b] for upper in ind_b: dg.add_node(upper, ind_a) return dg
[docs] def batch_digraph(dg: DependencyGraph, max_nodes: int) -> List[DependencyGraph]: """ Split a directed graph into multiple subgraphs with at most `max_nodes` nodes each. Parameters ---------- dg : DependencyGraph The input graph to be split into subgraphs. max_nodes : int The maximum number of nodes allowed in each subgraph. Returns ------- subgraphs : List[DependencyGraph] A list of `DependencyGraph` instances, each containing at most `max_nodes` nodes. The subgraphs preserve the internal structure of the original graph for the included node subsets. Notes ----- - This function does not attempt to preserve connectivity between subgraphs. - Nodes are partitioned in the order returned by `dg._graph.nodes`. - The `_graph` attribute is deep-copied into each subgraph to ensure isolation. Examples -------- >>> dg = DependencyGraph() >>> # Assume dg._graph has 10 nodes >>> subgraphs = split_digraph(dg, max_nodes=4) >>> len(subgraphs) 3 >>> [len(sg._graph.nodes) for sg in subgraphs] [4, 4, 2] """ subgraphs = [] nodes = list(dg._graph.nodes) # Simple greedy partition for i in range(0, len(nodes), max_nodes): node_subset = nodes[i : i + max_nodes] subgraph = DependencyGraph() subgraph._graph = dg._graph.subgraph(node_subset).copy() # type:ignore subgraphs.append(subgraph) return subgraphs
[docs] def stratify_digraph(dg: DependencyGraph) -> List[DependencyGraph]: subgraphs = [] generations = nx.topological_generations(dg._graph) for gen in generations: subgraph = DependencyGraph() subgraph._graph.add_nodes_from(gen) subgraphs.append(subgraph) return subgraphs