Skip to content
Snippets Groups Projects
Commit ec2f5da7 authored by mehtank's avatar mehtank
Browse files

Merge branch 'Ports' into 'main'

Ports attach mechanism clean up

See merge request roco/rocolib!5
parent 48eb5ea6
No related merge requests found
from rocolib.api.components import MechanicalComponent from rocolib.api.components import MechanicalComponent
from rocolib.api.composables.GraphComposable import DecorationComposable from rocolib.api.composables.GraphComposable import DecorationComposable
from rocolib.api.ports import MountPort from rocolib.api.ports import DecorationPort as DecoPort
class DecorationComponent(MechanicalComponent): class DecorationComponent(MechanicalComponent):
...@@ -9,7 +9,7 @@ class DecorationComponent(MechanicalComponent): ...@@ -9,7 +9,7 @@ class DecorationComponent(MechanicalComponent):
self.graph = DecorationComposable() self.graph = DecorationComposable()
self.addFace = self.graph.addFace self.addFace = self.graph.addFace
self.addInterface("decoration", MountPort(self, self.graph)) self.addInterface("decoration", DecoPort(self, self.graph))
def getGraph(self): def getGraph(self):
return self.graph return self.graph
...@@ -18,9 +18,13 @@ class Composable: ...@@ -18,9 +18,13 @@ class Composable:
raise AttributeError("Number of ports in each interface don't match") raise AttributeError("Number of ports in each interface don't match")
for (port1, port2) in zip(interface1, interface2): for (port1, port2) in zip(interface1, interface2):
self.attach(port1, port2, kwargs) self.attach(port1, port2, **kwargs)
def attach(self, fromPort, toPort, **kwargs):
fromPort.attachTo(toPort, self, **kwargs)
toPort.attachFrom(fromPort, self, **kwargs)
def attach(self, fromPort, toPort, kwargs):
raise NotImplementedError
def makeOutput(self, filedir, **kwargs): def makeOutput(self, filedir, **kwargs):
raise NotImplementedError raise NotImplementedError
...@@ -38,62 +38,6 @@ class GraphComposable(Composable, BaseGraph): ...@@ -38,62 +38,6 @@ class GraphComposable(Composable, BaseGraph):
self.faces.extend(g2.faces) self.faces.extend(g2.faces)
self.edges.extend(g2.edges) self.edges.extend(g2.edges)
def attach(self, port1, port2, kwargs):
# Test whether ports are of right type --
# Attach if both ports contain edges to attach along
try:
label1 = port1.getEdges()
label2 = port2.getEdges()
except AttributeError:
pass
else:
# XXX associate ports with specific composables so this isn't necessary
for i in range(len(label1)):
if label1[i] not in (e.name for e in self.edges):
return
if label2[i] not in (e.name for e in self.edges):
return
for i in range(len(label1)):
newargs = {}
for key, value in kwargs.items():
if isinstance(value, (list, tuple)):
newargs[key] = value[i]
else:
newargs[key] = value
self.mergeEdge(label1[i], label2[i], **newargs)
# If the first port contains a Face and the second contains a Decoration:
# Decorate the face with the decoration
try:
face = self.getFace(port1.getFaceName())
deco = port2.getDecoration()
except AttributeError:
pass
else:
# XXX associate ports with specific composables so this isn't necessary
if face is not None:
decorateGraph(face, decoration=deco, **kwargs)
# If the first port contains a Decoration and the second contains a Face
# Attach a face to the decoration's face
try:
deco = port1.getDecoration().faces[0]
face = port2.getFaceName()
except AttributeError:
pass
else:
self.mergeFace(deco.joinedFaces[0][0].name, face, np.dot(port2.getTransform(), deco.transform2D))
# If the first port contains a Face and the second contains a Face
# Attach the two faces
try:
face1 = self.getFace(port1.getFaceName())
face2 = self.getFace(port2.getFaceName())
except AttributeError:
pass
else:
self.mergeFace(face1.name, face2.name, np.eye(4))
def makeOutput(self, filedir, **kwargs): def makeOutput(self, filedir, **kwargs):
if "displayOnly" in kwargs: if "displayOnly" in kwargs:
......
from rocolib.api.ports import Port
from rocolib.utils.utils import prefix as prefixString
class AnchorPort(Port):
def __init__(self, parent, graph, face, transform):
Port.__init__(self, parent, {})
self.graph = graph
self.face = face
self.transform = transform
def prefix(self, prefix=""):
self.face = prefixString(prefix, self.face)
def getFace(self):
return self.graph.getFace(self.face)
def getTransform(self):
return self.transform
def getFaceName(self):
return self.face
def toString(self):
return str(self.face.name)
def canMate(self, otherPort):
return False
from rocolib.api.ports import Port, FacePort
from rocolib.utils.utils import decorateGraph
import numpy as np
class DecorationPort(Port):
def __init__(self, parent, decoration):
Port.__init__(self, parent, {})
self.decoration = decoration
def getDecoration(self):
return self.decoration
def toString(self):
return str(self.decoration)
def canMate(self, otherPort):
return (otherPort.getFaceName() is not None)
def attachFrom(self, fromPort, graph, **kwargs):
# If from face to decoration, we can decorate the face
if isinstance(fromPort, FacePort):
face = graph.getFace(fromPort.getFaceName())
deco = self.getDecoration()
if face is not None:
decorateGraph(face, decoration=deco, **kwargs)
class AnchorPort(FacePort):
def __init__(self, parent, graph, face, transform):
FacePort.__init__(self, parent, graph, face)
self.transform = transform
def getTransform(self):
return self.transform
def canMate(self, otherPort):
return False
def attachFrom(self, fromPort, graph, **kwargs):
if isinstance(fromPort, DecorationPort):
deco = fromPort.getDecoration().faces[0]
face = self.getFaceName()
graph.mergeFace(deco.joinedFaces[0][0].name, face, np.dot(self.getTransform(), deco.transform2D))
...@@ -30,3 +30,25 @@ class EdgePort(Port): ...@@ -30,3 +30,25 @@ class EdgePort(Port):
def toString(self): def toString(self):
return str(self.getEdges()) return str(self.getEdges())
def attachTo(self, toPort, graph, **kwargs):
# if connecting from edge to edge, merge them in order
if isinstance(toPort, EdgePort):
label1 = self.getEdges()
label2 = toPort.getEdges()
# XXX associate ports with specific composables so this isn't necessary
for i in range(len(label1)):
if label1[i] not in (e.name for e in graph.edges):
return
if label2[i] not in (e.name for e in graph.edges):
return
for i in range(len(label1)):
newargs = {}
for key, value in kwargs.items():
if isinstance(value, (list, tuple)):
newargs[key] = value[i]
else:
newargs[key] = value
graph.mergeEdge(label1[i], label2[i], **newargs)
...@@ -17,7 +17,7 @@ class FacePort(Port): ...@@ -17,7 +17,7 @@ class FacePort(Port):
return self.face return self.face
def toString(self): def toString(self):
return str(self.face.name) return str(self.face)
def canMate(self, otherPort): def canMate(self, otherPort):
try: try:
...@@ -30,3 +30,10 @@ class FacePort(Port): ...@@ -30,3 +30,10 @@ class FacePort(Port):
return True return True
except: except:
pass pass
def attachTo(self, toPort, graph, **kwargs):
# if connecting from face to face, merge them in order
if isinstance(toPort, FacePort):
face1 = graph.getFace(self.getFaceName())
face2 = graph.getFace(toPort.getFaceName())
graph.mergeFace(face1.name, face2.name)
from rocolib.api.ports import Port
from rocolib.utils.utils import prefix as prefixString
class MountPort(Port):
def __init__(self, parent, decoration):
Port.__init__(self, parent, {})
self.decoration = decoration
def getDecoration(self):
return self.decoration
def toString(self):
print("decoration")
def canMate(self, otherPort):
return (otherPort.getFaceName() is not None)
...@@ -17,17 +17,7 @@ class Port(Parameterized): ...@@ -17,17 +17,7 @@ class Port(Parameterized):
:type kwargs: dict :type kwargs: dict
""" """
super(Port, self).__init__() super(Port, self).__init__()
# XXX TODO(mehtank): Figure out better default values
self.isInput = False # True if self.valueFunction can be set via a connection from a port of a different component
self.isOutput = False # True if self.valueFunction can be completely determined by self.parent
self.valueFunction = None
# self.inputFunction = None
# self.outputFunction = None
self.parent = parent self.parent = parent
self._allowableMates = []
self._recommendedMates = []
self.setName(name) self.setName(name)
for key, value in params.items(): for key, value in params.items():
...@@ -45,25 +35,6 @@ class Port(Parameterized): ...@@ -45,25 +35,6 @@ class Port(Parameterized):
# Override to handle prefixing # Override to handle prefixing
pass pass
def setInputValue(self, value):
self.isInput = True
self.isOutput = False
self.valueFunction = lambda : value
def setOutputFunction(self, fn):
self.isInput = False
self.isOutput = True
self.valueFunction = fn
def setDrivenFunction(self, fn):
self.isInput = False
self.isOutput = False
self.valueFunction = fn
def getValue(self, default=None):
if self.valueFunction is None:
return default
return self.valueFunction()
def canMate(self, otherPort): def canMate(self, otherPort):
""" """
...@@ -82,79 +53,25 @@ class Port(Parameterized): ...@@ -82,79 +53,25 @@ class Port(Parameterized):
return False return False
return self.__class__ == otherPort.__class__ return self.__class__ == otherPort.__class__
def shouldMate(self, otherPort):
# Override for better matching
if not self.canMate(otherPort):
return False
if len(self._recommendedMates) > 0:
for nextType in self._recommendedMates:
if isinstance(otherPort, nextType):
return True
return False
def addAllowableMate(self, mateType):
if not isinstance(mateType, (list, tuple)):
mateType = [mateType]
for newType in mateType:
# XXX what exactly does this check?
if not isinstance(newType, type(self.__class__)):
continue
# If already have one that is a subclass of the desired one, do nothing
for mate in self._allowableMates:
if issubclass(mate, newType):
# XXX why do we return instead of breaking and checking the rest?
return
# Remove any that are a superclass of the new one
for mate in self._allowableMates:
if issubclass(newType, mate):
self._allowableMates.remove(mate)
self._allowableMates.append(newType)
def addRecommendedMate(self, mateType):
if not isinstance(mateType, (list, tuple)):
mateType = [mateType]
for newType in mateType:
if not isinstance(newType, type(self.__class__)):
continue
# If already have one that is a subclass of the desired one, do nothing
for mate in self._recommendedMates:
if issubclass(mate, newType):
return None
# Remove any that are a superclass of the new one
for mate in self._recommendedMates:
if issubclass(newType, mate):
self._recommendedMates.remove(mate)
self._recommendedMates.append(newType)
def setParent(self, newParent): def setParent(self, newParent):
self.parent = newParent self.parent = newParent
def getParent(self): def getParent(self):
return self.parent return self.parent
def setName(self, name): def setName(self, name):
self.name = str(name) self.name = str(name)
def getName(self): def getName(self):
return self.name return self.name
def toString(self): def toString(self):
return str(self.parent) + '.' + self.getName() return str(self.parent) + '.' + self.getName()
def attachTo(self, toPort, composable, **kwargs):
pass
def attachFrom(self, fromPort, composable, **kwargs):
pass
def getCompatiblePorts(self):
from rocolib.api.ports import all_ports
compat_ports = []
for port in all_ports:
if self.canMate(port(None)):
compat_ports.append(port)
return compat_ports
from .Port import Port from .Port import Port
from .EdgePort import EdgePort from .EdgePort import EdgePort
from .FacePort import FacePort from .FacePort import FacePort
from .MountPort import MountPort from .DecorationPort import DecorationPort
from .AnchorPort import AnchorPort from .DecorationPort import AnchorPort
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment