Skip to content

Commit

Permalink
feat: use Range1D<> class for extent (#1424)
Browse files Browse the repository at this point in the history
This PR uses the later introduced `Range1D<>` class in the `Geometry/Extent` class instead of duplication of the code concerning intersection, min/max, shrinking.

As a direct consequence, the code becomes more readable, by explicitly calling `range(binValue).min()` instead of `ranges[binValue].first` when the meaning of first is not clear from the context.
  • Loading branch information
asalzburger committed Aug 15, 2022
1 parent d9117a0 commit 81e9573
Show file tree
Hide file tree
Showing 13 changed files with 626 additions and 309 deletions.
282 changes: 189 additions & 93 deletions Core/include/Acts/Geometry/Extent.hpp
Original file line number Diff line number Diff line change
@@ -1,131 +1,227 @@
// This file is part of the Acts project.
//
// Copyright (C) 2016-2018 CERN for the benefit of the Acts project
// Copyright (C) 2022 CERN for the benefit of the Acts project
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#pragma once

/// @note This file is foreseen for the `Geometry` module to replace `Extent`

#include "Acts/Definitions/Algebra.hpp"
#include "Acts/Utilities/BinningType.hpp"
#include "Acts/Utilities/Helpers.hpp"
#include "Acts/Utilities/Enumerate.hpp"
#include "Acts/Utilities/Range1D.hpp"
#include "Acts/Utilities/RangeXD.hpp"

#include <algorithm>
#include <cmath>
#include <iosfwd>
#include <limits>
#include <utility>
#include <array>
#include <bitset>
#include <ostream>
#include <vector>

namespace Acts {

using Range = std::pair<double, double>;
using Envelope = std::array<ActsScalar, 2>;
using ExtentEnvelope = std::array<std::array<ActsScalar, 2>, binValues>;

constexpr Envelope zeroEnvelope = {0., 0};
constexpr ExtentEnvelope zeroEnvelopes = {
zeroEnvelope, zeroEnvelope, zeroEnvelope, zeroEnvelope,
zeroEnvelope, zeroEnvelope, zeroEnvelope, zeroEnvelope};

// @brief Extent in space
/// A class representing the geometric extent of an object in its possbile
/// dimensions, these can be all dimensions that are described as BinningValues
///
/// The extent object can have an optional envelope in all of those values
/// @note that the consistency of the different envelopes is not checked
///
/// This is a nested struct to the GeometryObject representation
/// which can be retrieved and used for surface parsing and will
/// give you the maximal extent in 3D space/
struct Extent {
/// Possible maximal value
static constexpr double maxval = std::numeric_limits<double>::max();

/// Start value
static constexpr Range maxrange = {maxval, -maxval};

// The different ranges
std::vector<Range> ranges{(int)binValues, maxrange};

// Constructor
Extent() = default;

/// Check if it intersects
/// @param other The source Extent
/// @param bVal The binning value for the check (binValues for all)
/// @param tolerance An additional tolerance for the intersection check
bool intersects(const Extent& other, BinningValue bVal = binValues,
double tolerance = s_epsilon) {
// Helper to check
auto checkRange = [&](BinningValue bvc) -> bool {
auto& a = ranges[bvc];
auto& b = other.ranges[bvc];
return (a.second + tolerance > b.first and
a.first - tolerance < b.second);
};

// Check all
if (bVal == binValues) {
for (int ibv = 0; ibv < (int)binValues; ++ibv) {
if (checkRange((BinningValue)ibv)) {
return true;
}
}
return false;
class Extent {
public:
/// Constructor with (optional) @param envelope
Extent(const ExtentEnvelope& envelope = zeroEnvelopes);

/// Extend with a position vertex
///
/// @param vtx the vertex to be used for extending
/// @param bValues the binning values
/// @param applyEnv boolean to steer if envelope should be applied
/// @param fillHistograms is a boolean flag to steer whether the values
/// to fill this extent should be stored
void extend(const Vector3& vtx,
const std::vector<BinningValue>& bValues = s_binningValues,
bool applyEnv = true, bool fillHistograms = false);

/// Extend with a set of vectors by iterators
///
/// @param start the start iterator of the loop
/// @param end the end iterator of the loop
/// @param bValues the binning values
/// @param applyEnv boolean to steer if envelope should be applied
/// @param fillHistograms is a boolean flag to steer whether the values
/// to fill this extent should be stored
template <typename vector_iterator_t>
void extend(const vector_iterator_t& start, const vector_iterator_t& end,
const std::vector<BinningValue>& bValues = s_binningValues,
bool applyEnv = true, bool fillHistograms = false) {
for (vector_iterator_t vIt = start; vIt < end; ++vIt) {
extend(*vIt, bValues, applyEnv, fillHistograms);
}
// Check specific
return checkRange(bVal);
}

/// Extend with another extent
/// @param other is the source Extent
void extend(const Extent& other) {
for (std::size_t ir = 0; ir < other.ranges.size(); ++ir) {
ranges[ir].first = std::min(ranges[ir].first, other.ranges[ir].first);
ranges[ir].second = std::max(ranges[ir].second, other.ranges[ir].second);
/// Extend with another geometric extent, usually pushes the
/// current range to the boundaries of the rhs extent,
/// unless the current extent is already bigger.
///
/// @note the extent can also simply set an envelope
/// which then is applied to the current one
///
/// @param rhs is the other source Extent
/// @param bValues the binning values
/// @param applyEnv boolean to steer if envelope should be applied
/// on the constraint values, if only an envelope is given
/// but the value not constraint, then it is always applied
///
/// @note that the histogram values can not be filled in this call
void extend(const Extent& rhs,
const std::vector<BinningValue>& bValues = s_binningValues,
bool applyEnv = true);

/// Set a range for a dedicated binning value
///
/// @param bValue the binning identification
/// @param min the minimum parameter
/// @param max the maximum parameter
void set(BinningValue bValue, ActsScalar min, ActsScalar max);

/// (re-)Set the envelope
///
/// @param envelope new envelope to be set
void setEnvelope(const ExtentEnvelope& envelope = zeroEnvelopes);

/// Return the individual 1-dimensional range
///
/// @param bValue is the binning value to be returned
///
/// @return a one dimensional arrange
Range1D<ActsScalar>& range(BinningValue bValue);

/// Return the individual 1-dimensional range
///
/// @param bValue is the binning value to be returned
///
/// @return a one dimensional arrange
const Range1D<ActsScalar>& range(BinningValue bValue) const;

/// Return the N-dimension range
const RangeXD<binValues, ActsScalar> range() const;

/// Return an D-dimensional sub range according to the
/// the given @param binValues
template <unsigned int kSUBDIM>
RangeXD<kSUBDIM, ActsScalar> range(
const std::array<BinningValue, kSUBDIM>& binValues) const {
RangeXD<kSUBDIM, ActsScalar> rRange;
for (auto [i, v] : enumerate(binValues)) {
rRange[i] = range(v);
}
return rRange;
}

/// Return the envelope - non-const access
ExtentEnvelope& envelope();

/// Return the envelope - const access
const ExtentEnvelope& envelope() const;

/// Return the histogram store
///
/// The histogram stroe can be used for automated binning detection
const std::array<std::vector<ActsScalar>, binValues>& valueHistograms() const;

/// Access the minimum parameter
///
/// @param bValue the binning identification
ActsScalar min(BinningValue bValue) const { return m_range[bValue].min(); }

/// Access the maximum parameter
///
/// @param bValue the binning identification
ActsScalar max(BinningValue bValue) const { return m_range[bValue].max(); }

/// Access the maximum parameter
///
/// @param bValue the binning identification
ActsScalar medium(BinningValue bValue) const {
return 0.5 * (m_range[bValue].min() + m_range[bValue].max());
}

/// Contains check
///
/// @param rhs the extent that is check if it is contained
/// @param bValue is the binning value, if set to binValues
/// the check on all is done
///
/// @return true if the rhs is contained
bool contains(const Extent& rhs, BinningValue bValue = binValues) const;

/// Intersection checks
///
/// @param rhs the extent that is check for intersection
/// @param bValue is the binning value, if set to binValues
/// the check on all is done
///
/// @return true if the rhs intersects
bool intersects(const Extent& rhs, BinningValue bValue = binValues) const;

/// Constraints check
///
/// @param bValue is the binning value, if all the check on all is done
bool constrains(BinningValue bValue = binValues) const;

/// Convert to output stream for screen output
/// @param sl [in,out] The output stream
std::ostream& toStream(std::ostream& sl) const;

/// Access the minimum parameter
/// @param bval the binning identification
double& min(BinningValue bval) { return ranges[bval].first; }
private:
/// A bitset that remembers the constraint values
std::bitset<binValues> m_constrains{0};
/// The actual range store
RangeXD<binValues, ActsScalar> m_range;
/// A potential envenelope
ExtentEnvelope m_envelope = zeroEnvelopes;
/// (Optional) Value histograms for bin detection
std::array<std::vector<ActsScalar>, binValues> m_valueHistograms;
};

/// Access the minimum parameter
/// @param bval the binning identification
double min(BinningValue bval) const { return ranges[bval].first; }
inline Range1D<ActsScalar>& Acts::Extent::range(BinningValue bValue) {
return m_range[bValue];
}

/// Access the max parameter
/// @param bval the binning identification
double& max(BinningValue bval) { return ranges[bval].second; }
inline const Range1D<ActsScalar>& Acts::Extent::range(
BinningValue bValue) const {
return m_range[bValue];
}

/// Access the max parameter
/// @param bval the binning identification
double max(BinningValue bval) const { return ranges[bval].second; }
inline const RangeXD<binValues, ActsScalar> Extent::range() const {
return m_range;
}

/// Access the medium parameter
/// @param bval the binning identification
double medium(BinningValue bval) const {
return 0.5 * (ranges[bval].first + ranges[bval].second);
}
inline ExtentEnvelope& Extent::envelope() {
return m_envelope;
}

/// Access the range - always positive
/// @param bval the binning identification
double range(BinningValue bval) const {
return std::abs(ranges[bval].second - ranges[bval].first);
}
inline const ExtentEnvelope& Extent::envelope() const {
return m_envelope;
}

/// Check the vertex
/// @param vtx the Vertex to be checked
void check(const Vector3& vtx) {
// min/max value check
auto minMax = [&](BinningValue bval, double value) -> void {
ranges[bval].first = std::min(value, ranges[bval].first);
ranges[bval].second = std::max(value, ranges[bval].second);
};
// Walk through the binning parameters
for (int bval = 0; bval < binValues; ++bval) {
BinningValue bValue = static_cast<BinningValue>(bval);
minMax(bValue, VectorHelpers::cast(vtx, bValue));
}
}
};
inline const std::array<std::vector<ActsScalar>, binValues>&
Extent::valueHistograms() const {
return m_valueHistograms;
}

/// Overload of << operator for std::ostream for debug output
std::ostream& operator<<(std::ostream& sl, const Extent& ext);
std::ostream& operator<<(std::ostream& sl, const Extent& rhs);

} // namespace Acts
4 changes: 4 additions & 0 deletions Core/include/Acts/Utilities/BinningType.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ enum BinningValue : int {
binValues = 9
};

/// @brief static list of all binning values
static std::vector<BinningValue> s_binningValues = {
binX, binY, binZ, binR, binPhi, binRPhi, binH, binEta, binMag};

/// @brief screen output option
inline const std::vector<std::string>& binningValueNames() {
static const std::vector<std::string> _binningValueNames = {
Expand Down
48 changes: 48 additions & 0 deletions Core/include/Acts/Utilities/Enumerate.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// This file is part of the Acts project.
//
// Copyright (C) 2022 CERN for the benefit of the Acts project
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#pragma once

namespace Acts {
/// Helper utility to allow indexed enumeration with structured binding
///
/// Usage:
///
/// for (auto [ i, value ] = enumerate(container) ) { ... };
///
/// with 'container' any stl-like container
///
template <typename container_type,
typename container_type_iter =
decltype(std::begin(std::declval<container_type>())),
typename = decltype(std::end(std::declval<container_type>()))>
constexpr auto enumerate(container_type &&iterable) {
struct iterator {
size_t i;
container_type_iter iter;

bool operator!=(const iterator &rhs) const { return iter != rhs.iter; }

/** Increase index and iterator at once */
void operator++() {
++i;
++iter;
}

/** Tie them together for returning */
auto operator*() const { return std::tie(i, *iter); }
};
struct iterable_wrapper {
container_type iterable;
auto begin() { return iterator{0, std::begin(iterable)}; }
auto end() { return iterator{0, std::end(iterable)}; }
};
return iterable_wrapper{std::forward<container_type>(iterable)};
}

} // namespace Acts

0 comments on commit 81e9573

Please sign in to comment.