diff --git a/rocolib/__main__.py b/rocolib/__main__.py index d00adae95801fad51b7bfce019df138935f3d0ab..32c397df2f6e1e1c86ab2d47226055a938769d74 100644 --- a/rocolib/__main__.py +++ b/rocolib/__main__.py @@ -15,7 +15,12 @@ def test(component, params, thickness, outdir=None, display=False): f = getComponent(component) if params is not None: for p in params: - f.setParameter(p[0], eval(str(p[1]))) + try: + v = eval(str(p[1])) + except NameError: + # Using a string as a dimensions parameter + v = str(p[1]) + f.setParameter(p[0], v) if thickness is None: t = 0 j = None @@ -39,7 +44,7 @@ def test(component, params, thickness, outdir=None, display=False): app.layout = html app.run_server(debug=True) -if __name__ == '__main__': +def cli_argparse(): LOG_LEVELS = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] DEFAULT_LOG_LEVEL = "WARNING" @@ -136,3 +141,6 @@ if __name__ == '__main__': if not acted: parser.print_help(sys.stderr) sys.exit(1) + +if __name__ == '__main__': + cli_argparse() diff --git a/rocolib/api/components/Component.py b/rocolib/api/components/Component.py index d5ca60d6bad3567aadf2468f8749731ceb8d1091..b94d56c7e8bfc6364b717c53ddbc7f00aa895b74 100644 --- a/rocolib/api/components/Component.py +++ b/rocolib/api/components/Component.py @@ -278,10 +278,10 @@ class Component(Parameterized): fromPort = self.getInterfaces(*fromInterface) toPort = self.getInterfaces(*toInterface) - if fromPort.canMate(toPort): + if fromPort.canMate(toPort) or toPort.canMate(fromPort): self.connections.setdefault(name, [fromInterface, toInterface, kwargs]) else: - raise AttributeError(f"{fromInterface} cannot connect to {toInterface} according to getMate") + raise AttributeError(f"{fromInterface} ({fromPort} : {type(fromPort)}) cannot connect to {toInterface} ({toPort} : {type(toPort)}) according to getMate") def delConnection(self, name): self.connections.pop(name) diff --git a/rocolib/api/composables/graph/Face.py b/rocolib/api/composables/graph/Face.py index 9be77bb764f586e3d41468dd1833095a0572f0d6..78b5579d7e69fb7f1d876a026e2d262d5fa7756a 100644 --- a/rocolib/api/composables/graph/Face.py +++ b/rocolib/api/composables/graph/Face.py @@ -220,8 +220,11 @@ class Face(object): def addDecoration(self, pts): self.decorations.append(pts) - def addFace(self, face, transform): + def addFace(self, face, transform, copyDecorations=False): self.joinedFaces.append((face, transform)) + if copyDecorations: + for d in self.decorations: + face.addDecoration(d) def preTransform(self, edge): index = self.edges.index(edge) diff --git a/rocolib/api/composables/graph/Graph.py b/rocolib/api/composables/graph/Graph.py index 5f11f921b3f034ad8fbcdf56bcc501510771b4c3..e8830da8118a31593586ce8550d3ef90b449394f 100644 --- a/rocolib/api/composables/graph/Graph.py +++ b/rocolib/api/composables/graph/Graph.py @@ -117,13 +117,14 @@ class Graph(): self.addFace(toFace, prefix) self.mergeFace(fromFace, toFace.name, transform) - def mergeFace(self, fromFaceName, toFaceName, transform=None): + def mergeFace(self, fromFaceName, toFaceName, transform=None, offset=(0,0), copyDecorations=False): fromFace = self.getFace(fromFaceName) toFace = self.getFace(toFaceName) + toFace.transform(origin=offset) if transform is None: transform = np.eye(4) - fromFace.addFace(toFace, transform) - toFace.addFace(fromFace, np.inv(transform)) + fromFace.addFace(toFace, transform, copyDecorations) + toFace.addFace(fromFace, np.inv(transform), copyDecorations) 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 diff --git a/rocolib/api/ports/EdgePort.py b/rocolib/api/ports/EdgePort.py index b4babc965cb6aa94912d080e62d632e3808072e2..7d07f05d9e2a28bb77f554d06cc81bc6e0f28e43 100644 --- a/rocolib/api/ports/EdgePort.py +++ b/rocolib/api/ports/EdgePort.py @@ -36,13 +36,14 @@ class EdgePort(Port): def toString(self): return str(self.getEdges()) - def attachTo(self, toPort, **kwargs): if isinstance(toPort, EdgePort): label1 = self.getEdges() label2 = toPort.getEdges() - for i in range(len(label1)): + numedges = len(label1) + + for i in range(numedges): newargs = {} for key, value in kwargs.items(): if isinstance(value, (list, tuple)): @@ -50,7 +51,7 @@ class EdgePort(Port): else: newargs[key] = value try: - self.graph.mergeEdge(label1[i], label2[i], **newargs) + self.graph.mergeEdge(label1[i], label2[numedges-i-1], **newargs) except AttributeError: # Edge not found, skipping pass diff --git a/rocolib/api/ports/FacePort.py b/rocolib/api/ports/FacePort.py index 808c68c0937af140688ad4b319f008a39d526a57..a857981fa5b0147195cd71a2db4a0b31f8b41524 100644 --- a/rocolib/api/ports/FacePort.py +++ b/rocolib/api/ports/FacePort.py @@ -1,5 +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 class FacePort(Port): def __init__(self, parent, graph, face): @@ -39,4 +41,8 @@ class FacePort(Port): if isinstance(toPort, FacePort): face1 = self.graph.getFace(self.getFaceName()) face2 = self.graph.getFace(toPort.getFaceName()) - self.graph.mergeFace(face1.name, face2.name) + 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) diff --git a/rocolib/builders/ESPSegBuilder.py b/rocolib/builders/ESPSegBuilder.py index d545bef8544067cdcc75cb3337f9d22cdca968ae..6ba94f413edce0ccc1fa15ff866b8d29d45f7559 100644 --- a/rocolib/builders/ESPSegBuilder.py +++ b/rocolib/builders/ESPSegBuilder.py @@ -43,10 +43,10 @@ c.addConstConstraint(("right","flip"), True) # connections c.addConnection(("brain", "topedge0"), - ("right", "botedge0"), + ("right", "topedge0"), angle=-90) c.addConnection(("brain", "botedge0"), - ("left", "topedge0"), + ("left", "botedge0"), angle=-90) # Sheath @@ -65,7 +65,7 @@ c.addConstraint(("sheathsplit","botlength"), ("driveservo", "width"), x[1] - 2*getDim(x[0],'motorheight'), \ getDim(x[0],'motorheight'))") -c.addConnection(("left", "botedge1"), +c.addConnection(("left", "topedge1"), ("sheathsplit", "botedge2"), angle=180) @@ -76,7 +76,7 @@ c.addConnection(("right", "botedge1"), ''' c.addConnection(("sheathsplit", "topedge0"), - ("sheath", "topedge1")) + ("sheath", "botedge1")) # Tail c.addSubcomponent("tail", "Tail", inherit=("flapwidth", "tailwidth"), prefix=None) @@ -85,14 +85,14 @@ c.addConstraint(("tail","height"), *depthfn(["height"], "%s/2.+x[2]")) c.addConstraint(("tail","depth"), *depthfn(["battery"], "%s+x[2]")) c.addConnection(("tail", "topedge"), - ("sheath", "botedge1"), + ("sheath", "topedge1"), angle=90) c.addSubcomponent("tailsplit", "SplitEdge") c.addConstraint(("tailsplit","toplength"), "width", "(x,)") c.addConstraint(("tailsplit","botlength"), ("width", "flapwidth"), "(x[0]*(1-x[1])/2., x[0]*x[1], x[0]*(1-x[1])/2.)") -c.addConnection(("sheath", "botedge3"), +c.addConnection(("sheath", "topedge3"), ("tailsplit", "topedge0")) c.addConnection(("tail", "flapedge"), diff --git a/rocolib/builders/MountedServoBuilder.py b/rocolib/builders/MountedServoBuilder.py index e1e8fda87d574025130b1e8cf0248460efd56b32..305bfa7bc6639694d6f7d4607c59696368a68d4e 100644 --- a/rocolib/builders/MountedServoBuilder.py +++ b/rocolib/builders/MountedServoBuilder.py @@ -9,6 +9,6 @@ c.addSubcomponent("servo", "ServoMotor", inherit=True, prefix=None) c.inheritAllInterfaces("mount", prefix=None) c.inheritAllInterfaces("servo", prefix=None) c.addConnection(("mount", "mount.decoration"), - ("servo", "horn")) + ("servo", "mount")) c.toLibrary("MountedServo") diff --git a/rocolib/builders/WheelBuilder.py b/rocolib/builders/WheelBuilder.py index 3dd5384055404ddd00d536b01b5f5224ccdd1fde..9ee698fcdf63e5993b80114928af382abf303a67 100644 --- a/rocolib/builders/WheelBuilder.py +++ b/rocolib/builders/WheelBuilder.py @@ -8,7 +8,7 @@ c.addSubcomponent("tire", "Tire", inherit="radius tire_thickness".split(), prefi c.inheritAllInterfaces("drive", prefix=None) -c.addConnection(("drive", "mount"), +c.addConnection(("drive", "horn"), ("tire", "face")) c.toLibrary("Wheel") diff --git a/rocolib/library/ESPSeg.yaml b/rocolib/library/ESPSeg.yaml index 0c84eb9d1b85634930a8f32bb69daa7ec70dfc30..77254a21aa4fd5f5f3c46944144e1a5739afc904 100644 --- a/rocolib/library/ESPSeg.yaml +++ b/rocolib/library/ESPSeg.yaml @@ -3,17 +3,17 @@ connections: - - brain - topedge0 - - right - - botedge0 + - topedge0 - angle: -90 connection1: - - brain - botedge0 - - left - - topedge0 + - botedge0 - angle: -90 connection2: - - left - - botedge1 + - topedge1 - - sheathsplit - botedge2 - angle: 180 @@ -21,17 +21,17 @@ connections: - - sheathsplit - topedge0 - - sheath - - topedge1 + - botedge1 - {} connection4: - - tail - topedge - - sheath - - botedge1 + - topedge1 - angle: 90 connection5: - - sheath - - botedge3 + - topedge3 - - tailsplit - topedge0 - {} diff --git a/rocolib/library/MountedServo.yaml b/rocolib/library/MountedServo.yaml index 3a41255ee69f0452b4ae6e4a4b7f373f2b239608..aaaa270f888997a30548a631d78a58d8ed5baa9a 100644 --- a/rocolib/library/MountedServo.yaml +++ b/rocolib/library/MountedServo.yaml @@ -3,7 +3,7 @@ connections: - - mount - mount.decoration - - servo - - horn + - mount - {} interfaces: botedge0: diff --git a/rocolib/library/ServoMotor.py b/rocolib/library/ServoMotor.py index 5881e47e4152317542664da196de296caf222703..954d2a97de209b5d2389449824e6c18022b3f4ab 100644 --- a/rocolib/library/ServoMotor.py +++ b/rocolib/library/ServoMotor.py @@ -1,6 +1,7 @@ from rocolib.api.components import FoldedComponent from rocolib.api.composables.graph.Face import Rectangle as Shape from rocolib.api.ports import AnchorPort +from rocolib.utils.utils import decorateGraph from rocolib.utils.dimensions import getDim from rocolib.utils.transforms import Translate, RotateZ from rocolib.utils.numsym import dot @@ -10,16 +11,19 @@ class ServoMotor(FoldedComponent): def define(self): self.addParameter("angle", 0, paramType="angle", minValue=None, maxValue=None) self.addParameter("servo", "fs90r", paramType="dimension") - self.addInterface("horn", AnchorPort(self, self.getGraph(), "h", Translate([0,0,0]))) - self.addFaceInterface("mount", "h") + self.addInterface("mount", AnchorPort(self, self.getGraph(), "horn", Translate([0,0,0]))) + self.addFaceInterface("horn", "horn") def assemble(self): s = self.getParameter("servo") a = self.getParameter("angle") dz = getDim(s, "hornheight") + dy = getDim(s, "motorlength") / 2 - getDim(s, "hornoffset") - self.addFace(Shape("h", 0, 0)) - self.setInterface("horn", AnchorPort(self, self.getGraph(), "h", dot(RotateZ(a), Translate([0,0,dz])))) + f = Shape("horn", 0, 0) + decorateGraph(f, Shape("hole", 1, 1)) + self.addFace(f) + self.setInterface("mount", AnchorPort(self, self.getGraph(), "horn", dot(RotateZ(a), Translate([0,-dy,dz])))) if __name__ == "__main__": ServoMotor.test() diff --git a/rocolib/library/SimpleRectBeam.py b/rocolib/library/SimpleRectBeam.py index 841762a26155c9f83ffb9d447bcb85568f30bf42..46b14c2fc6c8f48740a65edda7e4ceb5f6adcb63 100644 --- a/rocolib/library/SimpleRectBeam.py +++ b/rocolib/library/SimpleRectBeam.py @@ -10,8 +10,8 @@ class SimpleRectBeam(FoldedComponent): self.addParameter("addTabs", True, valueType="bool") for i in range(4): - self.addEdgeInterface("topedge%d" % i, "r%d.e0" % i, ["width", "depth"][i % 2]) - self.addEdgeInterface("botedge%d" % i, "r%d.e2" % i, ["width", "depth"][i % 2]) + self.addEdgeInterface("topedge%d" % i, "r%d.e2" % i, ["width", "depth"][i % 2]) + self.addEdgeInterface("botedge%d" % i, "r%d.e0" % i, ["width", "depth"][i % 2]) self.addFaceInterface("face%d" % i, "r%d" % i) self.addEdgeInterface("slotedge", "r3.e1", "length") self.addEdgeInterface("tabedge", "r0.e3", "length") diff --git a/rocolib/library/Wheel.yaml b/rocolib/library/Wheel.yaml index ed257d986c91906f882668734584376ecbb92dec..7f2f80227f316a0a8603bf71d8e0b2d849e6bc8f 100644 --- a/rocolib/library/Wheel.yaml +++ b/rocolib/library/Wheel.yaml @@ -1,7 +1,7 @@ connections: connection0: - - drive - - mount + - horn - - tire - face - {} diff --git a/rocolib/utils/dimensions.py b/rocolib/utils/dimensions.py index f54306ff40c4381180b794abfdea383c02a07366..4d87f57f6335a69363889b7f6f15bd43d22ed6e9 100644 --- a/rocolib/utils/dimensions.py +++ b/rocolib/utils/dimensions.py @@ -86,6 +86,17 @@ Should horn be a different object? ''' +dims["ds2g"] = { "type" : "servo", + "motorlength" : 17, + "motorwidth" : 8.5, + "motorheight" : 11, + "shoulderlength": 3.5, + "hornlength" : 11, + "hornheight" : 9, + "hornoffset" : 4, + "horndepth" : 1, +} + dims["s4303r"] = { "type" : "servo", "motorlength" : 31, "motorwidth" : 17, diff --git a/rocolib/utils/numsym.py b/rocolib/utils/numsym.py index 920417cdcbf13d939c473f07882bb8f0421fdac5..18a97ba5d3ef99208fffeda64d1f0a057f2f79b8 100644 --- a/rocolib/utils/numsym.py +++ b/rocolib/utils/numsym.py @@ -19,7 +19,7 @@ def cumsum(iterable): # Numpy mods def numpy_rows(x): - return x.shape[0] + return numpy.array(x) def numpy_N(x): return x @@ -79,9 +79,6 @@ def sympy_difference(pts1, pts2): def sympy_dex(pts1, pts2, tol): return sympy_difference(pts1, pts2) > tol -def sympy_rows(x): - return x.rows - def sympy_pi(): return sympy.pi diff --git a/setup.py b/setup.py index 20bcdf0b4d0b6249944d0c91638363899130a6ea..c17117ffeb993ef68fb85d51ff2441a0b73e8dbb 100644 --- a/setup.py +++ b/setup.py @@ -52,4 +52,9 @@ setup( 'License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)', 'Programming Language :: Python :: 3', ], + entry_points={ + 'console_scripts': [ + 'roco = rocolib.__main__:cli_argparse', + ] + }, )