diff --git a/rocolib/api/composables/graph/Face.py b/rocolib/api/composables/graph/Face.py index 78b5579d7e69fb7f1d876a026e2d262d5fa7756a..acd8570f6b779e2559b967da2c5033efbf149773 100644 --- a/rocolib/api/composables/graph/Face.py +++ b/rocolib/api/composables/graph/Face.py @@ -221,10 +221,7 @@ class Face(object): self.decorations.append(pts) def addFace(self, face, transform, copyDecorations=False): - self.joinedFaces.append((face, transform)) - if copyDecorations: - for d in self.decorations: - face.addDecoration(d) + self.joinedFaces.append(dict(face=face, transform=transform, copyDecorations=copyDecorations)) def preTransform(self, edge): index = self.edges.index(edge) @@ -332,16 +329,38 @@ class Face(object): e.remove(self) # now place attached faces - for (f, t) in self.joinedFaces: + for jf in self.joinedFaces: + if jf["transform"] is False: + continue if self.inverted: - t3d = np.dot(MirrorZ(), t) + t3d = np.dot(MirrorZ(), jf["transform"]) else: - t3d = t + t3d = jf["transform"] t3d = np.dot(self.transform3D, t3d) - f.place(None, None, t3d, facelists) + jf["face"].place(None, None, t3d, facelists) self.placeagain() + def copyDecorations(self): + for jf in self.joinedFaces: + if jf["copyDecorations"]: + thisdeco = self.transformDecorations(np.inv(jf["face"].transform3D)) + thatdeco = jf["face"].transformDecorations(np.inv(self.transform3D)) + self.decorations += thatdeco + jf["face"].decorations += thisdeco + jf["copyDecorations"] = False + + def transformDecorations(self, t2=None): + if self.transform3D is None: + return + t = self.transform3D + if t2 is not None: + t = np.dot(t2, t) + decorations = [] + for e in self.decorations: + decorations.append(([ np.dot(t, np.array(list(pt) + [0,1]))[0:2] for pt in e[0] ], e[1])) + return decorations + def polygon(self): from collections import namedtuple from shapely.geometry import Polygon diff --git a/rocolib/api/composables/graph/Graph.py b/rocolib/api/composables/graph/Graph.py index e8830da8118a31593586ce8550d3ef90b449394f..3cf2190b965b061040086b39b11de18da05b6aa0 100644 --- a/rocolib/api/composables/graph/Graph.py +++ b/rocolib/api/composables/graph/Graph.py @@ -122,9 +122,18 @@ class Graph(): toFace = self.getFace(toFaceName) toFace.transform(origin=offset) if transform is None: - transform = np.eye(4) + transform = np.eye(4) + invtransform = np.eye(4) + elif transform is False: + transform = False + invtransform = False + else: + invtransform = np.inv(transform) + fromFace.addFace(toFace, transform, copyDecorations) - toFace.addFace(fromFace, np.inv(transform), copyDecorations) + toFace.addFace(fromFace, invtransform, copyDecorations=False) + # XXX TODO have separate options for copyDecorations from->to vs to->from? + # Right now we'll do both during place, so only note it for one of the pair. def attachEdge(self, fromEdge, newFace, newEdge, prefix=None, root=False, angle=0, edgeType=None, joints=None): # XXX should set angle from a face, not absolute angle of the face @@ -300,11 +309,7 @@ class Graph(): import objgraph objgraph.show_refs(self.graphObj(), max_depth = 2, filter = lambda x : isinstance(x, (dict, HyperEdge))) - def place(self, force=False, transform3D=None): - if force: - self.unplace() - self.facelists = [] - + def place(self, transform3D=None): if transform3D is None: transform3D = np.eye(4) @@ -318,16 +323,9 @@ class Graph(): else: break - #IPython.embed() - self.rebuildEdges() - - def unplace(self): for f in self.faces: - f.transform2D = None - f.transform3D = None - for e in self.edges: - e.pts2D = None - e.pts3D = None + f.copyDecorations() + self.rebuildEdges() def makeMeshes(self, **kwargs): """Create a list of stl Mesh objects""" diff --git a/rocolib/api/ports/DecorationPort.py b/rocolib/api/ports/DecorationPort.py index 1707f2d9a5150e1c8cf29744a0c5d82dfd68b434..0dce347a6161d5d36198d2cabd7a93cc347f44ea 100644 --- a/rocolib/api/ports/DecorationPort.py +++ b/rocolib/api/ports/DecorationPort.py @@ -41,4 +41,4 @@ class AnchorPort(FacePort): if isinstance(fromPort, DecorationPort): deco = fromPort.getDecoration().faces[0] face = self.getFaceName() - self.graph.mergeFace(deco.joinedFaces[0][0].name, face, np.dot(self.getTransform(), deco.transform2D)) + self.graph.mergeFace(deco.joinedFaces[0]["face"].name, face, np.dot(self.getTransform(), deco.transform2D)) diff --git a/rocolib/api/ports/FacePort.py b/rocolib/api/ports/FacePort.py index a857981fa5b0147195cd71a2db4a0b31f8b41524..ee86c9cb0ee6879005a6c5094d399ddf1f6e1b57 100644 --- a/rocolib/api/ports/FacePort.py +++ b/rocolib/api/ports/FacePort.py @@ -1,7 +1,7 @@ from rocolib.api.ports import Port from rocolib.utils.utils import prefix as prefixString -from rocolib.utils.numsym import deg2rad -from rocolib.utils.transforms import RotateX +from rocolib.utils.numsym import deg2rad, dot +from rocolib.utils.transforms import RotateX, RotateZ class FacePort(Port): def __init__(self, parent, graph, face): @@ -41,8 +41,11 @@ class FacePort(Port): if isinstance(toPort, FacePort): face1 = self.graph.getFace(self.getFaceName()) face2 = self.graph.getFace(toPort.getFaceName()) - if kwargs.get("flip"): - transform = RotateX(deg2rad(180)) - else: - transform = None - self.graph.mergeFace(face1.name, face2.name, transform=transform, offset=kwargs.get("offset", (0,0)), copyDecorations=True) + + transform = kwargs.get("transform", None) + if transform is not False: + transform = dot(RotateX(deg2rad(kwargs.pop("flip", 0) and 180)), RotateZ(deg2rad(kwargs.pop("rotate", 0)))) + + self.graph.mergeFace(face1.name, face2.name, transform=transform, + offset=kwargs.pop("offset", (0,0)), + copyDecorations=kwargs.pop("copyDecorations", False)) diff --git a/rocolib/builders/ESPSegBuilder.py b/rocolib/builders/ESPSegBuilder.py index 6ba94f413edce0ccc1fa15ff866b8d29d45f7559..1928fc693aab60d524800cf249c35a37ae97c8a2 100644 --- a/rocolib/builders/ESPSegBuilder.py +++ b/rocolib/builders/ESPSegBuilder.py @@ -109,4 +109,11 @@ c.addConnection(("sheath", "face1"), mode="hole", offset=Function(*depthfn(["length", "battery"], "(4-(%s+x[3])/2, 0.5 * x[2] - 15)"))) +c.addConnection(("right", "face0"), + ("sheath", "face2"), copyDecorations=True, transform=False) +c.addConnection(("left", "face0"), + ("sheath", "face0"), copyDecorations=True, transform=False) +c.addConnection(("brain", "face1"), + ("sheath", "face1"), copyDecorations=True, transform=False) + c.toLibrary("ESPSeg") diff --git a/rocolib/builders/WheelBuilder.py b/rocolib/builders/WheelBuilder.py index 9ee698fcdf63e5993b80114928af382abf303a67..d64e525df20ea448b351eb9ed6e0b8a85a595374 100644 --- a/rocolib/builders/WheelBuilder.py +++ b/rocolib/builders/WheelBuilder.py @@ -9,6 +9,6 @@ c.addSubcomponent("tire", "Tire", inherit="radius tire_thickness".split(), prefi c.inheritAllInterfaces("drive", prefix=None) c.addConnection(("drive", "horn"), - ("tire", "face")) + ("tire", "face"), copyDecorations=True) c.toLibrary("Wheel") diff --git a/rocolib/library/ESPSeg.py b/rocolib/library/ESPSeg.py deleted file mode 100644 index 520b824992cac3dc851a0dff28b8236649e71cd8..0000000000000000000000000000000000000000 --- a/rocolib/library/ESPSeg.py +++ /dev/null @@ -1,15 +0,0 @@ -from rocolib.api.components import Component -from rocolib.utils.utils import copyDecorations - -class ESPSeg(Component): - def assemble(self): - copyDecorations(self, ("rightservoface", ("right", "face0", -1, 0)), - ("rightservosheath", ("sheath", "face2", -1, 0))) - copyDecorations(self, ("leftservoface", ("left", "face0", 2, 1)), - ("leftservosheath", ("sheath", "face0", 0, -1))) - copyDecorations(self, ("brainface", ("brain", "face1", 0, 1)), - ("brainsheath", ("sheath", "face1", 1, 2))) - -if __name__ == "__main__": - ESPSeg.test() - diff --git a/rocolib/library/ESPSeg.yaml b/rocolib/library/ESPSeg.yaml index 77254a21aa4fd5f5f3c46944144e1a5739afc904..8c941477d9bbc53a4d2aff93979cafe1e5086d10 100644 --- a/rocolib/library/ESPSeg.yaml +++ b/rocolib/library/ESPSeg.yaml @@ -11,6 +11,14 @@ connections: - - left - botedge0 - angle: -90 + connection10: + - - brain + - face1 + - &id001 + - sheath + - face1 + - copyDecorations: true + transform: false connection2: - - left - topedge1 @@ -48,8 +56,7 @@ connections: - driveservo - battery connection7: - - - sheath - - face1 + - *id001 - - usb - decoration - mode: hole @@ -61,6 +68,20 @@ connections: - driveservo - length - battery + connection8: + - - right + - face0 + - - sheath + - face2 + - copyDecorations: true + transform: false + connection9: + - - left + - face0 + - - sheath + - face0 + - copyDecorations: true + transform: false interfaces: {} parameters: battery: @@ -152,7 +173,7 @@ subcomponents: center: false length: function: x[0] - getDim(x[1],'width') - parameter: &id001 + parameter: &id002 - length - controller radius: @@ -172,7 +193,7 @@ subcomponents: flip: true length: function: x[0] - getDim(x[1],'width') - parameter: *id001 + parameter: *id002 radius: parameter: height servo: diff --git a/rocolib/library/Wheel.yaml b/rocolib/library/Wheel.yaml index 7f2f80227f316a0a8603bf71d8e0b2d849e6bc8f..51d9e01c2f7ca3f35118e9b7cab3c760ff056e21 100644 --- a/rocolib/library/Wheel.yaml +++ b/rocolib/library/Wheel.yaml @@ -4,7 +4,7 @@ connections: - horn - - tire - face - - {} + - copyDecorations: true interfaces: botedge0: interface: botedge0 diff --git a/rocolib/utils/utils.py b/rocolib/utils/utils.py index 20df92137105711e7dbed27aaa1cdf0cd93a5b27..14da40a11fcfb314c52f4581f0027aaf8b7e79b3 100644 --- a/rocolib/utils/utils.py +++ b/rocolib/utils/utils.py @@ -45,30 +45,3 @@ def transformDecorations(face, pts2d, offset=(0,0), rotate=0, flip=False, mode=N for p in pts2d], mode)) return np.dot(Translate([offset[0], offset[1], 0]), RotateZ(rotate)) - -def copyDecorations(self, deco_1, deco_2): - (ni1, (sc1, i1, p1a, p1b)) = deco_1 - (ni2, (sc2, i2, p2a, p2b)) = deco_2 - - self.inheritInterface(ni1, (sc1, i1)) - self.inheritInterface(ni2, (sc2, i2)) - - f1 = self.getInterface(ni1).getFace() - f2 = self.getInterface(ni2).getFace() - - p1o = f1.pts2d[p1a] - p1x = f1.pts2d[p1b] - p2o = f2.pts2d[p2a] - p2x = f2.pts2d[p2b] - - a1 = np.arctan2(p1x[1]-p1o[1], p1x[0]-p1o[0]) - a2 = np.arctan2(p2x[1]-p2o[1], p2x[0]-p2o[0]) - - for pts, mode in f1.decorations: - transformDecorations( - f2, - [(px - p1o[0], py - p1o[1]) for (px, py) in pts], - offset = p2o, - rotate = np.rad2deg(a2-a1), - mode = mode - )