Commit 125e6efb authored by David Thompson's avatar David Thompson
Browse files

Progress on identifier spaces.

parent 82d15974
......@@ -46,6 +46,11 @@ if (AEVA_ENABLE_PYTHON AND NOT SMTK_ENABLE_PYTHON_WRAPPING)
"Python support in the AEVA session requires Python support from SMTK.")
endif ()
##
## Boost (beyond SMTK, we use Boost::icl directly)
##
find_package(Boost REQUIRED)
##
## VTK
##
......
Markup Resources
----------------
The "markup" subsystem is an SMTK :smtk:`resource <smtk::markup::Resource>`
and :smtk:`components <smtk::markup::Component>` that facilitate the
annotation of geometric data.
The way in which geometry and annotations are represented
is what determines the capabilities of the resulting application.
The key concept this resource embodies is a set-theoretic approach
to referencing geometry for annotation.
The resource itself owns a catalog of :smtk:`domains <smtk::markup::Domain>`,
each of which provides a way to enumerate its members and reference sets
of members (and in some cases, the geometric boundaries of those members).
Annotations then combine a reference to domain members with
information relevant to the geometric context of the reference.
For example, simulations often annotate subregions of their domain
with material properties; boundaries of these regions with
boundary conditions; and other miscellaneous information
including initial conditions, termination conditions, and triggers for
checkpoints or post-processing.
Another example is medical studies, where annotations on
radiographic information relate regions to tissues, organs,
and landmarks of interest.
These annotations are used for morphological analysis in
cross-sectional and longitudinal studies in order to
characterize populations, diagnose patients, plan surgical
procedures, etc.
There are two types of domains: discrete and parametric.
Discrete domains have members represented by integer identifiers
which live in an :smtk:`"ID space" <smtk::markup::IdSpace>`.
Parametric domains have members represented by continuous ranges
which live in a :smtk:`"parameter space" <smtk::markup::ParameterSpace>`.
The markup subsystem is currently focused on discrete domains
although some capabilities exist for parametric domains.
Discrete domains
================
Discrete domains are composed of primitive shapes
(points, lines, triangles, quadrilaterals, tetrahedra,
hexahedra, etc.) that are defined by corner points.
The points have unique IDs assigned to them as does
each primitive, resulting in two ID spaces: points
and cells.
Parametric domains
==================
Because parametric domains are continuous,
they are difficult to represent computationally
since real numbers are only approximately
represented by digital computers.
In the markup resource, parameter spaces are
represented by mapping discrete spaces through
a continuous function (i.e., the map from
parameter space to world coordinates is
continuous but the domain of that map is
discretized).
......@@ -361,7 +361,7 @@ nodeClasses = {
{
return result;
}
auto space = allotment->space().lock();
auto space = allotment->space();
for (const auto& side : m_sides)
{
result = visitor(*space, side.first, side.second);
......
......@@ -8,29 +8,26 @@ namespace markup
AssignedIds::~AssignedIds() = default;
bool AssignedIds::setSpace(const std::weak_ptr<smtk::markup::IdSpace>& space)
AssignedIds::IdRange AssignedIds::range() const
{
auto mlocked = m_space.lock();
auto vlocked = space.lock();
if (mlocked == vlocked)
{
return false;
}
m_space = vlocked;
return true;
return m_range;
}
const std::weak_ptr<smtk::markup::IdSpace>& AssignedIds::space() const
{
return m_space;
}
#if 0
/// Returns a functor to iterate all assigned IDs. Call the functor until it returns false.
Iterable AssignedIds::iterable() const;
/// Returns functor to query the number of allotted ids in the half-open interval [begin, end[.
ContainsFunctor AssignedIds::contains() const;
/// Call \a visitor on each alloted ID.
smtk::common::Visit visit(Visitor visitor) const;
#endif
std::weak_ptr<smtk::markup::IdSpace>& AssignedIds::space()
std::shared_ptr<smtk::markup::IdSpace> AssignedIds::space()
{
return m_space;
return m_space.lock();
}
bool AssignedIds::setNature(const AssignedIds::Nature& nature)
bool AssignedIds::setNature(const Nature& nature)
{
if (m_nature == nature)
{
......@@ -40,12 +37,12 @@ bool AssignedIds::setNature(const AssignedIds::Nature& nature)
return true;
}
const AssignedIds::Nature& AssignedIds::nature() const
const Nature& AssignedIds::nature() const
{
return m_nature;
}
AssignedIds::Nature& AssignedIds::nature()
Nature& AssignedIds::nature()
{
return m_nature;
}
......
......@@ -5,40 +5,50 @@
#include "smtk/markup/Component.h"
#include "smtk/common/Visit.h"
#include "smtk/markup/IdSpace.h"
namespace smtk
{
namespace markup
{
class IdSpace;
/// The nature of how identifiers in an instance of AssignedIds are used.
enum Nature
{
Primary, //!< Identifiers are exclusively owned by the \a component.
Referential, //!< Identifiers are not owned – only referenced – by the \a component.
NonExclusive, //!< Ownership of identifiers is shared with other components.
Unassigned //!< Identifiers are not reserved for use.
};
/// An API for querying the IDs allotted to a component in an IdSpace.
class SMTKMARKUP_EXPORT AssignedIds : public smtk::markup::Component
class SMTKMARKUP_EXPORT AssignedIds : public std::enable_shared_from_this<AssignedIds>
{
public:
smtkTypeMacro(smtk::markup::AssignedIds);
smtkSuperclassMacro(smtk::markup::Component);
enum Nature
{ Primary,Referential,NonExclusive };
smtkTypeMacroBase(smtk::markup::AssignedIds);
using IdType = smtk::markup::IdSpace::IdType;
using IdType = std::size_t;
using IdIterator = struct { smtk::markup::IdSpace* idSpace; IdType begin; IdType end; Nature nature; };
using IdRange = struct { smtk::markup::IdSpace* idSpace; IdType begin; IdType end; };
using IdRange = std::array<IdType, 2>; // struct { smtk::markup::IdSpace* idSpace; IdType begin; IdType end; };
using Iterable = std::function<bool(IdIterator&)>;
using ContainsFunctor = std::function<std::size_t(const IdRange&)>;
using Visitor = std::function<smtk::common::Visit(smtk::markup::IdSpace&, IdType)>;
template<typename... Args>
AssignedIds(Args&&... args)
: smtk::markup::Component(std::forward<Args>(args)...)
AssignedIds(const std::shared_ptr<IdSpace>& space, Nature nature, IdType begin, IdType end, Args&&... args)
// : smtk::markup::Component(std::forward<Args>(args)...)
: m_space(space)
, m_range(IdRange{begin, end})
, m_nature(nature)
{
}
~AssignedIds() override;
virtual ~AssignedIds();
/// Returns the range of IDs in the allotment.
std::array<AssignedIds::IdType, 2> range() const;
IdRange range() const;
/// Returns a functor to iterate all assigned IDs. Call the functor until it returns false.
Iterable iterable() const;
/// Returns functor to query the number of allotted ids in the half-open interval [begin, end[.
......@@ -46,17 +56,16 @@ public:
/// Call \a visitor on each alloted ID.
smtk::common::Visit visit(Visitor visitor) const;
bool setSpace(const std::weak_ptr<smtk::markup::IdSpace>& space);
const std::weak_ptr<smtk::markup::IdSpace>& space() const;
std::weak_ptr<smtk::markup::IdSpace>& space();
std::shared_ptr<smtk::markup::IdSpace> space();
bool setNature(const AssignedIds::Nature& nature);
const AssignedIds::Nature& nature() const;
AssignedIds::Nature& nature();
bool setNature(const Nature& nature);
const Nature& nature() const;
Nature& nature();
protected:
std::weak_ptr<smtk::markup::IdSpace> m_space;
AssignedIds::Nature m_nature;
IdRange m_range;
Nature m_nature;
};
} // namespace markup
......
......@@ -5,7 +5,9 @@ set(headers
list(APPEND classes
Resource
DomainMap
AssignedIds
Component
Domain
BoundaryOperator
......@@ -66,6 +68,7 @@ target_link_libraries(smtkMarkup
VTK::AEVAExt
VTK::MeshingNetGen
nglib
Boost::boost
PRIVATE
VTK::CommonCore
VTK::CommonDataModel
......
......@@ -10,8 +10,8 @@ Collection::~Collection() = default;
bool Collection::setMembers(const std::set<std::weak_ptr<smtk::markup::Component>, std::owner_less<std::weak_ptr<smtk::markup::Component>>>& members)
{
std::unordered_map<smtk::markup::IdSpace::IdType, std::weak_ptr<smtk::markup::Component>> next;
std::unordered_set<smtk::markup::IdSpace::IdType> removed;
std::unordered_map<smtk::markup::AssignedIds::IdType, std::weak_ptr<smtk::markup::Component>> next;
std::unordered_set<smtk::markup::AssignedIds::IdType> removed;
auto newEntries = members;
for (const auto& entry : m_members)
{
......@@ -62,12 +62,12 @@ std::weak_ptr<smtk::markup::IdAllotment>& Collection::keys()
return m_keys;
}
const std::unordered_map<smtk::markup::IdSpace::IdType, std::weak_ptr<smtk::markup::Component>>& Collection::members() const
const std::unordered_map<smtk::markup::AssignedIds::IdType, std::weak_ptr<smtk::markup::Component>>& Collection::members() const
{
return m_members;
}
std::unordered_map<smtk::markup::IdSpace::IdType, std::weak_ptr<smtk::markup::Component>>& Collection::members()
std::unordered_map<smtk::markup::AssignedIds::IdType, std::weak_ptr<smtk::markup::Component>>& Collection::members()
{
return m_members;
}
......
......@@ -32,15 +32,15 @@ public:
const std::weak_ptr<smtk::markup::IdAllotment>& keys() const;
std::weak_ptr<smtk::markup::IdAllotment>& keys();
const std::unordered_map<smtk::markup::IdSpace::IdType, std::weak_ptr<smtk::markup::Component>>& members() const;
std::unordered_map<smtk::markup::IdSpace::IdType, std::weak_ptr<smtk::markup::Component>>& members();
const std::unordered_map<smtk::markup::AssignedIds::IdType, std::weak_ptr<smtk::markup::Component>>& members() const;
std::unordered_map<smtk::markup::AssignedIds::IdType, std::weak_ptr<smtk::markup::Component>>& members();
protected:
std::weak_ptr<smtk::markup::IdAllotment> m_keys;
std::unordered_map<smtk::markup::IdSpace::IdType, std::weak_ptr<smtk::markup::Component>> m_members;
std::unordered_map<smtk::markup::AssignedIds::IdType, std::weak_ptr<smtk::markup::Component>> m_members;
};
} // namespace markup
} // namespace smtk
#endif // smtk_markup_Collection_h
\ No newline at end of file
#endif // smtk_markup_Collection_h
......@@ -6,8 +6,5 @@ namespace smtk
namespace markup
{
Domain::~Domain() = default;
} // namespace markup
} // namespace smtk
......@@ -2,7 +2,12 @@
#ifndef smtk_markup_Domain_h
#define smtk_markup_Domain_h
#include "smtk/markup/Component.h"
#include "smtk/markup/Exports.h"
#include "smtk/SharedFromThis.h"
#include "smtk/string/Token.h"
#include <memory>
namespace smtk
{
......@@ -10,21 +15,21 @@ namespace markup
{
/// The domain of a discrete or parameterized dataset.
class SMTKMARKUP_EXPORT Domain : public smtk::markup::Component
class SMTKMARKUP_EXPORT Domain : public std::enable_shared_from_this<Domain>
{
public:
smtkTypeMacro(smtk::markup::Domain);
smtkSuperclassMacro(smtk::markup::Component);
smtkTypeMacroBase(smtk::markup::Domain);
template<typename... Args>
Domain(Args&&... args)
: smtk::markup::Component(std::forward<Args>(args)...)
{
}
Domain() = default;
Domain(const smtk::string::Token& name)
: m_name(name)
{ }
virtual ~Domain() = default;
~Domain() override;
smtk::string::Token name() const { return m_name; }
protected:
smtk::string::Token m_name;
};
} // namespace markup
......
// Copyright © Kitware Inc under the [BSD-3-clause license](https://opensource.org/licenses/BSD-3-Clause).
#include "smtk/markup/DomainMap.h"
#include "smtk/markup/Domain.h"
namespace smtk
{
namespace markup
{
bool DomainMap::contains(const smtk::string::Token& space) const
{
auto it = m_domains.find(space);
bool have = (it == m_domains.end());
return have;
}
bool DomainMap::insert(const smtk::string::Token& space, const std::shared_ptr<Domain>& domain)
{
auto it = m_domains.find(space);
if (it != m_domains.end())
{
return false;
}
m_domains[space] = domain;
return true;
}
std::shared_ptr<Domain> DomainMap::find(const smtk::string::Token& space) const
{
auto it = m_domains.find(space);
if (it == m_domains.end())
{
return nullptr;
}
return it->second;
}
std::shared_ptr<Domain> DomainMap::operator [] (const smtk::string::Token& space) const
{
return this->find(space);
}
} // namespace markup
} // namespace smtk
// Copyright © Kitware Inc under the [BSD-3-clause license](https://opensource.org/licenses/BSD-3-Clause).
#ifndef smtk_markup_DomainMap_h
#define smtk_markup_DomainMap_h
#include "smtk/markup/Exports.h"
#include "smtk/string/Token.h"
#include <memory>
#include <unordered_map>
namespace smtk
{
namespace markup
{
class Domain;
/**\brief Container for all the namespaces a resource presents for components to share.
*
* Components may own their own private domains, but if multiple components are
* intended to share a domain, the resource should own and manage it via this map.
*/
class SMTKMARKUP_EXPORT DomainMap
{
public:
DomainMap() = default;
~DomainMap() = default;
DomainMap(const DomainMap&) = delete;
void operator = (const DomainMap&) = delete;
/// Return true if the \a space exists in this map.
bool contains(const smtk::string::Token& space) const;
/**\brief Insert the \a domain instance into the map, returning
* true on success (and failure if \a space already existed).
*/
bool insert(const smtk::string::Token& space, const std::shared_ptr<Domain>& domain);
/// Find a domain for the given namespace, returning null if none exists.
std::shared_ptr<Domain> find(const smtk::string::Token& space) const;
/// Find a domain for the given namespace, returning null if none exists.
std::shared_ptr<Domain> operator [] (const smtk::string::Token& space) const;
/// A convenience to downcast a domain to the type expected for \a space.
template<typename DomainType>
std::shared_ptr<DomainType> findAs(const smtk::string::Token& space) const
{
auto domain = this->find(space);
return std::dynamic_pointer_cast<DomainType>(domain);
}
/// Create a domain of the given \a DomainType and insert it into the map at \a space.
template<typename DomainType, typename... Args>
bool create(const smtk::string::Token& space, Args&&... args)
{
auto domain = std::make_shared<DomainType>(std::forward<Args>(args)...);
return this->insert(space, domain);
}
protected:
std::unordered_map<smtk::string::Token, std::shared_ptr<Domain>> m_domains;
};
} // namespace markup
} // namespace smtk
#endif // smtk_markup_DomainMap_h
......@@ -8,7 +8,7 @@ namespace markup
ExplicitSubset::~ExplicitSubset() = default;
bool ExplicitSubset::setMembers(const std::set<smtk::markup::IdSpace::IdType>& members)
bool ExplicitSubset::setMembers(const std::set<smtk::markup::AssignedIds::IdType>& members)
{
if (m_members == members)
{
......@@ -18,12 +18,12 @@ bool ExplicitSubset::setMembers(const std::set<smtk::markup::IdSpace::IdType>& m
return true;
}
const std::set<smtk::markup::IdSpace::IdType>& ExplicitSubset::members() const
const std::set<smtk::markup::AssignedIds::IdType>& ExplicitSubset::members() const
{
return m_members;
}
std::set<smtk::markup::IdSpace::IdType>& ExplicitSubset::members()
std::set<smtk::markup::AssignedIds::IdType>& ExplicitSubset::members()
{
return m_members;
}
......
......@@ -24,12 +24,12 @@ public:
~ExplicitSubset() override;
bool setMembers(const std::set<smtk::markup::IdSpace::IdType>& members);
const std::set<smtk::markup::IdSpace::IdType>& members() const;
std::set<smtk::markup::IdSpace::IdType>& members();
bool setMembers(const std::set<smtk::markup::AssignedIds::IdType>& members);
const std::set<smtk::markup::AssignedIds::IdType>& members() const;
std::set<smtk::markup::AssignedIds::IdType>& members();
protected:
std::set<smtk::markup::IdSpace::IdType> m_members;
std::set<smtk::markup::AssignedIds::IdType> m_members;
};
} // namespace markup
......
......@@ -22,7 +22,7 @@ public:
enum Nature
{ Primary,Referential,NonExclusive };
using IdType = smtk::markup::IdSpace::IdType;
using IdType = smtk::markup::AssignedIds::IdType;
using Visitor = std::function<smtk::common::Visit(smtk::markup::IdSpace&, IdType)>;
template<typename... Args>
......@@ -53,4 +53,4 @@ protected:
} // namespace markup
} // namespace smtk
#endif // smtk_markup_IdAllotment_h
\ No newline at end of file
#endif // smtk_markup_IdAllotment_h
// Copyright © Kitware Inc under the [BSD-3-clause license](https://kitware.com/licenses/bsd.md).
#include "smtk/markup/IdSpace.h"
template<typename T>
using discrete_interval = boost::icl::discrete_interval<T>;
namespace smtk
{
namespace markup
......@@ -18,6 +21,208 @@ std::array<IdSpace::IdType, 2>& IdSpace::range()
return m_range;
}
std::shared_ptr<AssignedIds> IdSpace::requestRange(
Nature nature,
std::size_t rangeSize,
std::size_t offset
)
{
std::shared_ptr<AssignedIds> result;
bool needToCheckRange = true;
// If asked to choose an offset, do so.
if (offset == Invalid)
{
switch (nature)
{
case Nature::Referential:
return result;
default:
// Choose an offset just beyond the largest ID we hold:
// TODO: We could search for a hole to the left of or inside m_range.
offset = (m_range[0] == Invalid ? 1 : m_range[1]);
needToCheckRange = false;
break;
}
}
// Check that the range meets criteria imposed by \a nature.
if (needToCheckRange)
{
bool rangeOK = true;
switch (nature)
{
case Nature::Primary:
rangeOK = this->isRangeEmpty(offset, offset + rangeSize);
break;
case Nature::NonExclusive:
{
IdType nn = this->numberOfIdsInRangeOfNature(offset, offset + rangeSize, Nature::Primary);
rangeOK = (nn == 0);
}
break;
case Nature::Referential:
{
IdType nn = this->numberOfIdsInRangeOfNature(offset, offset + rangeSize, Nature::Unassigned);
rangeOK = (nn == 0);
}
break;
}
if (!rangeOK)
{
return result;
}
}
auto self = std::static_pointer_cast<IdSpace>(shared_from_this());
result = std::make_shared<AssignedIds>(self, nature, offset, offset + rangeSize);
// Update m_range.
if (m_range[0] == Invalid)
{
m_range = {offset, offset + rangeSize};
}
else
{
m_range[0] = offset < m_range[0] ? offset : m_range[0];
m_range[1] = offset + rangeSize > m_range[1] ? offset + rangeSize : m_range[1];
}
m_entries += std::make_pair(
discrete_interval<IdType>::right_open(result->range()[0], result->range()[1]),
std::set<AssignedIds*>{ result.get() });
return result;
}
std::set<std::shared_ptr<AssignedIds>> IdSpace::assignedIds(
IdType begin, IdType end, Nature nature) const
{