diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,19 @@
 
 ## Unreleased
 
+## 0.1.2.1 - 2024-01-09 
+
+### Added
+
+- Added `mirror` to `Transformable` typeclass, and `mirror2D` to `Transformable2D`
+- Added `fromPath2D` to `Waterfall.Path` to make 2D paths into 3D ones.
+- Added `centeredCylinder` and `unitCone` to `Waterfall.Solids`
+
+### Fixed
+
+- Handle offsetting by zero (return the unmodified shape)
+- Fix rotation of endcaps in a `sweep`
+
 ## 0.1.1.1 - 2023-12-12 
 
 ## 0.1.1.0 - 2023-12-12 
diff --git a/src/Waterfall/Internal/Edges.hs b/src/Waterfall/Internal/Edges.hs
--- a/src/Waterfall/Internal/Edges.hs
+++ b/src/Waterfall/Internal/Edges.hs
@@ -25,10 +25,10 @@
 gpVecToV3 vec = V3 <$> GP.Vec.getX vec <*> GP.Vec.getY vec <*> GP.Vec.getZ vec
 
 edgeEndpoints :: Ptr TopoDS.Edge -> IO (V3 Double, V3 Double)
-edgeEndpoints e = (`with` pure) $ do
-    curve <- BRep.Tool.curve e
-    p1 <- liftIO . BRep.Tool.curveParamFirst $ e
-    p2 <- liftIO . BRep.Tool.curveParamLast $ e
+edgeEndpoints edge = (`with` pure) $ do
+    curve <- BRep.Tool.curve edge
+    p1 <- liftIO . BRep.Tool.curveParamFirst $ edge
+    p2 <- liftIO . BRep.Tool.curveParamLast $ edge
     s <- (liftIO . gpPntToV3) =<< Geom.Curve.value curve p1
     e <- (liftIO . gpPntToV3) =<< Geom.Curve.value curve p2
     return (s, e)
@@ -39,7 +39,7 @@
     (s, _) <- edgeEndpoints v1
     let runToEnd = do
             edge <- WireExplorer.current explorer
-            (s, e') <- edgeEndpoints edge
+            (_s, e') <- edgeEndpoints edge
             WireExplorer.next explorer
             more <- WireExplorer.more explorer
             if more 
diff --git a/src/Waterfall/Offset.hs b/src/Waterfall/Offset.hs
--- a/src/Waterfall/Offset.hs
+++ b/src/Waterfall/Offset.hs
@@ -10,6 +10,7 @@
 import qualified OpenCascade.BRepOffset.Mode as Mode
 import qualified OpenCascade.GeomAbs.JoinType as GeomAbs.JoinType
 import qualified OpenCascade.BRepBuilderAPI.MakeSolid as MakeSolid
+import Linear.Epsilon (nearZero)
 
 -- | Expand or contract a `Solid` by a certain amount.
 -- 
@@ -19,7 +20,12 @@
     -> Double       -- ^ Tolerance, this can be relatively small
     -> Solid        -- ^ the `Solid` to offset 
     -> Solid
-offset value tolerance (Solid run) = Solid $ do
+offset value tolerance (Solid run)
+    | nearZero value = Solid run
+    | otherwise = 
+  Solid $ do
+
+
     builder <- MakeOffsetShape.new
     s <- run 
     --liftIO $ MakeOffsetShape.performBySimple builder s value
diff --git a/src/Waterfall/Path.hs b/src/Waterfall/Path.hs
--- a/src/Waterfall/Path.hs
+++ b/src/Waterfall/Path.hs
@@ -4,6 +4,7 @@
 module Waterfall.Path
 ( Path
 , module Waterfall.Path.Common
+, fromPath2D
 -- $ reexports
 , line3D
 , lineTo3D
@@ -22,9 +23,12 @@
 
 
 import Waterfall.Path.Common
+import Waterfall.TwoD.Internal.Path2D (Path2D (..))
 import Linear (V3)
 
-
+-- | convert a `Path2D` into a `Path` on the \( xy \) plane (with \( z = 0 \) )
+fromPath2D :: Path2D -> Path
+fromPath2D (Path2D run) = Path run
 
 -- $reexports
 --
diff --git a/src/Waterfall/Solids.hs b/src/Waterfall/Solids.hs
--- a/src/Waterfall/Solids.hs
+++ b/src/Waterfall/Solids.hs
@@ -6,18 +6,22 @@
 , box
 , unitSphere
 , unitCylinder
+, centeredCylinder
+, unitCone
 , prism
 ) where
 
 
 import Waterfall.Internal.Solid(Solid(..), nowhere)
 import Waterfall.TwoD.Internal.Shape (runShape)
+import Waterfall.Transforms (translate)
 import qualified Waterfall.TwoD.Shape as TwoD.Shape
 import qualified OpenCascade.BRepPrimAPI.MakeBox as MakeBox
 import qualified OpenCascade.BRepPrimAPI.MakeSphere as MakeSphere
 import qualified OpenCascade.BRepPrimAPI.MakeCylinder as MakeCylinder
+import qualified OpenCascade.BRepPrimAPI.MakeCone as MakeCone
 import qualified OpenCascade.GP as GP
-import Linear (V3 (..))
+import Linear (V3 (..), unit, _z, (^*))
 import qualified OpenCascade.GP.Pnt as GP.Pnt
 import qualified OpenCascade.GP.Vec as GP.Vec
 import qualified OpenCascade.BRepPrimAPI.MakePrism as MakePrism
@@ -56,6 +60,17 @@
 -- the other centered on \( (0, 0, 1) \)
 unitCylinder :: Solid
 unitCylinder = Solid $ Inheritance.upcast <$> MakeCylinder.fromRadiusAndHeight 1 1
+
+-- | A cylinder with radius 1, length 1,
+-- centered on the origin,
+centeredCylinder :: Solid
+centeredCylinder = translate (unit _z ^* (-0.5)) $ unitCylinder
+
+-- | A cone 
+-- With a point at the origin 
+-- and a circular face with Radius 1, centered on \( (0, 1, 1) \)
+unitCone :: Solid
+unitCone = Solid $ Inheritance.upcast <$> MakeCone.fromTwoRadiiAndHeight 0 1 1
 
 -- | Extruded a 2D face into a prism with a given length \(len\).
 --
diff --git a/src/Waterfall/Sweep.hs b/src/Waterfall/Sweep.hs
--- a/src/Waterfall/Sweep.hs
+++ b/src/Waterfall/Sweep.hs
@@ -23,8 +23,8 @@
         in if nearZero (vn - z)
             then pure face
             else
-                let axis = if nearZero (vn + z) then unit _x else cross vn z
-                    angle = acos (dot vn z) 
+                let axis = if nearZero (vn + z) then unit _x else z `cross` vn
+                    angle = acos (vn `dot` z)
                 in runSolid . rotate axis angle . Solid . pure $ face 
 
 positionFace :: V3 Double -> Ptr TopoDS.Shape -> Acquire (Ptr TopoDS.Shape)
diff --git a/src/Waterfall/Transforms.hs b/src/Waterfall/Transforms.hs
--- a/src/Waterfall/Transforms.hs
+++ b/src/Waterfall/Transforms.hs
@@ -6,15 +6,17 @@
 , uScale
 , rotate
 , translate
+, mirror
 ) where
 import Waterfall.Internal.Solid (Solid(..))
 import Linear.V3 (V3 (..))
-import Linear ((*^))
+import Linear ((*^), normalize, dot )
 import qualified Linear.Quaternion as Quaternion
 import qualified OpenCascade.GP.Trsf as GP.Trsf
 import qualified OpenCascade.GP as GP
 import qualified OpenCascade.GP.GTrsf as GP.GTrsf
 import qualified OpenCascade.GP.Ax1 as GP.Ax1
+import qualified OpenCascade.GP.Ax2 as GP.Ax2
 import qualified OpenCascade.GP.Dir as GP.Dir
 import qualified OpenCascade.GP.Vec as GP.Vec
 import qualified OpenCascade.BRepBuilderAPI.Transform  as BRepBuilderAPI.Transform
@@ -36,6 +38,8 @@
     rotate :: V3 Double -> Double -> a -> a
     -- | Translate by a vector in 3D space
     translate :: V3 Double -> a -> a
+    -- | Mirror in the plane, which passes through the origin, tangent to the specified vector
+    mirror :: V3 Double -> a -> a
 
 
 fromTrsfSolid :: Acquire (Ptr GP.Trsf) -> Solid -> Solid
@@ -96,6 +100,16 @@
     vec <- GP.Vec.new x y z
     liftIO $ GP.Trsf.setTranslation trsf vec
     return trsf
+
+mirrorTrsf :: V3 Double -> Acquire (Ptr GP.Trsf)
+mirrorTrsf (V3 x y z) = do
+    trsf <- GP.Trsf.new
+    dir <- GP.Dir.new x y z
+    axis <- GP.xoy
+    liftIO $ do 
+        GP.Ax2.setDirection axis dir
+        GP.Trsf.setMirrorAboutAx2 trsf axis
+    return trsf
     
 instance Transformable Solid where
     scale :: V3 Double -> Solid -> Solid
@@ -110,6 +124,9 @@
     translate :: V3 Double -> Solid -> Solid
     translate = fromTrsfSolid . translateTrsf
 
+    mirror :: V3 Double -> Solid -> Solid
+    mirror = fromTrsfSolid . mirrorTrsf
+
 instance Transformable Path where
     scale :: V3 Double -> Path -> Path
     scale = fromGTrsfPath . scaleTrsf
@@ -122,6 +139,9 @@
 
     translate :: V3 Double -> Path -> Path
     translate = fromTrsfPath . translateTrsf
+    
+    mirror :: V3 Double -> Path -> Path
+    mirror = fromTrsfPath . mirrorTrsf
 
         
 instance Transformable (V3 Double) where
@@ -137,4 +157,9 @@
 
     translate :: V3 Double -> V3 Double -> V3 Double 
     translate = (+)
+
+    mirror :: V3 Double -> V3 Double -> V3 Double 
+    mirror mirrorVec toMirror = 
+        let nm = normalize mirrorVec
+        in toMirror - (2 * (nm `dot` toMirror) *^ nm)
 
diff --git a/src/Waterfall/TwoD/Transforms.hs b/src/Waterfall/TwoD/Transforms.hs
--- a/src/Waterfall/TwoD/Transforms.hs
+++ b/src/Waterfall/TwoD/Transforms.hs
@@ -6,15 +6,17 @@
 , scale2D
 , uScale2D
 , translate2D
+, mirror2D
 ) where
 
 import Waterfall.TwoD.Internal.Path2D (Path2D (..))
 import Linear.V2 (V2 (..))
-import Linear ((*^))
+import Linear ((*^), normalize, dot)
 import qualified OpenCascade.GP.Trsf as GP.Trsf
 import qualified OpenCascade.GP as GP
 import qualified OpenCascade.GP.GTrsf as GP.GTrsf
 import qualified OpenCascade.GP.Ax1 as GP.Ax1
+import qualified OpenCascade.GP.Ax2 as GP.Ax2
 import qualified OpenCascade.GP.Dir as GP.Dir
 import qualified OpenCascade.GP.Vec as GP.Vec
 import qualified OpenCascade.BRepBuilderAPI.Transform  as BRepBuilderAPI.Transform
@@ -35,6 +37,11 @@
     uScale2D :: Double -> a -> a
     -- | Translate by a distance in 2D space
     translate2D :: V2 Double -> a -> a
+    -- | Mirror in the line, which passes through the origin, tangent to the specified vector
+    -- 
+    -- Note that in order to maintain consistency with 'Waterfall.Transforms.Transformable',
+    -- the mirror is in the line / tangent / to the vector, not in the line / parallel / to the vector
+    mirror2D :: V2 Double -> a -> a
 
 fromTrsfPath :: Acquire (Ptr GP.Trsf) -> Path2D -> Path2D
 fromTrsfPath mkTrsf (Path2D run) = Path2D $ do 
@@ -93,6 +100,16 @@
     vec <- GP.Vec.new x y 0
     liftIO $ GP.Trsf.setTranslation trsf vec
     return trsf
+    
+mirrorTrsf :: V2 Double -> Acquire (Ptr GP.Trsf)
+mirrorTrsf (V2 x y) = do 
+    trsf <- GP.Trsf.new
+    dir <- GP.Dir.new x y 0
+    axis <- GP.xoy
+    liftIO $ do
+        GP.Ax2.setDirection axis dir
+        GP.Trsf.setMirrorAboutAx2 trsf axis
+    return trsf
 
 instance Transformable2D Path2D where
     rotate2D :: Double -> Path2D -> Path2D
@@ -107,7 +124,11 @@
     translate2D :: V2 Double -> Path2D -> Path2D
     translate2D = fromTrsfPath .translateTrsf
 
+    mirror2D :: V2 Double -> Path2D -> Path2D
+    mirror2D = fromTrsfPath . mirrorTrsf
+    
 
+
 instance Transformable2D Shape where
     rotate2D :: Double -> Shape -> Shape
     rotate2D = fromTrsfShape . rotateTrsf  
@@ -121,6 +142,9 @@
     translate2D :: V2 Double -> Shape -> Shape
     translate2D = fromTrsfShape .translateTrsf
 
+    mirror2D :: V2 Double -> Shape -> Shape
+    mirror2D = fromTrsfShape . mirrorTrsf
+
 instance Transformable2D (V2 Double) where
     scale2D :: V2 Double -> V2 Double  -> V2 Double
     scale2D = (*)
@@ -137,3 +161,8 @@
 
     translate2D :: V2 Double -> V2 Double -> V2 Double 
     translate2D = (+)
+
+    mirror2D :: V2 Double -> V2 Double -> V2 Double 
+    mirror2D mirrorVec toMirror = 
+        let nm = normalize mirrorVec
+        in toMirror - (2 * (nm `dot` toMirror) *^ nm)
diff --git a/waterfall-cad.cabal b/waterfall-cad.cabal
--- a/waterfall-cad.cabal
+++ b/waterfall-cad.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           waterfall-cad
-version:        0.1.1.1
+version:        0.1.2.1
 synopsis:       Declarative CAD/Solid Modeling Library
 description:    Please see the README on GitHub at <https://github.com/joe-warren/opencascade-hs#readme>
 category:       Graphics
