diff --git a/Core/Basics/Algorithms.h b/Core/Basics/Algorithms.h
new file mode 100644
index 0000000000000000000000000000000000000000..39c2f1480b313e62166547e263ce9cd0bfa36694
--- /dev/null
+++ b/Core/Basics/Algorithms.h
@@ -0,0 +1,65 @@
+// ************************************************************************** //
+//
+//  BornAgain: simulate and fit scattering at grazing incidence
+//
+//! @file      Core/Basics/Algorithms
+//! @brief     Defines and implements namespace algo with some algorithms
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+// ************************************************************************** //
+
+#ifndef ALGORITHMS_H
+#define ALGORITHMS_H
+
+#include <algorithm>
+#include <functional>
+#include <vector>
+#include <cassert>
+
+//! Some additions to standard library algorithms.
+
+namespace algo {
+
+//! Returns the minimum value of function evaluate as applied to the elements of an iterator range.
+template<typename Evaluator, typename Iterator>
+double min_value(const Iterator& begin, const Iterator& end, const Evaluator& evaluate);
+
+//! Returns the maximum value of function evaluate as applied to the elements of an iterator range.
+template<typename Evaluator, typename Iterator>
+double max_value(const Iterator& begin, const Iterator& end, const Evaluator& evaluate);
+
+} // namespace algo
+
+
+// ************************************************************************** //
+// Implementation
+// ************************************************************************** //
+
+template<typename Evaluator, typename Iterator>
+double algo::min_value(const Iterator& begin, const Iterator& end, const Evaluator& evaluate)
+{
+    assert(begin != end);
+    double ret = evaluate(*begin);
+    Iterator it = begin;
+    while (++it != end)
+        ret = std::min(ret, evaluate(*it));
+    return ret;
+}
+
+template<typename Evaluator, typename Iterator>
+double algo::max_value(const Iterator& begin, const Iterator& end, const Evaluator& evaluate)
+{
+    assert(begin != end);
+    double ret = evaluate(*begin);
+    Iterator it = begin;
+    while (++it != end)
+        ret = std::max(ret, evaluate(*it));
+    return ret;
+}
+
+
+#endif // ALGORITHMS_H
diff --git a/Core/HardParticle/FormFactorAnisoPyramid.cpp b/Core/HardParticle/FormFactorAnisoPyramid.cpp
index cbab683362d286163e480752aa1803328bef7468..20618ede7de72be268a252000e230a515c8855b7 100644
--- a/Core/HardParticle/FormFactorAnisoPyramid.cpp
+++ b/Core/HardParticle/FormFactorAnisoPyramid.cpp
@@ -13,7 +13,6 @@
 // ************************************************************************** //
 
 #include "FormFactorAnisoPyramid.h"
-#include "AnisoPyramid.h"
 #include "BornAgainNamespace.h"
 #include "Exceptions.h"
 #include "MathConstants.h"
@@ -74,7 +73,6 @@ void FormFactorAnisoPyramid::onChange()
         ostr << "Check for '2*height <= (length,width)*tan(alpha)' failed.";
         throw Exceptions::ClassInitializationException(ostr.str());
     }
-    mP_shape.reset(new AnisoPyramid(m_length, m_width, m_height, m_alpha));
 
     double D = m_length / 2;
     double d = m_length / 2 * (1 - r);
diff --git a/Core/HardParticle/FormFactorBox.cpp b/Core/HardParticle/FormFactorBox.cpp
index 5ea1bd8cd8ee513f57b1d76acc13d66d0d9f83ff..519e7efc12ffa47b34b872ab11bb9efb32788d97 100644
--- a/Core/HardParticle/FormFactorBox.cpp
+++ b/Core/HardParticle/FormFactorBox.cpp
@@ -14,7 +14,6 @@
 
 #include "FormFactorBox.h"
 #include "BornAgainNamespace.h"
-#include "Box.h"
 #include "MathFunctions.h"
 #include "RealParameter.h"
 
@@ -23,7 +22,7 @@
 //! @param width: width of the base in nanometers
 //! @param height: height of the box in nanometers
 FormFactorBox::FormFactorBox(double length, double width, double height)
-    : m_length(length), m_width(width), m_height(height)
+    : FormFactorPolygonalPrism(height), m_length(length), m_width(width)
 {
     setName(BornAgain::FFBoxType);
     registerParameter(BornAgain::Length, &m_length).setUnit(BornAgain::UnitsNm).setNonnegative();
@@ -35,7 +34,7 @@ FormFactorBox::FormFactorBox(double length, double width, double height)
 complex_t FormFactorBox::evaluate_for_q(cvector_t q) const
 {
     complex_t qzHdiv2 = m_height / 2 * q.z();
-    return m_height * m_length * m_width * MathFunctions::sinc(m_length / 2 * q.x())
+    return m_length * m_width * m_height * MathFunctions::sinc(m_length / 2 * q.x())
            * MathFunctions::sinc(m_width / 2 * q.y()) * MathFunctions::sinc(qzHdiv2)
            * exp_I(qzHdiv2);
 }
@@ -50,5 +49,8 @@ IFormFactor* FormFactorBox::sliceFormFactor(ZLimits limits, const IRotation& rot
 
 void FormFactorBox::onChange()
 {
-    mP_shape.reset(new Box(m_length, m_width, m_height));
+    double a = m_length/2;
+    double b = m_width/2;
+    std::vector<kvector_t> V{{a, b, 0.}, {-a, b, 0.}, {-a, -b, 0.}, {a, -b, 0}};
+    setPrism(true, V);
 }
diff --git a/Core/HardParticle/FormFactorBox.h b/Core/HardParticle/FormFactorBox.h
index de00ff51de08fb7122c78d0a59c87023a067e6b5..285ff12875a7f943d2aca7bb0f36c1b3d169637c 100644
--- a/Core/HardParticle/FormFactorBox.h
+++ b/Core/HardParticle/FormFactorBox.h
@@ -15,12 +15,12 @@
 #ifndef FORMFACTORBOX_H
 #define FORMFACTORBOX_H
 
-#include "IFormFactorBorn.h"
+#include "FormFactorPolyhedron.h"
 
 //! A rectangular prism (parallelepiped).
 //! @ingroup hardParticle
 
-class BA_CORE_API_ FormFactorBox : public IFormFactorBorn
+class BA_CORE_API_ FormFactorBox : public FormFactorPolygonalPrism
 {
 public:
     FormFactorBox(double length, double width, double height);
@@ -33,11 +33,10 @@ public:
     void accept(INodeVisitor* visitor) const override final { visitor->visit(this); }
 
     double getLength() const { return m_length; }
-    double getHeight() const { return m_height; }
     double getWidth() const { return m_width; }
 
+    double volume() const override final { return m_length*m_height*m_width; }
     double radialExtension() const override final { return m_length / 2.0; }
-
     complex_t evaluate_for_q(cvector_t q) const override final;
 
 protected:
@@ -49,7 +48,6 @@ protected:
 private:
     double m_length;
     double m_width;
-    double m_height;
 };
 
 #endif // FORMFACTORBOX_H
diff --git a/Core/HardParticle/FormFactorCantellatedCube.cpp b/Core/HardParticle/FormFactorCantellatedCube.cpp
index f50eeef3c3573475a9099580a825d774b0a5bce3..ff7edfdd3bc71cb291e737d8c73e2555f681de85 100644
--- a/Core/HardParticle/FormFactorCantellatedCube.cpp
+++ b/Core/HardParticle/FormFactorCantellatedCube.cpp
@@ -16,7 +16,6 @@
 #include "BornAgainNamespace.h"
 #include "Exceptions.h"
 #include "RealParameter.h"
-#include "TruncatedCube.h" // TODO
 
 const PolyhedralTopology FormFactorCantellatedCube::topology = {
     {
@@ -73,8 +72,6 @@ void FormFactorCantellatedCube::onChange()
         ostr << "Check for removed_length <= 0.5*length failed.";
         throw Exceptions::ClassInitializationException(ostr.str());
     }
-    mP_shape.reset(new TruncatedCube(m_length, m_removed_length));
-
     double a = m_length / 2;
     double b = m_removed_length;
 
diff --git a/Core/HardParticle/FormFactorCone6.cpp b/Core/HardParticle/FormFactorCone6.cpp
index fea9204e1bdcfba93e34900fffd5959edb06558f..ab1719c0c90146af1e1bb538f7e8c4e589fbc030 100644
--- a/Core/HardParticle/FormFactorCone6.cpp
+++ b/Core/HardParticle/FormFactorCone6.cpp
@@ -17,7 +17,6 @@
 #include "Exceptions.h"
 #include "MathConstants.h"
 #include "MathFunctions.h"
-#include "Pyramid6.h"
 #include "RealParameter.h"
 
 const PolyhedralTopology FormFactorCone6::topology = {{{{5, 4, 3, 2, 1, 0}, true},
@@ -72,7 +71,6 @@ void FormFactorCone6::onChange()
         ostr << ", alpha[rad]:" << m_alpha << ")";
         throw Exceptions::ClassInitializationException(ostr.str());
     }
-    mP_shape.reset(new Pyramid6(m_base_edge, m_height, m_alpha));
 
     double a = m_base_edge;
     double as = a / 2;
diff --git a/Core/HardParticle/FormFactorCuboctahedron.cpp b/Core/HardParticle/FormFactorCuboctahedron.cpp
index 3b5d68a6e2632b30bbb4c41a1574e632f4067c64..0d4efa1952ec4649cc5f17dbb5eaf61a2a50349c 100644
--- a/Core/HardParticle/FormFactorCuboctahedron.cpp
+++ b/Core/HardParticle/FormFactorCuboctahedron.cpp
@@ -13,7 +13,6 @@
 // ************************************************************************** //
 
 #include "FormFactorCuboctahedron.h"
-#include "BiPyramid.h"
 #include "BornAgainNamespace.h"
 #include "Exceptions.h"
 #include "FormFactorPyramid.h"
@@ -96,7 +95,6 @@ void FormFactorCuboctahedron::onChange()
         ostr << "Check for '2.*height <= length*tan(alpha)*min(1.,1.0/height_ratio)' failed.";
         throw Exceptions::ClassInitializationException(ostr.str());
     }
-    mP_shape.reset(new BiPyramid(m_length, m_height, m_height_ratio, m_alpha));
 
     double a = m_length / 2 * (1 - r);
     double b = m_length / 2;
diff --git a/Core/HardParticle/FormFactorDodecahedron.cpp b/Core/HardParticle/FormFactorDodecahedron.cpp
index 37f0aa76f1108163bdaccd32495648dc3b21a3c9..7de93efecfeaf7670655020606b87f4a952f22ef 100644
--- a/Core/HardParticle/FormFactorDodecahedron.cpp
+++ b/Core/HardParticle/FormFactorDodecahedron.cpp
@@ -14,7 +14,6 @@
 
 #include "FormFactorDodecahedron.h"
 #include "BornAgainNamespace.h"
-#include "Dodecahedron.h"
 #include "RealParameter.h"
 
 const PolyhedralTopology FormFactorDodecahedron::topology = {{// bottom:
@@ -46,7 +45,6 @@ FormFactorDodecahedron::FormFactorDodecahedron(double edge) : FormFactorPolyhedr
 
 void FormFactorDodecahedron::onChange()
 {
-    mP_shape.reset(new Dodecahedron(m_edge));
     double a = m_edge;
     setPolyhedron(topology, -1.113516364411607 * a,
                   {{0.8506508083520399 * a, 0 * a, -1.113516364411607 * a},
diff --git a/Core/HardParticle/FormFactorDot.cpp b/Core/HardParticle/FormFactorDot.cpp
index a8e8cd858e1e088ab60f8f307f05e1f9f388b182..734421b9845ae377c2c626e770507da6ff3c24d6 100644
--- a/Core/HardParticle/FormFactorDot.cpp
+++ b/Core/HardParticle/FormFactorDot.cpp
@@ -14,7 +14,6 @@
 
 #include "FormFactorDot.h"
 #include "BornAgainNamespace.h"
-#include "Dot.h"
 #include "RealParameter.h"
 
 //! Constructor.
diff --git a/Core/HardParticle/FormFactorDot.h b/Core/HardParticle/FormFactorDot.h
index 315ee2bec285e958e915b14cb94096fcd5a68dcb..368c0b5e757a452f14b5f06d3d4a1491d07a31f3 100644
--- a/Core/HardParticle/FormFactorDot.h
+++ b/Core/HardParticle/FormFactorDot.h
@@ -33,7 +33,6 @@ public:
     double radialExtension() const override final { return 0; }
 
     double bottomZ(const IRotation&) const override final { return 0; }
-
     double topZ(const IRotation&) const override final { return 0; }
 
     complex_t evaluate_for_q(cvector_t q) const override final;
diff --git a/Core/HardParticle/FormFactorFullSphere.cpp b/Core/HardParticle/FormFactorFullSphere.cpp
index c923c93f223486a783d90260a74f5a23a8d62b19..486c2c1e8a217231765067e0dbb7337150953c85 100644
--- a/Core/HardParticle/FormFactorFullSphere.cpp
+++ b/Core/HardParticle/FormFactorFullSphere.cpp
@@ -19,7 +19,6 @@
 #include "MathConstants.h"
 #include "RealParameter.h"
 #include "Rotations.h"
-#include "TruncatedEllipsoid.h"
 
 //! Constructor of a full sphere.
 //! @param radius: radius of the sphere in nanometers
diff --git a/Core/HardParticle/FormFactorIcosahedron.cpp b/Core/HardParticle/FormFactorIcosahedron.cpp
index 33d8ff439e85a95803abb74c1171501eb781ec92..48316ff3892fa532ba9bdef83a1548ade70d9b7c 100644
--- a/Core/HardParticle/FormFactorIcosahedron.cpp
+++ b/Core/HardParticle/FormFactorIcosahedron.cpp
@@ -14,7 +14,6 @@
 
 #include "FormFactorIcosahedron.h"
 #include "BornAgainNamespace.h"
-#include "Icosahedron.h"
 #include "RealParameter.h"
 
 const PolyhedralTopology FormFactorIcosahedron::topology = {{// bottom:
@@ -56,7 +55,6 @@ FormFactorIcosahedron::FormFactorIcosahedron(double edge) : FormFactorPolyhedron
 
 void FormFactorIcosahedron::onChange()
 {
-    mP_shape.reset(new Icosahedron(m_edge));
     double a = m_edge;
     setPolyhedron(topology, -0.7557613140761708 * a,
                   {{0.5773502691896258 * a, 0 * a, -0.7557613140761708 * a},
diff --git a/Core/HardParticle/FormFactorPolyhedron.cpp b/Core/HardParticle/FormFactorPolyhedron.cpp
index 56dbe632383c238313e814d8637011f675bf2616..5d203326c297d33900550b190e2b280dd0a038dd 100644
--- a/Core/HardParticle/FormFactorPolyhedron.cpp
+++ b/Core/HardParticle/FormFactorPolyhedron.cpp
@@ -20,6 +20,7 @@
 #include "MathFunctions.h"
 #include "Precomputed.h"
 #include "RealParameter.h"
+#include "Rotations.h"
 #include <iomanip>
 #include <stdexcept> // need overlooked by g++ 5.4
 
@@ -421,11 +422,15 @@ void FormFactorPolyhedron::setLimits(double _q, int _n)
 
 //! Called by child classes to set faces and other internal variables.
 
-void FormFactorPolyhedron::setPolyhedron(const PolyhedralTopology& topology, double z_origin,
+void FormFactorPolyhedron::setPolyhedron(const PolyhedralTopology& topology, double z_bottom,
                                          const std::vector<kvector_t>& vertices)
 {
+    m_vertices.clear();
+    for (const kvector_t& vertex: vertices)
+        m_vertices.push_back(vertex - kvector_t{0, 0, z_bottom});
+
     try {
-        m_z_origin = z_origin;
+        m_z_bottom = z_bottom;
         m_sym_Ci = topology.symmetry_Ci;
 
         double diameter = 0;
@@ -472,12 +477,22 @@ void FormFactorPolyhedron::setPolyhedron(const PolyhedralTopology& topology, dou
     }
 }
 
-//! Returns the form factor F(q) of this polyhedron, respecting the offset z_origin.
+double FormFactorPolyhedron::bottomZ(const IRotation& rotation) const
+{
+    return BottomZ(m_vertices, rotation.getTransform3D());
+}
+
+double FormFactorPolyhedron::topZ(const IRotation& rotation) const
+{
+    return TopZ(m_vertices, rotation.getTransform3D());
+}
+
+//! Returns the form factor F(q) of this polyhedron, respecting the offset z_bottom.
 
 complex_t FormFactorPolyhedron::evaluate_for_q(cvector_t q) const
 {
     try {
-        return exp_I(-m_z_origin * q.z()) * evaluate_centered(q);
+        return exp_I(-m_z_bottom * q.z()) * evaluate_centered(q);
     } catch (std::logic_error& e) {
         throw std::logic_error("Bug in " + getName() + ": " + e.what()
                                + " [please report to the maintainers]");
@@ -587,6 +602,12 @@ void FormFactorPolyhedron::assert_platonic() const
 
 void FormFactorPolygonalPrism::setPrism(bool symmetry_Ci, const std::vector<kvector_t>& vertices)
 {
+    m_vertices.clear();
+    for (const kvector_t& vertex: vertices) {
+        m_vertices.push_back(vertex);
+        m_vertices.push_back(vertex + kvector_t{0, 0, m_height});
+    }
+
     try {
         m_base = std::unique_ptr<PolyhedralFace>(new PolyhedralFace(vertices, symmetry_Ci));
     } catch (std::invalid_argument& e) {
@@ -600,6 +621,16 @@ void FormFactorPolygonalPrism::setPrism(bool symmetry_Ci, const std::vector<kvec
     }
 }
 
+double FormFactorPolygonalPrism::bottomZ(const IRotation& rotation) const
+{
+    return BottomZ(m_vertices, rotation.getTransform3D());
+}
+
+double FormFactorPolygonalPrism::topZ(const IRotation& rotation) const
+{
+    return TopZ(m_vertices, rotation.getTransform3D());
+}
+
 //! Returns the volume of this prism.
 double FormFactorPolygonalPrism::volume() const
 {
diff --git a/Core/HardParticle/FormFactorPolyhedron.h b/Core/HardParticle/FormFactorPolyhedron.h
index 0b575727991c93b9b053e397f04557c9fd23e9ec..c1bb59bd8223a4dfc951d359f070ed3dbefd30bf 100644
--- a/Core/HardParticle/FormFactorPolyhedron.h
+++ b/Core/HardParticle/FormFactorPolyhedron.h
@@ -67,7 +67,6 @@ public:
                    bool _sym_S2 = false);
 
     double area() const { return m_area; }
-    kvector_t center() const { return m_center; }
     double pyramidalVolume() const { return m_rperp * m_area / 3; }
     double radius3d() const { return m_radius_3d; }
     //! Returns conj(q)*normal [BasicVector3D::dot is antilinear in 'this' argument]
@@ -88,7 +87,6 @@ private:
     double m_rperp;     //!< distance of this polygon's plane from the origin, along 'm_normal'
     double m_radius_2d; //!< radius of enclosing cylinder
     double m_radius_3d; //!< radius of enclosing sphere
-    kvector_t m_center; //!< center of mass
 
     void decompose_q(cvector_t q, complex_t& qperp, cvector_t& qpa) const;
     complex_t ff_n_core(int m, cvector_t qpa, complex_t qperp) const;
@@ -108,6 +106,9 @@ public:
 
     FormFactorPolyhedron() {}
 
+    double bottomZ(const IRotation& rotation) const override final;
+    double topZ(const IRotation& rotation) const override final;
+
     complex_t evaluate_for_q(cvector_t q) const override final;
     complex_t evaluate_centered(cvector_t q) const;
 
@@ -116,10 +117,10 @@ public:
     void assert_platonic() const;
 
 protected:
-    double m_z_origin;
+    double m_z_bottom;
     bool m_sym_Ci; //!< if true, then faces obtainable by inversion are not provided
 
-    void setPolyhedron(const PolyhedralTopology& topology, double z_origin,
+    void setPolyhedron(const PolyhedralTopology& topology, double z_bottom,
                        const std::vector<kvector_t>& vertices);
 
 private:
@@ -129,6 +130,7 @@ private:
     std::vector<PolyhedralFace> m_faces;
     double m_radius;
     double m_volume;
+    std::vector<kvector_t> m_vertices; //! for topZ, bottomZ computation only
 };
 
 //! A prism with a polygonal base, for form factor computation.
@@ -138,15 +140,19 @@ class BA_CORE_API_ FormFactorPolygonalPrism : public IFormFactorBorn
 public:
     FormFactorPolygonalPrism(double height) : m_height(height) {}
 
-    complex_t evaluate_for_q(cvector_t q) const override final;
-    double volume() const override final;
+    double bottomZ(const IRotation& rotation) const override final;
+    double topZ(const IRotation& rotation) const override final;
+
+    virtual complex_t evaluate_for_q(cvector_t q) const override;
+    virtual double volume() const override;
     double getHeight() const { return m_height; }
-    double radialExtension() const override final { return std::sqrt(m_base->area()); }
+    virtual double radialExtension() const override { return std::sqrt(m_base->area()); }
 
 protected:
     std::unique_ptr<PolyhedralFace> m_base;
     double m_height;
     void setPrism(bool symmetry_Ci, const std::vector<kvector_t>& vertices);
+    std::vector<kvector_t> m_vertices; //! for topZ, bottomZ computation only
 };
 
 //! A polygonal surface, for testing form factor computations.
diff --git a/Core/HardParticle/FormFactorPrism3.cpp b/Core/HardParticle/FormFactorPrism3.cpp
index 11b0a7b9697baf56c425cea7f98dcc14b7558de4..77530c430649eb95e212cc46f9079261e459c927 100644
--- a/Core/HardParticle/FormFactorPrism3.cpp
+++ b/Core/HardParticle/FormFactorPrism3.cpp
@@ -14,7 +14,6 @@
 
 #include "FormFactorPrism3.h"
 #include "BornAgainNamespace.h"
-#include "Pyramid3.h"
 #include "RealParameter.h"
 #include <iostream>
 
@@ -42,7 +41,6 @@ IFormFactor* FormFactorPrism3::sliceFormFactor(ZLimits limits, const IRotation&
 
 void FormFactorPrism3::onChange()
 {
-    mP_shape.reset(new Pyramid3(m_base_edge, m_height, M_PI_2));
     double a = m_base_edge;
     double as = a / 2;
     double ac = a / sqrt(3) / 2;
diff --git a/Core/HardParticle/FormFactorPrism6.cpp b/Core/HardParticle/FormFactorPrism6.cpp
index 885e0e698dde16d62ed821ef15c15d0da00ad631..625dad901cb65066911615917650eaa3b9e86109 100644
--- a/Core/HardParticle/FormFactorPrism6.cpp
+++ b/Core/HardParticle/FormFactorPrism6.cpp
@@ -14,7 +14,6 @@
 
 #include "FormFactorPrism6.h"
 #include "BornAgainNamespace.h"
-#include "Pyramid6.h"
 #include "RealParameter.h"
 
 //! Constructor of a prism with a regular hexagonal base.
@@ -41,7 +40,6 @@ IFormFactor* FormFactorPrism6::sliceFormFactor(ZLimits limits, const IRotation&
 
 void FormFactorPrism6::onChange()
 {
-    mP_shape.reset(new Pyramid6(m_base_edge, m_height, M_PI_2));
     double a = m_base_edge;
     double as = a * sqrt(3) / 2;
     double ac = a / 2;
diff --git a/Core/HardParticle/FormFactorPyramid.cpp b/Core/HardParticle/FormFactorPyramid.cpp
index e525e4f37a844041ed121b428f70bcc9870fb03c..704889f277ee49d7725ceb1771b89918332d5452 100644
--- a/Core/HardParticle/FormFactorPyramid.cpp
+++ b/Core/HardParticle/FormFactorPyramid.cpp
@@ -13,7 +13,6 @@
 // ************************************************************************** //
 
 #include "FormFactorPyramid.h"
-#include "AnisoPyramid.h"
 #include "BornAgainNamespace.h"
 #include "Exceptions.h"
 #include "MathConstants.h"
@@ -71,7 +70,6 @@ void FormFactorPyramid::onChange()
         ostr << "Check for 'height <= base_edge*tan(alpha)' failed.";
         throw Exceptions::ClassInitializationException(ostr.str());
     }
-    mP_shape.reset(new AnisoPyramid(m_base_edge, m_base_edge, m_height, m_alpha));
 
     double a = m_base_edge / 2;
     double b = a * (1 - r);
diff --git a/Core/HardParticle/FormFactorTetrahedron.cpp b/Core/HardParticle/FormFactorTetrahedron.cpp
index 78162e4d83cbcbbaae949ca64f16e850ccbb1b56..f92d4d54eb4c691346c514d2c8c0e0ab12d36652 100644
--- a/Core/HardParticle/FormFactorTetrahedron.cpp
+++ b/Core/HardParticle/FormFactorTetrahedron.cpp
@@ -17,7 +17,6 @@
 #include "Exceptions.h"
 #include "MathConstants.h"
 #include "MathFunctions.h"
-#include "Pyramid3.h"
 #include "RealParameter.h"
 
 const PolyhedralTopology FormFactorTetrahedron::topology = {{{{2, 1, 0}, false},
@@ -69,7 +68,6 @@ void FormFactorTetrahedron::onChange()
         ostr << ", alpha[rad]:" << m_alpha << ")";
         throw Exceptions::ClassInitializationException(ostr.str());
     }
-    mP_shape.reset(new Pyramid3(m_base_edge, m_height, m_alpha));
 
     double a = m_base_edge;
     double as = a / 2;
diff --git a/Core/HardParticle/FormFactorTriangle.cpp b/Core/HardParticle/FormFactorTriangle.cpp
index c646c527a18a7b06e4c455d0a093f8a14e16252e..1c1534620aa4bf067329e740a2f7e4da52abef18 100644
--- a/Core/HardParticle/FormFactorTriangle.cpp
+++ b/Core/HardParticle/FormFactorTriangle.cpp
@@ -16,7 +16,6 @@
 #include "BornAgainNamespace.h"
 #include "RealLimits.h"
 #include "RealParameter.h"
-#include "Triangle.h"
 
 FormFactorTriangle::FormFactorTriangle(const double base_edge) : m_base_edge(base_edge)
 {
@@ -29,7 +28,6 @@ FormFactorTriangle::FormFactorTriangle(const double base_edge) : m_base_edge(bas
 
 void FormFactorTriangle::onChange()
 {
-    mP_shape.reset(new Triangle(m_base_edge, 0.0));
     double a = m_base_edge;
     double as = a / 2;
     double ac = a / sqrt(3) / 2;
diff --git a/Core/HardParticle/FormFactorTruncatedCube.cpp b/Core/HardParticle/FormFactorTruncatedCube.cpp
index f807914a26150ced423d3deb62b71e2cd5a8c40d..2dbe513131d209400ff7768e2a8efe034f2fdc45 100644
--- a/Core/HardParticle/FormFactorTruncatedCube.cpp
+++ b/Core/HardParticle/FormFactorTruncatedCube.cpp
@@ -16,7 +16,6 @@
 #include "BornAgainNamespace.h"
 #include "Exceptions.h"
 #include "RealParameter.h"
-#include "TruncatedCube.h"
 
 const PolyhedralTopology FormFactorTruncatedCube::topology = {
     {{{0, 1, 7, 6, 9, 10, 4, 3}, true},
@@ -59,7 +58,6 @@ void FormFactorTruncatedCube::onChange()
         ostr << "Check for removed_length <= 0.5*length failed.";
         throw Exceptions::ClassInitializationException(ostr.str());
     }
-    mP_shape.reset(new TruncatedCube(m_length, m_removed_length));
 
     double a = m_length / 2;
     double b = m_removed_length;
diff --git a/Core/Particle/FormFactorWeighted.cpp b/Core/Particle/FormFactorWeighted.cpp
index 53f7d7bcec291b632523bbebf5bb76a8733332ef..9b19919fca9621ffb3cad387cbf7fffc90c73ae1 100644
--- a/Core/Particle/FormFactorWeighted.cpp
+++ b/Core/Particle/FormFactorWeighted.cpp
@@ -13,6 +13,7 @@
 // ************************************************************************** //
 
 #include "FormFactorWeighted.h"
+#include "Algorithms.h"
 #include "BornAgainNamespace.h"
 
 FormFactorWeighted::FormFactorWeighted()
@@ -47,10 +48,8 @@ double FormFactorWeighted::bottomZ(const IRotation& rotation) const
     if (m_form_factors.size() == 0)
         throw std::runtime_error("FormFactorWeighted::bottomZ() -> Error: "
                                  "'this' contains no form factors.");
-    double zmin = m_form_factors[0]->bottomZ(rotation);
-    for (size_t index = 1; index < m_form_factors.size(); ++index)
-        zmin = std::min(zmin, m_form_factors[index]->bottomZ(rotation));
-    return zmin;
+    return algo::min_value(m_form_factors.begin(), m_form_factors.end(),
+                           [&rotation](IFormFactor* ff) { return ff->bottomZ(rotation); });
 }
 
 double FormFactorWeighted::topZ(const IRotation& rotation) const
@@ -58,10 +57,8 @@ double FormFactorWeighted::topZ(const IRotation& rotation) const
     if (m_form_factors.size() == 0)
         throw std::runtime_error("FormFactorWeighted::topZ() -> Error: "
                                  "'this' contains no form factors.");
-    double zmax = m_form_factors[0]->topZ(rotation);
-    for (size_t index = 1; index < m_form_factors.size(); ++index)
-        zmax = std::min(zmax, m_form_factors[index]->topZ(rotation));
-    return zmax;
+    return algo::max_value(m_form_factors.begin(), m_form_factors.end(),
+                           [&rotation](IFormFactor* ff) { return ff->topZ(rotation); });
 }
 
 void FormFactorWeighted::addFormFactor(const IFormFactor& form_factor, double weight)
diff --git a/Core/Scattering/IFormFactorBorn.cpp b/Core/Scattering/IFormFactorBorn.cpp
index 3651ff7dae8ff16eb75a5bea9cdff412b4190dc5..93437c967b4711a494a44168407d21fc02d79ed6 100644
--- a/Core/Scattering/IFormFactorBorn.cpp
+++ b/Core/Scattering/IFormFactorBorn.cpp
@@ -13,13 +13,11 @@
 // ************************************************************************** //
 
 #include "IFormFactorBorn.h"
-#include "Dot.h"
+#include "Algorithms.h"
 #include "Exceptions.h"
 #include "Rotations.h"
 #include "WavevectorInfo.h"
 
-IFormFactorBorn::IFormFactorBorn() : mP_shape(new Dot()) {}
-
 complex_t IFormFactorBorn::evaluate(const WavevectorInfo& wavevectors) const
 {
     return evaluate_for_q(wavevectors.getQ());
@@ -32,11 +30,15 @@ Eigen::Matrix2cd IFormFactorBorn::evaluatePol(const WavevectorInfo& wavevectors)
 
 double IFormFactorBorn::bottomZ(const IRotation& rotation) const
 {
+    if (!mP_shape)
+        return 0;
     return BottomZ(mP_shape->vertices(), rotation.getTransform3D());
 }
 
 double IFormFactorBorn::topZ(const IRotation& rotation) const
 {
+    if (!mP_shape)
+        return 0;
     return TopZ(mP_shape->vertices(), rotation.getTransform3D());
 }
 
@@ -87,3 +89,23 @@ SlicingEffects IFormFactorBorn::computeSlicingEffects(ZLimits limits, const kvec
         new_position.setZ(lower_limit.m_value);
     return {new_position, dz_bottom, dz_top};
 }
+
+
+
+double IFormFactorBorn::BottomZ(const std::vector<kvector_t>& vertices, const Transform3D& rotation)
+{
+    if (vertices.size() == 0)
+        throw std::runtime_error("BottomZ() error: no vertices passed!");
+    return algo::min_value(vertices.begin(), vertices.end(),
+                           [&](const kvector_t& vertex) -> double
+                               { return rotation.transformed(vertex).z(); });
+}
+
+double IFormFactorBorn::TopZ(const std::vector<kvector_t>& vertices, const Transform3D& rotation)
+{
+    if (vertices.size() == 0)
+        throw std::runtime_error("TopZ() error: no vertices passed!");
+    return algo::max_value(vertices.begin(), vertices.end(),
+                           [&](const kvector_t& vertex) -> double
+                               { return rotation.transformed(vertex).z(); });
+}
diff --git a/Core/Scattering/IFormFactorBorn.h b/Core/Scattering/IFormFactorBorn.h
index 821a51b9c3b3c70e303be2cbe7241950d14d368e..7dc78b6fef784ad697895845dfa623b600c2da43 100644
--- a/Core/Scattering/IFormFactorBorn.h
+++ b/Core/Scattering/IFormFactorBorn.h
@@ -32,7 +32,7 @@ struct SlicingEffects;
 class BA_CORE_API_ IFormFactorBorn : public IFormFactor
 {
 public:
-    IFormFactorBorn();
+    IFormFactorBorn() = default;
     ~IFormFactorBorn() override {}
 
     IFormFactorBorn* clone() const override = 0;
@@ -45,9 +45,8 @@ public:
     Eigen::Matrix2cd evaluatePol(const WavevectorInfo& wavevectors) const override;
 #endif
 
-    double bottomZ(const IRotation& rotation) const override;
-
-    double topZ(const IRotation& rotation) const override;
+    virtual double bottomZ(const IRotation& rotation) const override;
+    virtual double topZ(const IRotation& rotation) const override;
 
     //! Returns scattering amplitude for complex scattering wavevector q=k_i-k_f.
     //! This method is public only for convenience of plotting form factors in Python.
@@ -71,6 +70,12 @@ protected:
     //! Helper method for slicing
     SlicingEffects computeSlicingEffects(ZLimits limits, const kvector_t& position,
                                          double height) const;
+
+    //! Calculates the z-coordinate of the lowest vertex after rotation
+    static double BottomZ(const std::vector<kvector_t>& vertices, const Transform3D& rotation);
+
+    //! Calculates the z-coordinate of the highest vertex after rotation
+    static double TopZ(const std::vector<kvector_t>& vertices, const Transform3D& rotation);
 };
 
 //! Nested structure that holds slicing effects on position and removed parts.
diff --git a/Core/Shapes/AnisoPyramid.cpp b/Core/Shapes/AnisoPyramid.cpp
deleted file mode 100644
index ca472ecc43699ee5b9c02811dcfb3cb4ca6a460b..0000000000000000000000000000000000000000
--- a/Core/Shapes/AnisoPyramid.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Shapes/AnisoPyramid.cpp
-//! @brief     Implements class AnisoPyramid.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#include "AnisoPyramid.h"
-#include "MathFunctions.h"
-
-#include <algorithm>
-
-AnisoPyramid::AnisoPyramid(double length, double width, double height, double alpha)
-{
-    m_vertices.resize(8);
-    double cot_alpha = MathFunctions::cot(alpha);
-    double delta = 2.0 * height * cot_alpha;
-    auto bottom_face = RectangleVertices(length, width, 0.0);
-    auto top_face = RectangleVertices(length - delta, width - delta, height);
-    std::move(bottom_face.begin(), bottom_face.end(), m_vertices.begin());
-    std::move(top_face.begin(), top_face.end(), m_vertices.begin() + 4);
-}
-
-AnisoPyramid::~AnisoPyramid() {}
diff --git a/Core/Shapes/AnisoPyramid.h b/Core/Shapes/AnisoPyramid.h
deleted file mode 100644
index 1a530d8af664e7f0193cad93399953beda282141..0000000000000000000000000000000000000000
--- a/Core/Shapes/AnisoPyramid.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Shapes/AnisoPyramid.h
-//! @brief     Defines class AnisoPyramid.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#ifndef ANISOPYRAMID_H
-#define ANISOPYRAMID_H
-
-#include "IShape.h"
-
-class AnisoPyramid : public IShape
-{
-public:
-    AnisoPyramid(double length, double width, double height, double alpha);
-    ~AnisoPyramid();
-};
-
-#endif // ANISOPYRAMID_H
diff --git a/Core/Shapes/BiPyramid.cpp b/Core/Shapes/BiPyramid.cpp
deleted file mode 100644
index 838a5a05c454a703b8d918b1644d42653c386597..0000000000000000000000000000000000000000
--- a/Core/Shapes/BiPyramid.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Shapes/BiPyramid.cpp
-//! @brief     Implements class BiPyramid.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#include "BiPyramid.h"
-#include "MathFunctions.h"
-
-#include <algorithm>
-
-BiPyramid::BiPyramid(double length, double height, double height_ratio, double alpha)
-{
-    m_vertices.resize(12);
-    double cot_alpha = MathFunctions::cot(alpha);
-    double delta_1 = 2.0 * height * cot_alpha;
-    double delta_2 = 2.0 * height_ratio * height * cot_alpha;
-    auto bottom_face = RectangleVertices(length, length, 0.0);
-    auto mid_face = RectangleVertices(length + delta_1, length + delta_1, height);
-    auto top_face = RectangleVertices(length + delta_1 - delta_2, length + delta_1 - delta_2,
-                                      (height_ratio + 1.0) * height);
-    std::move(bottom_face.begin(), bottom_face.end(), m_vertices.begin());
-    std::move(mid_face.begin(), mid_face.end(), m_vertices.begin() + 4);
-    std::move(top_face.begin(), top_face.end(), m_vertices.begin() + 8);
-}
-
-BiPyramid::~BiPyramid() {}
diff --git a/Core/Shapes/BiPyramid.h b/Core/Shapes/BiPyramid.h
deleted file mode 100644
index a9eea0a7e75adcfeb9662bbe82dbfdf7f6fb38e9..0000000000000000000000000000000000000000
--- a/Core/Shapes/BiPyramid.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Shapes/BiPyramid.h
-//! @brief     Defines class BiPyramid.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#ifndef BIPYRAMID_H
-#define BIPYRAMID_H
-
-#include "IShape.h"
-
-class BiPyramid : public IShape
-{
-public:
-    BiPyramid(double length, double height, double height_ratio, double alpha);
-    ~BiPyramid();
-};
-
-#endif // BIPYRAMID_H
diff --git a/Core/Shapes/Dodecahedron.cpp b/Core/Shapes/Dodecahedron.cpp
deleted file mode 100644
index d9e9896d675a3665409f2c0dd64c33905043be2c..0000000000000000000000000000000000000000
--- a/Core/Shapes/Dodecahedron.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Shapes/Dodecahedron.cpp
-//! @brief     Implements class Dodecahedron.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#include "Dodecahedron.h"
-
-Dodecahedron::Dodecahedron(double edge)
-{
-    m_vertices = {
-        {0.8506508083520399 * edge, 0 * edge, 0.0},
-        {0.2628655560595668 * edge, 0.8090169943749473 * edge, 0.0},
-        {-0.6881909602355868 * edge, 0.5 * edge, 0.0},
-        {-0.6881909602355868 * edge, -0.5 * edge, 0.0},
-        {0.2628655560595668 * edge, -0.8090169943749473 * edge, 0.0},
-        {1.376381920471174 * edge, 0 * edge, 0.8506508083520403 * edge},
-        {0.42532540417602 * edge, 1.309016994374947 * edge, 0.8506508083520403 * edge},
-        {-1.113516364411607 * edge, 0.8090169943749475 * edge, 0.8506508083520403 * edge},
-        {-1.113516364411607 * edge, -0.8090169943749475 * edge, 0.8506508083520403 * edge},
-        {0.42532540417602 * edge, -1.309016994374947 * edge, 0.8506508083520403 * edge},
-        {-1.376381920471174 * edge, 0 * edge, 1.3763819204711737 * edge},
-        {-0.42532540417602 * edge, -1.309016994374947 * edge, 1.3763819204711737 * edge},
-        {1.113516364411607 * edge, -0.8090169943749475 * edge, 1.3763819204711737 * edge},
-        {1.113516364411607 * edge, 0.8090169943749475 * edge, 1.3763819204711737 * edge},
-        {-0.42532540417602 * edge, 1.309016994374947 * edge, 1.3763819204711737 * edge},
-        {-0.8506508083520399 * edge, 0 * edge, 2.227032728823214 * edge},
-        {-0.2628655560595668 * edge, -0.8090169943749473 * edge, 2.227032728823214 * edge},
-        {0.6881909602355868 * edge, -0.5 * edge, 2.227032728823214 * edge},
-        {0.6881909602355868 * edge, 0.5 * edge, 2.227032728823214 * edge},
-        {-0.2628655560595668 * edge, 0.8090169943749473 * edge, 2.227032728823214 * edge}};
-}
-
-Dodecahedron::~Dodecahedron() {}
diff --git a/Core/Shapes/Dodecahedron.h b/Core/Shapes/Dodecahedron.h
deleted file mode 100644
index c56952b3567bd6124dc59401234101ebf34d367c..0000000000000000000000000000000000000000
--- a/Core/Shapes/Dodecahedron.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Shapes/Dodecahedron.h
-//! @brief     Defines class Dodecahedron.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#ifndef DODECAHEDRON_H
-#define DODECAHEDRON_H
-
-#include "IShape.h"
-
-class Dodecahedron : public IShape
-{
-public:
-    Dodecahedron(double edge);
-    ~Dodecahedron();
-};
-
-#endif // DODECAHEDRON_H
diff --git a/Core/Shapes/Dot.cpp b/Core/Shapes/Dot.cpp
deleted file mode 100644
index 7f4cd05453b8a0bb32c09deb252b4b8946ad55be..0000000000000000000000000000000000000000
--- a/Core/Shapes/Dot.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Shapes/Dot.cpp
-//! @brief     Implements class Dot.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#include "Dot.h"
-
-Dot::Dot()
-{
-    m_vertices.push_back(kvector_t(0.0, 0.0, 0.0));
-}
-
-Dot::~Dot() {}
diff --git a/Core/Shapes/Dot.h b/Core/Shapes/Dot.h
deleted file mode 100644
index 30c1577bb93cfccfb596043184a66105e5ae33b1..0000000000000000000000000000000000000000
--- a/Core/Shapes/Dot.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Shapes/Dot.h
-//! @brief     Defines class Dot.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#ifndef DOT_H
-#define DOT_H
-
-#include "IShape.h"
-
-class Dot : public IShape
-{
-public:
-    Dot();
-    ~Dot();
-};
-
-#endif // DOT_H
diff --git a/Core/Shapes/IShape.cpp b/Core/Shapes/IShape.cpp
index 6908bc04c03616c10030be73c17d789a9559fac2..ae4c0c89c372147a4a80601674951b7b52e86bfd 100644
--- a/Core/Shapes/IShape.cpp
+++ b/Core/Shapes/IShape.cpp
@@ -34,27 +34,6 @@ std::vector<kvector_t> RectangleVertices(double length, double width, double z)
     return result;
 }
 
-std::vector<kvector_t> TriangleVertices(double length, double z)
-{
-    static const double sqrt3 = std::sqrt(3.0);
-    double L = length / sqrt3;
-    std::vector<kvector_t> result = {
-        {L, 0.0, z}, {-L / 2.0, length / 2.0, z}, {-L / 2.0, -length / 2.0, z}};
-    return result;
-}
-
-std::vector<kvector_t> HexagonVertices(double length, double z)
-{
-    static const double sqrt3 = std::sqrt(3.0);
-    std::vector<kvector_t> result = {{length, 0.0, z},
-                                     {length / 2.0, length * sqrt3 / 2.0, z},
-                                     {-length / 2.0, length * sqrt3 / 2.0, z},
-                                     {-length, 0.0, z},
-                                     {-length / 2.0, -length * sqrt3 / 2.0, z},
-                                     {length / 2.0, -length * sqrt3 / 2.0, z}};
-    return result;
-}
-
 std::vector<kvector_t> EllipseVertices(double r_x, double r_y, double z)
 {
     static constexpr double delta_angle = 2.0 * M_PI / IShape::N_Circle;
diff --git a/Core/Shapes/IShape.h b/Core/Shapes/IShape.h
index 3bdfcfe1d05f19c5b3beb2bc4202217c8b26dd29..992eef58eb7a6456dc152d52bf4153f9eb416a7e 100644
--- a/Core/Shapes/IShape.h
+++ b/Core/Shapes/IShape.h
@@ -45,12 +45,6 @@ protected:
 //! Generate vertices of centered rectangle at height z
 std::vector<kvector_t> RectangleVertices(double length, double width, double z);
 
-//! Generate vertices of centered regular triangle with vertex on x-axis at height z
-std::vector<kvector_t> TriangleVertices(double length, double z);
-
-//! Generate vertices of centered regular hexagon with vertex on x-axis at height z
-std::vector<kvector_t> HexagonVertices(double length, double z);
-
 //! Generate vertices of centered ellipse with given semi-axes at height z
 std::vector<kvector_t> EllipseVertices(double r_x, double r_y, double z);
 
diff --git a/Core/Shapes/Icosahedron.cpp b/Core/Shapes/Icosahedron.cpp
deleted file mode 100644
index 3498ca8e23f22f54ade679ad7df967b2735a7c34..0000000000000000000000000000000000000000
--- a/Core/Shapes/Icosahedron.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Shapes/Icosahedron.cpp
-//! @brief     Implements class Icosahedron.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#include "Icosahedron.h"
-
-Icosahedron::Icosahedron(double edge)
-{
-    // key parameters used for the calculation of vertices:
-    // 1. dihedral angle = arccos(-sqrt(5)/3)
-    // 2. radius of inscribed sphere = sqrt(3)/12*(3+sqrt(5))*edge
-
-    m_vertices = {
-        {-0.57735026918962573 * edge, 0.0 * edge, 0.0},
-        {0.28867513459481281 * edge, 0.5 * edge, 0.0},
-        {0.28867513459481281 * edge, -0.5 * edge, 0.0},
-        {0.93417235896271578 * edge, 0.0 * edge, 0.57735026918962562 * edge},
-        {-0.46708617948135783 * edge, 0.80901699437494756 * edge, 0.57735026918962562 * edge},
-        {-0.46708617948135783 * edge, -0.80901699437494756 * edge, 0.57735026918962562 * edge},
-        {-0.93417235896271578 * edge, 0.0 * edge, 0.93417235896271589 * edge},
-        {0.46708617948135783 * edge, 0.80901699437494756 * edge, 0.93417235896271589 * edge},
-        {0.46708617948135783 * edge, -0.80901699437494756 * edge, 0.93417235896271589 * edge},
-        {0.57735026918962573 * edge, 0.0 * edge, 1.5115226281523415 * edge},
-        {-0.28867513459481281 * edge, 0.5 * edge, 1.5115226281523415 * edge},
-        {-0.28867513459481281 * edge, -0.5 * edge, 1.5115226281523415 * edge}};
-}
-
-Icosahedron::~Icosahedron() {}
diff --git a/Core/Shapes/Icosahedron.h b/Core/Shapes/Icosahedron.h
deleted file mode 100644
index fb3d9a21b468cd566e8ae14b6355e23f406a0b93..0000000000000000000000000000000000000000
--- a/Core/Shapes/Icosahedron.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Shapes/Icosahedron.h
-//! @brief     Defines class Icosahedron.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#ifndef ICOSAHEDRON_H
-#define ICOSAHEDRON_H
-
-#include "IShape.h"
-
-class Icosahedron : public IShape
-{
-public:
-    Icosahedron(double edge);
-    ~Icosahedron();
-};
-
-#endif // ICOSAHEDRON_H
diff --git a/Core/Shapes/Pyramid3.cpp b/Core/Shapes/Pyramid3.cpp
deleted file mode 100644
index 22830871b7dc78f9aa66d6d4aaf6cb2284d790f4..0000000000000000000000000000000000000000
--- a/Core/Shapes/Pyramid3.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Shapes/Pyramid3.cpp
-//! @brief     Implements class Pyramid3.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#include "Pyramid3.h"
-#include "MathFunctions.h"
-
-#include <algorithm>
-
-const double Pyramid3::sqrt3 = std::sqrt(3.0);
-
-Pyramid3::Pyramid3(double length, double height, double alpha)
-{
-    m_vertices.resize(6);
-    double cot_alpha = MathFunctions::cot(alpha);
-    double delta = 2.0 * sqrt3 * height * cot_alpha;
-    auto bottom_face = TriangleVertices(length, 0.0);
-    auto top_face = TriangleVertices(length - delta, height);
-    std::move(bottom_face.begin(), bottom_face.end(), m_vertices.begin());
-    std::move(top_face.begin(), top_face.end(), m_vertices.begin() + 3);
-}
-
-Pyramid3::~Pyramid3() {}
diff --git a/Core/Shapes/Pyramid3.h b/Core/Shapes/Pyramid3.h
deleted file mode 100644
index 4b997e681e6d7f686cf316ef1c1579dc1857110e..0000000000000000000000000000000000000000
--- a/Core/Shapes/Pyramid3.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Shapes/Pyramid3.h
-//! @brief     Defines class Pyramid3.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#ifndef PYRAMID3_H
-#define PYRAMID3_H
-
-#include "IShape.h"
-
-class Pyramid3 : public IShape
-{
-public:
-    Pyramid3(double length, double height, double alpha);
-    ~Pyramid3();
-
-private:
-    static const double sqrt3;
-};
-
-#endif // PYRAMID3_H
diff --git a/Core/Shapes/Pyramid6.cpp b/Core/Shapes/Pyramid6.cpp
deleted file mode 100644
index de10099847c7d85b25b2e14aab44765ea9ef8a96..0000000000000000000000000000000000000000
--- a/Core/Shapes/Pyramid6.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Shapes/Pyramid6.cpp
-//! @brief     Implements class Pyramid6.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#include "Pyramid6.h"
-#include "MathFunctions.h"
-
-#include <algorithm>
-
-const double Pyramid6::sqrt3 = std::sqrt(3.0);
-
-Pyramid6::Pyramid6(double length, double height, double alpha)
-{
-    m_vertices.resize(12);
-    double cot_alpha = MathFunctions::cot(alpha);
-    double delta = height * cot_alpha;
-    auto bottom_face = HexagonVertices(length, 0.0);
-    auto top_face = HexagonVertices(length - delta, height);
-    std::move(bottom_face.begin(), bottom_face.end(), m_vertices.begin());
-    std::move(top_face.begin(), top_face.end(), m_vertices.begin() + 6);
-}
-
-Pyramid6::~Pyramid6() {}
diff --git a/Core/Shapes/Pyramid6.h b/Core/Shapes/Pyramid6.h
deleted file mode 100644
index 7fe6e3d3faf1a7847b20b1e3fcdfaa202a6a8093..0000000000000000000000000000000000000000
--- a/Core/Shapes/Pyramid6.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Shapes/Pyramid6.h
-//! @brief     Defines class Pyramid6.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#ifndef PYRAMID6_H
-#define PYRAMID6_H
-
-#include "IShape.h"
-
-class Pyramid6 : public IShape
-{
-public:
-    Pyramid6(double length, double height, double alpha);
-    ~Pyramid6();
-
-private:
-    static const double sqrt3;
-};
-
-#endif // PYRAMID6_H
diff --git a/Core/Shapes/Triangle.cpp b/Core/Shapes/Triangle.cpp
deleted file mode 100644
index 260b228c9aea49f7a5299d5dae3da89ffabe994e..0000000000000000000000000000000000000000
--- a/Core/Shapes/Triangle.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Shapes/Triangle.cpp
-//! @brief     Implements class Triangle.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#include "Triangle.h"
-
-Triangle::Triangle(double length, double z)
-{
-    m_vertices = TriangleVertices(length, z);
-}
-
-Triangle::~Triangle() {}
diff --git a/Core/Shapes/Triangle.h b/Core/Shapes/Triangle.h
deleted file mode 100644
index 7f2a9209380fb9443ee0c88e1445031196a2d354..0000000000000000000000000000000000000000
--- a/Core/Shapes/Triangle.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Shapes/Triangle.h
-//! @brief     Defines class Triangle.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#ifndef TRIANGLE_H
-#define TRIANGLE_H
-
-#include "IShape.h"
-
-class Triangle : public IShape
-{
-public:
-    Triangle(double length, double z);
-    ~Triangle();
-};
-
-#endif // TRIANGLE_H
diff --git a/Core/Shapes/TruncatedCube.cpp b/Core/Shapes/TruncatedCube.cpp
deleted file mode 100644
index 622b1b268c47ccd5a8d01cd897f2f972ec734a36..0000000000000000000000000000000000000000
--- a/Core/Shapes/TruncatedCube.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Shapes/TruncatedCube.cpp
-//! @brief     Implements class TruncatedCube.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#include "TruncatedCube.h"
-
-TruncatedCube::TruncatedCube(double length, double removed_length)
-{
-    double L2 = length / 2.0;
-    double t = removed_length;
-    m_vertices = {{-L2 + t, -L2, 0.0},    {-L2, -L2 + t, 0.0},    {-L2, -L2, t},
-                  {L2 - t, -L2, 0.0},     {L2, -L2 + t, 0.0},     {L2, -L2, t},
-                  {-L2 + t, L2, 0.0},     {-L2, L2 - t, 0.0},     {-L2, L2, t},
-                  {L2 - t, L2, 0.0},      {L2, L2 - t, 0.0},      {L2, L2, t},
-                  {-L2 + t, -L2, length}, {-L2, -L2 + t, length}, {-L2, -L2, length - t},
-                  {L2 - t, -L2, length},  {L2, -L2 + t, length},  {L2, -L2, length - t},
-                  {-L2 + t, L2, length},  {-L2, L2 - t, length},  {-L2, L2, length - t},
-                  {L2 - t, L2, length},   {L2, L2 - t, length},   {L2, L2, length - t}};
-}
-
-TruncatedCube::~TruncatedCube() {}
diff --git a/Core/Shapes/TruncatedCube.h b/Core/Shapes/TruncatedCube.h
deleted file mode 100644
index 1bf9e3b8ff7f6000a61287348e3e0f176463369e..0000000000000000000000000000000000000000
--- a/Core/Shapes/TruncatedCube.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Shapes/TruncatedCube.h
-//! @brief     Defines class TruncatedCube.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#ifndef TRUNCATEDCUBE_H
-#define TRUNCATEDCUBE_H
-
-#include "IShape.h"
-
-class TruncatedCube : public IShape
-{
-public:
-    TruncatedCube(double length, double removed_length);
-    ~TruncatedCube();
-};
-
-#endif // TRUNCATEDCUBE_H
diff --git a/Core/Vector/Transform3D.cpp b/Core/Vector/Transform3D.cpp
index f400dd6867e637a8ad1938f7013dd7040ae40019..265914d173b3013e52f9f7cd0b00bfa50f6190d2 100644
--- a/Core/Vector/Transform3D.cpp
+++ b/Core/Vector/Transform3D.cpp
@@ -222,29 +222,3 @@ bool Transform3D::isZRotation() const
         return false;
     return true;
 }
-
-double BottomZ(const std::vector<kvector_t>& vertices, const Transform3D& rotation)
-{
-    if (vertices.size() == 0)
-        throw std::runtime_error("BottomZ() error: no vertices passed!");
-    kvector_t vertex_rot = rotation.transformed(vertices[0]);
-    double zmin = vertex_rot.z();
-    for (size_t index = 1; index < vertices.size(); ++index) {
-        vertex_rot = rotation.transformed(vertices[index]);
-        zmin = std::min(zmin, vertex_rot.z());
-    }
-    return zmin;
-}
-
-double TopZ(const std::vector<kvector_t>& vertices, const Transform3D& rotation)
-{
-    if (vertices.size() == 0)
-        throw std::runtime_error("TopZ() error: no vertices passed!");
-    kvector_t vertex_rot = rotation.transformed(vertices[0]);
-    double zmax = vertex_rot.z();
-    for (size_t index = 1; index < vertices.size(); ++index) {
-        vertex_rot = rotation.transformed(vertices[index]);
-        zmax = std::max(zmax, vertex_rot.z());
-    }
-    return zmax;
-}
diff --git a/Core/Vector/Transform3D.h b/Core/Vector/Transform3D.h
index 3c26aa77165ea72b16c6a40fecd05409bad6dd3b..35b8c93378f6a576177c858528f511649cfc078b 100644
--- a/Core/Vector/Transform3D.h
+++ b/Core/Vector/Transform3D.h
@@ -113,10 +113,4 @@ private:
 #endif
 };
 
-//! Calculates the z-coordinate of the lowest vertex after rotation
-double BottomZ(const std::vector<kvector_t>& vertices, const Transform3D& rotation);
-
-//! Calculates the z-coordinate of the highest vertex after rotation
-double TopZ(const std::vector<kvector_t>& vertices, const Transform3D& rotation);
-
 #endif // TRANSFORM3D_H
diff --git a/Tests/UnitTests/Core/Basics/MinMaxValueTest.cpp b/Tests/UnitTests/Core/Basics/MinMaxValueTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4cc4591e8d71e724666a51eba639aae1a7939d73
--- /dev/null
+++ b/Tests/UnitTests/Core/Basics/MinMaxValueTest.cpp
@@ -0,0 +1,32 @@
+#include "Algorithms.h"
+#include "google_test.h"
+#include <cmath>
+
+class MinMaxValueTest : public ::testing::Test
+{
+};
+
+TEST_F(MinMaxValueTest, MinMaxValueAlmostEq)
+{
+    double val;
+    std::vector<double> A{0.};
+    std::vector<int> C {1, 2, 3};
+
+    val = algo::min_value(A.begin(), A.end(), [](const double& x)->double { return x; });
+    EXPECT_EQ(val, 0.);
+    val = algo::max_value(A.begin(), A.end(), [](const double& x)->double { return 2+x; });
+    EXPECT_NEAR(val, 2., 1e-15);
+
+    val = algo::min_value(C.begin(), C.end(), [](const int& i)->double { return i; });
+    EXPECT_EQ(val, 1);
+    val = algo::min_value(C.begin(), C.end(), [](const int& i)->double { return -i; });
+    EXPECT_EQ(val, -3);
+    val = algo::min_value(C.begin(), C.end(), [](const int& i)->double { return pow(i-2.1,2); });
+    EXPECT_NEAR(val, 0.01, 1e-13);
+    val = algo::max_value(C.begin(), C.end(), [](const int& i)->double { return i; });
+    EXPECT_EQ(val, 3);
+    val = algo::max_value(C.begin(), C.end(), [](const int& i)->double { return -i; });
+    EXPECT_EQ(val, -1);
+    val = algo::max_value(C.begin(), C.end(), [](const int& i)->double { return -pow(i-2.1,2); });
+    EXPECT_NEAR(val, -0.01, 1e-13);
+}
diff --git a/Tests/UnitTests/Core/Sample/FormFactorBasicTest.cpp b/Tests/UnitTests/Core/Sample/FormFactorBasicTest.cpp
index e0dfbab5dfefeeb0b7dae1f70eb23c10939d6b9e..ab165ce172baeabf2755fa9fb2f13833aadb4d60 100644
--- a/Tests/UnitTests/Core/Sample/FormFactorBasicTest.cpp
+++ b/Tests/UnitTests/Core/Sample/FormFactorBasicTest.cpp
@@ -2,6 +2,8 @@
 #include "HardParticles.h"
 #include "IFormFactorBorn.h"
 #include "MathConstants.h"
+#include "Rotations.h"
+#include "Units.h"
 #include "google_test.h"
 
 class FormFactorBasicTest : public ::testing::Test
@@ -75,35 +77,37 @@ TEST_F(FormFactorBasicTest, AnisoPyramid)
                     * (length * width - (length + width) * height / tga
                        + 4.0 / 3.0 * height * height / (tga * tga));
 
-    FormFactorAnisoPyramid anisopyramid(length, width, height, alpha);
+    FormFactorAnisoPyramid particle(length, width, height, alpha);
 
-    EXPECT_EQ(BornAgain::FFAnisoPyramidType, anisopyramid.getName());
-    EXPECT_DOUBLE_EQ(volume, anisopyramid.volume());
-    EXPECT_EQ(12., anisopyramid.getLength());
-    EXPECT_EQ(14., anisopyramid.getWidth());
-    EXPECT_EQ(5., anisopyramid.getHeight());
-    EXPECT_EQ(0.8, anisopyramid.getAlpha());
+    EXPECT_EQ(BornAgain::FFAnisoPyramidType, particle.getName());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
+    EXPECT_EQ(length, particle.getLength());
+    EXPECT_EQ(width, particle.getWidth());
+    EXPECT_EQ(height, particle.getHeight());
+    EXPECT_EQ(alpha, particle.getAlpha());
+    EXPECT_EQ(0., particle.bottomZ(RotationZ()));
+    EXPECT_EQ(height, particle.topZ(RotationZ()));
 
-    test_ff(&anisopyramid);
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, HemiEllipsoid)
 {
-    double radius_a = 6.;
-    double radius_b = 7.;
+    double radiusx = 6.;
+    double radiusy = 7.;
     double height = 5.;
 
-    double volume = M_TWOPI * radius_a * radius_b * height / 3.;
+    double volume = M_TWOPI * radiusx * radiusy * height / 3.;
 
-    FormFactorHemiEllipsoid hemiellipsoid(radius_a, radius_b, height);
+    FormFactorHemiEllipsoid particle(radiusx, radiusy, height);
 
-    EXPECT_EQ(BornAgain::FFHemiEllipsoidType, hemiellipsoid.getName());
-    EXPECT_EQ(6., hemiellipsoid.getRadiusX());
-    EXPECT_EQ(7., hemiellipsoid.getRadiusY());
-    EXPECT_EQ(5., hemiellipsoid.getHeight());
-    EXPECT_DOUBLE_EQ(volume, hemiellipsoid.volume());
+    EXPECT_EQ(BornAgain::FFHemiEllipsoidType, particle.getName());
+    EXPECT_EQ(radiusx, particle.getRadiusX());
+    EXPECT_EQ(radiusy, particle.getRadiusY());
+    EXPECT_EQ(height, particle.getHeight());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
 
-    test_ff(&hemiellipsoid);
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, Box)
@@ -113,15 +117,24 @@ TEST_F(FormFactorBasicTest, Box)
     double width = 7.;
     double volume = length * height * width;
 
-    FormFactorBox box(length, width, height);
+    FormFactorBox particle(length, width, height);
 
-    EXPECT_EQ(BornAgain::FFBoxType, box.getName());
-    EXPECT_EQ(7., box.getWidth());
-    EXPECT_EQ(5., box.getHeight());
-    EXPECT_EQ(3., box.radialExtension());
-    EXPECT_DOUBLE_EQ(volume, box.volume());
+    EXPECT_EQ(BornAgain::FFBoxType, particle.getName());
+    EXPECT_EQ(width, particle.getWidth());
+    EXPECT_EQ(height, particle.getHeight());
+    EXPECT_EQ(3., particle.radialExtension());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
 
-    test_ff(&box);
+    EXPECT_EQ(0., particle.bottomZ(RotationZ()));
+    EXPECT_EQ(height, particle.topZ(RotationZ()));
+
+    EXPECT_EQ(0., particle.bottomZ(RotationZ(17*Units::degree)));
+    EXPECT_EQ(height, particle.topZ(RotationZ(39*Units::degree)));
+
+    EXPECT_NEAR(-width/2, particle.bottomZ(RotationX(90*Units::degree)), 1e-12);
+    EXPECT_NEAR(-length/2, particle.bottomZ(RotationY(90*Units::degree)), 1e-12);
+
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, Cone)
@@ -134,15 +147,17 @@ TEST_F(FormFactorBasicTest, Cone)
     double volume = M_PI / 3. * tga * radius * radius * radius
                     * (1. - (1. - HdivRtga) * (1. - HdivRtga) * (1. - HdivRtga));
 
-    FormFactorCone cone(radius, height, alpha);
+    FormFactorCone particle(radius, height, alpha);
 
-    EXPECT_EQ(BornAgain::FFConeType, cone.getName());
-    EXPECT_EQ(6., cone.getRadius());
-    EXPECT_EQ(5., cone.getHeight());
-    EXPECT_EQ(0.8, cone.getAlpha());
-    EXPECT_DOUBLE_EQ(volume, cone.volume());
+    EXPECT_EQ(BornAgain::FFConeType, particle.getName());
+    EXPECT_EQ(radius, particle.getRadius());
+    EXPECT_EQ(height, particle.getHeight());
+    EXPECT_EQ(alpha, particle.getAlpha());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
+    EXPECT_EQ(0., particle.bottomZ(RotationZ()));
+    EXPECT_EQ(height, particle.topZ(RotationZ()));
 
-    test_ff(&cone);
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, Cone6)
@@ -155,22 +170,22 @@ TEST_F(FormFactorBasicTest, Cone6)
     double volume = 3. / 4. * tga * base_edge * base_edge * base_edge
                     * (1. - (1. - HdivRtga) * (1. - HdivRtga) * (1. - HdivRtga));
 
-    FormFactorCone6 cone6(base_edge, height, alpha);
+    FormFactorCone6 particle(base_edge, height, alpha);
 
-    EXPECT_EQ(BornAgain::FFCone6Type, cone6.getName());
-    EXPECT_EQ(6., cone6.getBaseEdge());
-    EXPECT_EQ(5., cone6.getHeight());
-    EXPECT_EQ(0.8, cone6.getAlpha());
-    EXPECT_DOUBLE_EQ(volume, cone6.volume());
+    EXPECT_EQ(BornAgain::FFCone6Type, particle.getName());
+    EXPECT_EQ(base_edge, particle.getBaseEdge());
+    EXPECT_EQ(height, particle.getHeight());
+    EXPECT_EQ(alpha, particle.getAlpha());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
 
-    test_ff(&cone6);
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, Cuboctahedron)
 {
     double length = 10.;
     double height = 4;
-    double height_ratio = 1.;
+    double height_ratio = .7;
     double alpha = 0.8;
 
     double tga = std::tan(alpha);
@@ -180,16 +195,18 @@ TEST_F(FormFactorBasicTest, Cuboctahedron)
                     * (2. - (1. - H2divLtga) * (1. - H2divLtga) * (1. - H2divLtga)
                        - (1. - ratioH2divLtga) * (1. - ratioH2divLtga) * (1. - ratioH2divLtga));
 
-    FormFactorCuboctahedron cuboctahedron(length, height, height_ratio, alpha);
+    FormFactorCuboctahedron particle(length, height, height_ratio, alpha);
 
-    EXPECT_EQ(BornAgain::FFCuboctahedronType, cuboctahedron.getName());
-    EXPECT_EQ(4., cuboctahedron.getHeight());
-    EXPECT_EQ(10., cuboctahedron.getLength());
-    EXPECT_EQ(1., cuboctahedron.getHeightRatio());
-    EXPECT_EQ(0.8, cuboctahedron.getAlpha());
-    EXPECT_DOUBLE_EQ(volume, cuboctahedron.volume());
+    EXPECT_EQ(BornAgain::FFCuboctahedronType, particle.getName());
+    EXPECT_EQ(height, particle.getHeight());
+    EXPECT_EQ(length, particle.getLength());
+    EXPECT_EQ(height_ratio, particle.getHeightRatio());
+    EXPECT_EQ(alpha, particle.getAlpha());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
+    EXPECT_EQ(0., particle.bottomZ(RotationZ()));
+    EXPECT_EQ(height*(1+height_ratio), particle.topZ(RotationZ()));
 
-    test_ff(&cuboctahedron);
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, Cylinder)
@@ -198,14 +215,31 @@ TEST_F(FormFactorBasicTest, Cylinder)
     double height = 5.;
     double volume = M_PI * radius * radius * height;
 
-    FormFactorCylinder cylinder(radius, height);
+    FormFactorCylinder particle(radius, height);
+
+    EXPECT_EQ(BornAgain::FFCylinderType, particle.getName());
+    EXPECT_EQ(height, particle.getHeight());
+    EXPECT_EQ(radius, particle.getRadius());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
+
+    EXPECT_EQ(0., particle.bottomZ(RotationZ()));
+    EXPECT_EQ(height, particle.topZ(RotationZ()));
 
-    EXPECT_EQ(BornAgain::FFCylinderType, cylinder.getName());
-    EXPECT_EQ(5., cylinder.getHeight());
-    EXPECT_EQ(3., cylinder.getRadius());
-    EXPECT_DOUBLE_EQ(volume, cylinder.volume());
+    EXPECT_NEAR(-radius, particle.bottomZ(RotationX(90*Units::degree)), 1e-13);
+    EXPECT_NEAR(+radius, particle.topZ   (RotationX(90*Units::degree)), 1e-13);
+    EXPECT_NEAR(-radius, particle.bottomZ(RotationY(90*Units::degree)), 1e-13);
+    EXPECT_NEAR(+radius, particle.topZ   (RotationY(90*Units::degree)), 1e-13);
 
-    test_ff(&cylinder);
+    EXPECT_NEAR(-height, particle.bottomZ(RotationY(180*Units::degree)), 1e-13);
+    EXPECT_NEAR(0,       particle.topZ   (RotationY(180*Units::degree)), 1e-13);
+
+    for (double gamma: { 1.123, -2.34, 7.5, -9. })
+        // 7.5deg is worst case for 24-vertex circle
+        EXPECT_NEAR(-radius, particle.bottomZ(
+                        RotationEuler(0, 90*Units::degree, gamma*Units::degree)),
+                    3e-2); // TODO decrease epsilon after replacement of vertex-based approximation
+
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, Dodecahedron)
@@ -213,30 +247,35 @@ TEST_F(FormFactorBasicTest, Dodecahedron)
     double edge = 3.;
     double volume = (15 + 7 * sqrt(5)) / 4 * pow(edge, 3);
 
-    FormFactorDodecahedron dodecahedron(edge);
-    EXPECT_EQ(BornAgain::FFDodecahedronType, dodecahedron.getName());
-    EXPECT_EQ(edge, dodecahedron.getEdge());
-    EXPECT_DOUBLE_EQ(volume, dodecahedron.volume());
+    FormFactorDodecahedron particle(edge);
+    EXPECT_EQ(BornAgain::FFDodecahedronType, particle.getName());
+    EXPECT_EQ(edge, particle.getEdge());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
+    EXPECT_EQ(0., particle.bottomZ(RotationZ()));
+    EXPECT_NEAR(2*1.11352*edge, particle.topZ(RotationZ()), 1e-4);
+       // height=2*inradius from web ressource
 
-    test_ff(&dodecahedron);
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, EllipsoidalCylinder)
 {
-    double radius_a = 3.;
-    double radius_b = 5.;
+    double radiusx = 3.;
+    double radiusy = 5.;
     double height = 4;
-    double volume = M_PI * radius_a * radius_b * height;
+    double volume = M_PI * radiusx * radiusy * height;
 
-    FormFactorEllipsoidalCylinder ellipscyl(radius_a, radius_b, height);
+    FormFactorEllipsoidalCylinder particle(radiusx, radiusy, height);
 
-    EXPECT_EQ(BornAgain::FFEllipsoidalCylinderType, ellipscyl.getName());
-    EXPECT_EQ(4., ellipscyl.getHeight());
-    EXPECT_EQ(3., ellipscyl.getRadiusX());
-    EXPECT_EQ(5., ellipscyl.getRadiusY());
-    EXPECT_DOUBLE_EQ(volume, ellipscyl.volume());
+    EXPECT_EQ(BornAgain::FFEllipsoidalCylinderType, particle.getName());
+    EXPECT_EQ(height, particle.getHeight());
+    EXPECT_EQ(radiusx, particle.getRadiusX());
+    EXPECT_EQ(radiusy, particle.getRadiusY());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
+    EXPECT_EQ(0., particle.bottomZ(RotationZ()));
+    EXPECT_EQ(height, particle.topZ(RotationZ()));
 
-    test_ff(&ellipscyl);
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, CantellatedCube)
@@ -245,13 +284,15 @@ TEST_F(FormFactorBasicTest, CantellatedCube)
     double t = 2.; // side length of removed trirectangular tetrahedron at each vertex
     double volume = L * L * L - 6 * L * t * t + 16 * t * t * t / 3;
 
-    FormFactorCantellatedCube trcube(L, t);
+    FormFactorCantellatedCube particle(L, t);
 
-    EXPECT_EQ(L, trcube.getLength());
-    EXPECT_DOUBLE_EQ(t, trcube.getRemovedLength());
-    EXPECT_DOUBLE_EQ(trcube.volume(), volume);
+    EXPECT_EQ(L, particle.getLength());
+    EXPECT_DOUBLE_EQ(t, particle.getRemovedLength());
+    EXPECT_DOUBLE_EQ(particle.volume(), volume);
+    EXPECT_EQ(0., particle.bottomZ(RotationZ()));
+    EXPECT_EQ(L, particle.topZ(RotationZ()));
 
-    test_ff(&trcube);
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, FullSphere)
@@ -259,12 +300,14 @@ TEST_F(FormFactorBasicTest, FullSphere)
     double radius = 5.;
     double volume = 4. / 3. * M_PI * radius * radius * radius;
 
-    FormFactorFullSphere fullsphere(radius);
-    EXPECT_EQ(BornAgain::FFFullSphereType, fullsphere.getName());
-    EXPECT_EQ(5., fullsphere.getRadius());
-    EXPECT_DOUBLE_EQ(volume, fullsphere.volume());
+    FormFactorFullSphere particle(radius);
+    EXPECT_EQ(BornAgain::FFFullSphereType, particle.getName());
+    EXPECT_EQ(radius, particle.getRadius());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
+    EXPECT_EQ(0., particle.bottomZ(RotationZ()));
+    EXPECT_EQ(2*radius, particle.topZ(RotationZ()));
 
-    test_ff(&fullsphere);
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, FullSpheroid)
@@ -273,14 +316,16 @@ TEST_F(FormFactorBasicTest, FullSpheroid)
     double height = 5.;
     double volume = 2. / 3. * M_PI * radius * radius * height;
 
-    FormFactorFullSpheroid fullspheroid(radius, height);
+    FormFactorFullSpheroid particle(radius, height);
 
-    EXPECT_EQ(BornAgain::FFFullSpheroidType, fullspheroid.getName());
-    EXPECT_EQ(3., fullspheroid.getRadius());
-    EXPECT_EQ(5., fullspheroid.getHeight());
-    EXPECT_DOUBLE_EQ(volume, fullspheroid.volume());
+    EXPECT_EQ(BornAgain::FFFullSpheroidType, particle.getName());
+    EXPECT_EQ(radius, particle.getRadius());
+    EXPECT_EQ(height, particle.getHeight());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
+    EXPECT_EQ(0., particle.bottomZ(RotationZ()));
+    EXPECT_EQ(height, particle.topZ(RotationZ()));
 
-    test_ff(&fullspheroid);
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, Icosahedron)
@@ -288,13 +333,16 @@ TEST_F(FormFactorBasicTest, Icosahedron)
     double edge = 7.;
     double volume = 5 * (3 + sqrt(5)) / 12 * pow(edge, 3);
 
-    FormFactorIcosahedron icosahedron(edge);
+    FormFactorIcosahedron particle(edge);
 
-    EXPECT_EQ(BornAgain::FFIcosahedronType, icosahedron.getName());
-    EXPECT_EQ(edge, icosahedron.getEdge());
-    EXPECT_DOUBLE_EQ(volume, icosahedron.volume());
+    EXPECT_EQ(BornAgain::FFIcosahedronType, particle.getName());
+    EXPECT_EQ(edge, particle.getEdge());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
+    EXPECT_EQ(0., particle.bottomZ(RotationZ()));
+    EXPECT_NEAR(2*0.755761*edge, particle.topZ(RotationZ()), 1e-4);
+       // height=2*inradius from web ressource
 
-    test_ff(&icosahedron);
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, Prism3)
@@ -303,14 +351,16 @@ TEST_F(FormFactorBasicTest, Prism3)
     double base_edge = 6.;
     double volume = sqrt(3.) / 4. * height * base_edge * base_edge;
 
-    FormFactorPrism3 prism3(base_edge, height);
+    FormFactorPrism3 particle(base_edge, height);
 
-    EXPECT_EQ(BornAgain::FFPrism3Type, prism3.getName());
-    EXPECT_EQ(4., prism3.getHeight());
-    EXPECT_EQ(6., prism3.getBaseEdge());
-    EXPECT_DOUBLE_EQ(volume, prism3.volume());
+    EXPECT_EQ(BornAgain::FFPrism3Type, particle.getName());
+    EXPECT_EQ(height, particle.getHeight());
+    EXPECT_EQ(base_edge, particle.getBaseEdge());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
+    EXPECT_EQ(0., particle.bottomZ(RotationZ()));
+    EXPECT_EQ(height, particle.topZ(RotationZ()));
 
-    test_ff(&prism3);
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, Prism6)
@@ -319,35 +369,39 @@ TEST_F(FormFactorBasicTest, Prism6)
     double base_edge = 3.;
     double volume = 3. * sqrt(3.) / 2. * height * base_edge * base_edge;
 
-    FormFactorPrism6 prism6(base_edge, height);
+    FormFactorPrism6 particle(base_edge, height);
 
-    EXPECT_EQ(BornAgain::FFPrism6Type, prism6.getName());
-    EXPECT_EQ(4., prism6.getHeight());
-    EXPECT_EQ(3., prism6.getBaseEdge());
-    EXPECT_DOUBLE_EQ(volume, prism6.volume());
+    EXPECT_EQ(BornAgain::FFPrism6Type, particle.getName());
+    EXPECT_EQ(height, particle.getHeight());
+    EXPECT_EQ(base_edge, particle.getBaseEdge());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
+    EXPECT_EQ(0., particle.bottomZ(RotationZ()));
+    EXPECT_EQ(height, particle.topZ(RotationZ()));
 
-    test_ff(&prism6);
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, Pyramid)
 {
     double height = 4.;
-    double length = 10.;
+    double base_edge = 10.;
     double alpha = 0.8;
     double tga = std::tan(alpha);
-    double H2divLtga = height * 2. / length / tga;
-    double volume = 1. / 6. * tga * length * length * length
+    double H2divLtga = height * 2. / base_edge / tga;
+    double volume = 1. / 6. * tga * base_edge * base_edge * base_edge
                     * (1. - (1. - H2divLtga) * (1. - H2divLtga) * (1. - H2divLtga));
 
-    FormFactorPyramid pyramid(length, height, alpha);
+    FormFactorPyramid particle(base_edge, height, alpha);
 
-    EXPECT_EQ(BornAgain::FFPyramidType, pyramid.getName());
-    EXPECT_EQ(4., pyramid.getHeight());
-    EXPECT_EQ(10., pyramid.getBaseEdge());
-    EXPECT_EQ(0.8, pyramid.getAlpha());
-    EXPECT_DOUBLE_EQ(volume, pyramid.volume());
+    EXPECT_EQ(BornAgain::FFPyramidType, particle.getName());
+    EXPECT_EQ(height, particle.getHeight());
+    EXPECT_EQ(base_edge, particle.getBaseEdge());
+    EXPECT_EQ(alpha, particle.getAlpha());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
+    EXPECT_EQ(0., particle.bottomZ(RotationZ()));
+    EXPECT_EQ(height, particle.topZ(RotationZ()));
 
-    test_ff(&pyramid);
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, TruncatedSphere)
@@ -358,13 +412,15 @@ TEST_F(FormFactorBasicTest, TruncatedSphere)
     double volume = M_PI / 3. * radius * radius * radius
                     * (3. * HdivR - 1. - (HdivR - 1.) * (HdivR - 1.) * (HdivR - 1.));
 
-    FormFactorTruncatedSphere trsphere(radius, height);
+    FormFactorTruncatedSphere particle(radius, height);
 
-    EXPECT_EQ(BornAgain::FFTruncatedSphereType, trsphere.getName());
-    EXPECT_EQ(3., trsphere.getHeight());
-    EXPECT_DOUBLE_EQ(volume, trsphere.volume());
+    EXPECT_EQ(BornAgain::FFTruncatedSphereType, particle.getName());
+    EXPECT_EQ(height, particle.getHeight());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
+    EXPECT_EQ(0., particle.bottomZ(RotationZ()));
+    EXPECT_EQ(height, particle.topZ(RotationZ()));
 
-    test_ff(&trsphere);
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, TruncatedSpheroid)
@@ -375,14 +431,16 @@ TEST_F(FormFactorBasicTest, TruncatedSpheroid)
     double volume =
         M_PI * radius * height * height / flattening * (1. - height / (3. * flattening * radius));
 
-    FormFactorTruncatedSpheroid trspheroid(radius, height, flattening);
+    FormFactorTruncatedSpheroid particle(radius, height, flattening);
 
-    EXPECT_EQ(BornAgain::FFTruncatedSpheroidType, trspheroid.getName());
-    EXPECT_EQ(5., trspheroid.getHeight());
-    EXPECT_EQ(3., trspheroid.getRadius());
-    EXPECT_DOUBLE_EQ(volume, trspheroid.volume());
+    EXPECT_EQ(BornAgain::FFTruncatedSpheroidType, particle.getName());
+    EXPECT_EQ(height, particle.getHeight());
+    EXPECT_EQ(radius, particle.getRadius());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
+    EXPECT_EQ(0., particle.bottomZ(RotationZ()));
+    EXPECT_EQ(height, particle.topZ(RotationZ()));
 
-    test_ff(&trspheroid);
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, Tetrahedron)
@@ -395,15 +453,15 @@ TEST_F(FormFactorBasicTest, Tetrahedron)
     double volume = tga / 24. * base_edge * base_edge * base_edge
                     * (1. - (1. - sqrt3H2divLtga) * (1. - sqrt3H2divLtga) * (1. - sqrt3H2divLtga));
 
-    FormFactorTetrahedron tetrahedron(base_edge, height, alpha);
+    FormFactorTetrahedron particle(base_edge, height, alpha);
 
-    EXPECT_EQ(BornAgain::FFTetrahedronType, tetrahedron.getName());
-    EXPECT_EQ(4., tetrahedron.getHeight());
-    EXPECT_EQ(16., tetrahedron.getBaseEdge());
-    EXPECT_EQ(0.8, tetrahedron.getAlpha());
-    EXPECT_DOUBLE_EQ(volume, tetrahedron.volume());
+    EXPECT_EQ(BornAgain::FFTetrahedronType, particle.getName());
+    EXPECT_EQ(height, particle.getHeight());
+    EXPECT_EQ(base_edge, particle.getBaseEdge());
+    EXPECT_EQ(alpha, particle.getAlpha());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
 
-    test_ff(&tetrahedron);
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, Ripple1Box)
@@ -413,15 +471,17 @@ TEST_F(FormFactorBasicTest, Ripple1Box)
     double length = 100.0;
     double volume = 0.5 * height * width * length;
 
-    FormFactorRipple1Box ripple1(length, width, height);
+    FormFactorRipple1Box particle(length, width, height);
 
-    EXPECT_EQ(BornAgain::FFRipple1BoxType, ripple1.getName());
-    EXPECT_EQ(4., ripple1.getHeight());
-    EXPECT_EQ(20., ripple1.getWidth());
-    EXPECT_EQ(100., ripple1.getLength());
-    EXPECT_DOUBLE_EQ(volume, ripple1.volume());
+    EXPECT_EQ(BornAgain::FFRipple1BoxType, particle.getName());
+    EXPECT_EQ(height, particle.getHeight());
+    EXPECT_EQ(width, particle.getWidth());
+    EXPECT_EQ(length, particle.getLength());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
+    EXPECT_EQ(0., particle.bottomZ(RotationZ()));
+    EXPECT_EQ(height, particle.topZ(RotationZ()));
 
-    test_ff(&ripple1);
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, TruncatedCube)
@@ -430,14 +490,16 @@ TEST_F(FormFactorBasicTest, TruncatedCube)
     double t = 6.; // side length of removed trirectangular tetrahedron at each vertex
     double volume = length * length * length - 4. / 3. * t * t * t;
 
-    FormFactorTruncatedCube trcube(length, t);
+    FormFactorTruncatedCube particle(length, t);
 
-    EXPECT_EQ(BornAgain::FFTruncatedCubeType, trcube.getName());
-    EXPECT_EQ(length, trcube.getLength());
-    EXPECT_DOUBLE_EQ(t, trcube.getRemovedLength());
-    EXPECT_DOUBLE_EQ(trcube.volume(), volume);
+    EXPECT_EQ(BornAgain::FFTruncatedCubeType, particle.getName());
+    EXPECT_EQ(length, particle.getLength());
+    EXPECT_DOUBLE_EQ(t, particle.getRemovedLength());
+    EXPECT_DOUBLE_EQ(particle.volume(), volume);
+    EXPECT_EQ(0., particle.bottomZ(RotationZ()));
+    EXPECT_EQ(length, particle.topZ(RotationZ()));
 
-    test_ff(&trcube);
+    test_ff(&particle);
 }
 
 TEST_F(FormFactorBasicTest, Ripple2Box)
@@ -448,11 +510,13 @@ TEST_F(FormFactorBasicTest, Ripple2Box)
     double d = 0.3; // asymmetry
     double volume = 0.5 * height * width * length;
 
-    FormFactorRipple2Box ripple2(length, width, height, d);
+    FormFactorRipple2Box particle(length, width, height, d);
 
-    EXPECT_EQ(BornAgain::FFRipple2BoxType, ripple2.getName());
-    EXPECT_EQ(4., ripple2.getHeight());
-    EXPECT_DOUBLE_EQ(volume, ripple2.volume());
+    EXPECT_EQ(BornAgain::FFRipple2BoxType, particle.getName());
+    EXPECT_EQ(height, particle.getHeight());
+    EXPECT_DOUBLE_EQ(volume, particle.volume());
+    EXPECT_EQ(0., particle.bottomZ(RotationZ()));
+    EXPECT_EQ(height, particle.topZ(RotationZ()));
 
-    // test_ff( &ripple2 ); WAITING: restore once getRadius returns the umkreis radius
+    // test_ff( &particle ); WAITING: restore once getRadius returns the umkreis radius
 }
diff --git a/auto/Wrap/doxygen_core.i b/auto/Wrap/doxygen_core.i
index a88839728280a0506ffb375e5fae725f6a775171..849be6a4216f4e301d2f276cd0f91e684aac1ea9 100644
--- a/auto/Wrap/doxygen_core.i
+++ b/auto/Wrap/doxygen_core.i
@@ -118,16 +118,6 @@ Sets angular resolution values via  RangedDistribution and values of standard de
 ";
 
 
-// File: classAnisoPyramid.xml
-%feature("docstring") AnisoPyramid "";
-
-%feature("docstring")  AnisoPyramid::AnisoPyramid "AnisoPyramid::AnisoPyramid(double length, double width, double height, double alpha)
-";
-
-%feature("docstring")  AnisoPyramid::~AnisoPyramid "AnisoPyramid::~AnisoPyramid()
-";
-
-
 // File: classAsymRippleBuilder.xml
 %feature("docstring") AsymRippleBuilder "";
 
@@ -631,16 +621,6 @@ creation on  Bin1DKVector from alpha and phi bins
 ";
 
 
-// File: classBiPyramid.xml
-%feature("docstring") BiPyramid "";
-
-%feature("docstring")  BiPyramid::BiPyramid "BiPyramid::BiPyramid(double length, double height, double height_ratio, double alpha)
-";
-
-%feature("docstring")  BiPyramid::~BiPyramid "BiPyramid::~BiPyramid()
-";
-
-
 // File: classBox.xml
 %feature("docstring") Box "";
 
@@ -2137,16 +2117,6 @@ Calls the  INodeVisitor's visit method.
 ";
 
 
-// File: classDodecahedron.xml
-%feature("docstring") Dodecahedron "";
-
-%feature("docstring")  Dodecahedron::Dodecahedron "Dodecahedron::Dodecahedron(double edge)
-";
-
-%feature("docstring")  Dodecahedron::~Dodecahedron "Dodecahedron::~Dodecahedron()
-";
-
-
 // File: classExceptions_1_1DomainErrorException.xml
 %feature("docstring") Exceptions::DomainErrorException "";
 
@@ -2154,16 +2124,6 @@ Calls the  INodeVisitor's visit method.
 ";
 
 
-// File: classDot.xml
-%feature("docstring") Dot "";
-
-%feature("docstring")  Dot::Dot "Dot::Dot()
-";
-
-%feature("docstring")  Dot::~Dot "Dot::~Dot()
-";
-
-
 // File: classDoubleEllipse.xml
 %feature("docstring") DoubleEllipse "";
 
@@ -4291,6 +4251,16 @@ C++ includes: FormFactorPolyhedron.h
 %feature("docstring")  FormFactorPolygonalPrism::FormFactorPolygonalPrism "FormFactorPolygonalPrism::FormFactorPolygonalPrism(double height)
 ";
 
+%feature("docstring")  FormFactorPolygonalPrism::bottomZ "double FormFactorPolygonalPrism::bottomZ(const IRotation &rotation) const override final
+
+Returns the z-coordinate of the lowest point in this shape after a given rotation. 
+";
+
+%feature("docstring")  FormFactorPolygonalPrism::topZ "double FormFactorPolygonalPrism::topZ(const IRotation &rotation) const override final
+
+Returns the z-coordinate of the lowest point in this shape after a given rotation. 
+";
+
 %feature("docstring")  FormFactorPolygonalPrism::evaluate_for_q "complex_t FormFactorPolygonalPrism::evaluate_for_q(cvector_t q) const override final
 
 Returns the form factor F(q) of this polyhedron, respecting the offset height/2. 
@@ -4348,9 +4318,19 @@ C++ includes: FormFactorPolyhedron.h
 %feature("docstring")  FormFactorPolyhedron::FormFactorPolyhedron "FormFactorPolyhedron::FormFactorPolyhedron()
 ";
 
+%feature("docstring")  FormFactorPolyhedron::bottomZ "double FormFactorPolyhedron::bottomZ(const IRotation &rotation) const override final
+
+Returns the z-coordinate of the lowest point in this shape after a given rotation. 
+";
+
+%feature("docstring")  FormFactorPolyhedron::topZ "double FormFactorPolyhedron::topZ(const IRotation &rotation) const override final
+
+Returns the z-coordinate of the lowest point in this shape after a given rotation. 
+";
+
 %feature("docstring")  FormFactorPolyhedron::evaluate_for_q "complex_t FormFactorPolyhedron::evaluate_for_q(cvector_t q) const override final
 
-Returns the form factor F(q) of this polyhedron, respecting the offset z_origin. 
+Returns the form factor F(q) of this polyhedron, respecting the offset z_bottom. 
 ";
 
 %feature("docstring")  FormFactorPolyhedron::evaluate_centered "complex_t FormFactorPolyhedron::evaluate_centered(cvector_t q) const
@@ -5381,7 +5361,7 @@ Returns the negative of the second order derivative in q space around q=0.
 // File: classFTDistribution1DTriangle.xml
 %feature("docstring") FTDistribution1DTriangle "
 
-Triangle IFTDistribution1D [1-|x|/omega if |x|<omega, and 0 otherwise]; its Fourier transform evaluate(q) is a squared sinc function starting at evaluate(0)=1.
+Triangle  IFTDistribution1D [1-|x|/omega if |x|<omega, and 0 otherwise]; its Fourier transform evaluate(q) is a squared sinc function starting at evaluate(0)=1.
 
 C++ includes: FTDistributions1D.h
 ";
@@ -6372,16 +6352,6 @@ C++ includes: IComputation.h
 ";
 
 
-// File: classIcosahedron.xml
-%feature("docstring") Icosahedron "";
-
-%feature("docstring")  Icosahedron::Icosahedron "Icosahedron::Icosahedron(double edge)
-";
-
-%feature("docstring")  Icosahedron::~Icosahedron "Icosahedron::~Icosahedron()
-";
-
-
 // File: classIdentityRotation.xml
 %feature("docstring") IdentityRotation "";
 
@@ -6894,7 +6864,7 @@ In contrast to the generic  IFormFactor, a Born form factor does not depend on t
 C++ includes: IFormFactorBorn.h
 ";
 
-%feature("docstring")  IFormFactorBorn::IFormFactorBorn "IFormFactorBorn::IFormFactorBorn()
+%feature("docstring")  IFormFactorBorn::IFormFactorBorn "IFormFactorBorn::IFormFactorBorn()=default
 ";
 
 %feature("docstring")  IFormFactorBorn::~IFormFactorBorn "IFormFactorBorn::~IFormFactorBorn() override
@@ -13049,9 +13019,6 @@ true if face has a perpedicular two-fold symmetry axis
 %feature("docstring")  PolyhedralFace::area "double PolyhedralFace::area() const
 ";
 
-%feature("docstring")  PolyhedralFace::center "kvector_t PolyhedralFace::center() const
-";
-
 %feature("docstring")  PolyhedralFace::pyramidalVolume "double PolyhedralFace::pyramidalVolume() const
 ";
 
@@ -13462,26 +13429,6 @@ C++ includes: PyFittingCallbacks.h
 ";
 
 
-// File: classPyramid3.xml
-%feature("docstring") Pyramid3 "";
-
-%feature("docstring")  Pyramid3::Pyramid3 "Pyramid3::Pyramid3(double length, double height, double alpha)
-";
-
-%feature("docstring")  Pyramid3::~Pyramid3 "Pyramid3::~Pyramid3()
-";
-
-
-// File: classPyramid6.xml
-%feature("docstring") Pyramid6 "";
-
-%feature("docstring")  Pyramid6::Pyramid6 "Pyramid6::Pyramid6(double length, double height, double alpha)
-";
-
-%feature("docstring")  Pyramid6::~Pyramid6 "Pyramid6::~Pyramid6()
-";
-
-
 // File: classQSpecScan.xml
 %feature("docstring") QSpecScan "
 
@@ -16587,16 +16534,6 @@ C++ includes: TransformationsBuilder.h
 ";
 
 
-// File: classTriangle.xml
-%feature("docstring") Triangle "";
-
-%feature("docstring")  Triangle::Triangle "Triangle::Triangle(double length, double z)
-";
-
-%feature("docstring")  Triangle::~Triangle "Triangle::~Triangle()
-";
-
-
 // File: classTriangularRippleBuilder.xml
 %feature("docstring") TriangularRippleBuilder "
 
@@ -16612,16 +16549,6 @@ C++ includes: RipplesBuilder.h
 ";
 
 
-// File: classTruncatedCube.xml
-%feature("docstring") TruncatedCube "";
-
-%feature("docstring")  TruncatedCube::TruncatedCube "TruncatedCube::TruncatedCube(double length, double removed_length)
-";
-
-%feature("docstring")  TruncatedCube::~TruncatedCube "TruncatedCube::~TruncatedCube()
-";
-
-
 // File: classTruncatedEllipsoid.xml
 %feature("docstring") TruncatedEllipsoid "";
 
@@ -17085,10 +17012,10 @@ C++ includes: WavevectorInfo.h
 ";
 
 
-// File: classConvolve_1_1Workspace.xml
+// File: classFourierTransform_1_1Workspace.xml
 
 
-// File: classFourierTransform_1_1Workspace.xml
+// File: classConvolve_1_1Workspace.xml
 
 
 // File: classZLimits.xml
@@ -17118,172 +17045,184 @@ C++ includes: ZLimits.h
 ";
 
 
-// File: namespace_0d103.xml
+// File: namespace_0d104.xml
 
 
-// File: namespace_0d105.xml
+// File: namespace_0d106.xml
 
 
-// File: namespace_0d107.xml
+// File: namespace_0d108.xml
 
 
-// File: namespace_0d111.xml
+// File: namespace_0d112.xml
 
 
 // File: namespace_0d12.xml
 
 
-// File: namespace_0d126.xml
+// File: namespace_0d127.xml
 
 
-// File: namespace_0d135.xml
+// File: namespace_0d136.xml
 
 
-// File: namespace_0d140.xml
+// File: namespace_0d141.xml
 
 
-// File: namespace_0d149.xml
+// File: namespace_0d150.xml
 
 
-// File: namespace_0d151.xml
+// File: namespace_0d152.xml
 
 
-// File: namespace_0d155.xml
+// File: namespace_0d156.xml
 
 
 // File: namespace_0d18.xml
 
 
-// File: namespace_0d191.xml
+// File: namespace_0d192.xml
 
 
 // File: namespace_0d20.xml
 
 
-// File: namespace_0d226.xml
+// File: namespace_0d227.xml
 
 
-// File: namespace_0d234.xml
+// File: namespace_0d235.xml
 
 
-// File: namespace_0d240.xml
+// File: namespace_0d241.xml
 
 
-// File: namespace_0d244.xml
+// File: namespace_0d245.xml
 
 
-// File: namespace_0d294.xml
+// File: namespace_0d295.xml
 
 
-// File: namespace_0d303.xml
+// File: namespace_0d304.xml
 
 
-// File: namespace_0d311.xml
+// File: namespace_0d312.xml
 
 
-// File: namespace_0d315.xml
+// File: namespace_0d316.xml
 
 
-// File: namespace_0d317.xml
+// File: namespace_0d318.xml
 
 
 // File: namespace_0d32.xml
 
 
-// File: namespace_0d329.xml
+// File: namespace_0d330.xml
 
 
-// File: namespace_0d335.xml
+// File: namespace_0d336.xml
 
 
-// File: namespace_0d356.xml
+// File: namespace_0d357.xml
 
 
-// File: namespace_0d360.xml
+// File: namespace_0d361.xml
 
 
-// File: namespace_0d362.xml
+// File: namespace_0d363.xml
 
 
-// File: namespace_0d364.xml
+// File: namespace_0d365.xml
 
 
-// File: namespace_0d374.xml
+// File: namespace_0d375.xml
 
 
-// File: namespace_0d386.xml
+// File: namespace_0d387.xml
 
 
-// File: namespace_0d390.xml
+// File: namespace_0d391.xml
 
 
 // File: namespace_0d40.xml
 
 
-// File: namespace_0d402.xml
+// File: namespace_0d403.xml
 
 
-// File: namespace_0d408.xml
+// File: namespace_0d409.xml
 
 
-// File: namespace_0d413.xml
+// File: namespace_0d414.xml
 
 
-// File: namespace_0d415.xml
+// File: namespace_0d416.xml
 
 
-// File: namespace_0d419.xml
+// File: namespace_0d42.xml
 
 
-// File: namespace_0d42.xml
+// File: namespace_0d420.xml
 
 
-// File: namespace_0d421.xml
+// File: namespace_0d422.xml
 
 
-// File: namespace_0d431.xml
+// File: namespace_0d432.xml
 
 
-// File: namespace_0d444.xml
+// File: namespace_0d445.xml
 
 
-// File: namespace_0d453.xml
+// File: namespace_0d454.xml
 
 
-// File: namespace_0d455.xml
+// File: namespace_0d456.xml
 
 
-// File: namespace_0d489.xml
+// File: namespace_0d490.xml
 
 
-// File: namespace_0d496.xml
+// File: namespace_0d497.xml
 
 
-// File: namespace_0d534.xml
+// File: namespace_0d517.xml
 
 
-// File: namespace_0d542.xml
+// File: namespace_0d525.xml
 
 
-// File: namespace_0d544.xml
+// File: namespace_0d527.xml
 
 
-// File: namespace_0d546.xml
+// File: namespace_0d529.xml
 
 
 // File: namespace_0d6.xml
 
 
-// File: namespace_0d630.xml
+// File: namespace_0d613.xml
+
+
+// File: namespace_0d617.xml
+
+
+// File: namespace_0d641.xml
 
 
-// File: namespace_0d634.xml
+// File: namespace_0d98.xml
 
 
-// File: namespace_0d658.xml
+// File: namespacealgo.xml
+%feature("docstring")  algo::min_value "double algo::min_value(const Iterator &begin, const Iterator &end, const Evaluator &evaluate)
 
+Returns the minimum value of function evaluate as applied to the elements of an iterator range. 
+";
+
+%feature("docstring")  algo::max_value "double algo::max_value(const Iterator &begin, const Iterator &end, const Evaluator &evaluate)
 
-// File: namespace_0d97.xml
+Returns the maximum value of function evaluate as applied to the elements of an iterator range. 
+";
 
 
 // File: namespaceArrayUtils.xml
@@ -18488,6 +18427,9 @@ Helper factory function to use in  GISASSimulation. Depending on the type of det
 // File: ParticleLayout_8h.xml
 
 
+// File: Algorithms_8h.xml
+
+
 // File: BornAgainNamespace_8h.xml
 
 
@@ -20045,48 +19987,18 @@ Returns concatenated rotation (first right, then left).
 ";
 
 
-// File: AnisoPyramid_8cpp.xml
-
-
-// File: AnisoPyramid_8h.xml
-
-
-// File: BiPyramid_8cpp.xml
-
-
-// File: BiPyramid_8h.xml
-
-
 // File: Box_8cpp.xml
 
 
 // File: Box_8h.xml
 
 
-// File: Dodecahedron_8cpp.xml
-
-
-// File: Dodecahedron_8h.xml
-
-
-// File: Dot_8cpp.xml
-
-
-// File: Dot_8h.xml
-
-
 // File: DoubleEllipse_8cpp.xml
 
 
 // File: DoubleEllipse_8h.xml
 
 
-// File: Icosahedron_8cpp.xml
-
-
-// File: Icosahedron_8h.xml
-
-
 // File: IShape_8cpp.xml
 %feature("docstring")  RectangleVertices "std::vector<kvector_t> RectangleVertices(double length, double width, double z)
 
@@ -20095,16 +20007,6 @@ Helper functions to construct lists of vertices
 Generate vertices of centered rectangle at height z 
 ";
 
-%feature("docstring")  TriangleVertices "std::vector<kvector_t> TriangleVertices(double length, double z)
-
-Generate vertices of centered regular triangle with vertex on x-axis at height z. 
-";
-
-%feature("docstring")  HexagonVertices "std::vector<kvector_t> HexagonVertices(double length, double z)
-
-Generate vertices of centered regular hexagon with vertex on x-axis at height z. 
-";
-
 %feature("docstring")  EllipseVertices "std::vector<kvector_t> EllipseVertices(double r_x, double r_y, double z)
 
 Generate vertices of centered ellipse with given semi-axes at height z. 
@@ -20119,34 +20021,12 @@ Helper functions to construct lists of vertices
 Generate vertices of centered rectangle at height z 
 ";
 
-%feature("docstring")  TriangleVertices "std::vector<kvector_t> TriangleVertices(double length, double z)
-
-Generate vertices of centered regular triangle with vertex on x-axis at height z. 
-";
-
-%feature("docstring")  HexagonVertices "std::vector<kvector_t> HexagonVertices(double length, double z)
-
-Generate vertices of centered regular hexagon with vertex on x-axis at height z. 
-";
-
 %feature("docstring")  EllipseVertices "std::vector<kvector_t> EllipseVertices(double r_x, double r_y, double z)
 
 Generate vertices of centered ellipse with given semi-axes at height z. 
 ";
 
 
-// File: Pyramid3_8cpp.xml
-
-
-// File: Pyramid3_8h.xml
-
-
-// File: Pyramid6_8cpp.xml
-
-
-// File: Pyramid6_8h.xml
-
-
 // File: RippleCosine_8cpp.xml
 
 
@@ -20159,18 +20039,6 @@ Generate vertices of centered ellipse with given semi-axes at height z.
 // File: RippleSawtooth_8h.xml
 
 
-// File: Triangle_8cpp.xml
-
-
-// File: Triangle_8h.xml
-
-
-// File: TruncatedCube_8cpp.xml
-
-
-// File: TruncatedCube_8h.xml
-
-
 // File: TruncatedEllipsoid_8cpp.xml
 
 
@@ -20623,27 +20491,9 @@ Creates a vector<double> as a wavevector with given wavelength and angles. Speci
 
 
 // File: Transform3D_8cpp.xml
-%feature("docstring")  BottomZ "double BottomZ(const std::vector< kvector_t > &vertices, const Transform3D &rotation)
-
-Calculates the z-coordinate of the lowest vertex after rotation. 
-";
-
-%feature("docstring")  TopZ "double TopZ(const std::vector< kvector_t > &vertices, const Transform3D &rotation)
-
-Calculates the z-coordinate of the highest vertex after rotation. 
-";
 
 
 // File: Transform3D_8h.xml
-%feature("docstring")  BottomZ "double BottomZ(const std::vector< kvector_t > &vertices, const Transform3D &rotation)
-
-Calculates the z-coordinate of the lowest vertex after rotation. 
-";
-
-%feature("docstring")  TopZ "double TopZ(const std::vector< kvector_t > &vertices, const Transform3D &rotation)
-
-Calculates the z-coordinate of the highest vertex after rotation. 
-";
 
 
 // File: Vectors3D_8h.xml
diff --git a/auto/Wrap/libBornAgainCore.py b/auto/Wrap/libBornAgainCore.py
index dfc4e8df16c5e4bb0df0ffd05e7eaf0f0b704ea6..27463c3dbd81cd40fc0a646beeddffe7b419be72 100644
--- a/auto/Wrap/libBornAgainCore.py
+++ b/auto/Wrap/libBornAgainCore.py
@@ -7851,7 +7851,7 @@ class FTDistribution1DTriangle(IFTDistribution1D):
     r"""
 
 
-    Triangle IFTDistribution1D [1-|x|/omega if |x|<omega, and 0 otherwise]; its Fourier transform evaluate(q) is a squared sinc function starting at evaluate(0)=1.
+    Triangle  IFTDistribution1D [1-|x|/omega if |x|<omega, and 0 otherwise]; its Fourier transform evaluate(q) is a squared sinc function starting at evaluate(0)=1.
 
     C++ includes: FTDistributions1D.h
 
@@ -8887,7 +8887,7 @@ class IFormFactorBorn(IFormFactor):
     def __init__(self):
         r"""
         __init__(IFormFactorBorn self) -> IFormFactorBorn
-        IFormFactorBorn::IFormFactorBorn()
+        IFormFactorBorn::IFormFactorBorn()=default
 
         """
         if self.__class__ == IFormFactorBorn:
@@ -9278,14 +9278,6 @@ class PolyhedralFace(object):
         """
         return _libBornAgainCore.PolyhedralFace_area(self)
 
-    def center(self):
-        r"""
-        center(PolyhedralFace self) -> kvector_t
-        kvector_t PolyhedralFace::center() const
-
-        """
-        return _libBornAgainCore.PolyhedralFace_center(self)
-
     def pyramidalVolume(self):
         r"""
         pyramidalVolume(PolyhedralFace self) -> double
@@ -9376,12 +9368,32 @@ class FormFactorPolyhedron(IFormFactorBorn):
         raise AttributeError("No constructor defined - class is abstract")
     __repr__ = _swig_repr
 
+    def bottomZ(self, rotation):
+        r"""
+        bottomZ(FormFactorPolyhedron self, IRotation rotation) -> double
+        double FormFactorPolyhedron::bottomZ(const IRotation &rotation) const override final
+
+        Returns the z-coordinate of the lowest point in this shape after a given rotation. 
+
+        """
+        return _libBornAgainCore.FormFactorPolyhedron_bottomZ(self, rotation)
+
+    def topZ(self, rotation):
+        r"""
+        topZ(FormFactorPolyhedron self, IRotation rotation) -> double
+        double FormFactorPolyhedron::topZ(const IRotation &rotation) const override final
+
+        Returns the z-coordinate of the lowest point in this shape after a given rotation. 
+
+        """
+        return _libBornAgainCore.FormFactorPolyhedron_topZ(self, rotation)
+
     def evaluate_for_q(self, q):
         r"""
         evaluate_for_q(FormFactorPolyhedron self, cvector_t q) -> complex_t
         complex_t FormFactorPolyhedron::evaluate_for_q(cvector_t q) const override final
 
-        Returns the form factor F(q) of this polyhedron, respecting the offset z_origin. 
+        Returns the form factor F(q) of this polyhedron, respecting the offset z_bottom. 
 
         """
         return _libBornAgainCore.FormFactorPolyhedron_evaluate_for_q(self, q)
@@ -9446,6 +9458,26 @@ class FormFactorPolygonalPrism(IFormFactorBorn):
         raise AttributeError("No constructor defined - class is abstract")
     __repr__ = _swig_repr
 
+    def bottomZ(self, rotation):
+        r"""
+        bottomZ(FormFactorPolygonalPrism self, IRotation rotation) -> double
+        double FormFactorPolygonalPrism::bottomZ(const IRotation &rotation) const override final
+
+        Returns the z-coordinate of the lowest point in this shape after a given rotation. 
+
+        """
+        return _libBornAgainCore.FormFactorPolygonalPrism_bottomZ(self, rotation)
+
+    def topZ(self, rotation):
+        r"""
+        topZ(FormFactorPolygonalPrism self, IRotation rotation) -> double
+        double FormFactorPolygonalPrism::topZ(const IRotation &rotation) const override final
+
+        Returns the z-coordinate of the lowest point in this shape after a given rotation. 
+
+        """
+        return _libBornAgainCore.FormFactorPolygonalPrism_topZ(self, rotation)
+
     def evaluate_for_q(self, q):
         r"""
         evaluate_for_q(FormFactorPolygonalPrism self, cvector_t q) -> complex_t
diff --git a/auto/Wrap/libBornAgainCore_wrap.cpp b/auto/Wrap/libBornAgainCore_wrap.cpp
index d4e6afc7c20a0f842f4e3d0e85f52751cf1628cb..fb7e4ed8af832fed6cc7bb1e70f3e019de5366d9 100644
--- a/auto/Wrap/libBornAgainCore_wrap.cpp
+++ b/auto/Wrap/libBornAgainCore_wrap.cpp
@@ -68197,29 +68197,6 @@ fail:
 }
 
 
-SWIGINTERN PyObject *_wrap_PolyhedralFace_center(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
-  PyObject *resultobj = 0;
-  PolyhedralFace *arg1 = (PolyhedralFace *) 0 ;
-  void *argp1 = 0 ;
-  int res1 = 0 ;
-  PyObject *swig_obj[1] ;
-  kvector_t result;
-  
-  if (!args) SWIG_fail;
-  swig_obj[0] = args;
-  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_PolyhedralFace, 0 |  0 );
-  if (!SWIG_IsOK(res1)) {
-    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "PolyhedralFace_center" "', argument " "1"" of type '" "PolyhedralFace const *""'"); 
-  }
-  arg1 = reinterpret_cast< PolyhedralFace * >(argp1);
-  result = ((PolyhedralFace const *)arg1)->center();
-  resultobj = SWIG_NewPointerObj((new kvector_t(static_cast< const kvector_t& >(result))), SWIGTYPE_p_BasicVector3DT_double_t, SWIG_POINTER_OWN |  0 );
-  return resultobj;
-fail:
-  return NULL;
-}
-
-
 SWIGINTERN PyObject *_wrap_PolyhedralFace_pyramidalVolume(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
   PyObject *resultobj = 0;
   PolyhedralFace *arg1 = (PolyhedralFace *) 0 ;
@@ -68499,6 +68476,72 @@ SWIGINTERN PyObject *PolyhedralFace_swiginit(PyObject *SWIGUNUSEDPARM(self), PyO
   return SWIG_Python_InitShadowInstance(args);
 }
 
+SWIGINTERN PyObject *_wrap_FormFactorPolyhedron_bottomZ(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  FormFactorPolyhedron *arg1 = (FormFactorPolyhedron *) 0 ;
+  IRotation *arg2 = 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  void *argp2 = 0 ;
+  int res2 = 0 ;
+  PyObject *swig_obj[2] ;
+  double result;
+  
+  if (!SWIG_Python_UnpackTuple(args, "FormFactorPolyhedron_bottomZ", 2, 2, swig_obj)) SWIG_fail;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_FormFactorPolyhedron, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "FormFactorPolyhedron_bottomZ" "', argument " "1"" of type '" "FormFactorPolyhedron const *""'"); 
+  }
+  arg1 = reinterpret_cast< FormFactorPolyhedron * >(argp1);
+  res2 = SWIG_ConvertPtr(swig_obj[1], &argp2, SWIGTYPE_p_IRotation,  0  | 0);
+  if (!SWIG_IsOK(res2)) {
+    SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "FormFactorPolyhedron_bottomZ" "', argument " "2"" of type '" "IRotation const &""'"); 
+  }
+  if (!argp2) {
+    SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "FormFactorPolyhedron_bottomZ" "', argument " "2"" of type '" "IRotation const &""'"); 
+  }
+  arg2 = reinterpret_cast< IRotation * >(argp2);
+  result = (double)((FormFactorPolyhedron const *)arg1)->bottomZ((IRotation const &)*arg2);
+  resultobj = SWIG_From_double(static_cast< double >(result));
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_FormFactorPolyhedron_topZ(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  FormFactorPolyhedron *arg1 = (FormFactorPolyhedron *) 0 ;
+  IRotation *arg2 = 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  void *argp2 = 0 ;
+  int res2 = 0 ;
+  PyObject *swig_obj[2] ;
+  double result;
+  
+  if (!SWIG_Python_UnpackTuple(args, "FormFactorPolyhedron_topZ", 2, 2, swig_obj)) SWIG_fail;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_FormFactorPolyhedron, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "FormFactorPolyhedron_topZ" "', argument " "1"" of type '" "FormFactorPolyhedron const *""'"); 
+  }
+  arg1 = reinterpret_cast< FormFactorPolyhedron * >(argp1);
+  res2 = SWIG_ConvertPtr(swig_obj[1], &argp2, SWIGTYPE_p_IRotation,  0  | 0);
+  if (!SWIG_IsOK(res2)) {
+    SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "FormFactorPolyhedron_topZ" "', argument " "2"" of type '" "IRotation const &""'"); 
+  }
+  if (!argp2) {
+    SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "FormFactorPolyhedron_topZ" "', argument " "2"" of type '" "IRotation const &""'"); 
+  }
+  arg2 = reinterpret_cast< IRotation * >(argp2);
+  result = (double)((FormFactorPolyhedron const *)arg1)->topZ((IRotation const &)*arg2);
+  resultobj = SWIG_From_double(static_cast< double >(result));
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
 SWIGINTERN PyObject *_wrap_FormFactorPolyhedron_evaluate_for_q(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
   PyObject *resultobj = 0;
   FormFactorPolyhedron *arg1 = (FormFactorPolyhedron *) 0 ;
@@ -68672,6 +68715,72 @@ SWIGINTERN PyObject *FormFactorPolyhedron_swigregister(PyObject *SWIGUNUSEDPARM(
   return SWIG_Py_Void();
 }
 
+SWIGINTERN PyObject *_wrap_FormFactorPolygonalPrism_bottomZ(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  FormFactorPolygonalPrism *arg1 = (FormFactorPolygonalPrism *) 0 ;
+  IRotation *arg2 = 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  void *argp2 = 0 ;
+  int res2 = 0 ;
+  PyObject *swig_obj[2] ;
+  double result;
+  
+  if (!SWIG_Python_UnpackTuple(args, "FormFactorPolygonalPrism_bottomZ", 2, 2, swig_obj)) SWIG_fail;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_FormFactorPolygonalPrism, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "FormFactorPolygonalPrism_bottomZ" "', argument " "1"" of type '" "FormFactorPolygonalPrism const *""'"); 
+  }
+  arg1 = reinterpret_cast< FormFactorPolygonalPrism * >(argp1);
+  res2 = SWIG_ConvertPtr(swig_obj[1], &argp2, SWIGTYPE_p_IRotation,  0  | 0);
+  if (!SWIG_IsOK(res2)) {
+    SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "FormFactorPolygonalPrism_bottomZ" "', argument " "2"" of type '" "IRotation const &""'"); 
+  }
+  if (!argp2) {
+    SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "FormFactorPolygonalPrism_bottomZ" "', argument " "2"" of type '" "IRotation const &""'"); 
+  }
+  arg2 = reinterpret_cast< IRotation * >(argp2);
+  result = (double)((FormFactorPolygonalPrism const *)arg1)->bottomZ((IRotation const &)*arg2);
+  resultobj = SWIG_From_double(static_cast< double >(result));
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_FormFactorPolygonalPrism_topZ(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  FormFactorPolygonalPrism *arg1 = (FormFactorPolygonalPrism *) 0 ;
+  IRotation *arg2 = 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  void *argp2 = 0 ;
+  int res2 = 0 ;
+  PyObject *swig_obj[2] ;
+  double result;
+  
+  if (!SWIG_Python_UnpackTuple(args, "FormFactorPolygonalPrism_topZ", 2, 2, swig_obj)) SWIG_fail;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_FormFactorPolygonalPrism, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "FormFactorPolygonalPrism_topZ" "', argument " "1"" of type '" "FormFactorPolygonalPrism const *""'"); 
+  }
+  arg1 = reinterpret_cast< FormFactorPolygonalPrism * >(argp1);
+  res2 = SWIG_ConvertPtr(swig_obj[1], &argp2, SWIGTYPE_p_IRotation,  0  | 0);
+  if (!SWIG_IsOK(res2)) {
+    SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "FormFactorPolygonalPrism_topZ" "', argument " "2"" of type '" "IRotation const &""'"); 
+  }
+  if (!argp2) {
+    SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "FormFactorPolygonalPrism_topZ" "', argument " "2"" of type '" "IRotation const &""'"); 
+  }
+  arg2 = reinterpret_cast< IRotation * >(argp2);
+  result = (double)((FormFactorPolygonalPrism const *)arg1)->topZ((IRotation const &)*arg2);
+  resultobj = SWIG_From_double(static_cast< double >(result));
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
 SWIGINTERN PyObject *_wrap_FormFactorPolygonalPrism_evaluate_for_q(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
   PyObject *resultobj = 0;
   FormFactorPolygonalPrism *arg1 = (FormFactorPolygonalPrism *) 0 ;
@@ -126456,7 +126565,7 @@ static PyMethodDef SwigMethods[] = {
 	 { "vector_IFormFactorPtr_t_swiginit", vector_IFormFactorPtr_t_swiginit, METH_VARARGS, NULL},
 	 { "new_IFormFactorBorn", _wrap_new_IFormFactorBorn, METH_O, "\n"
 		"new_IFormFactorBorn(PyObject * _self) -> IFormFactorBorn\n"
-		"IFormFactorBorn::IFormFactorBorn()\n"
+		"IFormFactorBorn::IFormFactorBorn()=default\n"
 		"\n"
 		""},
 	 { "delete_IFormFactorBorn", _wrap_delete_IFormFactorBorn, METH_O, "\n"
@@ -126678,11 +126787,6 @@ static PyMethodDef SwigMethods[] = {
 		"double PolyhedralFace::area() const\n"
 		"\n"
 		""},
-	 { "PolyhedralFace_center", _wrap_PolyhedralFace_center, METH_O, "\n"
-		"PolyhedralFace_center(PolyhedralFace self) -> kvector_t\n"
-		"kvector_t PolyhedralFace::center() const\n"
-		"\n"
-		""},
 	 { "PolyhedralFace_pyramidalVolume", _wrap_PolyhedralFace_pyramidalVolume, METH_O, "\n"
 		"PolyhedralFace_pyramidalVolume(PolyhedralFace self) -> double\n"
 		"double PolyhedralFace::pyramidalVolume() const\n"
@@ -126731,11 +126835,25 @@ static PyMethodDef SwigMethods[] = {
 	 { "delete_PolyhedralFace", _wrap_delete_PolyhedralFace, METH_O, "delete_PolyhedralFace(PolyhedralFace self)"},
 	 { "PolyhedralFace_swigregister", PolyhedralFace_swigregister, METH_O, NULL},
 	 { "PolyhedralFace_swiginit", PolyhedralFace_swiginit, METH_VARARGS, NULL},
+	 { "FormFactorPolyhedron_bottomZ", _wrap_FormFactorPolyhedron_bottomZ, METH_VARARGS, "\n"
+		"FormFactorPolyhedron_bottomZ(FormFactorPolyhedron self, IRotation rotation) -> double\n"
+		"double FormFactorPolyhedron::bottomZ(const IRotation &rotation) const override final\n"
+		"\n"
+		"Returns the z-coordinate of the lowest point in this shape after a given rotation. \n"
+		"\n"
+		""},
+	 { "FormFactorPolyhedron_topZ", _wrap_FormFactorPolyhedron_topZ, METH_VARARGS, "\n"
+		"FormFactorPolyhedron_topZ(FormFactorPolyhedron self, IRotation rotation) -> double\n"
+		"double FormFactorPolyhedron::topZ(const IRotation &rotation) const override final\n"
+		"\n"
+		"Returns the z-coordinate of the lowest point in this shape after a given rotation. \n"
+		"\n"
+		""},
 	 { "FormFactorPolyhedron_evaluate_for_q", _wrap_FormFactorPolyhedron_evaluate_for_q, METH_VARARGS, "\n"
 		"FormFactorPolyhedron_evaluate_for_q(FormFactorPolyhedron self, cvector_t q) -> complex_t\n"
 		"complex_t FormFactorPolyhedron::evaluate_for_q(cvector_t q) const override final\n"
 		"\n"
-		"Returns the form factor F(q) of this polyhedron, respecting the offset z_origin. \n"
+		"Returns the form factor F(q) of this polyhedron, respecting the offset z_bottom. \n"
 		"\n"
 		""},
 	 { "FormFactorPolyhedron_evaluate_centered", _wrap_FormFactorPolyhedron_evaluate_centered, METH_VARARGS, "\n"
@@ -126768,6 +126886,20 @@ static PyMethodDef SwigMethods[] = {
 		""},
 	 { "delete_FormFactorPolyhedron", _wrap_delete_FormFactorPolyhedron, METH_O, "delete_FormFactorPolyhedron(FormFactorPolyhedron self)"},
 	 { "FormFactorPolyhedron_swigregister", FormFactorPolyhedron_swigregister, METH_O, NULL},
+	 { "FormFactorPolygonalPrism_bottomZ", _wrap_FormFactorPolygonalPrism_bottomZ, METH_VARARGS, "\n"
+		"FormFactorPolygonalPrism_bottomZ(FormFactorPolygonalPrism self, IRotation rotation) -> double\n"
+		"double FormFactorPolygonalPrism::bottomZ(const IRotation &rotation) const override final\n"
+		"\n"
+		"Returns the z-coordinate of the lowest point in this shape after a given rotation. \n"
+		"\n"
+		""},
+	 { "FormFactorPolygonalPrism_topZ", _wrap_FormFactorPolygonalPrism_topZ, METH_VARARGS, "\n"
+		"FormFactorPolygonalPrism_topZ(FormFactorPolygonalPrism self, IRotation rotation) -> double\n"
+		"double FormFactorPolygonalPrism::topZ(const IRotation &rotation) const override final\n"
+		"\n"
+		"Returns the z-coordinate of the lowest point in this shape after a given rotation. \n"
+		"\n"
+		""},
 	 { "FormFactorPolygonalPrism_evaluate_for_q", _wrap_FormFactorPolygonalPrism_evaluate_for_q, METH_VARARGS, "\n"
 		"FormFactorPolygonalPrism_evaluate_for_q(FormFactorPolygonalPrism self, cvector_t q) -> complex_t\n"
 		"complex_t FormFactorPolygonalPrism::evaluate_for_q(cvector_t q) const override final\n"