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 b3d3632d80beb0f416c2b62e9697b9be63b9cd30..fb70d20e2aa7daedb02ceb36ed98c4b14417511f 100644
--- a/rocolib/api/Parameterized.py
+++ b/rocolib/api/Parameterized.py
@@ -1,7 +1,8 @@
 import re
 
-from rocolib.utils.dimensions import isDim
 from rocolib.utils.numsym import Dummy
+import logging
+import yaml
 
 
 ### XXX "." is used to separate subcomponent parameters when inherited
@@ -29,10 +30,6 @@ PARAM_TYPES = {
         "valueType": "int",
         "minValue": 0,
     },
-    "dimension": {
-        "valueType": "str",
-        #"isValid": isDim,
-    },
 }
 
 class Parameter:
@@ -78,7 +75,21 @@ class Parameter:
             self.symbol = None
         self.value = None
 
+    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
@@ -95,8 +106,7 @@ class Parameter:
     def assertValid(self, value):
         if value is None:
             if self.spec.get("overrides", False):
-                self.value = value
-                return
+                return True
             else:
                 raise ValueError(f"Parameter {self.name} is not optional (via overrides) and cannot be set to None")
 
@@ -107,12 +117,19 @@ 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
 
@@ -209,7 +226,7 @@ class Parameterized(object):
 
     def validate(self):
         for p in self.parameters.values():
-            p.assertValid(p.getValue())
+            p.validate()
 
     def getParameter(self, name):
         """
diff --git a/rocolib/builders/ESPBrainsBuilder.py b/rocolib/builders/ESPBrainsBuilder.py
deleted file mode 100644
index 272317a7add8fa2a96a0a9f1ac77f57570a97fa1..0000000000000000000000000000000000000000
--- a/rocolib/builders/ESPBrainsBuilder.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from rocolib.api.components.Component import Component
-from rocolib.api.Function import Function
-
-
-self = Component()
-
-self.addSubcomponent("beam", "SimpleRectBeam")
-self.addSubcomponent("header", "Header")
-self.addSubcomponent("servoPins", "Cutout")
-
-self.addParameter("brain", "nodeMCU", paramType="dimension")
-self.addParameter("length", 90, paramType="length")
-self.addParameter("width", 50, paramType="length")
-self.addParameter("depth", 20, 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.addConstraint(("beam", "width#minValue"), *getBrainParameter("height"))
-self.addConstraint(("beam", "depth#minValue"), *getBrainParameter("width"))
-self.addConstraint(("beam", "length#minValue"), *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("ESPBrains")
diff --git a/rocolib/builders/ESPSegBuilder.py b/rocolib/builders/ESPSegBuilder.py
index 1928fc693aab60d524800cf249c35a37ae97c8a2..177a765cca2c38a461285d19c8dbb559da9e9da2 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 Component
 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..e60d51215fcd04ed251134b920d9149aa614afdf
--- /dev/null
+++ b/rocolib/builders/MountedBrainsBuilder.py
@@ -0,0 +1,29 @@
+from rocolib.api.components.Component import Component
+from rocolib.api.Function import Function
+
+
+self = Component()
+
+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("MountedBrains")
diff --git a/rocolib/builders/ServoMountBuilder.py b/rocolib/builders/ServoMountBuilder.py
index 900cde2bee26aaca5763731e18ebf5de336e79c1..1fff49b20056d8369096177ddb87589fcb7b3c10 100644
--- a/rocolib/builders/ServoMountBuilder.py
+++ b/rocolib/builders/ServoMountBuilder.py
@@ -1,10 +1,10 @@
+from rocolib.library import getComponent
 from rocolib.api.components.Component import Component
 from rocolib.api.Function import Function
 
 c = Component()
 
-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")
@@ -15,12 +15,12 @@ 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", "length#minValue"), "servo", 'getDim(x, "motorlength") + 2 * getDim(x, "shoulderlength")')
+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 feb51f78d409d19cf57763e17717436a0a12d479..c639c89354d2b131f50a2ce1cc245641e8d780c3 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": lambda c : c.getParameter("d"),
-            "dy": lambda c : c.getParameter("d"),
+            "dx": 'c.getParameter("d")',
+            "dy": 'c.getParameter("d")',
         })
 
     def assemble(self):
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 22827533dcc68707f52de3d36bb541fd5c2bb9f9..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.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__":
-    Header.test()
diff --git a/rocolib/library/ESPBrains.yaml b/rocolib/library/MountedBrains.yaml
similarity index 58%
rename from rocolib/library/ESPBrains.yaml
rename to rocolib/library/MountedBrains.yaml
index 36578232d530a1b44f683ed3831bffd26e929b49..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,7 +54,43 @@ 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: 20
     spec:
@@ -80,13 +103,18 @@ parameters:
       minValue: 0
       units: mm
       valueType: (float, int)
+  offset:
+    defaultValue:
+    - 0
+    - 0
+    spec: {}
   width:
     defaultValue: 50
     spec:
       minValue: 0
       units: mm
       valueType: (float, int)
-source: ../builders/ESPBrainsBuilder.py
+source: ../builders/MountedBrainsBuilder.py
 subcomponents:
   beam:
     classname: SimpleRectBeam
@@ -95,37 +123,21 @@ subcomponents:
       depth:
         parameter: width
       depth#minValue:
-        function: getDim(x, 'width')
+        function: x.get("width")
         parameter: brain
       length:
         parameter: length
       length#minValue:
-        function: getDim(x, 'length')
+        function: x.get("length")
         parameter: brain
       width:
         parameter: depth
       width#minValue:
-        function: getDim(x, 'height')
+        function: x.get("height")
         parameter: brain
-  header:
-    classname: Header
+  brain:
+    classname: Brains
     kwargs: {}
     parameters:
-      colsep:
-        function: getDim(x, 'colsep')
-        parameter: brain
-      ncols:
-        function: getDim(x, 'ncols')
+      brain:
         parameter: brain
-      nrows:
-        function: getDim(x, 'nrows')
-        parameter: brain
-      rowsep:
-        function: getDim(x, 'rowsep')
-        parameter: brain
-  servoPins:
-    classname: Cutout
-    kwargs: {}
-    parameters:
-      dx: 27
-      dy: 8
diff --git a/rocolib/library/MountedServo.yaml b/rocolib/library/MountedServo.yaml
index 2bcbbe856c365fb6e99e765ce77c4e9eb30eb08d..59533eae6be6fd4f0ed49668b961fc6a7479407d 100644
--- a/rocolib/library/MountedServo.yaml
+++ b/rocolib/library/MountedServo.yaml
@@ -138,7 +138,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/library/RectBeam.py b/rocolib/library/RectBeam.py
index 12a323d930b73a635beb4066cf622199345bec8b..d61abc6d2f72e09967ae8763a4cd7799a17bbdb3 100644
--- a/rocolib/library/RectBeam.py
+++ b/rocolib/library/RectBeam.py
@@ -10,11 +10,11 @@ class RectBeam(FoldedComponent):
 
     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", 0, valueType="int", minValue=0)
+    self.addParameter("root", 0, values=[0,1,2,3])
     self.addParameter("addTabs", True, valueType="bool")
 
     for i in range(4):
@@ -25,17 +25,10 @@ class RectBeam(FoldedComponent):
     self.addEdgeInterface("slotedge", "r0.e3", "length")
 
   def assemble(self):
-    if self.getParameter("angle") is not None:
-      bangle = 90 - self.getParameter("angle")
-      tangle = 90 - self.getParameter("angle")
-    else:
-      bangle = 90 - self.getParameter("bangle")
-      tangle = 90 - self.getParameter("tangle")
+    bangle = 90 - self.getParameter("bangle")
+    tangle = 90 - self.getParameter("tangle")
 
-    try:
-      root = self.getParameter("root")
-    except KeyError:
-      root = None
+    root = self.getParameter("root")
 
     length = self.getParameter("length")
     width = self.getParameter("width")
@@ -66,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 954d2a97de209b5d2389449824e6c18022b3f4ab..dafdd4ed9a30dbd859e7b27751bfd5749b61a5b8 100644
--- a/rocolib/library/ServoMotor.py
+++ b/rocolib/library/ServoMotor.py
@@ -2,28 +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
 
 
 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.getParameter("servo")
-    a = self.getParameter("angle")
-    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(a), 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.getParameter("servo")
+        a = self.getParameter("angle")
+        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(a), 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 88bc3127a65d5ac36794ee92577399a05a7ff299..0c88010a1f4e2f536622348aa8162c3a548a925a 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
 
 
@@ -13,9 +12,9 @@ class ServoMount(Component):
 
     l = self.getParameter("length")
 
-    ml = getDim(servo, "motorlength")
-    sl = getDim(servo, "shoulderlength")
-    ho = getDim(servo, "hornoffset")
+    ml = servo.get("motorlength")
+    sl = servo.get("shoulderlength")
+    ho = servo.get("hornoffset")
 
     s = self.getParameter("shift")
 
diff --git a/rocolib/library/ServoMount.yaml b/rocolib/library/ServoMount.yaml
index 9aa85d0a9c0343683df7bf9cd76862ac8c6781bf..45d9c9e63ebbef06b611ffa8755129071419b4df 100644
--- a/rocolib/library/ServoMount.yaml
+++ b/rocolib/library/ServoMount.yaml
@@ -78,7 +78,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:
@@ -94,23 +130,23 @@ subcomponents:
       addTabs:
         parameter: addTabs
       depth:
-        function: getDim(x, "motorheight")
+        function: x.get("motorheight")
         parameter: servo
       length:
         parameter: length
       length#minValue:
-        function: getDim(x, "motorlength") + 2 * getDim(x, "shoulderlength")
+        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/Stool.py b/rocolib/library/Stool.py
index a7d4165d83199419a49f285b620fb337193dd2a6..92db4e3a7caeb02086847cf747f1a12e2f0f8799 100644
--- a/rocolib/library/Stool.py
+++ b/rocolib/library/Stool.py
@@ -9,7 +9,7 @@ class Stool(FoldedComponent):
         self.addParameter("height", 60, paramType="length", minValue=10)
         self.addParameter("legs", 3, paramType="count", minValue=1)
         self.addParameter("radius", minValue=10, overrides={
-            "legwidth": lambda c: r2l.r2l(c.getParameter("radius"), c.getParameter("legs")*2)
+            "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")
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 088b9acbf4c0694e6b1a95d4dbad2af19edf4a41..f2ede902bc4d84bdbc9f4255d580a0f90e033c87 100644
--- a/rocolib/library/UChannel.py
+++ b/rocolib/library/UChannel.py
@@ -11,10 +11,7 @@ class UChannel(FoldedComponent):
     # Minimum of 45deg to make sure the geometry stays convex
     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": lambda c : c.getParameter("angle"),
-        "tangle": lambda c : c.getParameter("angle"),
-    })
+    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])
diff --git a/rocolib/library/Wheel.yaml b/rocolib/library/Wheel.yaml
index f902aa7f8e451b6254f78e167f1f747a333f9a4c..6de1d93af4b845fe912b2cf84b60e44275325bc3 100644
--- a/rocolib/library/Wheel.yaml
+++ b/rocolib/library/Wheel.yaml
@@ -144,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,
-}