Skip to content

Commit

Permalink
Improve object layer class
Browse files Browse the repository at this point in the history
  • Loading branch information
albin-johansson committed Nov 18, 2023
1 parent d08c201 commit cd08605
Show file tree
Hide file tree
Showing 3 changed files with 206 additions and 0 deletions.
73 changes: 73 additions & 0 deletions modules/core/inc/tactile/core/map/layer/object_layer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@
#pragma once

#include "tactile/core/api.hpp"
#include "tactile/core/container/smart_ptr.hpp"
#include "tactile/core/container/tree_map.hpp"
#include "tactile/core/container/vector.hpp"
#include "tactile/core/map/layer/layer.hpp"
#include "tactile/core/map/layer/layer_behavior_delegate.hpp"
#include "tactile/core/map/layer/object.hpp"
#include "tactile/core/misc/id_types.hpp"
#include "tactile/core/prelude.hpp"

namespace tactile {
Expand All @@ -18,12 +23,79 @@ class TACTILE_CORE_API ObjectLayer final : public ILayer {

void accept(ILayerVisitor& visitor) override;

/**
* \brief Adds an object to the layer.
*
* \note An existing object in the layer with the specified ID will be overwritten.
*
* \param id the ID to associate with the object.
* \param object the object to add.
*/
void add_object(ObjectID id, Shared<Object> object);

/**
* \brief Removes an object from the layer.
*
* \param id the ID associated with the object.
*
* \return the removed object.
*/
auto remove_object(ObjectID id) -> Shared<Object>;

void set_persistent_id(Maybe<int32> id) override;

void set_opacity(float opacity) override;

void set_visible(bool visible) override;

/**
* \brief Returns an object in the layer.
*
* \note Avoid calling this function if you only need to "look" at the returned object,
* prefer calling `find_object` in such cases. This avoids expensive copies of
* shared pointers.
*
* \param id the ID of the desired object.
*
* \return a shared pointer to the found object.
*/
[[nodiscard]]
auto get_object(ObjectID id) -> Shared<Object>;

/**
* \brief Attempts to find and return an object in the layer.
*
* \param id the ID of the desired object.
*
* \return a pointer to the found object, or a null pointer.
*/
[[nodiscard]]
auto find_object(ObjectID id) -> Object*;

/**
* \copydoc find_object()
*/
[[nodiscard]]
auto find_object(ObjectID id) const -> const Object*;

/**
* \brief Indicates whether the layer contains an object with the specified ID.
*
* \param id the object ID to check.
*
* \return true if an object was found; false otherwise.
*/
[[nodiscard]]
auto has_object(ObjectID id) const -> bool;

/**
* \brief Returns the number of objects in the layer.
*
* \return an object count.
*/
[[nodiscard]]
auto object_count() const -> usize;

[[nodiscard]]
auto get_persistent_id() const -> Maybe<int32> override;

Expand All @@ -44,6 +116,7 @@ class TACTILE_CORE_API ObjectLayer final : public ILayer {

private:
LayerBehaviorDelegate mDelegate;
TreeMap<ObjectID, Shared<Object>> mObjects;
};

} // namespace tactile
44 changes: 44 additions & 0 deletions modules/core/src/tactile/core/map/layer/object_layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

#include "tactile/core/map/layer/object_layer.hpp"

#include <utility> // move

#include "tactile/core/container/lookup.hpp"
#include "tactile/core/map/layer/layer_visitor.hpp"

namespace tactile {
Expand All @@ -16,6 +19,20 @@ void ObjectLayer::accept(ILayerVisitor& visitor)
visitor.visit(*this);
}

void ObjectLayer::add_object(const ObjectID id, Shared<Object> object)
{
mObjects[id] = std::move(object);
}

auto ObjectLayer::remove_object(const ObjectID id) -> Shared<Object>
{
if (auto object = erase_from(mObjects, id)) {
return std::move(object).value();
}

return nullptr;
}

void ObjectLayer::set_persistent_id(const Maybe<int32> id)
{
mDelegate.set_persistent_id(id);
Expand All @@ -31,6 +48,33 @@ void ObjectLayer::set_visible(const bool visible)
mDelegate.set_visible(visible);
}

auto ObjectLayer::get_object(const ObjectID id) -> Shared<Object>
{
return lookup_in(mObjects, id);
}

auto ObjectLayer::find_object(const ObjectID id) -> Object*
{
const auto iter = mObjects.find(id);
return (iter != mObjects.end()) ? iter->second.get() : nullptr;
}

auto ObjectLayer::find_object(const ObjectID id) const -> const Object*
{
const auto iter = mObjects.find(id);
return (iter != mObjects.end()) ? iter->second.get() : nullptr;
}

auto ObjectLayer::has_object(const ObjectID id) const -> bool
{
return mObjects.contains(id);
}

auto ObjectLayer::object_count() const -> usize
{
return mObjects.size();
}

auto ObjectLayer::get_persistent_id() const -> Maybe<int32>
{
return mDelegate.get_persistent_id();
Expand Down
89 changes: 89 additions & 0 deletions modules/core/test/map/layer/object_layer_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright (C) 2023 Albin Johansson (GNU General Public License v3.0)

#include "tactile/core/map/layer/object_layer.hpp"

#include <gtest/gtest.h>

using namespace tactile;
using tactile::int_literals::operator""_uz;

/// \tests tactile::ObjectLayer::add_object
TEST(ObjectLayer, AddObject)
{
const ObjectID object_id {42};

const auto object1 = make_shared<Object>(ObjectType::kRect);
const auto object2 = make_shared<Object>(ObjectType::kEllipse);

ObjectLayer layer;
EXPECT_EQ(layer.object_count(), 0_uz);

layer.add_object(object_id, object1);
EXPECT_EQ(layer.object_count(), 1_uz);
EXPECT_EQ(layer.get_object(object_id), object1);

layer.add_object(object_id, object2);
EXPECT_EQ(layer.object_count(), 1_uz);
EXPECT_EQ(layer.get_object(object_id), object2);
}

/// \tests tactile::ObjectLayer::remove_object
/// \tests tactile::ObjectLayer::has_object
TEST(ObjectLayer, RemoveObject)
{
const ObjectID object1_id {212};
const ObjectID object2_id {832};

const auto object1 = make_shared<Object>(ObjectType::kPoint);
const auto object2 = make_shared<Object>(ObjectType::kRect);

ObjectLayer layer;
layer.add_object(object1_id, object1);
layer.add_object(object2_id, object2);
ASSERT_EQ(layer.object_count(), 2_uz);
EXPECT_TRUE(layer.has_object(object1_id));
EXPECT_TRUE(layer.has_object(object2_id));

EXPECT_EQ(layer.remove_object(object1_id), object1);
EXPECT_EQ(layer.object_count(), 1_uz);
EXPECT_FALSE(layer.has_object(object1_id));
EXPECT_TRUE(layer.has_object(object2_id));

EXPECT_EQ(layer.remove_object(object2_id), object2);
EXPECT_EQ(layer.object_count(), 0_uz);
EXPECT_FALSE(layer.has_object(object1_id));
EXPECT_FALSE(layer.has_object(object2_id));

EXPECT_EQ(layer.remove_object(object1_id), nullptr);
EXPECT_EQ(layer.remove_object(object2_id), nullptr);
}

/// \tests tactile::ObjectLayer::get_object
TEST(ObjectLayer, GetObject)
{
const ObjectID object_id {123};
const auto object = make_shared<Object>(ObjectType::kPoint);

ObjectLayer layer;
EXPECT_ANY_THROW((void) layer.get_object(object_id));

layer.add_object(object_id, object);
EXPECT_EQ(layer.get_object(object_id), object);
}

/// \tests tactile::ObjectLayer::find_object
TEST(ObjectLayer, FindObject)
{
const ObjectID object_id {923};
const auto object = make_shared<Object>(ObjectType::kEllipse);

ObjectLayer layer;
const auto& const_layer = layer;

EXPECT_EQ(layer.find_object(object_id), nullptr);
EXPECT_EQ(const_layer.find_object(object_id), nullptr);

layer.add_object(object_id, object);
EXPECT_EQ(layer.find_object(object_id), object.get());
EXPECT_EQ(const_layer.find_object(object_id), object.get());
}

0 comments on commit cd08605

Please sign in to comment.