Source code for Karana.KUtils.MultibodyTUI.graph

from abc import ABC, abstractmethod
from typing import TypeVar, Generic

from Karana.Dynamics import PhysicalBody, Multibody
from Karana.Frame import Frame, FrameContainer

T = TypeVar("T")


[docs] class GraphAdapter(ABC, Generic[T]): """Common interface for various types of directed graphs The purpose of this class is to define a common interface for wrapper classes that adapt different graphs, each with their own interface, to a single common interface. """
[docs] @abstractmethod def parents(self, node: T, /) -> list[T]: """Get the list of parents for a given node"""
[docs] @abstractmethod def children(self, node: T, /) -> list[T]: """Get the list of children for a given node"""
[docs] @abstractmethod def nodes(self) -> list[T]: """Get the list of all nodes in the graph"""
[docs] def descendants(self, node: T, /) -> list[T]: """Get the list of all descendants for a given node""" # Use a dictionary instead of set to preserve ordering visited = {} stack = [] stack.append(node) results = [] # do a dfs traversal while stack: curr = stack.pop() for child in self.children(curr): if child not in visited: visited[child] = None stack.append(child) return list(visited)
[docs] def ancestors(self, node: T, /) -> list[T]: """Get the list of all ancestors for a given node""" # Use a dictionary instead of set to preserve ordering visited = {} stack = [] stack.append(node) results = [] # do a dfs traversal while stack: curr = stack.pop() for parent in self.parents(curr): if parent not in visited: visited[parent] = None stack.append(parent) return list(visited)
[docs] class BodyGraphAdapter(GraphAdapter[PhysicalBody]): """Multibody wrapper implementing the GraphAdapater interface""" def __init__(self, multibody: Multibody): self.multibody = multibody
[docs] def parents(self, body: PhysicalBody) -> list[PhysicalBody]: if body == self.multibody.virtualRoot(): return [] parent = body.physicalParentBody() return [parent]
[docs] def children(self, body: PhysicalBody) -> list[PhysicalBody]: # return body.childBodies() return body.multibody().childrenBodies(body)
[docs] def nodes(self) -> list[PhysicalBody]: return [self.multibody.virtualRoot()] + self.multibody.physicalBodiesList()
[docs] class FrameGraphAdapter(GraphAdapter[Frame]): """FrameContainer wrapper implementing the GraphAdapater interface""" def __init__(self, frame_container: FrameContainer): self.frame_container = frame_container
[docs] def parents(self, frame: Frame) -> list[Frame]: parent = frame.parent() if parent is None: return [] return [parent]
[docs] def children(self, frame: Frame) -> list[Frame]: # Not ideal, but there doesn't seem to be a way to get the # children directly children = [] for candidate in self.frame_container.frames(): if candidate.parent() == frame: children.append(candidate) return children
[docs] def nodes(self) -> list[Frame]: return self.frame_container.frames()