💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

395 lines
9.4 KiB

/*
* Copyright (C) 2023 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code 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.
*
* Antkeeper source code 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 Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GEOM_BREP_ATTRIBUTE_MAP_HPP
#define ANTKEEPER_GEOM_BREP_ATTRIBUTE_MAP_HPP
#include <engine/geom/brep/brep-attribute.hpp>
#include <engine/utility/hash/fnv1a.hpp>
#include <iterator>
#include <memory>
#include <stdexcept>
#include <type_traits>
#include <unordered_map>
#include <utility>
namespace geom {
/**
* Maps names to B-rep attributes.
*/
class brep_attribute_map
{
public:
template <class Iter, bool Const>
class iterator_template
{
public:
using iterator_type = Iter;
using iterator_category = std::bidirectional_iterator_tag;
using iterator_concept = std::bidirectional_iterator_tag;
using difference_type = std::iter_difference_t<Iter>;
using value_type = brep_attribute_base;
using pointer = std::conditional<Const, const value_type*, value_type*>::type;
using reference = std::conditional<Const, const value_type&, value_type&>::type;
[[nodiscard]] inline constexpr reference operator*() const noexcept
{
return *m_it->second;
}
[[nodiscard]] inline constexpr pointer operator->() const noexcept
{
return &(*m_it->second);
}
[[nodiscard]] inline constexpr reference operator[](difference_type i) const noexcept
{
return *(m_it[i]);
}
inline iterator_template& operator++() noexcept
{
++m_it;
return *this;
}
[[nodiscard]] inline iterator_template operator++(int) noexcept
{
iterator_template tmp = *this;
++(*this);
return tmp;
}
inline iterator_template& operator--() noexcept
{
--m_it;
return *this;
}
[[nodiscard]] inline iterator_template operator--(int) noexcept
{
iterator_template tmp = *this;
--(*this);
return tmp;
}
inline iterator_template& operator+=(difference_type n) noexcept
{
m_it += n;
return *this;
}
inline iterator_template& operator-=(difference_type n) noexcept
{
m_it -= n;
return *this;
}
[[nodiscard]] inline bool operator==(const iterator_template& other) const noexcept
{
return m_it == other.m_it;
};
[[nodiscard]] inline std::weak_ordering operator<=>(const iterator_template& other) const noexcept
{
return m_it <=> other.m_it;
}
[[nodiscard]] inline difference_type operator-(const iterator_template& rhs) const noexcept
{
return m_it - rhs.m_it;
}
[[nodiscard]] inline iterator_template operator+(difference_type n) const noexcept
{
return iterator_template{m_it + n};
}
[[nodiscard]] inline iterator_template operator-(difference_type n) const noexcept
{
return iterator_template{m_it - n};
}
[[nodiscard]] friend iterator_template operator+(difference_type lhs, const iterator_template& rhs) noexcept
{
return iterator_template{lhs + rhs.m_it};
}
[[nodiscard]] friend iterator_template operator-(difference_type lhs, const iterator_template& rhs) noexcept
{
return iterator_template{lhs - rhs.m_it};
}
private:
friend class brep_attribute_map;
iterator_type m_it;
};
using iterator = iterator_template<std::unordered_map<hash::fnv1a32_t, std::unique_ptr<brep_attribute_base>>::iterator, false>;
using const_iterator = iterator_template<std::unordered_map<hash::fnv1a32_t, std::unique_ptr<brep_attribute_base>>::const_iterator, true>;
/// @name Iterators
/// @{
/// Returns an iterator to the first attribute.
/// @{
[[nodiscard]] inline const_iterator begin() const noexcept
{
const_iterator it;
it.m_it = m_attributes.begin();
return it;
}
[[nodiscard]] inline iterator begin() noexcept
{
iterator it;
it.m_it = m_attributes.begin();
return it;
}
[[nodiscard]] inline const_iterator cbegin() const noexcept
{
return begin();
}
/// @}
/// Returns an iterator to the attribute following the last attribute.
/// @{
[[nodiscard]] inline const_iterator end() const noexcept
{
const_iterator it;
it.m_it = m_attributes.end();
return it;
}
[[nodiscard]] inline iterator end() noexcept
{
iterator it;
it.m_it = m_attributes.end();
return it;
}
[[nodiscard]] inline const_iterator cend() const noexcept
{
return end();
}
/// @}
/// @}
/// @name Capacity
/// @{
/// Returns `true` if the container is empty, `false` otherwise.
[[nodiscard]] inline bool empty() const noexcept
{
return m_attributes.empty();
}
/// Returns the number of attributes in the container.
[[nodiscard]] inline std::size_t size() const noexcept
{
return m_attributes.size();
}
/// @}
/// @name Modifiers
/// @{
/**
* Removes all attributes from the container.
*/
inline void clear() noexcept
{
m_attributes.clear();
}
/**
* Constructs a new attribute. If an attribute with the given name exists, it will be replaced.
*
* @tparam T Attribute data type.
*
* @param name Name of the new attribute.
*
* @return Iterator to the new attribute.
*/
template <class T>
iterator emplace(hash::fnv1a32_t name)
{
if (auto i = m_attributes.find(name); i != m_attributes.end())
{
i->second.reset();
i->second = std::make_unique<brep_attribute<T>>(name, m_element_count);
iterator it;
it.m_it = i;
return it;
}
iterator it;
it.m_it = m_attributes.emplace(name, std::make_unique<brep_attribute<T>>(name, m_element_count)).first;
return it;
}
/**
* Removes an attribute from the container.
*
* @param pos Iterator to the attribute to remove.
*
* @return Iterator following the erased attribute.
*/
inline iterator erase(iterator pos)
{
iterator it;
it.m_it = m_attributes.erase(pos.m_it);
return it;
}
/**
* Removes an attribute from the container.
*
* @param name Name of the attribute to remove.
*
* @return Number of attributes removed (0 or 1).
*/
inline std::size_t erase(hash::fnv1a32_t name)
{
return m_attributes.erase(name);
}
/**
* Constructs a new attribute if an attribute with the given name does not exist.
*
* @tparam T Attribute data type.
*
* @param name Name of the new attribute.
*
* @return Pair consisting of an iterator to the new or pre-existing attribute, and a Boolean value that's `true` if the new attribute was constructed, or `false` if an attribute with the given name pre-existed.
*/
template <class T>
std::pair<iterator, bool> try_emplace(hash::fnv1a32_t name)
{
if (auto i = m_attributes.find(name); i != m_attributes.end())
{
iterator it;
it.m_it = i;
return {it, false};
}
auto pair = m_attributes.emplace(name, std::make_unique<brep_attribute<T>>(name, m_element_count));
iterator it;
it.m_it = pair.first;
return {it, pair.second};
}
/// @}
/// @name Lookup
/// @{
/**
* Returns a reference to the attribute with the given name. If no such attribute exists, an exception of type std::out_of_range is thrown.
*
* @tparam T Attribute data type.
*
* @return Reference to the attribute with the given name.
*
* @except std::out_of_range B-rep attribute not found.
*/
/// @{
template <class T>
[[nodiscard]] const brep_attribute<T>& at(hash::fnv1a32_t name) const
{
auto it = find(name);
if (it == end())
{
throw std::out_of_range("B-rep attribute not found");
}
return static_cast<const brep_attribute<T>&>(*it);
}
template <class T>
[[nodiscard]] brep_attribute<T>& at(hash::fnv1a32_t name)
{
auto it = find(name);
if (it == end())
{
throw std::out_of_range("B-rep attribute not found");
}
return static_cast<brep_attribute<T>&>(*it);
}
/// @}
/**
* Finds an attribute with the given name.
*
* @param name Name of an attribute.
*
* @return Iterator to the attribute with the given name. If no such attribute is found, an end iterator is returned.
*/
/// @{
[[nodiscard]] inline const_iterator find(hash::fnv1a32_t name) const
{
const_iterator it;
it.m_it = m_attributes.find(name);
return it;
}
[[nodiscard]] inline iterator find(hash::fnv1a32_t name)
{
iterator it;
it.m_it = m_attributes.find(name);
return it;
}
/// @}
/**
* Checks if there is an attribute with a given name in the container.
*
* @param name Attribute name.
*
* @return `true` if an attribute with the given name was found, `false` otherwise.
*/
[[nodiscard]] inline bool contains(hash::fnv1a32_t name) const
{
return m_attributes.contains(name);
}
/// @}
private:
template <class T>
friend class brep_element_container;
friend class brep_mesh;
brep_attribute_map& operator=(const brep_attribute_map& other)
{
m_element_count = other.m_element_count;
m_attributes.clear();
for (const auto& [key, value]: other.m_attributes)
{
m_attributes.emplace(key, value->clone());
}
return *this;
}
std::size_t m_element_count{};
std::unordered_map<hash::fnv1a32_t, std::unique_ptr<brep_attribute_base>> m_attributes;
};
} // namespace geom
#endif // ANTKEEPER_GEOM_BREP_ATTRIBUTE_MAP_HPP