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