diff --git a/requirements.txt b/requirements.txt index ece88e046512ba0e6abb71c2ea6520b6e3e212bc..aeaf1bc730c7ae583f7940b1bb2a73a4d230ab94 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,3 +12,5 @@ kaleido circlify shapely dash +platformdirs +importlib_metadata; python_version < '3.10' diff --git a/rocolib/__init__.py b/rocolib/__init__.py index 7a971c1f76035bbbc735c6da2f4a0dca1487ac70..49f4740578ac954616914f94dd1dad3e79874ed8 100644 --- a/rocolib/__init__.py +++ b/rocolib/__init__.py @@ -15,8 +15,3 @@ __author__ = 'UCLA LEMUR' __credits__ = 'The Laboratory for Embedded Machines and Ubiquitous Robots' __url__ = 'https://git.uclalemur.com/roco/rocolib' __description__ = 'Robot Compiler' - -ROCOLIB_DIR = dirname(realpath(__file__)) - -def rocopath(f): - return relpath(f, ROCOLIB_DIR) diff --git a/rocolib/__main__.py b/rocolib/__main__.py index fc980a6f6d0ecd89ee182bfcf05298ad8d551d08..212d1c8f1d168d056906b7dbe659f1c1c2215b34 100644 --- a/rocolib/__main__.py +++ b/rocolib/__main__.py @@ -8,7 +8,7 @@ from os.path import basename from pprint import pprint from textwrap import shorten from dash import Dash -from rocolib.library import getComponent, getComponentTree +from rocolib.library import getComponent, getComponentTree, libDirs from rocolib.api.composables.graph.Joint import FingerJoint @@ -58,30 +58,35 @@ def cli_argparse(): parser = argparse.ArgumentParser() + parser.add_argument("--list", "-l", help="List known library components (use multiple times for more detail)", + dest="component_list", action="append_const", const=1) + parser.add_argument("--plugins", "-L", help="List known plugins (use multiple times for more detail)", + dest="plugin_list", action="append_const", const=1) + + parser.add_argument("--file", "-f", type=str, help="Load component and parameters from file") parser.add_argument("component", nargs='?', help="Name of the component you'd like to test") - parser.add_argument("--file", "-f", type=str, help="Load component and parameters from file") - parser.add_argument("--output", "-o", type=str, help="Output directory") - parser.add_argument("--list", "-l", action='store_true', - help="List all known library components") parser.add_argument("--verbose", "-v", help="Increase logging level (can be used multiple times)", dest="log_level", action="append_const", const=-1) parser.add_argument("--quiet", "-q", help="Decrease logging level (can be used multiple times)", dest="log_level", action="append_const", const=1) + parser.add_argument("-I", help="List component interfaces (use multiple times for more detail)", dest="interface_list", action="append_const", const=1) parser.add_argument("-P", help="List component parameters (use multiple times for more detail)", dest="param_list", action="append_const", const=1) - parser.add_argument("-t", type=float, help="Thickness (i.e. making with wood)") - if not rocoview: - parser.add_argument("-d", action='store_true', help="Display visualizations") - parser.add_argument("-s", type=str, action='append', help="Select specific outputs to save") parser.add_argument("-S", help="List composable outputs (use multiple times for more detail)", dest="output_list", action="append_const", const=1) + parser.add_argument("-p", nargs=2, action='append', metavar=("NAME", "VALUE"), help="Set component parameter NAME to value VALUE") + parser.add_argument("-t", type=float, help="Thickness (i.e. making with wood)") + parser.add_argument("--output", "-o", type=str, help="Output directory") + parser.add_argument("-s", type=str, action='append', help="Select specific outputs to save") + if not rocoview: + parser.add_argument("-d", action='store_true', help="Display visualizations") if len(sys.argv)==1: parser.print_help(sys.stderr) @@ -99,13 +104,23 @@ def cli_argparse(): log_level_name = LOG_LEVELS[log_level] logging.basicConfig(level=getattr(logging, log_level_name)) + def printList(arg, lsfn, f=None): + if not arg: + return False + ls = lsfn(f) + ind = min(len(arg), len(ls)) - 1 + pprint(ls[ind]) + return True + acted = False - if args.list: - t = getComponentTree() - for i, cs in enumerate(t): - chunks = [cs[x:x+4] for x in range(0, len(cs), 4)] - print(f"{i:2}: " + "\n ".join((" ".join(x) for x in chunks))) - acted = True + + acted |= printList(args.component_list, lambda c : ( + dict(enumerate(getComponentTree())), + ) ) + acted |= printList(args.plugin_list, lambda c : ( + list(libDirs.keys()), + libDirs, + ) ) if args.file: with open(args.file) as f: @@ -125,38 +140,31 @@ def cli_argparse(): outdir = not rocoview and "output" or None if args.component: - def printList(arg, lsfn, make=False): - if not arg: - return - f = getComponent(args.component) - if make: - f.make() - ls = lsfn(f) - ind = min(len(arg), len(ls)) - 1 - pprint(ls[ind]) - if not args.p or args.t or args.d: - exit(0) - - printList(args.interface_list, lambda c : (lambda info : ( + f = getComponent(args.component) + + acted |= printList(args.interface_list, lambda c : (lambda info : ( list(info.keys()), {k: v["port"] for k, v in info.items()}, info, - ))(c.getInterfaceInfo())) + ))(c.getInterfaceInfo()), f) - printList(args.param_list, lambda c : (lambda info : ( + acted |= printList(args.param_list, lambda c : (lambda info : ( list(info.keys()), {k: v["defaultValue"] for k, v in info.items()}, {k: {x: v[x] for x in ('defaultValue', 'spec')} for k,v in info.items()}, info, - ))(c.getParameterInfo())) + ))(c.getParameterInfo()), f) + + f.make() - printList(args.output_list, lambda c : (lambda info : ( + acted |= printList(args.output_list, lambda c : (lambda info : ( {composable: list(outputs.keys()) for composable, outputs in info.items()}, {composable: {k: {x: v[x] for x in ('stub', 'widget')} for k, v in outputs.items()} for composable, outputs in info.items()}, info, - ))(c.listOutputs()), make=True) + ))(c.listOutputs()), f) - test(args.component, args.p, thickness=args.t, outdir=outdir, display=args.d, outputs=args.s) + if args.p or args.t or args.d or args.s or args.output or not acted: + test(args.component, args.p, thickness=args.t, outdir=outdir, display=args.d, outputs=args.s) acted = True if not acted: diff --git a/rocolib/api/components/Component.py b/rocolib/api/components/Component.py index dd253459dbec4306292b4352b7e593f29b3767c9..9776246ff74f691f781c02462fa8dba858c1e484 100644 --- a/rocolib/api/components/Component.py +++ b/rocolib/api/components/Component.py @@ -4,39 +4,28 @@ import sys import yaml import logging import networkx as nx +import inspect from circlify import circlify -from rocolib import ROCOLIB_DIR from rocolib.api.composables.ComponentComposable import ComponentComposable from rocolib.api.Parameterized import Parameterized from rocolib.api.Function import YamlFunction from rocolib.utils.utils import prefix as prefixString from rocolib.utils.utils import tryImport -from rocolib.utils.io import load_yaml from rocolib.utils.nx2go import GraphVisualization as gv +from rocolib.library import getComponent from dash import html, dcc log = logging.getLogger(__name__) -# XXX circular import: -# from rocolib.library import ROCOLIB_LIBRARY -# instead: -ROCOLIB_LIBRARY = os.path.join(ROCOLIB_DIR, "library") - -def getSubcomponentObject(component, name=None): - try: - obj = tryImport(component, component) - # XXX hack to get around derived components not having name parameter in their __init__ - c = obj() - c.name = name - return c - except ImportError: - return newComponent(name, component) - -def newComponent(name, yamlFile=None): - class C(Component): + +def newComponent(cName, name=None, yamlFile=None): + class _C(Component): + def __init__(self): + Component.__init__(self, name, yamlFile) + self.cName = cName def define(self): pass - return C(name, yamlFile=yamlFile) + return _C() class Component(Parameterized): @classmethod @@ -49,29 +38,31 @@ class Component(Parameterized): def __init__(self, name=None, yamlFile=None): Parameterized.__init__(self, name) - self.cName = type(self).__name__ + self.cName = self.__class__.__name__ self.reset() - yf = yamlFile + if not yamlFile: - yf = type(self).__name__ + ".yaml" + filepath = inspect.getmodule(self.__class__).__file__ + base, ext = os.path.splitext(filepath) + yf = base + ".yaml" + if os.path.isfile(yf): + yamlFile = yf + log.debug(f"Found implicit yamlFile at {yamlFile}") else: - self.cName = yf - for fn in (yf, - os.path.join(ROCOLIB_DIR, yf), - os.path.join(ROCOLIB_LIBRARY, yf)): - try: - self.fromYaml(fn) - break - except IOError: - pass - if yamlFile: - raise ValueError(f"No suitable yamlfile found for {yamlFile}") + self.cName, _ = os.path.splitext(os.path.basename(yamlFile)) + log.debug(f"Creating new Component with cName {self.cName}") + + if yamlFile: + log.debug(f"Loading details from {yamlFile}") + self.fromYaml(yamlFile) + self.composables['component'] = ComponentComposable(self) self.predefine() self.define() def fromYaml(self, filename): - definition = load_yaml(filename) + with open(filename, 'r') as fd: + definition = yaml.safe_load(fd) # keys are (parameters, subcomponents, connections, interfaces) if "parameters" in definition: @@ -136,8 +127,9 @@ class Component(Parameterized): should be python file/class or yaml name :type classname: str or unicode ''' + # XXX will silently fail if subcomponent name is already taken? - obj = getSubcomponentObject(classname, self.name + '.' + name) + obj = getComponent(classname, name = self.name + '.' + name) sc = {"classname": classname, "parameters": {}, "kwargs": kwargs, "object": obj} self.subcomponents.setdefault(name, sc) @@ -291,16 +283,11 @@ class Component(Parameterized): def delParameter(self, name): ''' - def toLibrary(self): - # XXX TODO: Check for collisions! - # if collision: - # flag to allow rebuilding, and fail otherwise? - # if no flag, check if source matches and rebuild if so, fail otherwise? - return self.toYaml(ROCOLIB_LIBRARY, self.name + ".yaml") - - def toYaml(self, basedir, filename): + def toYaml(self, basedir, filename, reldir=None): + if reldir is None: + reldir = basedir filepath = os.path.join(basedir, filename) - source = os.path.relpath(sys.argv[0], basedir).replace(os.sep, posixpath.sep) + source = os.path.relpath(sys.argv[0], reldir).replace(os.sep, posixpath.sep) parameters = {} for k, v in self._parameters.items(): @@ -559,12 +546,7 @@ class Component(Parameterized): self.make(ka.pop("useDefaultParameters", True)) log.debug(f"... done making {self.name}.") - # XXX: Is this the right way to do it? - import os - try: - filedir and os.makedirs(filedir) - except: - pass + filedir and os.makedirs(filedir, exist_ok=True) rets = {} for (name, composable) in self.composables.items(): diff --git a/rocolib/builders/ChairBackBuilder.py b/rocolib/builders/ChairBackBuilder.py deleted file mode 100644 index 9c6792dc195a2635b97fc5b59cb1ae93dac85aa9..0000000000000000000000000000000000000000 --- a/rocolib/builders/ChairBackBuilder.py +++ /dev/null @@ -1,24 +0,0 @@ -from rocolib.api.components.Component import newComponent - -c = newComponent("ChairBack") - -c.addSubcomponent("panel","ChairPanel", inherit=("width", "thickness"), prefix=None) - -c.addParameter("gapheight", 20, paramType="length") -c.addParameter("backheight", 40, paramType="length") - -c.addConstraint(("panel","depth"), "backheight") - -c.addSubcomponent("sider","Rectangle") -c.addConstraint(("sider","l"), "thickness") -c.addConstraint(("sider","w"), "gapheight") -c.addConnection(("panel","tr"),("sider","t"), angle=0) -c.inheritInterface("right", ("sider", "b")) - -c.addSubcomponent("sidel","Rectangle") -c.addConstraint(("sidel","l"), "thickness") -c.addConstraint(("sidel","w"), "gapheight") -c.addConnection(("panel","tl"),("sidel","t"), angle=0) -c.inheritInterface("left", ("sidel", "b")) - -c.toLibrary() diff --git a/rocolib/builders/ChairPanelBuilder.py b/rocolib/builders/ChairPanelBuilder.py deleted file mode 100644 index 099b78008b1133e13b8851afd772cee723729ceb..0000000000000000000000000000000000000000 --- a/rocolib/builders/ChairPanelBuilder.py +++ /dev/null @@ -1,31 +0,0 @@ -from rocolib.api.components.Component import newComponent - -c = newComponent("ChairPanel") - -c.addSubcomponent("back","Rectangle", root=True) -c.addSubcomponent("sidel","Rectangle") -c.addSubcomponent("sider","Rectangle") - -c.addParameter("depth", 50, paramType="length") -c.addParameter("width", 70, paramType="length") -c.addParameter("thickness", 10, paramType="length") - -c.addConstraint(("back","l"), "width") -c.addConstraint(("back","w"), "depth") - -c.addConstraint(("sidel","l"), "thickness") -c.addConstraint(("sidel","w"), "depth") -c.addConstraint(("sider","l"), "thickness") -c.addConstraint(("sider","w"), "depth") - -c.addConnection(("sidel","r"),("back","l"), angle=90) -c.addConnection(("back","r"),("sider","l"), angle=90) - -c.inheritInterface("tr", ("sider", "t")) -c.inheritInterface("tl", ("sidel", "t")) -c.inheritInterface("br", ("sider", "b")) -c.inheritInterface("bl", ("sidel", "b")) -c.inheritInterface("right", ("sider", "r")) -c.inheritInterface("left", ("sidel", "l")) - -c.toLibrary() diff --git a/rocolib/builders/ChairSeatBuilder.py b/rocolib/builders/ChairSeatBuilder.py deleted file mode 100644 index 6cd1524d41ba0bb17df5cb15d6e8a26dd8a77c07..0000000000000000000000000000000000000000 --- a/rocolib/builders/ChairSeatBuilder.py +++ /dev/null @@ -1,24 +0,0 @@ -from rocolib.api.components.Component import newComponent - -c = newComponent("ChairSeat") - -c.addSubcomponent("seat","ChairPanel", inherit=True, prefix=None, root=True) -c.addSubcomponent("back","ChairBack", inherit=True, prefix=None) -c.addSubcomponent("kitel","Kite", inherit="thickness", prefix=None) -c.addSubcomponent("kiter","Kite", inherit="thickness", prefix=None) - -c.addParameter("recline", 110, paramType="angle") - -c.addConstraint(("kitel","angle"), "recline", "180 - x") -c.addConstraint(("kiter","angle"), "recline", "180 - x") - -c.addConnection(("back","left"),("kitel","t"), angle=0) -c.addConnection(("seat","bl"),("kitel","b"), angle=0) - -c.addConnection(("back","right"),("kiter","b"), angle=0) -c.addConnection(("seat","br"),("kiter","t"), angle=0) - -c.inheritInterface("right", ("seat", "right")) -c.inheritInterface("left", ("seat", "left")) - -c.toLibrary() diff --git a/rocolib/builders/ESPSegBuilder.py b/rocolib/builders/ESPSegBuilder.py index 60207d7e41aa1a6bbca292240f29e88fe6736dd1..f7899bc49bd2dbc296b0b1c3c26c4d95b0ed6c87 100644 --- a/rocolib/builders/ESPSegBuilder.py +++ b/rocolib/builders/ESPSegBuilder.py @@ -1,5 +1,6 @@ from rocolib.library import getComponent from rocolib.api.components.Component import newComponent +from rocolib.library import save from rocolib.api.Function import Function c = newComponent("ESPSeg") @@ -115,4 +116,4 @@ c.addConnection(("left", "face0"), c.addConnection(("brain", "face1"), ("sheath", "face1"), copyDecorations=True, transform=False) -c.toLibrary() +save(c, True) diff --git a/rocolib/builders/MountedBrainsBuilder.py b/rocolib/builders/MountedBrainsBuilder.py index aa8beeb249da735a28a9a5c265746527301ac81d..5983744f121694b71a2b3e8d21bb3ec9433ae6a7 100644 --- a/rocolib/builders/MountedBrainsBuilder.py +++ b/rocolib/builders/MountedBrainsBuilder.py @@ -1,29 +1,30 @@ from rocolib.api.components.Component import newComponent +from rocolib.library import save from rocolib.api.Function import Function -self = newComponent("MountedBrains") +c = newComponent("MountedBrains") -self.addSubcomponent("beam", "SimpleRectBeam") -self.addSubcomponent("brain", "Brains", inherit="brain", prefix=None) +c.addSubcomponent("beam", "SimpleRectBeam") +c.addSubcomponent("brain", "Brains", inherit="brain", prefix=None) -self.addParameter("length", 90, paramType="length") -self.addParameter("width", 50, paramType="length") -self.addParameter("depth", 20, paramType="length") -self.addParameter("offset", (0,0)) +c.addParameter("length", 90, paramType="length") +c.addParameter("width", 50, paramType="length") +c.addParameter("depth", 20, paramType="length") +c.addParameter("offset", (0,0)) -self.addConstraint(("beam", "width"), "depth") -self.addConstraint(("beam", "depth"), "width") -self.addConstraint(("beam", "length"), "length") +c.addConstraint(("beam", "width"), "depth") +c.addConstraint(("beam", "depth"), "width") +c.addConstraint(("beam", "length"), "length") -self.addConstraint(("beam", "width#minValue"), "brain", 'x.get("height")') -self.addConstraint(("beam", "depth#minValue"), "brain", 'x.get("width")') -self.addConstraint(("beam", "length#minValue"), "brain", 'x.get("length")') +c.addConstraint(("beam", "width#minValue"), "brain", 'x.get("height")') +c.addConstraint(("beam", "depth#minValue"), "brain", 'x.get("width")') +c.addConstraint(("beam", "length#minValue"), "brain", 'x.get("length")') -self.addConnection(("beam", "face1"), +c.addConnection(("beam", "face1"), ("brain", "decoration"), mode="hole", offset=Function(params=("offset"))) -self.inheritAllInterfaces("beam", prefix=None) +c.inheritAllInterfaces("beam", prefix=None) -self.toLibrary() +save(c, True) diff --git a/rocolib/builders/MountedServoBuilder.py b/rocolib/builders/MountedServoBuilder.py index 2b667d30b5d46e65dfcba3a824d618f7ceea4d84..82e784d492ff2bbc96fba645476db2d09bf84f83 100644 --- a/rocolib/builders/MountedServoBuilder.py +++ b/rocolib/builders/MountedServoBuilder.py @@ -1,4 +1,5 @@ from rocolib.api.components.Component import newComponent +from rocolib.library import save from rocolib.api.Function import Function c = newComponent("MountedServo") @@ -11,4 +12,4 @@ c.inheritAllInterfaces("servo", prefix=None) c.addConnection(("mount", "mount.decoration"), ("servo", "mount")) -c.toLibrary() +save(c, True) diff --git a/rocolib/builders/PaperbotBuilder.py b/rocolib/builders/PaperbotBuilder.py index 92e08209cbcb73ccf4c340be6e6ecbcfcd685db6..85e9f6c4b94ce3879d104fd5a5eb28bf80dfa8dc 100644 --- a/rocolib/builders/PaperbotBuilder.py +++ b/rocolib/builders/PaperbotBuilder.py @@ -1,4 +1,5 @@ from rocolib.api.components.Component import newComponent +from rocolib.library import save c = newComponent("Paperbot") @@ -8,4 +9,4 @@ c.addParameter("height", 25, paramType="length", minValue=20) c.addSubcomponent("paperbot", "ESPSeg", inherit="length width height battery tire_thickness".split(), prefix=None) -c.toLibrary() +save(c, True) diff --git a/rocolib/builders/RockerChairBuilder.py b/rocolib/builders/RockerChairBuilder.py deleted file mode 100644 index be7dc6302d320f6455c3b830b15804dbfa5a96fe..0000000000000000000000000000000000000000 --- a/rocolib/builders/RockerChairBuilder.py +++ /dev/null @@ -1,19 +0,0 @@ -from rocolib.api.components.Component import newComponent - -c = newComponent("RockerChair") - -c.addSubcomponent("seat","ChairSeat", inherit=True, prefix=None, root=True) -c.addSubcomponent("legl","RockerLeg", inherit=True, prefix=None, mirror=True) -c.addSubcomponent("legr","RockerLeg", inherit=True, prefix=None) -c.addSubcomponent("crossbar","Rectangle") - -c.addConstraint(("crossbar","l"), "width") -c.addConstraint(("crossbar","w"), ("height", "rocker"), "x[0] * np.sin(np.deg2rad(x[1]))") - -c.addConnection(("seat","left"),("legl","topedge"), angle=0) -c.addConnection(("seat","right"),("legr","topedge"), angle=0) - -c.addConnection(("crossbar","l"),("legl","crossbar"), angle=90) -c.addConnection(("crossbar","r"),("legr","crossbar"), angle=90) - -c.toLibrary() diff --git a/rocolib/builders/RockerLegBuilder.py b/rocolib/builders/RockerLegBuilder.py deleted file mode 100644 index 2e7d63b99e424a1735e300b484b57ed3614c8f24..0000000000000000000000000000000000000000 --- a/rocolib/builders/RockerLegBuilder.py +++ /dev/null @@ -1,54 +0,0 @@ -from numbers import Number - -from rocolib.api.components.Component import newComponent - - -c = newComponent("RockerLeg") - -c.addParameter("height", 40, paramType="length") -c.addParameter("depth", 50, paramType="length") -c.addParameter("thickness", 10, paramType="length") -c.addParameter("rocker", 10, paramType="angle") - -l = [ - ["depth"], - ["height"], - ["depth"], - ["height"], - (("height", "rocker"), "x[0] * np.sin(np.deg2rad(x[1]))"), - (("height", "rocker"), "x[0] * np.sin(np.deg2rad(x[1]))"), - ] - -a = [ - 90, - 90, - ["rocker", "90-(x*2)"], - ["rocker", "90+x"], - 0, - ["rocker"], - ] - -n = len(l) - -for i in range(n): - c.addSubcomponent("beam%d" % i, "Rectangle") - c.addSubcomponent("kite%d" % i, "Kite", inherit="thickness", prefix=None) - - c.addConstraint(("beam%d" % i, "w"), *l[i]) - c.addConstraint(("beam%d" % i, "l"), "thickness") - if isinstance(a[i], Number): - c.addConstConstraint(("kite%d" % i, "angle"), a[i]) - else: - c.addConstraint(("kite%d" % i, "angle"), *a[i]) - - c.addConnection(("beam%d" % i,"t"),("kite%d" % i,"b"), angle=0) - if i: - c.addConnection(("beam%d" % i,"b"),("kite%d" % ((i - 1) % n),"t"), angle=0) - -c.addConnection(("beam0","b"),("kite%d" % (n - 1),"t"), angle=0) - - -c.inheritInterface("topedge", ("beam2", "r")) -c.inheritInterface("crossbar", ("beam5", "l")) - -c.toLibrary() diff --git a/rocolib/builders/ServoMountBuilder.py b/rocolib/builders/ServoMountBuilder.py index 0640496730cd357bfb9b4e247767791120f9a22d..6bce1cc6cd61fb74376c8a8a167641a6ca61d42c 100644 --- a/rocolib/builders/ServoMountBuilder.py +++ b/rocolib/builders/ServoMountBuilder.py @@ -1,5 +1,6 @@ from rocolib.library import getComponent from rocolib.api.components.Component import newComponent +from rocolib.library import save from rocolib.api.Function import Function c = newComponent("ServoMount") @@ -28,4 +29,4 @@ c.addConnection(("beam", "face0"), ("mount", "decoration"), mode="hole", offset=Function(params="offset")) -c.toLibrary() +save(c, True) diff --git a/rocolib/builders/SimpleChairBuilder.py b/rocolib/builders/SimpleChairBuilder.py deleted file mode 100644 index 5feeff319a65a3618216bab4766a0763fd01b865..0000000000000000000000000000000000000000 --- a/rocolib/builders/SimpleChairBuilder.py +++ /dev/null @@ -1,15 +0,0 @@ -from rocolib.api.components.Component import newComponent - -c = newComponent("SimpleChair") - -c.addSubcomponent("seat","ChairSeat", inherit=True, prefix=None, root=True) -c.addSubcomponent("legl","VLeg", inherit=True, prefix=None) -c.addSubcomponent("legr","VLeg", inherit=True, prefix=None) - -c.addConstraint(("legl","width"), "depth") -c.addConstraint(("legr","width"), "depth") - -c.addConnection(("seat","left"),("legl","topedge"), angle=0) -c.addConnection(("seat","right"),("legr","topedge"), angle=0) - -c.toLibrary() diff --git a/rocolib/builders/SimpleTableBuilder.py b/rocolib/builders/SimpleTableBuilder.py deleted file mode 100644 index 745c988866bffb3b00c528822b1ae1e55f8934b2..0000000000000000000000000000000000000000 --- a/rocolib/builders/SimpleTableBuilder.py +++ /dev/null @@ -1,30 +0,0 @@ -from rocolib.api.components.Component import newComponent - -c = newComponent("SimpleTable") - -c.addSubcomponent("top","Rectangle", root=True) -c.addSubcomponent("legl","VLeg", inherit=True, prefix=None) -c.addSubcomponent("legr","VLeg", inherit=True, prefix=None) -c.addSubcomponent("legt","VLeg", inherit=True, prefix=None) -c.addSubcomponent("legb","VLeg", inherit=True, prefix=None) - -c.addParameter("length", 70, paramType="length") - -c.addConstraint(("top","l"), "length") -c.addConstraint(("top","w"), "width") - -c.addConstraint(("legt","width"), "length") -c.addConstraint(("legb","width"), "length") - -c.addConnection(("top","l"),("legl","topedge"), angle=90) -c.addConnection(("top","r"),("legr","topedge"), angle=90) -c.addConnection(("top","t"),("legt","topedge"), angle=90) -c.addConnection(("top","b"),("legb","topedge"), angle=90) - -c.addConnection(("legl","rightedge"),("legb","leftedge"), angle=90) -c.addConnection(("legb","rightedge"),("legr","leftedge"), angle=90) -c.addConnection(("legr","rightedge"),("legt","leftedge"), angle=90) -#c.addConnection(("legt","rightedge"),("legl","leftedge"), angle=90) -c.addConnection(("legl","leftedge"),("legt","rightedge"), angle=90) - -c.toLibrary() diff --git a/rocolib/builders/WheelBuilder.py b/rocolib/builders/WheelBuilder.py index 01c9b9a91b80169e9dde634738d410c3cd69335f..e735fd092d0f521066f59856e56e446d2f777395 100644 --- a/rocolib/builders/WheelBuilder.py +++ b/rocolib/builders/WheelBuilder.py @@ -1,4 +1,5 @@ from rocolib.api.components.Component import newComponent +from rocolib.library import save from rocolib.api.Function import Function c = newComponent("Wheel") @@ -11,4 +12,4 @@ c.inheritAllInterfaces("drive", prefix=None) c.addConnection(("drive", "horn"), ("tire", "face"), copyDecorations=True) -c.toLibrary() +save(c, True) diff --git a/rocolib/library/BoatPoint.py b/rocolib/library/BoatPoint.py deleted file mode 100644 index 788d0eb4344b48f9c1fa7a4de638ae13ce96011b..0000000000000000000000000000000000000000 --- a/rocolib/library/BoatPoint.py +++ /dev/null @@ -1,82 +0,0 @@ -from rocolib.api.components import FoldedComponent -from rocolib.api.composables.graph.Face import Face, Rectangle, RightTriangle -import rocolib.utils.numsym as math - - -def pyramid(l,w,h): - def baseangle(x, y): - x2 = x/2. - hx = math.sqrt(h*h+x2*x2) - xa = math.rad2deg(math.arctan2(h,x2)) - ba = math.rad2deg(math.arctan2(hx,y/2.)) - return x2, hx, xa, ba - - l2, hl, la, bla = baseangle(l, w) - w2, hw, wa, bwa = baseangle(w, l) - diag = math.sqrt(h*h + w2*w2 + l2+l2) - da = math.rad2deg(math.arccos(1/math.sqrt((1 + h*h/w2/w2)*(1+h*h/l2/l2)))) - return hl, la, bla, hw, wa, bwa, diag, da - - -class BoatPoint(FoldedComponent): - def define(self): - self.addParameter("width", 50, paramType="length") - self.addParameter("depth", 25, paramType="length") - self.addParameter("point", 50, paramType="length", minValue=0) - - self.addEdgeInterface("ledge", "sl.e1", "depth") - self.addEdgeInterface("cedge", "sc.e2", "width") - self.addEdgeInterface("redge", "sr.e1", "depth") - - self.addEdgeInterface("edge", ["sl.e1", "sc.e2", "sr.e1"], ["depth", "width", "depth"]) - - def assemble(self): - w = self.p.width - d = self.p.depth - p = self.p.point - - hl, la, bla, hw, wa, bwa, diag, da = pyramid(d*2,w,p) - - # Flaps - ba = (180 - bla - bwa)/2. - fx = d * math.tan(math.deg2rad(ba)) - - fll = Face("", ((fx, 0), (hw, 0), (0, d), (0,0))) - flc = RightTriangle("", w/2., hl); - frc = RightTriangle("", hl, w/2.); - frr = Face("", ((d, 0), (0, hw), (0, fx), (0,0))) - - # Main point - self.addFace(fll, "lt"); - self.attachEdge("lt.e2", flc, "e1", prefix="lc", angle=da) - self.attachEdge("lc.e2", frc, "e0", prefix="rc", angle=0) - self.attachEdge("rc.e1", frr, "e1", prefix="rt", angle=da) - - # Flaps - fla = RightTriangle("", d, fx); - flb = RightTriangle("", fx, d); - self.attachEdge("lt.e3", fla, "e0", prefix="fla", angle=180) - self.attachEdge("fla.e1", flb, "e1", prefix="flb", angle=-180) - - fra = RightTriangle("", fx, d); - frb = RightTriangle("", d, fx); - self.attachEdge("rt.e0", fra, "e2", prefix="fra", angle=180) - self.attachEdge("fra.e1", frb, "e1", prefix="frb", angle=-180) - - self.addTab("flb.e0", "lt.e0", angle = -174, width=fx/3) - self.addTab("frb.e2", "rt.e3", angle = -174, width=fx/3) - - # To allow 0 degree attachments at base - se = Face("", ((0,0), (w/2., 0), (-w/2., 0))) - self.attachEdge("lc.e0", se, "e0", prefix="sc", angle=90-la) - self.mergeEdge("rc.e2", "sc.e1", angle=90-la) - - se = Face("", ((0,0), (d,0))) - self.attachEdge("flb.e2", se, "e0", prefix="sl", angle=90-wa) - - se = Face("", ((0,0), (d,0))) - self.attachEdge("frb.e0", se, "e0", prefix="sr", angle=90-wa) - -if __name__ == "__main__": - BoatPoint.test() - diff --git a/rocolib/library/Brains.py b/rocolib/library/Brains.py index dfc5b9841a6909ba3e102b02f01de438fc617480..26f7a2c9a1582a854d458e7f2a18dd7884b45609 100644 --- a/rocolib/library/Brains.py +++ b/rocolib/library/Brains.py @@ -36,15 +36,15 @@ class Brains(ThruHole): def define(self): ThruHole.define(self) self.addParameter("brain", "nodeMCU", values=self.brains, overrides = dict( - nrows = 'c.getParameter("brain").get("nrows")', - ncols = 'c.getParameter("brain").get("ncols")', - rowsep = 'c.getParameter("brain").get("rowsep")', - colsep = 'c.getParameter("brain").get("colsep")', + nrows = 'c.p.brain.get("nrows")', + ncols = 'c.p.brain.get("ncols")', + rowsep = 'c.p.brain.get("rowsep")', + colsep = 'c.p.brain.get("colsep")', )) def assemble(self): ThruHole.assemble(self) - brain = self.getParameter("brain") + brain = self.p.brain self.graph.dotransform(origin = brain.get("origin")) for n, e in brain.get("extras").items(): x, y = e.get("center", (0,0)) diff --git a/rocolib/library/ChairBack.yaml b/rocolib/library/ChairBack.yaml deleted file mode 100644 index b14efdb46c51e598fe2518a4614591cbaa8b35da..0000000000000000000000000000000000000000 --- a/rocolib/library/ChairBack.yaml +++ /dev/null @@ -1,73 +0,0 @@ -connections: - connection0: - - - panel - - tr - - - sider - - t - - angle: 0 - connection1: - - - panel - - tl - - - sidel - - t - - angle: 0 -interfaces: - left: - interface: b - subcomponent: sidel - right: - interface: b - subcomponent: sider -parameters: - backheight: - defaultValue: 40 - spec: - minValue: 0 - units: mm - valueType: (float, int) - gapheight: - defaultValue: 20 - spec: - minValue: 0 - units: mm - valueType: (float, int) - thickness: - defaultValue: 10 - spec: - minValue: 0 - units: mm - valueType: (float, int) - width: - defaultValue: 70 - spec: - minValue: 0 - units: mm - valueType: (float, int) -source: ../builders/ChairBackBuilder.py -subcomponents: - panel: - classname: ChairPanel - kwargs: {} - parameters: - depth: - parameter: backheight - thickness: - parameter: thickness - width: - parameter: width - sidel: - classname: Rectangle - kwargs: {} - parameters: - l: - parameter: thickness - w: - parameter: gapheight - sider: - classname: Rectangle - kwargs: {} - parameters: - l: - parameter: thickness - w: - parameter: gapheight diff --git a/rocolib/library/ChairPanel.yaml b/rocolib/library/ChairPanel.yaml deleted file mode 100644 index 58fb8d8a3bcf094aaf758eb024cae05f14afe9af..0000000000000000000000000000000000000000 --- a/rocolib/library/ChairPanel.yaml +++ /dev/null @@ -1,78 +0,0 @@ -connections: - connection0: - - - sidel - - r - - - back - - l - - angle: 90 - connection1: - - - back - - r - - - sider - - l - - angle: 90 -interfaces: - bl: - interface: b - subcomponent: sidel - br: - interface: b - subcomponent: sider - left: - interface: l - subcomponent: sidel - right: - interface: r - subcomponent: sider - tl: - interface: t - subcomponent: sidel - tr: - interface: t - subcomponent: sider -parameters: - depth: - defaultValue: 50 - spec: - minValue: 0 - units: mm - valueType: (float, int) - thickness: - defaultValue: 10 - spec: - minValue: 0 - units: mm - valueType: (float, int) - width: - defaultValue: 70 - spec: - minValue: 0 - units: mm - valueType: (float, int) -source: ../builders/ChairPanelBuilder.py -subcomponents: - back: - classname: Rectangle - kwargs: - root: true - parameters: - l: - parameter: width - w: - parameter: depth - sidel: - classname: Rectangle - kwargs: {} - parameters: - l: - parameter: thickness - w: - parameter: depth - sider: - classname: Rectangle - kwargs: {} - parameters: - l: - parameter: thickness - w: - parameter: depth diff --git a/rocolib/library/ChairSeat.yaml b/rocolib/library/ChairSeat.yaml deleted file mode 100644 index 78bc361d75da67f4e4523d0ef760a3410e2f6193..0000000000000000000000000000000000000000 --- a/rocolib/library/ChairSeat.yaml +++ /dev/null @@ -1,113 +0,0 @@ -connections: - connection0: - - - back - - left - - - kitel - - t - - angle: 0 - connection1: - - - seat - - bl - - - kitel - - b - - angle: 0 - connection2: - - - back - - right - - - kiter - - b - - angle: 0 - connection3: - - - seat - - br - - - kiter - - t - - angle: 0 -interfaces: - left: - interface: left - subcomponent: seat - right: - interface: right - subcomponent: seat -parameters: - backheight: - defaultValue: 40 - spec: - minValue: 0 - units: mm - valueType: (float, int) - depth: - defaultValue: 50 - spec: - minValue: 0 - units: mm - valueType: (float, int) - gapheight: - defaultValue: 20 - spec: - minValue: 0 - units: mm - valueType: (float, int) - recline: - defaultValue: 110 - spec: - maxValue: 360 - minValue: 0 - units: degrees - valueType: (float, int) - thickness: - defaultValue: 10 - spec: - minValue: 0 - units: mm - valueType: (float, int) - width: - defaultValue: 70 - spec: - minValue: 0 - units: mm - valueType: (float, int) -source: ../builders/ChairSeatBuilder.py -subcomponents: - back: - classname: ChairBack - kwargs: {} - parameters: - backheight: - parameter: backheight - gapheight: - parameter: gapheight - thickness: - parameter: thickness - width: - parameter: width - kitel: - classname: Kite - kwargs: {} - parameters: - angle: - function: 180 - x - parameter: recline - thickness: - parameter: thickness - kiter: - classname: Kite - kwargs: {} - parameters: - angle: - function: 180 - x - parameter: recline - thickness: - parameter: thickness - seat: - classname: ChairPanel - kwargs: - root: true - parameters: - depth: - parameter: depth - thickness: - parameter: thickness - width: - parameter: width diff --git a/rocolib/library/Cutout.py b/rocolib/library/Cutout.py index 06e6df69065e62f3a95e1dd138a4de800c1a57ca..5787e64e7a368de0f12567b87170997aa53c6ea4 100644 --- a/rocolib/library/Cutout.py +++ b/rocolib/library/Cutout.py @@ -6,8 +6,8 @@ class Cutout(DecorationComponent): self.addParameter("dx", 10, paramType="length") self.addParameter("dy", 20, paramType="length") self.addParameter("d", overrides={ - "dx": 'c.getParameter("d")', - "dy": 'c.getParameter("d")', + "dx": 'c.p.d', + "dy": 'c.p.d', }) def assemble(self): diff --git a/rocolib/library/ESPSeg.yaml b/rocolib/library/ESPSeg.yaml index 7de2c228fb1aaf9a34c2dc9021f3a9d6f39def98..28215c3a9ff7f4d99b4fa496b30d506572f7d62f 100644 --- a/rocolib/library/ESPSeg.yaml +++ b/rocolib/library/ESPSeg.yaml @@ -94,10 +94,10 @@ parameters: defaultValue: nodeMCU spec: overrides: - colsep: c.getParameter("brain").get("colsep") - ncols: c.getParameter("brain").get("ncols") - nrows: c.getParameter("brain").get("nrows") - rowsep: c.getParameter("brain").get("rowsep") + colsep: c.p.brain.get("colsep") + ncols: c.p.brain.get("ncols") + nrows: c.p.brain.get("nrows") + rowsep: c.p.brain.get("rowsep") values: nodeMCU: colsep: 22.86 diff --git a/rocolib/library/MountedBrains.yaml b/rocolib/library/MountedBrains.yaml index aff7f4571405a33c673fc786fd3de74012fa9a7b..3dc99a0b7ec90dd482b3043758eb387cbea5ae5c 100644 --- a/rocolib/library/MountedBrains.yaml +++ b/rocolib/library/MountedBrains.yaml @@ -55,10 +55,10 @@ parameters: defaultValue: nodeMCU spec: overrides: - colsep: c.getParameter("brain").get("colsep") - ncols: c.getParameter("brain").get("ncols") - nrows: c.getParameter("brain").get("nrows") - rowsep: c.getParameter("brain").get("rowsep") + colsep: c.p.brain.get("colsep") + ncols: c.p.brain.get("ncols") + nrows: c.p.brain.get("nrows") + rowsep: c.p.brain.get("rowsep") values: nodeMCU: colsep: 22.86 diff --git a/rocolib/library/RockerChair.yaml b/rocolib/library/RockerChair.yaml deleted file mode 100644 index 76795a885103f495986a5090e8d32f8401a9ba44..0000000000000000000000000000000000000000 --- a/rocolib/library/RockerChair.yaml +++ /dev/null @@ -1,132 +0,0 @@ -connections: - connection0: - - - seat - - left - - - legl - - topedge - - angle: 0 - connection1: - - - seat - - right - - - legr - - topedge - - angle: 0 - connection2: - - - crossbar - - l - - - legl - - crossbar - - angle: 90 - connection3: - - - crossbar - - r - - - legr - - crossbar - - angle: 90 -interfaces: {} -parameters: - backheight: - defaultValue: 40 - spec: - minValue: 0 - units: mm - valueType: (float, int) - depth: - defaultValue: 50 - spec: - minValue: 0 - units: mm - valueType: (float, int) - gapheight: - defaultValue: 20 - spec: - minValue: 0 - units: mm - valueType: (float, int) - height: - defaultValue: 40 - spec: - minValue: 0 - units: mm - valueType: (float, int) - recline: - defaultValue: 110 - spec: - maxValue: 360 - minValue: 0 - units: degrees - valueType: (float, int) - rocker: - defaultValue: 10 - spec: - maxValue: 360 - minValue: 0 - units: degrees - valueType: (float, int) - thickness: - defaultValue: 10 - spec: - minValue: 0 - units: mm - valueType: (float, int) - width: - defaultValue: 70 - spec: - minValue: 0 - units: mm - valueType: (float, int) -source: ../builders/RockerChairBuilder.py -subcomponents: - crossbar: - classname: Rectangle - kwargs: {} - parameters: - l: - parameter: width - w: - function: x[0] * np.sin(np.deg2rad(x[1])) - parameter: - - height - - rocker - legl: - classname: RockerLeg - kwargs: - mirror: true - parameters: - depth: - parameter: depth - height: - parameter: height - rocker: - parameter: rocker - thickness: - parameter: thickness - legr: - classname: RockerLeg - kwargs: {} - parameters: - depth: - parameter: depth - height: - parameter: height - rocker: - parameter: rocker - thickness: - parameter: thickness - seat: - classname: ChairSeat - kwargs: - root: true - parameters: - backheight: - parameter: backheight - depth: - parameter: depth - gapheight: - parameter: gapheight - recline: - parameter: recline - thickness: - parameter: thickness - width: - parameter: width diff --git a/rocolib/library/RockerLeg.yaml b/rocolib/library/RockerLeg.yaml deleted file mode 100644 index 59c4027f4f3d1ce064f26393fd8e54989cd30ddf..0000000000000000000000000000000000000000 --- a/rocolib/library/RockerLeg.yaml +++ /dev/null @@ -1,207 +0,0 @@ -connections: - connection0: - - - beam0 - - t - - - kite0 - - b - - angle: 0 - connection1: - - - beam1 - - t - - - kite1 - - b - - angle: 0 - connection10: - - - beam5 - - b - - - kite4 - - t - - angle: 0 - connection11: - - - beam0 - - b - - - kite5 - - t - - angle: 0 - connection2: - - - beam1 - - b - - - kite0 - - t - - angle: 0 - connection3: - - - beam2 - - t - - - kite2 - - b - - angle: 0 - connection4: - - - beam2 - - b - - - kite1 - - t - - angle: 0 - connection5: - - - beam3 - - t - - - kite3 - - b - - angle: 0 - connection6: - - - beam3 - - b - - - kite2 - - t - - angle: 0 - connection7: - - - beam4 - - t - - - kite4 - - b - - angle: 0 - connection8: - - - beam4 - - b - - - kite3 - - t - - angle: 0 - connection9: - - - beam5 - - t - - - kite5 - - b - - angle: 0 -interfaces: - crossbar: - interface: l - subcomponent: beam5 - topedge: - interface: r - subcomponent: beam2 -parameters: - depth: - defaultValue: 50 - spec: - minValue: 0 - units: mm - valueType: (float, int) - height: - defaultValue: 40 - spec: - minValue: 0 - units: mm - valueType: (float, int) - rocker: - defaultValue: 10 - spec: - maxValue: 360 - minValue: 0 - units: degrees - valueType: (float, int) - thickness: - defaultValue: 10 - spec: - minValue: 0 - units: mm - valueType: (float, int) -source: ../builders/RockerLegBuilder.py -subcomponents: - beam0: - classname: Rectangle - kwargs: {} - parameters: - l: - parameter: thickness - w: - parameter: depth - beam1: - classname: Rectangle - kwargs: {} - parameters: - l: - parameter: thickness - w: - parameter: height - beam2: - classname: Rectangle - kwargs: {} - parameters: - l: - parameter: thickness - w: - parameter: depth - beam3: - classname: Rectangle - kwargs: {} - parameters: - l: - parameter: thickness - w: - parameter: height - beam4: - classname: Rectangle - kwargs: {} - parameters: - l: - parameter: thickness - w: - function: x[0] * np.sin(np.deg2rad(x[1])) - parameter: &id001 - - height - - rocker - beam5: - classname: Rectangle - kwargs: {} - parameters: - l: - parameter: thickness - w: - function: x[0] * np.sin(np.deg2rad(x[1])) - parameter: *id001 - kite0: - classname: Kite - kwargs: {} - parameters: - angle: 90 - thickness: - parameter: thickness - kite1: - classname: Kite - kwargs: {} - parameters: - angle: 90 - thickness: - parameter: thickness - kite2: - classname: Kite - kwargs: {} - parameters: - angle: - function: 90-(x*2) - parameter: rocker - thickness: - parameter: thickness - kite3: - classname: Kite - kwargs: {} - parameters: - angle: - function: 90+x - parameter: rocker - thickness: - parameter: thickness - kite4: - classname: Kite - kwargs: {} - parameters: - angle: 0 - thickness: - parameter: thickness - kite5: - classname: Kite - kwargs: {} - parameters: - angle: - parameter: rocker - thickness: - parameter: thickness diff --git a/rocolib/library/SimpleChair.yaml b/rocolib/library/SimpleChair.yaml deleted file mode 100644 index 672e7b286a2f4e553a2ff625350097c66e182dcd..0000000000000000000000000000000000000000 --- a/rocolib/library/SimpleChair.yaml +++ /dev/null @@ -1,184 +0,0 @@ -connections: - connection0: - - - seat - - left - - - legl - - topedge - - angle: 0 - connection1: - - - seat - - right - - - legr - - topedge - - angle: 0 -interfaces: {} -parameters: - _dx: - defaultValue: 0 - spec: - hidden: true - minValue: null - units: mm - valueType: (float, int) - _dy: - defaultValue: 0 - spec: - hidden: true - minValue: null - units: mm - valueType: (float, int) - _dz: - defaultValue: 0 - spec: - hidden: true - minValue: null - units: mm - valueType: (float, int) - _q_a: - defaultValue: 1 - spec: - hidden: true - maxValue: 1 - minValue: -1 - valueType: (int, float) - _q_i: - defaultValue: 0 - spec: - hidden: true - maxValue: 1 - minValue: -1 - valueType: (int, float) - _q_j: - defaultValue: 0 - spec: - hidden: true - maxValue: 1 - minValue: -1 - valueType: (int, float) - _q_k: - defaultValue: 0 - spec: - hidden: true - maxValue: 1 - minValue: -1 - valueType: (int, float) - backheight: - defaultValue: 40 - spec: - minValue: 0 - units: mm - valueType: (float, int) - depth: - defaultValue: 50 - spec: - minValue: 0 - units: mm - valueType: (float, int) - gapheight: - defaultValue: 20 - spec: - minValue: 0 - units: mm - valueType: (float, int) - height: - defaultValue: 40 - spec: - minValue: 0 - units: mm - valueType: (float, int) - recline: - defaultValue: 110 - spec: - maxValue: 360 - minValue: 0 - units: degrees - valueType: (float, int) - taper: - defaultValue: 0.5 - spec: - maxValue: 1 - minValue: 0 - valueType: (float, int) - thickness: - defaultValue: 10 - spec: - minValue: 0 - units: mm - valueType: (float, int) - width: - defaultValue: 70 - spec: - minValue: 0 - units: mm - valueType: (float, int) -source: ../builders/SimpleChairBuilder.py -subcomponents: - legl: - classname: VLeg - kwargs: {} - parameters: - _dx: - parameter: _dx - _dy: - parameter: _dy - _dz: - parameter: _dz - _q_a: - parameter: _q_a - _q_i: - parameter: _q_i - _q_j: - parameter: _q_j - _q_k: - parameter: _q_k - height: - parameter: height - taper: - parameter: taper - thickness: - parameter: thickness - width: - parameter: depth - legr: - classname: VLeg - kwargs: {} - parameters: - _dx: - parameter: _dx - _dy: - parameter: _dy - _dz: - parameter: _dz - _q_a: - parameter: _q_a - _q_i: - parameter: _q_i - _q_j: - parameter: _q_j - _q_k: - parameter: _q_k - height: - parameter: height - taper: - parameter: taper - thickness: - parameter: thickness - width: - parameter: depth - seat: - classname: ChairSeat - kwargs: - root: true - parameters: - backheight: - parameter: backheight - depth: - parameter: depth - gapheight: - parameter: gapheight - recline: - parameter: recline - thickness: - parameter: thickness - width: - parameter: width diff --git a/rocolib/library/SimpleTable.yaml b/rocolib/library/SimpleTable.yaml deleted file mode 100644 index 5a77e45e77f45fded0bcd175645f67567f650d48..0000000000000000000000000000000000000000 --- a/rocolib/library/SimpleTable.yaml +++ /dev/null @@ -1,245 +0,0 @@ -connections: - connection0: - - - top - - l - - - legl - - topedge - - angle: 90 - connection1: - - - top - - r - - - legr - - topedge - - angle: 90 - connection2: - - - top - - t - - - legt - - topedge - - angle: 90 - connection3: - - - top - - b - - - legb - - topedge - - angle: 90 - connection4: - - - legl - - rightedge - - - legb - - leftedge - - angle: 90 - connection5: - - - legb - - rightedge - - - legr - - leftedge - - angle: 90 - connection6: - - - legr - - rightedge - - - legt - - leftedge - - angle: 90 - connection7: - - - legl - - leftedge - - - legt - - rightedge - - angle: 90 -interfaces: {} -parameters: - _dx: - defaultValue: 0 - spec: - hidden: true - minValue: null - units: mm - valueType: (float, int) - _dy: - defaultValue: 0 - spec: - hidden: true - minValue: null - units: mm - valueType: (float, int) - _dz: - defaultValue: 0 - spec: - hidden: true - minValue: null - units: mm - valueType: (float, int) - _q_a: - defaultValue: 1 - spec: - hidden: true - maxValue: 1 - minValue: -1 - valueType: (int, float) - _q_i: - defaultValue: 0 - spec: - hidden: true - maxValue: 1 - minValue: -1 - valueType: (int, float) - _q_j: - defaultValue: 0 - spec: - hidden: true - maxValue: 1 - minValue: -1 - valueType: (int, float) - _q_k: - defaultValue: 0 - spec: - hidden: true - maxValue: 1 - minValue: -1 - valueType: (int, float) - height: - defaultValue: 40 - spec: - minValue: 0 - units: mm - valueType: (float, int) - length: - defaultValue: 70 - spec: - minValue: 0 - units: mm - valueType: (float, int) - taper: - defaultValue: 0.5 - spec: - maxValue: 1 - minValue: 0 - valueType: (float, int) - thickness: - defaultValue: 10 - spec: - minValue: 0 - units: mm - valueType: (float, int) - width: - defaultValue: 50 - spec: - minValue: 0 - units: mm - valueType: (float, int) -source: ../builders/SimpleTableBuilder.py -subcomponents: - legb: - classname: VLeg - kwargs: {} - parameters: - _dx: - parameter: _dx - _dy: - parameter: _dy - _dz: - parameter: _dz - _q_a: - parameter: _q_a - _q_i: - parameter: _q_i - _q_j: - parameter: _q_j - _q_k: - parameter: _q_k - height: - parameter: height - taper: - parameter: taper - thickness: - parameter: thickness - width: - parameter: length - legl: - classname: VLeg - kwargs: {} - parameters: - _dx: - parameter: _dx - _dy: - parameter: _dy - _dz: - parameter: _dz - _q_a: - parameter: _q_a - _q_i: - parameter: _q_i - _q_j: - parameter: _q_j - _q_k: - parameter: _q_k - height: - parameter: height - taper: - parameter: taper - thickness: - parameter: thickness - width: - parameter: width - legr: - classname: VLeg - kwargs: {} - parameters: - _dx: - parameter: _dx - _dy: - parameter: _dy - _dz: - parameter: _dz - _q_a: - parameter: _q_a - _q_i: - parameter: _q_i - _q_j: - parameter: _q_j - _q_k: - parameter: _q_k - height: - parameter: height - taper: - parameter: taper - thickness: - parameter: thickness - width: - parameter: width - legt: - classname: VLeg - kwargs: {} - parameters: - _dx: - parameter: _dx - _dy: - parameter: _dy - _dz: - parameter: _dz - _q_a: - parameter: _q_a - _q_i: - parameter: _q_i - _q_j: - parameter: _q_j - _q_k: - parameter: _q_k - height: - parameter: height - taper: - parameter: taper - thickness: - parameter: thickness - width: - parameter: length - top: - classname: Rectangle - kwargs: - root: true - parameters: - l: - parameter: length - w: - parameter: width diff --git a/rocolib/library/Stool.py b/rocolib/library/Stool.py deleted file mode 100644 index 8c36db6363edd2fdd15ee654141bedeeca35c399..0000000000000000000000000000000000000000 --- a/rocolib/library/Stool.py +++ /dev/null @@ -1,33 +0,0 @@ -from rocolib.api.components import FoldedComponent -from rocolib.api.composables.graph.Face import Face, Rectangle, RegularNGon -from rocolib.api.composables.graph.Face import RegularNGon2 as r2l -from rocolib.utils.numsym import sin, deg2rad - - -class Stool(FoldedComponent): - def define(self): - self.addParameter("height", 60, paramType="length", minValue=10) - self.addParameter("legs", 3, paramType="count", minValue=1) - self.addParameter("radius", minValue=10, overrides={ - "legwidth": 'c.getParameter("radius") * 2 * np.sin( np.pi() / c.getParameter("legs") / 2)', - }) - self.addParameter("legwidth", 20, paramType="length", minValue=9) - self.addParameter("angle", 80, paramType="angle") - - def assemble(self): - h = self.p.height - lp = self.p.legs - e = self.p.legwidth - ap = self.p.angle - - n = lp * 2 - self.addFace(RegularNGon("", n, e), "seat") - - for i in range(0, int(n/2)): - s = Rectangle("", e, h) - self.attachEdge("seat.e%d" % (2*i), s, "e0", "leg%d" % i, angle=ap) - -if __name__ == "__main__": - from rocolib.api.composables.graph.Joint import FingerJoint - Stool.test(thickness=4, joint=FingerJoint(thickness=4)) - diff --git a/rocolib/library/ThruHole.py b/rocolib/library/ThruHole.py index b61671480639d9731fe14a335c8ff7e73eee81e2..faccc436606f0a1fd463ceb2d89d44fe95aa4033 100644 --- a/rocolib/library/ThruHole.py +++ b/rocolib/library/ThruHole.py @@ -12,13 +12,13 @@ class ThruHole(DecorationComponent): self.addParameter("diameter", 1, paramType="length") def assemble(self): - diam = self.getParameter("diameter")/2. - nr = self.getParameter("nrows") - nc = self.getParameter("ncols") + diam = self.p.diameter/2. + nr = self.p.nrows + nc = self.p.ncols def hole(i, j, d): - dx = (j - (nc-1)/2.)*self.getParameter("colsep") - dy = (i - (nr-1)/2.)*self.getParameter("rowsep") + dx = (j - (nc-1)/2.)*self.p.colsep + dy = (i - (nr-1)/2.)*self.p.rowsep return Face("r-%d-%d" % (i,j), ((dx-d, dy-d), (dx+d, dy-d), (dx+d, dy+d), (dx-d, dy+d)), recenter=False) diff --git a/rocolib/library/VLeg.py b/rocolib/library/VLeg.py deleted file mode 100644 index ff37fdd2d37d7e7d5dc6fbfc4d7e9639ebfd0b06..0000000000000000000000000000000000000000 --- a/rocolib/library/VLeg.py +++ /dev/null @@ -1,27 +0,0 @@ -from rocolib.api.components import FoldedComponent -from rocolib.api.composables.graph.Face import Face - - -class VLeg(FoldedComponent): - def define(self): - self.addParameter("height", 40, paramType="length") - self.addParameter("width", 50, paramType="length") - self.addParameter("thickness", 10, paramType="length") - self.addParameter("taper", 0.5, paramType="ratio") - - self.addEdgeInterface("leftedge", "leg.e0", "height") - self.addEdgeInterface("topedge", "leg.e1", "width") - self.addEdgeInterface("rightedge", "leg.e2", "height") - - def assemble(self): - h = self.p.height - w = self.p.width - t = self.p.thickness - r = self.p.taper * t - - s = Face("", ((h, 0), (h, w), (0, w), (0, w-r), (h-t, w-t), (h-t, t), (0, r), (0,0))) - self.addFace(s, "leg") - -if __name__ == "__main__": - VLeg.test() - diff --git a/rocolib/library/__init__.py b/rocolib/library/__init__.py index 309c68313d001fa8619d78b8339232902b93ae36..273604e13175eeb81d31fd6f7e1512ea4ff236dc 100644 --- a/rocolib/library/__init__.py +++ b/rocolib/library/__init__.py @@ -1,51 +1,116 @@ import networkx as nx -from os import system +import os from os.path import dirname, realpath, basename, join from glob import glob import importlib import logging - -from rocolib import rocopath -from rocolib.utils.io import load_yaml -from rocolib.api.components import newComponent +from yaml import safe_load +import sys +if sys.version_info[:2] >= (3, 10): + # pylint: disable=no-name-in-module + from importlib.metadata import entry_points +else: + from importlib_metadata import entry_points +from platformdirs import user_data_dir +from collections import OrderedDict log = logging.getLogger(__name__) ROCOLIB_LIBRARY = dirname(realpath(__file__)) - -pyComponents = [ basename(f)[:-3] for f in glob(ROCOLIB_LIBRARY + "/[!_]*.py")] -yamlComponents = [ basename(f)[:-5] for f in glob(ROCOLIB_LIBRARY + "/*.yaml")] -allComponents = set(pyComponents + yamlComponents) - -def getSubcomponents(c): +USER_LIBRARY = user_data_dir("rocolib", "rocolib") + +def _makeLibDirs(): + dirs = OrderedDict(user = USER_LIBRARY) + mods = OrderedDict() + libs = entry_points(group="rocolib.libraries") + for lib in libs.names: + libmodule = libs[lib] + dirs[lib] = libmodule.load().ROCOLIB_LIBRARY + mods[lib] = libmodule.value + dirs["rocolib"] = ROCOLIB_LIBRARY + mods["rocolib"] = "rocolib.library" + return dirs, mods + +libDirs, libModules = _makeLibDirs() + +def _makeLibItems(ext): + data = {} + for k, d in libDirs.items(): + files = [ basename(f)[:-len(ext)-1] for f in glob(d + f"/[!_]*.{ext}")] + for f in files: + fullname = f"{k}:{f}" + # XXX TODO: what if a pyComponent in one library is a yamlComponent in another or vice versa? + if f not in data: + data[f] = fullname + else: + data[fullname] = fullname + return data + +yamlComponents = _makeLibItems("yaml") +pyComponents = _makeLibItems("py") + +allComponents = {**pyComponents, **yamlComponents} + +def _load_yaml(c): + with open(getYamlFile(c), 'r') as fd: + return safe_load(fd) + +def _getSubcomponents(c): try: - return set((x['classname'] for x in load_yaml(c).get('subcomponents', dict()).values())) - except FileNotFoundError: + return set((x['classname'] for x in _load_yaml(c).get('subcomponents', dict()).values())) + except (ValueError, FileNotFoundError): return set() -def getComponentNx(): +def _getComponentNx(): net = nx.DiGraph() - net.add_nodes_from(allComponents) + net.add_nodes_from(allComponents.keys()) for c in allComponents: - for sc in getSubcomponents(c): - net.add_edge(sc, c) + for sc in _getSubcomponents(c): + if sc in allComponents: + net.add_edge(sc, c) + else: + for k, v in allComponents.items(): + if sc == v: + net.add_edge(k, c) + break + else: + log.warning(f"Subcomponent {sc} of component {c} not found, ignoring") return net def getComponentTree(): - return nx.topological_generations(getComponentNx()) + return nx.topological_generations(_getComponentNx()) + +def getYamlFile(c): + if c in yamlComponents: + c = yamlComponents[c] + elif ":" not in c: + raise ValueError(f"Cannot find appropriate yaml file for {c}") + d, c = c.split(":") + return f"{libDirs[d]}/{c}.yaml" + +def getPyComponent(c): + if c in pyComponents: + c = pyComponents[c] + elif ":" not in c: + raise ValueError(f"Cannot find appropriate python file / plugin library for {c}") + d, c = c.split(":") + + obj = getattr(importlib.import_module(f"{libModules[d]}.{c}"), c) + return obj() def getComponent(c, **kwargs): ''' Here we are doing Dynamic instantiation from string name of a class in dynamically imported module Parameter c (str): component name e.g. 'Stool' + kwargs: component parameter / value pairs (possibly including name) ''' - if c in pyComponents: - # Load "module.submodule.MyClass" - obj = getattr(importlib.import_module("rocolib.library." + c), c) - # Instantiate the class (pass arguments to the constructor, if needed) - my_obj = obj() - elif c in yamlComponents: - my_obj = newComponent(c, f"{ROCOLIB_LIBRARY}/{c}.yaml") + from rocolib.api.components import newComponent + if c in pyComponents.keys() or c in pyComponents.values(): + log.debug("Found python component in library, instantiating and returning object") + my_obj = getPyComponent(c) + elif c in yamlComponents.keys() or c in yamlComponents.values(): + log.debug("Found yaml component in library, creating newComponent from yamlFile") + my_obj = newComponent(c, yamlFile=getYamlFile(c)) else: raise ValueError(f"Component {c} not found in library") @@ -59,6 +124,21 @@ def getComponent(c, **kwargs): return my_obj +def save(c, lib=None): + if lib is True: + d = ROCOLIB_LIBRARY + elif lib: + d = libDirs[lib] + else: + d = USER_LIBRARY + os.makedirs(d, exist_ok=True) + + # XXX TODO: Check for collisions? + # if collision: + # flag to allow rebuilding, and fail otherwise? + # if no flag, check if source matches and rebuild if so, fail otherwise? + return c.toYaml(d, c.cName + ".yaml", reldir=ROCOLIB_LIBRARY) + def rebuild(built=None): if built is None: built = set() @@ -77,7 +157,7 @@ def rebuildComponent(c, built=None, throw=True): log.debug(f"{c} has been rebuilt, skipping") return True - definition = load_yaml(c) + definition = _load_yaml(c) src = definition.get("source", None) success = True if src: @@ -91,7 +171,7 @@ def rebuildComponent(c, built=None, throw=True): # XXX TODO: Test to make sure we don't call this script and then infinitely recurse! log.debug(f"Calling os.system: % python {ROCOLIB_LIBRARY}/{src}") - if system(f"python {ROCOLIB_LIBRARY}/{src}"): + if os.system(f"python {ROCOLIB_LIBRARY}/{src}"): success = False built.add(c) @@ -100,15 +180,3 @@ def rebuildComponent(c, built=None, throw=True): if throw: assert success, f"Error rebuilding {c}, check stdout/stderr for details" return success - -def getComponentPaths(c): - paths = {} - if c in pyComponents: - paths["python"] = rocopath(join(ROCOLIB_LIBRARY, f"{c}.py")) - if c in yamlComponents: - paths["yaml"] = rocopath(join(ROCOLIB_LIBRARY, f"{c}.yaml")) - definition = load_yaml(c) - src = definition.get("source", None) - if src: - paths["builder"] = rocopath(join(ROCOLIB_LIBRARY, src)) - return paths diff --git a/rocolib/test/test_library.py b/rocolib/test/test_library.py index 61b6379da60768fd5ff1237d8247e932566c08f0..7ef66f0430550e653a331f8caaa012c19d467028 100644 --- a/rocolib/test/test_library.py +++ b/rocolib/test/test_library.py @@ -1,6 +1,6 @@ import pytest import logging -from rocolib.library import getComponent, pyComponents, yamlComponents, allComponents, rebuild, rebuildComponent +from rocolib.library import getComponent, pyComponents, yamlComponents, rebuild, rebuildComponent def test_rebuild(c = None): @@ -9,13 +9,13 @@ def test_rebuild(c = None): else: rebuild() -@pytest.mark.parametrize("component",pyComponents) +@pytest.mark.parametrize("component",pyComponents.keys()) def test_component_python(component): getComponent(component).test() -@pytest.mark.parametrize("component",yamlComponents) +@pytest.mark.parametrize("component",yamlComponents.keys()) def test_component_yaml(component): - getComponent(component).makeOutput() + getComponent(component).test() if __name__ == "__main__": logging.basicConfig(level=logging.INFO) diff --git a/rocolib/utils/io.py b/rocolib/utils/io.py deleted file mode 100644 index 1290898f51993670fb77e567c7c434b7516762a9..0000000000000000000000000000000000000000 --- a/rocolib/utils/io.py +++ /dev/null @@ -1,14 +0,0 @@ -from os.path import join - -from yaml import safe_load - -from rocolib import ROCOLIB_DIR - -def load_yaml(file_name): - if file_name[-5:] != '.yaml': - file_name += '.yaml' - - fqn = join(ROCOLIB_DIR, 'library', file_name) - with open(fqn, 'r') as fd: - return safe_load(fd) -