diff --git a/rocolib/api/Function.py b/rocolib/api/Function.py
index 8024cdb56f60588e33f29411bab2e65dc3c556c9..b0d8d076ea91266d71f6ede932157146d13be02c 100644
--- a/rocolib/api/Function.py
+++ b/rocolib/api/Function.py
@@ -21,7 +21,6 @@ class Function:
 
   def eval(self, parameterizable):
     import rocolib.utils.numsym as np
-    from rocolib.utils.dimensions import getDim
     function = eval("lambda x : " + self.fnstring, locals())
     if isinstance(self.params, (list, tuple)):
       output = function([parameterizable.getParameter(x) for x in self.params])
diff --git a/rocolib/api/Parameterized.py b/rocolib/api/Parameterized.py
index 43b8f37ef4ab8cedb273acc33beae4a8e2a1f888..18f0ad2bb69fba74d5c0a02882e030eef92621d6 100644
--- a/rocolib/api/Parameterized.py
+++ b/rocolib/api/Parameterized.py
@@ -1,6 +1,13 @@
-from rocolib.utils.dimensions import isDim
+import re
+
 from rocolib.utils.numsym import Dummy
+import logging
+import yaml
+
 
+### XXX "." is used to separate subcomponent parameters when inherited
+###     but we can't tell the difference here, so it's not invalid I guess
+VALID_ATTR = re.compile('^[a-zA-Z_][a-zA-Z0-9_.]*$')
 
 PARAM_TYPES = {
     "length": {
@@ -23,23 +30,19 @@ PARAM_TYPES = {
         "valueType": "int",
         "minValue": 0,
     },
-    "dimension": {
-        "valueType": "str",
-        #"isValid": isDim,
-    },
 }
 
 class Parameter:
     def __init__(self, name, defaultValue=None, paramType=None, **kwargs):
-        ### XXX "." is used to separate subcomponent parameters when inherited
-        ###     but we can't tell the difference here, so it's not invalid I guess
+        if not VALID_ATTR.match(name):
+            raise ValueError("Invalid parameter name " + name)
+        if name[0] == '_' and not kwargs.get("hidden", False):
+            raise ValueError("Leading _ character reserved for hidden parameters, either set hidden flag or change name from " + name)
 
-        #if "." in name:
-            #raise ValueError("Invalid character '.' in parameter name " + name)
         self.name = name
 
-        if defaultValue is None and not kwargs.get("optional", False):
-            raise ValueError(f"Must specify either defaultValue or optional=True for parameter {name}")
+        if defaultValue is None and not kwargs.get("overrides", False):
+            raise ValueError(f"Must specify either defaultValue or overrides (implies optional) for parameter {name}")
 
         self.defaultValue = defaultValue
         self.spec = {}
@@ -54,7 +57,7 @@ class Parameter:
         self.assertValid(defaultValue)
 
         vt = self.spec.get("valueType", "")
-        no = not(self.spec.get("optional", False))
+        no = not(self.spec.get("overrides", False))
 
         if no and ("int" in vt or "float" in vt):
             integer=None
@@ -72,21 +75,40 @@ class Parameter:
             self.symbol = None
         self.value = None
 
-    def setValue(self, value):
-        self.assertValid(value)
+    def resolveValue(self, value):
+        if "values" not in self.spec:
+            return value
+        sanitize = lambda x : yaml.safe_load(yaml.safe_dump(x))
+        vtest = sanitize(value)
+        v = sanitize(self.spec["values"])
+        if isinstance(v, dict) and vtest not in list(v.keys()):
+            ks = list(v.keys())
+            vs = list(v.values())
+            if vtest in vs:
+                return ks[vs.index(vtest)]
+        return value
+
+    def setValue(self, value, validate=True):
+        value = self.resolveValue(value)
+        if validate:
+            self.assertValid(value)
         self.value = value
 
     def setDefault(self, force=False):
         if force or self.value is None:
             self.setValue(self.defaultValue)
 
+    def setSpec(self, spec, value, validate=False):
+        self.spec[spec] = value
+        if validate:
+            self.assertValid(self.value)
+
     def assertValid(self, value):
         if value is None:
-            if self.spec.get("optional", False):
-                self.value = value
-                return
+            if self.spec.get("overrides", False):
+                return True
             else:
-                raise ValueError(f"Parameter {self.name} is not optional and cannot be set to None")
+                raise ValueError(f"Parameter {self.name} is not optional (via overrides) and cannot be set to None")
 
         def check(spec, test, error):
             if (self.spec.get(spec, None) is not None and not test(self.spec[spec])):
@@ -95,15 +117,48 @@ class Parameter:
         check("valueType", lambda x : isinstance(value, eval(x)), "is not of type")
         check("minValue", lambda x : value >= x, "is less than")
         check("maxValue", lambda x : value <= x, "is greater than")
-        check("isValid", lambda x : x(value), "is invalid")
+        check("values", lambda x : value in x, "is not in")
+        check("isValid", lambda x : x(self, value), "is invalid")
         return True
 
+    def validate(self):
+        return self.assertValid(self.value)
+
     def getValue(self):
         if self.value is not None:
-            return self.value
+            if "values" in self.spec:
+                return self.spec["values"][self.value]
+            else:
+                return self.value
         else:
             return self.symbol
 
+    def doOverrides(self, c):
+        if self.value is None:
+            return
+        overrides = self.spec.get("overrides", [])
+        if isinstance(overrides, dict):
+            for k, fn in overrides.items():
+                try:
+                    import rocolib.utils.numsym as np
+                    value = eval(fn, locals())
+                    c.setParameter(k, value)
+                except ValueError as e:
+                    raise ValueError(str(e) + f" (via setting overriding parameter {self.name} to {self.value})")
+                except KeyError as e:
+                    logging.debug(str(e) + f" (via setting overriding parameter {self.name} to {self.value}) -- ignoring")
+        elif isinstance(overrides, (list, tuple)):
+            for k in overrides:
+                try:
+                    c.setParameter(k, self.value)
+                except KeyError as e:
+                    logging.debug(str(e) + f" (via setting overriding parameter {self.name} to {self.value}) -- ignoring")
+        else:
+            try:
+                c.setParameter(overrides, self.value)
+            except KeyError as e:
+                logging.debug(str(e) + f" (via setting overriding parameter {self.name} to {self.value}) -- ignoring")
+
     def getInfo(self, keys=None):
         if isinstance(keys, str):
             return getattr(self, keys)
@@ -161,20 +216,33 @@ class Parameterized(object):
         """
         for n, p in self._parameters.items():
             p.setDefault(force)
+        for n, p in self._parameters.items():
+            p.doOverrides(self)
 
 
-    def setParameter(self, n, v):
+    def setParameter(self, n, v, validate=True):
         """
         Sets a k/v pair to the internal store if the key has been added previously
         Raises KeyError if the key has not been added before
         Passes along any ValueErrors from Parameter object
         """
+        if "#" in n:
+            n, spec = n.split("#")
+        else:
+            spec = None
         if n in self._parameters:
-            self._parameters[n].setValue(v)
+            if spec:
+                self._parameters[n].setSpec(spec, v, validate)
+            else:
+                self._parameters[n].setValue(v, validate)
         else:
             raise KeyError("Parameter %s not does not exist on object %s" % (n, str(self)))
 
 
+    def validate(self):
+        for p in self._parameters.values():
+            p.validate()
+
     def getParameter(self, name):
         """
         Retrieves the parameter value with the given name
@@ -183,14 +251,14 @@ class Parameterized(object):
         return self._parameters[name].getValue()
 
 
-    def getParameterInfo(self, name=None, keys=None):
+    def getParameterInfo(self, name=None, keys=None, hidden=False):
         """
         Retrieves the parameter metadata info
         """
         if name:
             return self._parameters[name].getInfo(keys)
         else:
-            return {k: v.getInfo(keys) for k, v in self._parameters.items()}
+            return {k: v.getInfo(keys) for k, v in self._parameters.items() if hidden or not v.getSpec().get("hidden", False)}
 
 
     def hasParameter(self, name):
diff --git a/rocolib/api/components/Component.py b/rocolib/api/components/Component.py
index 960a0db822bd5611d1b8d33ba98ef7e3ec34ea03..dd253459dbec4306292b4352b7e593f29b3767c9 100644
--- a/rocolib/api/components/Component.py
+++ b/rocolib/api/components/Component.py
@@ -145,7 +145,7 @@ class Component(Parameterized):
             if prefix == "":
                 prefix = name
 
-            for key, value in obj.getParameterInfo().items():
+            for key, value in obj.getParameterInfo(hidden=True).items():
                 # inherit = True : inherit all parameters
                 if inherit is True or key in inherit:
                     try:
@@ -339,7 +339,7 @@ class Component(Parameterized):
         return self.subcomponents[name]['parameters']
 
     def setSubParameter(self, c, n, v):
-        self.getSubcomponent(c).setParameter(n, v)
+        self.getSubcomponent(c).setParameter(n, v, validate=False)
 
     def getInterfaces(self, component, name, transient=False):
         return self.getSubcomponent(component).getInterface(name, transient)
@@ -506,6 +506,7 @@ class Component(Parameterized):
         if useDefaultParameters:
             self.useDefaultParameters()
         self.modifyParameters()
+        self.validate()
 
         scOrder = self.traverseGraph()
         for scName in scOrder:
diff --git a/rocolib/api/components/MechanicalComponent.py b/rocolib/api/components/MechanicalComponent.py
index aa07db84b001e0852a7a0a2bbb88863c140bab43..a9f73f81087146d9ecb47493179faa661ceb3b1c 100644
--- a/rocolib/api/components/MechanicalComponent.py
+++ b/rocolib/api/components/MechanicalComponent.py
@@ -14,18 +14,18 @@ def vals(x):
 
 class MechanicalComponent(Component):
     def predefine(self, **kwargs):
-        self._origin = [ self.addParameter("_d"+x, 0, paramType="length", minValue=None) 
+        self._origin = [ self.addParameter("_d"+x, 0, paramType="length", minValue=None, hidden=True)
             for x in "xyz"
         ]
 
         if kwargs.get("euler", False):
-            self._euler = [ self.addParameter(x, 0, paramType="angle") 
+            self._euler = [ self.addParameter(x, 0, paramType="angle", hidden=True)
                 for x in "_roll _pitch _yaw".split()
             ]
         else:
             self._euler = None
-            self._quat = [ self.addParameter("_q_"+x, int(x == "a"), 
-                                valueType="(int, float)", minValue=-1, maxValue=1) 
+            self._quat = [ self.addParameter("_q_"+x, int(x == "a"),
+                                valueType="(int, float)", minValue=-1, maxValue=1, hidden=True)
                 for x in "aijk"
             ]
             #self.addSemanticConstraint(np.Eq(np.norm(self._quat), 1))
diff --git a/rocolib/builders/ESPBrainsBuilder.py b/rocolib/builders/ESPBrainsBuilder.py
deleted file mode 100644
index f8d36edfc8bd2f097e5c6b848ef01f857f306f18..0000000000000000000000000000000000000000
--- a/rocolib/builders/ESPBrainsBuilder.py
+++ /dev/null
@@ -1,47 +0,0 @@
-from rocolib.api.components.Component import newComponent
-from rocolib.api.Function import Function
-
-
-self = newComponent("ESPBrains")
-
-self.addSubcomponent("beam", "RectBeam")
-self.addSubcomponent("header", "Header")
-self.addSubcomponent("servoPins", "Cutout")
-
-self.addParameter("brain", "nodeMCU", paramType="dimension")
-self.addParameter("length", 90, paramType="length")
-self.addParameter("width", 10, paramType="length")
-self.addParameter("depth", 10, paramType="length")
-
-### Set specific relationships between parameters
-def getBrainParameter(p):
-  return "brain", "getDim(x, '%s')" % p
-
-self.addConstraint(("beam", "width"), "depth")
-self.addConstraint(("beam", "depth"), "width")
-self.addConstraint(("beam", "length"), "length")
-self.addConstConstraint(("beam", "angle"), 90)
-
-self.addConstraint(("beam", "minwidth"), *getBrainParameter("height"))
-self.addConstraint(("beam", "mindepth"), *getBrainParameter("width"))
-self.addConstraint(("beam", "minlength"), *getBrainParameter("length"))
-
-self.addConstraint(("header", "nrows"), *getBrainParameter("nrows"))
-self.addConstraint(("header", "ncols"), *getBrainParameter("ncols"))
-self.addConstraint(("header", "rowsep"), *getBrainParameter("rowsep"))
-self.addConstraint(("header", "colsep"), *getBrainParameter("colsep"))
-
-self.addConstConstraint(("servoPins", "dy"), 8)
-self.addConstConstraint(("servoPins", "dx"), 27)
-
-self.addConnection(("beam", "face1"),
-                   ("header", "decoration"),
-                   mode="hole", offset=Function(params=("length", "brain"), fnstring="(7.5, -4.5 + 0.5*(x[0]-getDim(x[1], 'length')))"))
-
-self.addConnection(("beam", "face1"),
-                   ("servoPins", "decoration"),
-                   mode="hole", rotate=True, offset=Function(params=("length", "brain"), fnstring="(-17.25, (0.5 * (x[0]-getDim(x[1], 'length')))-14)"))
-
-self.inheritAllInterfaces("beam", prefix=None)
-
-self.toLibrary()
diff --git a/rocolib/builders/ESPSegBuilder.py b/rocolib/builders/ESPSegBuilder.py
index a024e01ef212725fe5a0ec25fec16e951819f94b..60207d7e41aa1a6bbca292240f29e88fe6736dd1 100644
--- a/rocolib/builders/ESPSegBuilder.py
+++ b/rocolib/builders/ESPSegBuilder.py
@@ -1,3 +1,4 @@
+from rocolib.library import getComponent
 from rocolib.api.components.Component import newComponent
 from rocolib.api.Function import Function
 
@@ -9,10 +10,9 @@ c.addParameter("height", 40, paramType="length")
 c.addParameter("tire_thickness", 0, paramType="length")
 
 
-c.addParameter("controller", "nodeMCU", paramType="dimension")
-c.addParameter("driveservo", "fs90r", paramType="dimension")
+c.addParameter("driveservo", "fs90r", values=getComponent("ServoMotor").dims)
 
-c.addSubcomponent("brain", "ESPBrains")
+c.addSubcomponent("brain", "MountedBrains", inherit="brain", prefix=None)
 c.addSubcomponent("right", "Wheel", invert=True, inherit="angle")
 c.addSubcomponent("left", "Wheel", invert=True, inherit="angle")
 
@@ -23,18 +23,17 @@ c.addConstraint(("left", "tire_thickness"), "tire_thickness")
 def depthfn(params = None, fnmod = None):
     if params is None: params = []
     if fnmod is None: fnmod = "%s"
-    return ["controller", "driveservo"] + params, \
-            fnmod % "max(getDim(x[0],'height'), getDim(x[1],'motorwidth'))"
+    return ["brain", "driveservo"] + params, \
+            fnmod % "max(x[0].get('height'), x[1].get('motorwidth'))"
 
 # Set microcontroller
 c.addConstraint(("brain", "depth"), *depthfn())
 c.addConstraint(("brain", "length"), "width")
-c.addConstraint(("brain", "brain"), "controller")
 
 for servo in ("right", "left"):
     c.addConstraint((servo, "length"),
-                    ("length", "controller"),
-                    "x[0] - getDim(x[1],'width')")
+                    ("length", "brain"),
+                    "x[0] - x[1].get('width')")
     c.addConstConstraint((servo, "center"), False)
     c.addConstraint((servo, "servo"), "driveservo")
     c.addConstraint((servo, "radius"), "height")
@@ -61,9 +60,9 @@ c.addConstraint(("sheath","width"), *depthfn(["battery"], "%s + x[2]"))
 c.addSubcomponent("sheathsplit", "SplitEdge")
 c.addConstraint(("sheathsplit","toplength"), "width", "(x,)")
 c.addConstraint(("sheathsplit","botlength"), ("driveservo", "width"),
-        "(getDim(x[0],'motorheight'), \
-          x[1] - 2*getDim(x[0],'motorheight'), \
-          getDim(x[0],'motorheight'))")
+        "(x[0].get('motorheight'), \
+          x[1] - 2*x[0].get('motorheight'), \
+          x[0].get('motorheight'))")
 
 c.addConnection(("left", "topedge1"),
                 ("sheathsplit", "botedge2"),
diff --git a/rocolib/builders/MountedBrainsBuilder.py b/rocolib/builders/MountedBrainsBuilder.py
new file mode 100644
index 0000000000000000000000000000000000000000..aa8beeb249da735a28a9a5c265746527301ac81d
--- /dev/null
+++ b/rocolib/builders/MountedBrainsBuilder.py
@@ -0,0 +1,29 @@
+from rocolib.api.components.Component import newComponent
+from rocolib.api.Function import Function
+
+
+self = newComponent("MountedBrains")
+
+self.addSubcomponent("beam", "SimpleRectBeam")
+self.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))
+
+self.addConstraint(("beam", "width"), "depth")
+self.addConstraint(("beam", "depth"), "width")
+self.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")')
+
+self.addConnection(("beam", "face1"),
+                   ("brain", "decoration"),
+                   mode="hole", offset=Function(params=("offset")))
+
+self.inheritAllInterfaces("beam", prefix=None)
+
+self.toLibrary()
diff --git a/rocolib/builders/ServoMountBuilder.py b/rocolib/builders/ServoMountBuilder.py
index 84e2c05d210d022217f2da245966e37a5d731ef8..0640496730cd357bfb9b4e247767791120f9a22d 100644
--- a/rocolib/builders/ServoMountBuilder.py
+++ b/rocolib/builders/ServoMountBuilder.py
@@ -1,23 +1,26 @@
+from rocolib.library import getComponent
 from rocolib.api.components.Component import newComponent
 from rocolib.api.Function import Function
 
 c = newComponent("ServoMount")
 
-c.addParameter("servo", "fs90r", paramType="dimension")
+c.addParameter("servo", "fs90r", values=getComponent("ServoMotor").dims)
 c.addParameter("flip", False, valueType="bool")
 c.addParameter("center", True, valueType="bool")
 c.addParameter("shift", 0, paramType="length")
+
 # XXX TODO: Define type: tuple of two numbers
-c.addParameter("offset", optional=True, overrides=("flip", "center", "shift"))
+c.addParameter("offset", 0)
 
 c.addSubcomponent("beam", "SimpleRectBeam", inherit=("length", "addTabs"), prefix=None)
 c.addSubcomponent("mount", "Cutout")
 
-c.addConstraint(("mount", "dx"), "servo", 'getDim(x, "motorwidth") * 0.99')
-c.addConstraint(("mount", "dy"), "servo", 'getDim(x, "motorlength")')
+c.addConstraint(("mount", "dx"), "servo", 'x.get("motorwidth") * 0.99')
+c.addConstraint(("mount", "dy"), "servo", 'x.get("motorlength")')
 
-c.addConstraint(("beam", "width"), "servo", 'getDim(x, "motorwidth")')
-c.addConstraint(("beam", "depth"), "servo", 'getDim(x, "motorheight")')
+c.addConstraint(("beam", "width"), "servo", 'x.get("motorwidth")')
+c.addConstraint(("beam", "depth"), "servo", 'x.get("motorheight")')
+c.addConstraint(("beam", "length#minValue"), "servo", 'x.get("motorlength") + 2 * x.get("shoulderlength")')
 
 c.inheritAllInterfaces("beam", prefix=None)
 c.inheritAllInterfaces("mount")
diff --git a/rocolib/library/Brains.py b/rocolib/library/Brains.py
new file mode 100644
index 0000000000000000000000000000000000000000..dfc5b9841a6909ba3e102b02f01de438fc617480
--- /dev/null
+++ b/rocolib/library/Brains.py
@@ -0,0 +1,58 @@
+from rocolib.library import getComponent
+from rocolib.api.composables.graph.Face import Face
+
+
+ThruHole = getComponent("ThruHole").__class__
+
+class Brains(ThruHole):
+
+    brains = dict(
+        proMini = dict(
+            length    = 39,
+            width     = 19,
+            height    = 9,
+            nrows     = 12,
+            ncols     = 2,
+            rowsep    = 0.1 * 25.4,
+            colsep    = 0.6 * 25.4,
+            origin    = (0,0),
+            extras    = (),
+        ),
+        nodeMCU = dict(
+            length    = 59.5,
+            width     = 44,
+            height    = 13,
+            nrows     = 15,
+            ncols     = 2,
+            rowsep    = 0.1 * 25.4,
+            colsep    = 0.9 * 25.4,
+            origin    = (7.5, -4.5),
+            extras    = dict(
+                servoPins = dict(center = (-17.25, -14), size = (8, 27)),
+            ),
+        ),
+    )
+
+    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")',
+        ))
+
+    def assemble(self):
+        ThruHole.assemble(self)
+        brain = self.getParameter("brain")
+        self.graph.dotransform(origin = brain.get("origin"))
+        for n, e in brain.get("extras").items():
+            x, y = e.get("center", (0,0))
+            dx, dy = e.get("size", (1,1))
+            self.addFace(Face(n,
+                          ((x-dx/2, y-dy/2), (x+dx/2, y-dy/2), (x+dx/2, y+dy/2), (x-dx/2, y+dy/2)),
+                          recenter=False
+            ))
+
+if __name__ == "__main__":
+    Brains.test()
diff --git a/rocolib/library/Cutout.py b/rocolib/library/Cutout.py
index cf2f49e9a4bbab332bb3681578933361c9a95242..06e6df69065e62f3a95e1dd138a4de800c1a57ca 100644
--- a/rocolib/library/Cutout.py
+++ b/rocolib/library/Cutout.py
@@ -2,18 +2,16 @@ from rocolib.api.components import DecorationComponent
 from rocolib.api.composables.graph.Face import Rectangle
 
 class Cutout(DecorationComponent):
-  def define(self):
-      self.addParameter("dx", 10, paramType="length")
-      self.addParameter("dy", 20, paramType="length")
-      self.addParameter("d", optional=True, overrides=("dx", "dy"))
+    def define(self):
+        self.addParameter("dx", 10, paramType="length")
+        self.addParameter("dy", 20, paramType="length")
+        self.addParameter("d", overrides={
+            "dx": 'c.getParameter("d")',
+            "dy": 'c.getParameter("d")',
+        })
 
-  def modifyParameters(self):
-    if self.p.d is not None:
-      self.p.dx = self.p.d
-      self.p.dy = self.p.d
-
-  def assemble(self):
-    self.addFace(Rectangle("r0", self.p.dx, self.p.dy), prefix="r0")
+    def assemble(self):
+        self.addFace(Rectangle("r0", self.p.dx, self.p.dy), prefix="r0")
 
 if __name__ == "__main__":
     Cutout.test()
diff --git a/rocolib/library/ESPSeg.yaml b/rocolib/library/ESPSeg.yaml
index 8c941477d9bbc53a4d2aff93979cafe1e5086d10..7de2c228fb1aaf9a34c2dc9021f3a9d6f39def98 100644
--- a/rocolib/library/ESPSeg.yaml
+++ b/rocolib/library/ESPSeg.yaml
@@ -50,9 +50,9 @@ connections:
     - botedge1
   - angle: 90
     tabWidth:
-      function: max(getDim(x[0],'height'), getDim(x[1],'motorwidth'))+x[2]
+      function: max(x[0].get('height'), x[1].get('motorwidth'))+x[2]
       parameter:
-      - controller
+      - brain
       - driveservo
       - battery
   connection7:
@@ -61,10 +61,10 @@ connections:
     - decoration
   - mode: hole
     offset:
-      function: (4-(max(getDim(x[0],'height'), getDim(x[1],'motorwidth'))+x[3])/2,
-        0.5 * x[2] - 15)
+      function: (4-(max(x[0].get('height'), x[1].get('motorwidth'))+x[3])/2, 0.5 *
+        x[2] - 15)
       parameter:
-      - controller
+      - brain
       - driveservo
       - length
       - battery
@@ -90,14 +90,86 @@ parameters:
       minValue: 0
       units: mm
       valueType: (float, int)
-  controller:
+  brain:
     defaultValue: nodeMCU
     spec:
-      valueType: str
+      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")
+      values:
+        nodeMCU:
+          colsep: 22.86
+          extras:
+            servoPins:
+              center:
+              - -17.25
+              - -14
+              size:
+              - 8
+              - 27
+          height: 13
+          length: 59.5
+          ncols: 2
+          nrows: 15
+          origin:
+          - 7.5
+          - -4.5
+          rowsep: 2.54
+          width: 44
+        proMini:
+          colsep: 15.239999999999998
+          extras: []
+          height: 9
+          length: 39
+          ncols: 2
+          nrows: 12
+          origin:
+          - 0
+          - 0
+          rowsep: 2.54
+          width: 19
   driveservo:
     defaultValue: fs90r
     spec:
-      valueType: str
+      values:
+        ds2g:
+          horndepth: 1
+          hornheight: 9
+          hornlength: 11
+          hornoffset: 4
+          motorheight: 11
+          motorlength: 17
+          motorwidth: 8.5
+          shoulderlength: 3.5
+        fs90r:
+          horndepth: 2
+          hornheight: 16
+          hornlength: 10
+          hornoffset: 8
+          motorheight: 19
+          motorlength: 23
+          motorwidth: 13
+          shoulderlength: 5
+        s4303r:
+          horndepth: 2
+          hornheight: 14
+          hornlength: 38
+          hornoffset: 7
+          motorheight: 29
+          motorlength: 31
+          motorwidth: 17
+          shoulderlength: 10
+        tgy1370a:
+          horndepth: 2
+          hornheight: 10
+          hornlength: 7
+          hornoffset: 4
+          motorheight: 14
+          motorlength: 20
+          motorwidth: 9
+          shoulderlength: 4
   flapwidth:
     defaultValue: 0.2
     spec:
@@ -151,15 +223,15 @@ parameters:
 source: ../builders/ESPSegBuilder.py
 subcomponents:
   brain:
-    classname: ESPBrains
+    classname: MountedBrains
     kwargs: {}
     parameters:
       brain:
-        parameter: controller
+        parameter: brain
       depth:
-        function: max(getDim(x[0],'height'), getDim(x[1],'motorwidth'))
+        function: max(x[0].get('height'), x[1].get('motorwidth'))
         parameter:
-        - controller
+        - brain
         - driveservo
       length:
         parameter: width
@@ -172,10 +244,10 @@ subcomponents:
         parameter: left.angle
       center: false
       length:
-        function: x[0] - getDim(x[1],'width')
+        function: x[0] - x[1].get('width')
         parameter: &id002
         - length
-        - controller
+        - brain
       radius:
         parameter: height
       servo:
@@ -192,7 +264,7 @@ subcomponents:
       center: false
       flip: true
       length:
-        function: x[0] - getDim(x[1],'width')
+        function: x[0] - x[1].get('width')
         parameter: *id002
       radius:
         parameter: height
@@ -209,9 +281,9 @@ subcomponents:
       length:
         parameter: length
       width:
-        function: max(getDim(x[0],'height'), getDim(x[1],'motorwidth')) + x[2]
+        function: max(x[0].get('height'), x[1].get('motorwidth')) + x[2]
         parameter:
-        - controller
+        - brain
         - driveservo
         - battery
   sheathsplit:
@@ -219,7 +291,7 @@ subcomponents:
     kwargs: {}
     parameters:
       botlength:
-        function: (getDim(x[0],'motorheight'),           x[1] - 2*getDim(x[0],'motorheight'),           getDim(x[0],'motorheight'))
+        function: (x[0].get('motorheight'),           x[1] - 2*x[0].get('motorheight'),           x[0].get('motorheight'))
         parameter:
         - driveservo
         - width
@@ -231,17 +303,17 @@ subcomponents:
     kwargs: {}
     parameters:
       depth:
-        function: max(getDim(x[0],'height'), getDim(x[1],'motorwidth'))+x[2]
+        function: max(x[0].get('height'), x[1].get('motorwidth'))+x[2]
         parameter:
-        - controller
+        - brain
         - driveservo
         - battery
       flapwidth:
         parameter: flapwidth
       height:
-        function: max(getDim(x[0],'height'), getDim(x[1],'motorwidth'))/2.+x[2]
+        function: max(x[0].get('height'), x[1].get('motorwidth'))/2.+x[2]
         parameter:
-        - controller
+        - brain
         - driveservo
         - height
       tailwidth:
diff --git a/rocolib/library/Header.py b/rocolib/library/Header.py
deleted file mode 100644
index 8b68315f0a6a1c11c0eb2ec4b6fb80acf58006a4..0000000000000000000000000000000000000000
--- a/rocolib/library/Header.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from rocolib.api.components import DecorationComponent
-from rocolib.api.composables.graph.Face import Face
-
-
-class Header(DecorationComponent):
-  def define(self):
-      self.addParameter("nrows", 3, paramType="count")
-      self.addParameter("ncols", 1, paramType="count")
-      self.addParameter("rowsep", 2.54, paramType="length")
-      self.addParameter("colsep", 2.54, paramType="length")
-      self.addParameter("diameter", 1, paramType="length")
-
-  def assemble(self):
-    diam = self.p.diameter/2.
-    nr = self.p.nrows
-    nc = self.p.ncols
-
-    def hole(i, j, d):
-      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)
-
-    for i in range(nr):
-      for j in range(nc):
-        d = diam
-        if (i == 0 and j == 0) or \
-           (i == nr-1 and j == nc-1):
-               d = diam*3
-        self.addFace(hole(i,j,d), prefix="r-%d-%d" % (i,j))
-
-if __name__ == "__main__":
-    Header.test()
diff --git a/rocolib/library/ESPBrains.yaml b/rocolib/library/MountedBrains.yaml
similarity index 53%
rename from rocolib/library/ESPBrains.yaml
rename to rocolib/library/MountedBrains.yaml
index 902c2bd4be821dbec9a41f038a19062f80099857..aff7f4571405a33c673fc786fd3de74012fa9a7b 100644
--- a/rocolib/library/ESPBrains.yaml
+++ b/rocolib/library/MountedBrains.yaml
@@ -1,25 +1,12 @@
 connections:
   connection0:
-  - &id001
-    - beam
+  - - beam
     - face1
-  - - header
+  - - brain
     - decoration
   - mode: hole
     offset:
-      function: (7.5, -4.5 + 0.5*(x[0]-getDim(x[1], 'length')))
-      parameter: &id002
-      - length
-      - brain
-  connection1:
-  - *id001
-  - - servoPins
-    - decoration
-  - mode: hole
-    offset:
-      function: (-17.25, (0.5 * (x[0]-getDim(x[1], 'length')))-14)
-      parameter: *id002
-    rotate: true
+      parameter: offset
 interfaces:
   botedge0:
     interface: botedge0
@@ -67,9 +54,45 @@ parameters:
   brain:
     defaultValue: nodeMCU
     spec:
-      valueType: str
+      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")
+      values:
+        nodeMCU:
+          colsep: 22.86
+          extras:
+            servoPins:
+              center:
+              - -17.25
+              - -14
+              size:
+              - 8
+              - 27
+          height: 13
+          length: 59.5
+          ncols: 2
+          nrows: 15
+          origin:
+          - 7.5
+          - -4.5
+          rowsep: 2.54
+          width: 44
+        proMini:
+          colsep: 15.239999999999998
+          extras: []
+          height: 9
+          length: 39
+          ncols: 2
+          nrows: 12
+          origin:
+          - 0
+          - 0
+          rowsep: 2.54
+          width: 19
   depth:
-    defaultValue: 10
+    defaultValue: 20
     spec:
       minValue: 0
       units: mm
@@ -80,53 +103,41 @@ parameters:
       minValue: 0
       units: mm
       valueType: (float, int)
+  offset:
+    defaultValue:
+    - 0
+    - 0
+    spec: {}
   width:
-    defaultValue: 10
+    defaultValue: 50
     spec:
       minValue: 0
       units: mm
       valueType: (float, int)
-source: ../builders/ESPBrainsBuilder.py
+source: ../builders/MountedBrainsBuilder.py
 subcomponents:
   beam:
-    classname: RectBeam
+    classname: SimpleRectBeam
     kwargs: {}
     parameters:
-      angle: 90
       depth:
         parameter: width
+      depth#minValue:
+        function: x.get("width")
+        parameter: brain
       length:
         parameter: length
-      mindepth:
-        function: getDim(x, 'width')
-        parameter: brain
-      minlength:
-        function: getDim(x, 'length')
-        parameter: brain
-      minwidth:
-        function: getDim(x, 'height')
+      length#minValue:
+        function: x.get("length")
         parameter: brain
       width:
         parameter: depth
-  header:
-    classname: Header
-    kwargs: {}
-    parameters:
-      colsep:
-        function: getDim(x, 'colsep')
-        parameter: brain
-      ncols:
-        function: getDim(x, 'ncols')
-        parameter: brain
-      nrows:
-        function: getDim(x, 'nrows')
+      width#minValue:
+        function: x.get("height")
         parameter: brain
-      rowsep:
-        function: getDim(x, 'rowsep')
-        parameter: brain
-  servoPins:
-    classname: Cutout
+  brain:
+    classname: Brains
     kwargs: {}
     parameters:
-      dx: 27
-      dy: 8
+      brain:
+        parameter: brain
diff --git a/rocolib/library/MountedServo.yaml b/rocolib/library/MountedServo.yaml
index aaaa270f888997a30548a631d78a58d8ed5baa9a..59533eae6be6fd4f0ed49668b961fc6a7479407d 100644
--- a/rocolib/library/MountedServo.yaml
+++ b/rocolib/library/MountedServo.yaml
@@ -61,42 +61,49 @@ 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)
@@ -126,17 +133,48 @@ parameters:
       units: mm
       valueType: (float, int)
   offset:
-    defaultValue: null
-    spec:
-      optional: true
-      overrides:
-      - flip
-      - center
-      - shift
+    defaultValue: 0
+    spec: {}
   servo:
     defaultValue: fs90r
     spec:
-      valueType: str
+      values:
+        ds2g:
+          horndepth: 1
+          hornheight: 9
+          hornlength: 11
+          hornoffset: 4
+          motorheight: 11
+          motorlength: 17
+          motorwidth: 8.5
+          shoulderlength: 3.5
+        fs90r:
+          horndepth: 2
+          hornheight: 16
+          hornlength: 10
+          hornoffset: 8
+          motorheight: 19
+          motorlength: 23
+          motorwidth: 13
+          shoulderlength: 5
+        s4303r:
+          horndepth: 2
+          hornheight: 14
+          hornlength: 38
+          hornoffset: 7
+          motorheight: 29
+          motorlength: 31
+          motorwidth: 17
+          shoulderlength: 10
+        tgy1370a:
+          horndepth: 2
+          hornheight: 10
+          hornlength: 7
+          hornoffset: 4
+          motorheight: 14
+          motorlength: 20
+          motorwidth: 9
+          shoulderlength: 4
   shift:
     defaultValue: 0
     spec:
diff --git a/rocolib/library/RectBeam.py b/rocolib/library/RectBeam.py
index 474e247b39209ba69f17a9318e956041462ea762..74ebf2375dd66e0c2cf4221770ff2cafae0b7017 100644
--- a/rocolib/library/RectBeam.py
+++ b/rocolib/library/RectBeam.py
@@ -8,18 +8,13 @@ class RectBeam(FoldedComponent):
     self.addParameter("width", 20, paramType="length")
     self.addParameter("depth", 50, paramType="length")
 
-    # XXX TODO: incorporate into minValue of parameters somehow
-    self.addParameter("minlength", 0, paramType="length")
-    self.addParameter("minwidth", 0, paramType="length")
-    self.addParameter("mindepth", 0, paramType="length")
-
     self.addParameter("phase", 0, valueType="int")
 
-    self.addParameter("angle", optional=True, overrides=("tangle", "bangle"))
+    self.addParameter("angle", overrides=("tangle", "bangle"))
     self.addParameter("tangle", 80, paramType="angle", maxValue=180)
     self.addParameter("bangle", 135, paramType="angle", maxValue=180)
 
-    self.addParameter("root", optional=True, valueType="int")
+    self.addParameter("root", 0, values=[0,1,2,3])
     self.addParameter("addTabs", True, valueType="bool")
 
     for i in range(4):
@@ -29,23 +24,11 @@ class RectBeam(FoldedComponent):
     self.addEdgeInterface("tabedge", "r3.e1", "length")
     self.addEdgeInterface("slotedge", "r0.e3", "length")
 
-  def modifyParameters(self):
-    self.p.width  = max(self.p.width,  self.p.minwidth)
-    self.p.depth  = max(self.p.depth,  self.p.mindepth)
-    self.p.length = max(self.p.length, self.p.minlength)
-
   def assemble(self):
-    if self.p.angle is not None:
-      bangle = 90 - self.p.angle
-      tangle = 90 - self.p.angle
-    else:
-      bangle = 90 - self.p.bangle
-      tangle = 90 - self.p.tangle
+    bangle = 90 - self.p.bangle
+    tangle = 90 - self.p.tangle
 
-    try:
-      root = self.p.root
-    except KeyError:
-      root = None
+    root = self.p.root
 
     length = self.p.length
     width = self.p.width
@@ -76,7 +59,7 @@ class RectBeam(FoldedComponent):
 
     fromEdge = None
     for i in range(4):
-      self.attachEdge(fromEdge, rs[i], "e3", prefix="r%d"%i, angle=90, root=((i == root) if root is not None else False))
+      self.attachEdge(fromEdge, rs[i], "e3", prefix="r%d"%i, angle=90, root=((i == root)))
       fromEdge = 'r%d.e1' % i
       self.setFaceInterface("face%d" % i, "r%d" % ((i-phase)%4))
 
diff --git a/rocolib/library/ServoMotor.py b/rocolib/library/ServoMotor.py
index a4f2761937f9fc127b04d1383a7024c423b52312..969c4a2b0c0cb6f525d841962ae43eba19d96c43 100644
--- a/rocolib/library/ServoMotor.py
+++ b/rocolib/library/ServoMotor.py
@@ -2,27 +2,99 @@ 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, deg2rad
 
 
 class ServoMotor(FoldedComponent):
-  def define(self):
-    self.addParameter("angle", 0, paramType="angle", minValue=None, maxValue=None)
-    self.addParameter("servo", "fs90r", paramType="dimension")
-    self.addInterface("mount", AnchorPort(self, self.getGraph(), "horn", Translate([0,0,0])))
-    self.addFaceInterface("horn", "horn")
-
-  def assemble(self):
-    s = self.p.servo
-    dz = getDim(s, "hornheight")
-    dy = getDim(s, "motorlength") / 2 - getDim(s, "hornoffset")
-
-    f = Shape("horn", 0, 0)
-    decorateGraph(f, Shape("hole", 1, 1))
-    self.addFace(f)
-    self.setInterface("mount", AnchorPort(self, self.getGraph(), "horn", dot(RotateZ(deg2rad(self.p.angle)), Translate([0,-dy,dz]))))
+    def define(self):
+        self.addParameter("angle", 0, paramType="angle", minValue=None, maxValue=None)
+        self.addParameter("servo", "fs90r", values=ServoMotor.dims)
+        self.addInterface("mount", AnchorPort(self, self.getGraph(), "horn", Translate([0,0,0])))
+        self.addFaceInterface("horn", "horn")
+
+    def assemble(self):
+        s = self.p.servo
+
+        dz = s.get("hornheight")
+        dy = s.get("motorlength") / 2 - s.get("hornoffset")
+
+        f = Shape("horn", 0, 0)
+        decorateGraph(f, Shape("hole", 1, 1))
+        self.addFace(f)
+        self.setInterface("mount", AnchorPort(self, self.getGraph(), "horn", dot(RotateZ(deg2rad(self.p.angle)), Translate([0,-dy,dz]))))
+
+
+    '''
+    Servo dimension parameters:
+
+                 |<-G->|
+    ^      =====v===== { H
+    E          _I_
+    v ________| | |_____
+      ^ |       |<-F->|<> D
+      | |             |
+      B | <--- A ---> |
+      | | (X) C       |
+      v |_____________|
+
+    A : motorlength
+    B : motorheight
+    C : motorwidth
+    D : shoulderlength
+
+    E : hornheight
+    F : hornoffset
+
+    G : hornlength
+    H : horndepth
+
+    '''
+
+    dims = dict(
+        ds2g = dict(
+            motorlength   = 17,
+            motorwidth    = 8.5,
+            motorheight   = 11,
+            shoulderlength= 3.5,
+            hornlength    = 11,
+            hornheight    = 9,
+            hornoffset    = 4,
+            horndepth     = 1,
+        ),
+        s4303r = dict(
+            motorlength   = 31,
+            motorwidth    = 17,
+            motorheight   = 29,
+            shoulderlength= 10,
+            hornlength    = 38,
+            hornheight    = 14,
+            hornoffset    = 7,
+            horndepth     = 2,
+        ),
+        tgy1370a = dict(
+            motorlength   = 20,
+            motorwidth    = 9,
+            motorheight   = 14,
+            shoulderlength= 4,
+            hornlength    = 7,
+            hornheight    = 10,
+            hornoffset    = 4,
+            horndepth     = 2,
+        ),
+        fs90r = dict(
+            motorlength   = 23,
+            motorwidth    = 13,
+            motorheight   = 19,
+            shoulderlength= 5,
+            hornlength    = 10,
+            hornheight    = 16,
+            hornoffset    = 8,
+            horndepth     = 2,
+        ),
+    )
+
 
 if __name__ == "__main__":
     ServoMotor.test()
+    print(ServoMotor.dims)
diff --git a/rocolib/library/ServoMount.py b/rocolib/library/ServoMount.py
index 797a1e9823560b0849711f0bc35e1a7021e3c7e1..2d1be2ab79cd3826c834b6a88ed7046ecacacd85 100644
--- a/rocolib/library/ServoMount.py
+++ b/rocolib/library/ServoMount.py
@@ -1,6 +1,5 @@
 from rocolib.api.components.Component import Component
 from rocolib.api.ports.EdgePort import EdgePort
-from rocolib.utils.dimensions import getDim
 from rocolib.api.Function import Function
 
 
@@ -11,9 +10,9 @@ class ServoMount(Component):
     if self.p.offset:
         return
 
-    ml = getDim(self.p.servo, "motorlength")
-    sl = getDim(self.p.servo, "shoulderlength")
-    ho = getDim(self.p.servo, "hornoffset")
+    ml = self.p.servo.get("motorlength")
+    sl = self.p.servo.get("shoulderlength")
+    ho = self.p.servo.get("hornoffset")
 
     dy = self.p.length/2. - ml/2. - sl
     if self.p.center:
diff --git a/rocolib/library/ServoMount.yaml b/rocolib/library/ServoMount.yaml
index f45cb93dc81a8f21bba35a103e820070315f0633..45d9c9e63ebbef06b611ffa8755129071419b4df 100644
--- a/rocolib/library/ServoMount.yaml
+++ b/rocolib/library/ServoMount.yaml
@@ -73,17 +73,48 @@ parameters:
       units: mm
       valueType: (float, int)
   offset:
-    defaultValue: null
-    spec:
-      optional: true
-      overrides:
-      - flip
-      - center
-      - shift
+    defaultValue: 0
+    spec: {}
   servo:
     defaultValue: fs90r
     spec:
-      valueType: str
+      values:
+        ds2g:
+          horndepth: 1
+          hornheight: 9
+          hornlength: 11
+          hornoffset: 4
+          motorheight: 11
+          motorlength: 17
+          motorwidth: 8.5
+          shoulderlength: 3.5
+        fs90r:
+          horndepth: 2
+          hornheight: 16
+          hornlength: 10
+          hornoffset: 8
+          motorheight: 19
+          motorlength: 23
+          motorwidth: 13
+          shoulderlength: 5
+        s4303r:
+          horndepth: 2
+          hornheight: 14
+          hornlength: 38
+          hornoffset: 7
+          motorheight: 29
+          motorlength: 31
+          motorwidth: 17
+          shoulderlength: 10
+        tgy1370a:
+          horndepth: 2
+          hornheight: 10
+          hornlength: 7
+          hornoffset: 4
+          motorheight: 14
+          motorlength: 20
+          motorwidth: 9
+          shoulderlength: 4
   shift:
     defaultValue: 0
     spec:
@@ -99,20 +130,23 @@ subcomponents:
       addTabs:
         parameter: addTabs
       depth:
-        function: getDim(x, "motorheight")
+        function: x.get("motorheight")
         parameter: servo
       length:
         parameter: length
+      length#minValue:
+        function: x.get("motorlength") + 2 * x.get("shoulderlength")
+        parameter: servo
       width:
-        function: getDim(x, "motorwidth")
+        function: x.get("motorwidth")
         parameter: servo
   mount:
     classname: Cutout
     kwargs: {}
     parameters:
       dx:
-        function: getDim(x, "motorwidth") * 0.99
+        function: x.get("motorwidth") * 0.99
         parameter: servo
       dy:
-        function: getDim(x, "motorlength")
+        function: x.get("motorlength")
         parameter: servo
diff --git a/rocolib/library/SimpleChair.yaml b/rocolib/library/SimpleChair.yaml
index 3756a211f2a105b26ef98575405efa90ef6af541..672e7b286a2f4e553a2ff625350097c66e182dcd 100644
--- a/rocolib/library/SimpleChair.yaml
+++ b/rocolib/library/SimpleChair.yaml
@@ -16,42 +16,49 @@ 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)
diff --git a/rocolib/library/SimpleTable.yaml b/rocolib/library/SimpleTable.yaml
index f377c9740c7f777beb667cda5e00dfa87c7e95ef..5a77e45e77f45fded0bcd175645f67567f650d48 100644
--- a/rocolib/library/SimpleTable.yaml
+++ b/rocolib/library/SimpleTable.yaml
@@ -52,42 +52,49 @@ 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)
diff --git a/rocolib/library/Stool.py b/rocolib/library/Stool.py
index c5ce4be0f99dc40017c6b366c503ac933169ce20..8c36db6363edd2fdd15ee654141bedeeca35c399 100644
--- a/rocolib/library/Stool.py
+++ b/rocolib/library/Stool.py
@@ -8,14 +8,12 @@ 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", optional=True, overrides=("legwidth",))
-        self.addParameter("legwidth", 20, paramType="length", minValue=10)
+        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 modifyParameters(self):
-        if self.p.radius is not None:
-          self.p.legwidth = r2l.r2l(self.p.radius, self.p.legs*2)
-
     def assemble(self):
         h =  self.p.height
         lp = self.p.legs
diff --git a/rocolib/library/ThruHole.py b/rocolib/library/ThruHole.py
new file mode 100644
index 0000000000000000000000000000000000000000..b61671480639d9731fe14a335c8ff7e73eee81e2
--- /dev/null
+++ b/rocolib/library/ThruHole.py
@@ -0,0 +1,35 @@
+from rocolib.api.components import DecorationComponent
+from rocolib.api.composables.graph.Face import Face
+
+
+class ThruHole(DecorationComponent):
+
+    def define(self):
+        self.addParameter("nrows", 3, paramType="count")
+        self.addParameter("ncols", 1, paramType="count")
+        self.addParameter("rowsep", 2.54, paramType="length")
+        self.addParameter("colsep", 2.54, paramType="length")
+        self.addParameter("diameter", 1, paramType="length")
+
+    def assemble(self):
+        diam = self.getParameter("diameter")/2.
+        nr = self.getParameter("nrows")
+        nc = self.getParameter("ncols")
+
+        def hole(i, j, d):
+            dx = (j - (nc-1)/2.)*self.getParameter("colsep")
+            dy = (i - (nr-1)/2.)*self.getParameter("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)
+
+        for i in range(nr):
+            for j in range(nc):
+                d = diam
+                if (i == 0 and j == 0) or \
+                   (i == nr-1 and j == nc-1):
+                       d = diam*3
+                self.addFace(hole(i,j,d), prefix="r-%d-%d" % (i,j))
+
+if __name__ == "__main__":
+    ThruHole.test()
diff --git a/rocolib/library/UChannel.py b/rocolib/library/UChannel.py
index b16166970d50c2968ff538ebd3922c6e2f76e999..f1e46835d38c5d1b7321744bcc39079c2a571836 100644
--- a/rocolib/library/UChannel.py
+++ b/rocolib/library/UChannel.py
@@ -9,9 +9,9 @@ class UChannel(FoldedComponent):
     self.addParameter("depth", 20, paramType="length")
 
     # Minimum of 45deg to make sure the geometry stays convex
-    self.addParameter("angle", optional=True, overrides=("tangle", "bangle"))
     self.addParameter("tangle", 80, paramType="angle", minValue=45)
     self.addParameter("bangle", 135, paramType="angle", minValue=45)
+    self.addParameter("angle", paramType="angle", minValue=45, overrides=("bangle", "tangle"))
 
     for i in range(3):
       self.addEdgeInterface("topedge%d" % i, "r%d.e0" % i, ["depth", "width"][i % 2])
@@ -23,11 +23,6 @@ class UChannel(FoldedComponent):
     self.addEdgeInterface("ledge", "r0.e3", "length")
     self.addEdgeInterface("redge", "r2.e1", "length")
 
-  def modifyParameters(self):
-    if self.p.angle is not None:
-      self.p.bangle = self.p.angle
-      self.p.tangle = self.p.angle
-
   def assemble(self):
     bangle = 90 - self.p.bangle
     tangle = 90 - self.p.tangle
diff --git a/rocolib/library/Wheel.yaml b/rocolib/library/Wheel.yaml
index 51d9e01c2f7ca3f35118e9b7cab3c760ff056e21..6de1d93af4b845fe912b2cf84b60e44275325bc3 100644
--- a/rocolib/library/Wheel.yaml
+++ b/rocolib/library/Wheel.yaml
@@ -61,42 +61,49 @@ 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)
@@ -126,13 +133,8 @@ parameters:
       units: mm
       valueType: (float, int)
   offset:
-    defaultValue: null
-    spec:
-      optional: true
-      overrides:
-      - flip
-      - center
-      - shift
+    defaultValue: 0
+    spec: {}
   radius:
     defaultValue: 25
     spec:
@@ -142,7 +144,43 @@ parameters:
   servo:
     defaultValue: fs90r
     spec:
-      valueType: str
+      values:
+        ds2g:
+          horndepth: 1
+          hornheight: 9
+          hornlength: 11
+          hornoffset: 4
+          motorheight: 11
+          motorlength: 17
+          motorwidth: 8.5
+          shoulderlength: 3.5
+        fs90r:
+          horndepth: 2
+          hornheight: 16
+          hornlength: 10
+          hornoffset: 8
+          motorheight: 19
+          motorlength: 23
+          motorwidth: 13
+          shoulderlength: 5
+        s4303r:
+          horndepth: 2
+          hornheight: 14
+          hornlength: 38
+          hornoffset: 7
+          motorheight: 29
+          motorlength: 31
+          motorwidth: 17
+          shoulderlength: 10
+        tgy1370a:
+          horndepth: 2
+          hornheight: 10
+          hornlength: 7
+          hornoffset: 4
+          motorheight: 14
+          motorlength: 20
+          motorwidth: 9
+          shoulderlength: 4
   shift:
     defaultValue: 0
     spec:
diff --git a/rocolib/utils/dimensions.py b/rocolib/utils/dimensions.py
deleted file mode 100644
index 4d87f57f6335a69363889b7f6f15bd43d22ed6e9..0000000000000000000000000000000000000000
--- a/rocolib/utils/dimensions.py
+++ /dev/null
@@ -1,156 +0,0 @@
-from sympy import symbols
-
-dims = {}
-
-def isDim(obj):
-    return obj in dims
-
-def getDim(obj, param):
-    return dims[obj][param]
-
-'''
-Brain dimension parameters:
-      params.setdefault("length")
-      params.setdefault("width")
-      params.setdefault("height")
-
-      params.setdefault("nrows")
-      params.setdefault("ncols")
-      params.setdefault("rowsep")
-      params.setdefault("colsep")
-'''
-dims["proMini"] = { "type" : "brains",
-    "length"    : 39,
-    "width"     : 19,
-    "height"    : 9,
-    "nrows"     : 12,
-    "ncols"     : 2,
-    "rowsep"    : 0.1 * 25.4,
-    "colsep"    : 0.6 * 25.4,
-}
-
-dims["nodeMCU"] = { "type" : "brains",
-    "length"    : 59.5,
-    "width"     : 44,
-    "height"    : 13,
-    "nrows"     : 15,
-    "ncols"     : 2,
-    "rowsep"    : 0.1 * 25.4,
-    "colsep"    : 0.9 * 25.4,
-}
-
-'''
-Servo dimension parameters:
-
-             |<-G->|
-^      =====v===== { H
-E          _I_
-v ________| | |_____
-  ^ |       |<-F->|<> D
-  | |             |
-  B | <--- A ---> |
-  | | (X) C       |
-  v |_____________|
-
-A : motorlength
-B : motorheight
-C : motorwidth
-D : shoulderlength
-
-E : hornheight
-F : hornoffset
-
-G : hornlength
-H : horndepth
-
-        params.setdefault("motorlength")
-        params.setdefault("motorwidth")
-        params.setdefault("motorheight")
-        params.setdefault("shoulderlength", 0)
-
-        params.setdefault("hornheight", 0)
-        params.setdefault("hornoffset", 0)
-
-        params.setdefault("hornlength", 0)
-        params.setdefault("horndepth", 0)
-
-If horn is not symmetric?
-
-        params.setdefault("rhornlength", 0)
-        params.setdefault("lhornlength", 0)
-        if name == "hornlength":
-            self.setParameter("rhornlength", val)
-            self.setParameter("lhornlength", val)
-
-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,
-    "motorheight"   : 29,
-    "shoulderlength": 10,
-    "hornlength"    : 38,
-    "hornheight"    : 14,
-    "hornoffset"    : 7,
-    "horndepth"     : 2,
-}
-
-dims["tgy1370a"] = { "type" : "servo",
-    "motorlength"   : 20,
-    "motorwidth"    : 9,
-    "motorheight"   : 14,
-    "shoulderlength": 4,
-    "hornlength"    : 7,
-    "hornheight"    : 10,
-    "hornoffset"    : 4,
-    "horndepth"     : 2,
-}
-
-dims["fs90r"] = { "type" : "servo",
-    "motorlength"   : 23,
-    "motorwidth"    : 13,
-    "motorheight"   : 19,
-    "shoulderlength": 5,
-    "hornlength"    : 10,
-    "hornheight"    : 16,
-    "hornoffset"    : 8,
-    "horndepth"     : 2,
-}
-
-l, w, h, r, c, rs, cs = symbols("brainLength brainWidth brainHeight brainNRows brainNCols brainRowSep brainColSep", positive=True)
-
-dims["brainSymbols"] = { "type" : "brains",
-    "length"    : l,
-    "width"     : w,
-    "height"    : h,
-    "nrows"     : r,
-    "ncols"     : c,
-    "rowsep"    : rs,
-    "colsep"    : cs,
-}
-
-l, w, h, s, hl, hh, ho, hd = symbols("servoLength servoWidth servoHeight servoShoulder servoHornLength servoHornHeight servoHornOffset servoHornDepth", positive=True)
-
-dims["servoSymbols"] = { "type" : "servo",
-    "motorlength"   : l,
-    "motorwidth"    : w,
-    "motorheight"   : h,
-    "shoulderlength": s,
-    "hornlength"    : hl,
-    "hornheight"    : hh,
-    "hornoffset"    : ho,
-    "horndepth"     : hd,
-}