Commit 719e806d authored by Quentin Khan's avatar Quentin Khan

Add FBox and tests

FBox is a class that represents a N dimensions interval. To do so, it
keeps the maximum and minimum corners of the interval.
parent c853d5e1
#ifndef SCALFMM_BOX_HPP_
#define SCALFMM_BOX_HPP_
#include <ostream>
#include "FZCurve.hpp"
/* Implements a N dimensions box
*
* \author Quentin Khan <quentin.khan@inria.fr>
*
* The box is described by two opposite corners : the maximum and
* the minimum one. All the class transformations maintain this
* predicate.
*
* \tparam Real Floating number representation.
* \tparam Dim Space dimension count.
* \tparam SpaceFillingCurve A templatize implementation of a space filling curve
*
*/
template<class _Position, template<std::size_t> class SpaceFillingCurve = FZCurve>
class FBox {
public:
/// Position type
using position_t = _Position;
/// Floating number representation
using Real = typename position_t::Real;
/// Space dimension
constexpr static const std::size_t Dim = position_t::Dim;
/// Space filling curve type
using space_filling_curve_t = SpaceFillingCurve<Dim>;
private:
position_t _c1; ///< Minimum corner
position_t _c2; ///< Maximum corner
position_t _center; ///< Center
/** Rearranges the corners to ensure the maximum-minimum predicate */
void rearrange_corners () {
for(std::size_t i = 0; i < Dim; ++i){
if(_c1[i] > _c2[i]) {
std::swap(_c1[i], _c2[i]);
}
}
_center = (_c1 + _c2) / 2;
}
public:
/// Accessor for the minimum corner
const position_t& c1 = _c1;
/// Accessor for the maximum corner
const position_t& c2 = _c2;
/** Builds an empty box at the origin */
FBox() = default;
/** Copies an existing box */
FBox(const FBox& other) : _c1(other._c1), _c2(other._c2), _center(other._center) {}
/** Copies an other box */
FBox& operator=(const FBox& other) {
this->_c1 = other._c1;
this->_c2 = other._c2;
this->_center = other._center;
return *this;
}
/** Builds a cube from the lower corner and its side length
*
* \param min_corner The lowest corner
* \param side_length The cube's side length
**/
FBox(const position_t& min_corner, Real side_length) :
_c1(min_corner),
_c2(min_corner)
{
if(side_length < 0) {
side_length = - side_length;
}
for(auto&& d : _c2) {
d += side_length;
}
_center = (_c1 + _c2) / 2;
}
/** Builds a box from two corners
*
* The maximum and minimum corners are deduced from the given corners.
*
* \param corner_1 The first corner.
* \param corner_2 The second corner.
*/
FBox(const position_t& corner_1, const position_t& corner_2) : _c1(corner_1), _c2(corner_2) {
rearrange_corners();
}
/** Changes the box corners
*
* The maximum and minimum corners are deduced from the given corners.
*
* \param corner_1 The first corner.
* \param corner_2 The second corner.
*/
void set(const position_t& corner_1, const position_t& corner_2) {
_c1 = corner_1;
_c2 = corner_2;
rearrange_corners();
}
/** Checks whether a position is within the box bounds
*
* \param p The position to check.
*/
bool contains(const position_t& p) const {
for(std::size_t i = 0; i < Dim; i++) {
if(p[i] < _c1[i] || p[i] > _c2[i]) {
return false;
}
}
return true;
}
/** Checks whether an object's position is within the box bounds
*
* The object must implement a `position_t position() const;` method.
*
* \tparam T The object type.
* \param obj The object which position to check.
*/
template<class T>
bool contains(const T& obj) const {
return contains(obj.position());
}
/** Accessor for the box center */
const position_t& center() const {
return _center;
}
/** Accessor for the box corners
*
* The corners are numbered using a space filling curve.
*
* \param idx The corner index.
* \return The idx'th corner.
*/
position_t corner(std::size_t idx) const {
position_t c;
std::size_t i = 0;
for(bool choice : space_filling_curve_t::position(idx)) {
c[i] = choice ? _c2[i] : _c1[i];
++i;
}
return c;
}
/** Setter for the corners
*
* Moves a corner to a new position and modifies the relevant other
* ones. The corners are numbered using a space filling curve.
*
* \param idx The moved corner index.
* \param pos The new corner position.
*/
void corner(std::size_t idx, const position_t& pos) {
std::size_t i = 0;
for(bool choice : space_filling_curve_t::position(idx)) {
if(choice) {
_c2[i] = pos[i];
} else {
_c1[i] = pos[i];
}
++i;
}
rearrange_corners();
}
/** Sums the corners of two boxes */
FBox operator+(const FBox& other) const {
return FBox(_c1 + other._c1, _c2 + other._c2);
}
/** Tests two boxes equality */
bool operator==(const FBox& other) const {
return c1 == other.c1 && c2 == other.c2;
}
/** Tests two boxes inequality */
bool operator!=(const FBox& other) const {
return ! this->operator==(other);
}
friend std::ostream& operator<<(std::ostream& os, const FBox& box) {
return os << "[" << box.c1 << "," << box.c2 << "]";
}
};
#endif
#include <cassert>
#include <cmath>
#include <iostream>
#include "test_Abstract.hpp"
#include "Utils/FPoint.hpp"
#include "Utils/FConstFuncs.hpp"
#include "Adaptive/new/FBox.hpp"
namespace scalfmm {
namespace tests {
struct test_Box : public test_Abstract<test_Box> {
constexpr static const std::size_t Dim = 3;
using position_t = FPoint<double, Dim>;
using box_t = FBox<position_t>;
void run_all() {
RUN(test_constructor_1 );
RUN(test_constructor_2 );
RUN(test_constructor_3 );
RUN(test_constructor_4 );
RUN(test_constructor_5 );
RUN(test_assign_copy_operator);
RUN(test_assign_move_operator);
RUN(test_center );
RUN(test_corner );
RUN(test_contains );
}
/// Test default constructor
void test_constructor_1() {
box_t b;
position_t p {0,0,0};
assert(b.c1 == p);
assert(b.c2 == p);
}
/// Test constructor with minimum and maximum corners
void test_constructor_2() {
position_t p1 = {0,0,0};
position_t p2 = {100,100,100};
box_t b(p1, p2);
assert(b.c1 == p1);
assert(b.c2 == p2);
}
/// Test constructor with initializer lists
void test_constructor_3() {
position_t p {0,0,0};
box_t d({0,0,0},{0,0,0});
assert(d.c1 == p);
assert(d.c2 == p);
}
/// Test copy constructor
void test_constructor_4() {
position_t p1 = {0,0,0};
position_t p2 = {100,100,100};
box_t c(p1, p2);
box_t e(c);
assert(c.c1 == e.c1);
assert(c.c2 == e.c2);
// test deep copy
c.set(p2, p1 + position_t{-1,0,0});
assert(c.c1 != e.c1);
assert(c.c2 == e.c2);
}
void test_constructor_5() {
position_t p1 = {0,100,0};
position_t p2 = {100,0,100};
box_t c(p1, p2);
for(std::size_t i = 0; i < Dim; ++i) {
assert(Ffeq(c.c1[i], std::fmin(p1[i], p2[i])));
assert(Ffeq(c.c2[i], std::fmax(p1[i], p2[i])));
}
}
void test_assign_copy_operator() {
position_t p1 = {0,0,0};
position_t p2 = {100,100,100};
box_t a(p1, p2);
box_t b;
b = a;
assert(a.c1 == b.c1);
assert(a.c2 == b.c2);
assert(a.center() == b.center());
}
void test_assign_move_operator() {
position_t p1 = {0,0,0};
position_t p2 = {100,100,100};
box_t a(p1, p2);
box_t b;
b = std::move(a);
assert(a.c1 == b.c1);
assert(a.c2 == b.c2);
}
void test_center() {
position_t p1 = {0,10,50};
position_t p2 = {100,100,100};
position_t p3 = {50,55,75};
box_t b{p1, p2};
assert(b.center() == p3);
// Test center after copy
box_t c(b);
assert(c.center() == p3);
b.set({20,30,40}, {120, 130, 140});
assert(b.center() == position_t(70, 80, 90));
}
void test_corner() {
position_t p0 = { 0, 0, 0};
position_t p1 = { 0, 0,100};
position_t p2 = { 0,100, 0};
position_t p3 = { 0,100,100};
position_t p4 = {100, 0, 0};
position_t p5 = {100, 0,100};
position_t p6 = {100,100, 0};
position_t p7 = {100,100,100};
box_t b = {p0, p7};
assert(b.corner(0) == p0);
assert(b.corner(1) == p1);
assert(b.corner(2) == p2);
assert(b.corner(3) == p3);
assert(b.corner(4) == p4);
assert(b.corner(5) == p5);
assert(b.corner(6) == p6);
assert(b.corner(7) == p7);
b.corner(0, {1,1,1});
assert(b.corner(0) == b.c1);
assert(b.corner(0) == position_t(1,1,1));
b.corner(0, {0,0,0});
b.corner(2, {20, 80, 30});
assert(b.c1 == position_t(20, 0, 30));
assert(b.c2 == position_t(100, 80, 100));
b.corner(5, {0, 20, 25});
assert(b.c1 == position_t(0, 20, 25));
assert(b.c2 == position_t(20, 80, 30));
}
struct DummyObject {
position_t p;
position_t position() const {return p;}
};
void test_contains() {
box_t b = {{0,0,0}, {100,100,100}};
assert(b.contains(b.c1));
assert(b.contains(b.c2));
assert(b.contains({50,50,50}));
assert(! b.contains({150,50,50}));
assert(! b.contains({50,150,50}));
assert(! b.contains({50,50,150}));
assert(! b.contains({50,150,150}));
assert(! b.contains({150,150,50}));
assert(! b.contains({150,50,150}));
assert(! b.contains({150,150,150}));
assert(b.contains(DummyObject {{ 50, 50, 50}}));
assert(! b.contains(DummyObject{{150, 50, 50}}));
assert(! b.contains(DummyObject{{ 50,150, 50}}));
assert(! b.contains(DummyObject{{ 50, 50,150}}));
assert(! b.contains(DummyObject{{ 50,150,150}}));
assert(! b.contains(DummyObject{{150,150, 50}}));
assert(! b.contains(DummyObject{{150, 50,150}}));
assert(! b.contains(DummyObject{{150,150,150}}));
}
};
}
}
int main() {
scalfmm::tests::test_Box().run_all();
}
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