Commit 3715313f authored by Dennis Gläser's avatar Dennis Gläser
Browse files

Merge branch 'feature/bbox-merge' into 'master'

add functionality for bounding boxes

See merge request tools/frackit!198
parents c4195c3b d89e2e20
Pipeline #2731 passed with stages
in 11 minutes and 13 seconds
......@@ -24,6 +24,9 @@
#ifndef FRACKIT_GEOMETRY_BOUNDING_BOX_HH
#define FRACKIT_GEOMETRY_BOUNDING_BOX_HH
#include <array>
#include <cmath>
#include "box.hh"
namespace Frackit {
......@@ -32,14 +35,76 @@ namespace Frackit {
* \ingroup Geometry
* \brief Class that implements axis-aligned boxes in 3d space.
* \tparam CT The type used for coordinates
* \note For now this is only an alias for the Box class. But, we introduce
* this distinction here to be able to uniquely define interfaces where
* axis-aligned boxes are required such that they can easily identified
* once the Box class is made more general (to also describe non-axis-aligned
* boxes or maybe general hexahedra).
* \note This class is different from the Box class in the sense that it
* is guaranteed that the box is axis-aligned.
*/
template<class CT>
using BoundingBox = Box<CT>;
class BoundingBox : public Box<CT>
{
using ThisType = BoundingBox<CT>;
using ParentType = Box<CT>;
public:
//! pull up base's constructors
using ParentType::ParentType;
//! construction from a box
BoundingBox(const Box<CT>& box)
: ParentType(box)
{}
//! default constructor (zero volume bounding box)
BoundingBox()
: ParentType(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
{}
/*!
* \brief Merge bounding box with another.
*/
BoundingBox<CT>& operator+=(const BoundingBox<CT>& other)
{
const auto coords = getMergedCoords(other);
this->setXMin(coords[0][0]); this->setXMax(coords[0][1]);
this->setYMin(coords[1][0]); this->setYMax(coords[1][1]);
this->setZMin(coords[2][0]); this->setZMax(coords[2][1]);
return *this;
}
/*!
* \brief Merge bounding box with another.
*/
BoundingBox<CT> operator+(const BoundingBox<CT>& other) const
{
const auto coords = getMergedCoords(other);
return BoundingBox<CT>(coords[0][0], coords[1][0], coords[2][0],
coords[0][1], coords[1][1], coords[2][1]);
}
//! Return the name of this geometry.
std::string name() const override { return "BoundingBox"; }
private:
//! returns the coordinates after merge with other bounding box
std::array< std::array<CT, 2>, 3>
getMergedCoords(const BoundingBox<CT>& other) const
{
using std::min; using std::max;
std::array<CT, 2> xCoords;
xCoords[0] = min(this->xMin(), other.xMin());
xCoords[1] = max(this->xMax(), other.xMax());
std::array<CT, 2> yCoords;
yCoords[0] = min(this->yMin(), other.yMin());
yCoords[1] = max(this->yMax(), other.yMax());
std::array<CT, 2> zCoords;
zCoords[0] = min(this->zMin(), other.zMin());
zCoords[1] = max(this->zMax(), other.zMax());
return {std::move(xCoords), std::move(yCoords), std::move(zCoords)};
}
};
} // end namespace Frackit
......
......@@ -205,6 +205,16 @@ public:
return contains(p, eps);
}
protected:
//! Setter functions for updating coordinates
void setXMin(ctype val) { xMin_ = val; }
void setYMin(ctype val) { yMin_ = val; }
void setZMin(ctype val) { zMin_ = val; }
void setXMax(ctype val) { xMax_ = val; }
void setYMax(ctype val) { yMax_ = val; }
void setZMax(ctype val) { zMax_ = val; }
private:
ctype xMin_; ctype xMax_;
ctype yMin_; ctype yMax_;
......
install(FILES
boundingbox.hh
box.hh
brepwrapper.hh
brepwrappers.hh
......
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=4 sw=4 sts=4:
/*****************************************************************************
* See the file COPYING for full copying permissions. *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*****************************************************************************/
#ifndef FRACKIT_PYTHON_GEOMETRY_BOUNDING_BOX_HH
#define FRACKIT_PYTHON_GEOMETRY_BOUNDING_BOX_HH
#include <pybind11/pybind11.h>
#include <pybind11/operators.h>
#include <frackit/geometry/box.hh>
#include <frackit/geometry/boundingbox.hh>
#include "registerdimensionproperties.hh"
namespace Frackit::Python {
namespace py = pybind11;
template<class ctype>
void registerBoundingBox(py::module& module)
{
using namespace py::literals;
using Box = Box<ctype>;
using BoundingBox = BoundingBox<ctype>;
using Point = typename BoundingBox::Point;
// define constructors
py::class_<BoundingBox, Box> cls(module, "BoundingBox");
cls.def(py::init<ctype, ctype, ctype, ctype, ctype, ctype>(),
"xmin"_a, "ymin"_a, "zmin"_a, "xmax"_a, "ymax"_a, "zmax"_a);
cls.def(py::init<const Point&, const Point&>(), "firstCorner"_a, "secondCorner"_a);
cls.def(py::init<const Box&>(), "box"_a);
// dimensionality properties
registerDimensionProperties(cls);
cls.def("name", &BoundingBox::name, "name of the geometry class");
cls.def("__repr__", [&] (const BoundingBox& b) { return "Frackit::BoundingBox"; });
cls.def(py::self += BoundingBox());
cls.def(py::self + BoundingBox());
}
} // end namespace Frackit::Python
#endif
......@@ -40,7 +40,7 @@ void registerBox(py::module& module)
py::class_<Box, Geometry> cls(module, "Box");
cls.def(py::init<ctype, ctype, ctype, ctype, ctype, ctype>(),
"xmin"_a, "ymin"_a, "zmin"_a, "xmax"_a, "ymax"_a, "zmax"_a);
cls.def(py::init<const Point&, Point>(), "firstCorner"_a, "secondCorner"_a);
cls.def(py::init<const Point&, const Point&>(), "firstCorner"_a, "secondCorner"_a);
// dimensionality properties
registerDimensionProperties(cls);
......
......@@ -24,6 +24,7 @@
#include <pybind11/pybind11.h>
#include <frackit/geometry/box.hh>
#include <frackit/geometry/boundingbox.hh>
#include <frackit/geometry/circle.hh>
#include <frackit/geometry/cylinder.hh>
#include <frackit/geometry/cylindersurface.hh>
......@@ -42,7 +43,7 @@
namespace Frackit::Python {
template<class Geo>
Frackit::Box<typename CoordinateTypeTraits<Geo>::type>
Frackit::BoundingBox<typename CoordinateTypeTraits<Geo>::type>
getBoundingBox(const Geo& geo)
{ return Frackit::getBoundingBox( getUnwrappedShape(geo) ); }
......
......@@ -45,6 +45,11 @@ namespace Frackit {
*
* \tparam ctype The type used for coordinates
* \tparam T The traits class containing the distributions
* \todo TODO: This actually implements a sampler for axis-aligned
* boxes (bounding boxes). Currently, the box class implements
* an axis-aligned box. Once this is generalized, we should
* move this to "BoundingBoxSampler" and implement a more generic
* sampler in this class.
*/
template<class ctype, class T>
class BoxPointSampler
......
......@@ -25,6 +25,7 @@
#define FRACKIT_MAKE_POINT_SAMPLER_HH
#include <frackit/geometry/box.hh>
#include <frackit/geometry/boundingbox.hh>
#include <frackit/geometry/cylinder.hh>
#include <frackit/geometry/hollowcylinder.hh>
......@@ -79,18 +80,30 @@ makePointSampler(const HollowCylinder<ctype>& hollowCylinder)
/*!
* \ingroup Sampling
* \brief Overload of the free function to create point
* samplers for boxes.
* samplers for bounding boxes.
*/
template< class Traits, class ctype >
BoxPointSampler< ctype, Traits >
makePointSampler(const Box<ctype>& box)
makePointSampler(const BoundingBox<ctype>& bbox)
{
using Sampler = BoxPointSampler<ctype, Traits>;
return Sampler( typename Traits::DistributionBase1(box.xMin(), box.xMax()),
typename Traits::DistributionBase2(box.yMin(), box.yMax()),
typename Traits::DistributionBase3(box.zMin(), box.zMax()) );
return Sampler( typename Traits::DistributionBase1(bbox.xMin(), bbox.xMax()),
typename Traits::DistributionBase2(bbox.yMin(), bbox.yMax()),
typename Traits::DistributionBase3(bbox.zMin(), bbox.zMax()) );
}
/*!
* \ingroup Sampling
* \brief Overload of the free function to create point
* samplers for boxes.
* \todo TODO: Once Box class is made more generic, this
* should be adapted.
*/
template< class Traits, class ctype >
BoxPointSampler< ctype, Traits >
makePointSampler(const Box<ctype>& box)
{ return makePointSampler<Traits>(BoundingBox<ctype>(box)); }
} // end namespace Frackit
#endif // FRACKIT_MAKE_POINT_SAMPLER_HH
......@@ -37,6 +37,7 @@
#include <frackit/python/geometry/hollowcylinder.hh>
#include <frackit/python/geometry/cylindersurface.hh>
#include <frackit/python/geometry/box.hh>
#include <frackit/python/geometry/boundingbox.hh>
#include <frackit/python/geometry/sphere.hh>
#include <frackit/python/geometry/brepwrappers.hh>
#include <frackit/python/geometry/emptyintersection.hh>
......@@ -73,6 +74,7 @@ PYBIND11_MODULE(_geometry, module)
Frackit::Python::registerCylinder<double>(module);
Frackit::Python::registerHollowCylinder<double>(module);
Frackit::Python::registerBox<double>(module);
Frackit::Python::registerBoundingBox<double>(module);
Frackit::Python::registerSphere<double>(module);
// wrapper classes for OpenCascade BRep shapes
......
......@@ -24,6 +24,9 @@ class BoxPointSampler:
from frackit.geometry import Point_3
return Point_3(x, y, z)
class BoundingBoxPointSampler(BoxPointSampler):
"""Class to sample random points within a bounding box."""
pass
class CylinderPointSampler:
"""Class to sample random points within a cylinder."""
......@@ -118,6 +121,13 @@ def makePointSampler(geometry,
samplerZ = samplerCreatorBase3(geometry.zMin(), geometry.zMax())
return BoxPointSampler(samplerX, samplerY, samplerZ)
# bounding box point sampler
if geometry.name() == "BoundingBox":
samplerX = samplerCreatorBase1(geometry.xMin(), geometry.xMax())
samplerY = samplerCreatorBase2(geometry.yMin(), geometry.yMax())
samplerZ = samplerCreatorBase3(geometry.zMin(), geometry.zMax())
return BoundingBoxPointSampler(samplerX, samplerY, samplerZ)
# cylinder point sampler
if geometry.name() == "Cylinder":
import math
......@@ -134,7 +144,7 @@ def makePointSampler(geometry,
samplerZ = samplerCreatorBase3(0.0, geometry.height())
return CylinderPointSampler(geometry.fullCylinder(), samplerR2, samplerPhi, samplerZ)
raise NotImplementedError("No point sample creation formula implemented for provided geometry: " + geometry.name())
raise NotImplementedError("No point sampler creation formula implemented for provided geometry: " + geometry.name())
......
......@@ -19,6 +19,7 @@
#include <pybind11/pybind11.h>
#include <frackit/geometry/box.hh>
#include <frackit/geometry/boundingbox.hh>
#include <frackit/geometry/cylinder.hh>
#include <frackit/geometry/hollowcylinder.hh>
#include <frackit/sampling/makeuniformpointsampler.hh>
......@@ -35,6 +36,11 @@ auto makeUniformBoxPointSampler(const Frackit::Box<ctype>& box)
-> decltype(Frackit::makeUniformPointSampler(box))
{ return Frackit::makeUniformPointSampler(box); }
template<class ctype>
auto makeUniformBoundingBoxPointSampler(const Frackit::BoundingBox<ctype>& box)
-> decltype(Frackit::makeUniformPointSampler(box))
{ return Frackit::makeUniformPointSampler(box); }
template<class ctype>
auto makeUniformCylinderPointSampler(const Frackit::Cylinder<ctype>& cylinder)
-> decltype(Frackit::makeUniformPointSampler(cylinder))
......@@ -51,6 +57,8 @@ void registerPointSamplerCreatorFunctions(pybind11::module& module)
using namespace pybind11::literals;
module.def("makeUniformPointSampler", &makeUniformBoxPointSampler<ctype>,
"box"_a, "returns a uniform point sampler on the given box");
module.def("makeUniformPointSampler", &makeUniformBoundingBoxPointSampler<ctype>,
"boundingBox"_a, "returns a uniform point sampler on the given bounding box");
module.def("makeUniformPointSampler", &makeUniformCylinderPointSampler<ctype>,
"cylinder"_a, "returns a uniform point sampler on the given cylinder");
module.def("makeUniformPointSampler", &makeUniformHollowCylinderPointSampler<ctype>,
......
......@@ -16,5 +16,6 @@ frackit_add_test(NAME test_triangle SOURCES test_triangle.cc LABELS geometry)
frackit_add_test(NAME test_polygon SOURCES test_polygon.cc LABELS geometry)
frackit_add_test(NAME test_quadrilateral SOURCES test_quadrilateral.cc LABELS geometry)
frackit_add_test(NAME test_box SOURCES test_box.cc LABELS geometry)
frackit_add_test(NAME test_boundingbox SOURCES test_boundingbox.cc LABELS geometry)
frackit_add_test(NAME test_sphere SOURCES test_sphere.cc LABELS geometry)
frackit_add_test(NAME test_name SOURCES test_name.cc LABELS geometry)
#include <stdexcept>
#include <string>
#include <vector>
#include <cmath>
#include <frackit/geometry/point.hh>
#include <frackit/geometry/box.hh>
#include <frackit/geometry/boundingbox.hh>
//! test some functionality of bounding boxes
int main()
{
using ctype = double;
using Box = Frackit::Box<ctype>;
using BoundingBox = Frackit::BoundingBox<ctype>;
Box box(0., 0., 0., 1., 1., 1.);
const auto& a = box.corner(0);
const auto& b = box.corner(7);
// lambda to test corner equality
auto testCorners = [&box] (const BoundingBox& bbox,
const auto& p0,
const auto& p7) -> bool
{
if (!bbox.corner(0).isEqual(p0)) return false;
if (!bbox.corner(7).isEqual(p7)) return false;
return true;
};
// construction from box and corners
if (!testCorners(BoundingBox(box), a, b)) throw std::runtime_error("Corners not equal");
if (!testCorners(BoundingBox(a, b), a, b)) throw std::runtime_error("Corners not equal");
// construction from coordinates
BoundingBox bbox(a.x(), a.y(), a.z(), b.x(), b.y(), b.z());
if (!testCorners(bbox, a, b)) throw std::runtime_error("Corners not equal");
// test bbox merging
BoundingBox bbox2(-1., -1., -1., 2., 2., 2.);
BoundingBox bbox3(0.5, 0.5, 0.5, 2., 2., 2.);
const auto merge1 = bbox + bbox2;
const auto merge2 = bbox + bbox3;
if (!testCorners(merge1, bbox2.corner(0), bbox2.corner(7))) throw std::runtime_error("merge1 failed");
if (!testCorners(merge2, bbox.corner(0), bbox2.corner(7))) throw std::runtime_error("merge2 failed");
bbox += bbox3;
if (!testCorners(bbox, merge2.corner(0), merge2.corner(7))) throw std::runtime_error("in-place merge1 failed");
bbox += bbox2;
if (!testCorners(bbox, merge1.corner(0), merge1.corner(7))) throw std::runtime_error("in-place merge2 failed");
std::cout << "All tests passed" << std::endl;
return 0;
}
......@@ -9,7 +9,7 @@
#include <frackit/geometry/box.hh>
#include <frackit/precision/defaultepsilon.hh>
//! test some functionality of triangles
//! test some functionality of box
int main()
{
using ctype = double;
......
......@@ -12,6 +12,7 @@
#include <TopoDS_Compound.hxx>
#include <frackit/geometry/box.hh>
#include <frackit/geometry/boundingbox.hh>
#include <frackit/geometry/circle.hh>
#include <frackit/geometry/cylinder.hh>
#include <frackit/geometry/cylindersurface.hh>
......@@ -88,6 +89,7 @@ void test()
std::cout << i++ << ": " << geometryName(Box<ctype>(p, p + v)) << std::endl;
std::cout << i++ << ": " << geometryName(BoundingBox<ctype>(p, p + v)) << std::endl;
std::cout << i++ << ": " << geometryName(Sphere<ctype>(p, 1.0)) << std::endl;
std::cout << i++ << ": " << geometryName(Cylinder<ctype>(1.0, 1.0)) << std::endl;
std::cout << i++ << ": " << geometryName(HollowCylinder(0.5, 1.0, 1.0)) << std::endl;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment