Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit 2d810289 authored by Nicolas Carion's avatar Nicolas Carion

[Timeline2][Model] Structure for group storing + tests

parent cf300559
......@@ -3,5 +3,6 @@ set(kdenlive_SRCS
timeline2/model/timelinemodel.cpp
timeline2/model/trackmodel.cpp
timeline2/model/clipmodel.cpp
timeline2/model/groupsmodel.cpp
PARENT_SCOPE)
/***************************************************************************
* Copyright (C) 2017 by Nicolas Carion *
* This file is part of Kdenlive. See www.kdenlive.org. *
* *
* This program 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 2 of the License, or *
* (at your option) version 3 or any later version accepted by the *
* membership of KDE e.V. (or its successor approved by the membership *
* of KDE e.V.), which shall act as a proxy defined in Section 14 of *
* version 3 of the license. *
* *
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include "groupsmodel.hpp"
#include <assert.h>
#include <queue>
GroupsModel::GroupsModel()
{
}
void GroupsModel::createGroupItem(int id)
{
assert(m_upLink.count(id) == 0);
assert(m_downLink.count(id) == 0);
m_upLink[id] = -1;
m_downLink[id] = std::unordered_set<int>();
}
void GroupsModel::destructGroupItem(int id)
{
removeFromGroup(id);
for (int child : m_downLink[id]) {
m_upLink[child] = -1;
}
m_downLink.erase(id);
m_upLink.erase(id);
}
int GroupsModel::getRootId(int id) const
{
assert(m_upLink.count(id) > 0);
int father = m_upLink.at(id);
if (father == -1) {
return id;
}
return getRootId(father);
}
bool GroupsModel::isLeaf(int id) const
{
return m_downLink.at(id).size() == 0;
}
std::unordered_set<int> GroupsModel::getSubtree(int id) const
{
std::unordered_set<int> result;
result.insert(id);
std::queue<int> queue;
queue.push(id);
while (!queue.empty()) {
int current = queue.front();
queue.pop();
for (const int& child : m_downLink.at(current)) {
result.insert(child);
queue.push(child);
}
}
return result;
}
std::unordered_set<int> GroupsModel::getLeaves(int id) const
{
std::unordered_set<int> result;
std::queue<int> queue;
queue.push(id);
while (!queue.empty()) {
int current = queue.front();
queue.pop();
for (const int& child : m_downLink.at(current)) {
queue.push(child);
}
if (m_downLink.at(current).size() == 0) {
result.insert(current);
}
}
return result;
}
void GroupsModel::setGroup(int id, int groupId)
{
assert(m_upLink.count(id) > 0);
assert(m_downLink.count(groupId) > 0);
removeFromGroup(id);
m_upLink[id] = groupId;
m_downLink[groupId].insert(id);
}
void GroupsModel::removeFromGroup(int id)
{
assert(m_upLink.count(id) > 0);
assert(m_downLink.count(id) > 0);
int parent = m_upLink[id];
if (parent != -1) {
m_downLink[parent].erase(id);
}
m_upLink[id] = -1;
}
/***************************************************************************
* Copyright (C) 2017 by Nicolas Carion *
* This file is part of Kdenlive. See www.kdenlive.org. *
* *
* This program 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 2 of the License, or *
* (at your option) version 3 or any later version accepted by the *
* membership of KDE e.V. (or its successor approved by the membership *
* of KDE e.V.), which shall act as a proxy defined in Section 14 of *
* version 3 of the license. *
* *
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include <unordered_map>
#include <unordered_set>
/* @brief This class represents the group hiearchy. This is basically a tree structure
In this class, we consider that a groupItem is either a clip or a group
*/
class GroupsModel
{
public:
GroupsModel();
/* @brief Create a groupItem in the hierarchy. Initially it is not part of a group
@param id id of the groupItem
*/
void createGroupItem(int id);
/* @brief Destruct a groupItem in the hierarchy.
All its children will become their own roots
@param id id of the groupitem
*/
void destructGroupItem(int id);
/* @brief Get the overall father of a given groupItem
@param id id of the groupitem
*/
int getRootId(int id) const;
/* @brief Returns true if the groupItem has no descendant
@param id of the groupItem
*/
bool isLeaf(int id) const;
/* @brief Returns the id of all the descendant of given item (including item)
@param id of the groupItem
*/
std::unordered_set<int> getSubtree(int id) const;
/* @brief Returns the id of all the leaves in the subtree of the given item
This should correspond to the ids of the clips, since they should be the only items with no descendants
@param id of the groupItem
*/
std::unordered_set<int> getLeaves(int id) const;
/* @brief change the group of a given item
@param id of the groupItem
@param groupId id of the group to assign it to
*/
void setGroup(int id, int groupId);
/* @brief Remove an item from all the groups it belongs to.
@param id of the groupItem
*/
void removeFromGroup(int id);
private:
std::unordered_map<int, int> m_upLink; //edges toward parent
std::unordered_map<int, std::unordered_set<int>> m_downLink; //edges toward children
};
......@@ -11,9 +11,11 @@ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -fexceptions")
SET(Tests_SRCS
TestMain.cpp
modeltest.cpp
groupstest.cpp
../src/timeline2/model/trackmodel.cpp
../src/timeline2/model/timelinemodel.cpp
../src/timeline2/model/clipmodel.cpp
../src/timeline2/model/groupsmodel.cpp
)
INCLUDE_DIRECTORIES(../src/)
......
#include "catch.hpp"
#include <unordered_set>
#define private public
#include "timeline2/model/groupsmodel.hpp"
TEST_CASE("Functional test of the group hierarchy", "[GroupsModel]")
{
GroupsModel groups;
for (int i = 0; i < 10; i++) {
groups.createGroupItem(i);
}
SECTION("Test Basic Creation") {
for (int i = 0; i < 10; i++) {
REQUIRE(groups.getRootId(i) == i);
REQUIRE(groups.isLeaf(i));
REQUIRE(groups.getLeaves(i).size() == 1);
REQUIRE(groups.getSubtree(i).size() == 1);
}
}
groups.setGroup(0,1);
groups.setGroup(1,2);
groups.setGroup(3,2);
groups.setGroup(9,3);
groups.setGroup(6,3);
groups.setGroup(4,3);
groups.setGroup(7,3);
groups.setGroup(8,5);
SECTION("Test leaf nodes"){
std::unordered_set<int> nodes = {1,2,3,5};
for (int i = 0; i < 10; i++) {
REQUIRE(groups.isLeaf(i) != (nodes.count(i) > 0));
if (nodes.count(i) == 0) {
REQUIRE(groups.getSubtree(i) == std::unordered_set<int>({i}));
REQUIRE(groups.getLeaves(i) == std::unordered_set<int>({i}));
}
}
}
SECTION("Test leaves retrieving"){
REQUIRE(groups.getLeaves(2) == std::unordered_set<int>({0,4,6,7,9}));
REQUIRE(groups.getLeaves(3) == std::unordered_set<int>({4,6,7,9}));
REQUIRE(groups.getLeaves(1) == std::unordered_set<int>({0}));
REQUIRE(groups.getLeaves(5) == std::unordered_set<int>({8}));
}
SECTION("Test subtree retrieving"){
REQUIRE(groups.getSubtree(2) == std::unordered_set<int>({0,1,2,3,4,6,7,9}));
REQUIRE(groups.getSubtree(3) == std::unordered_set<int>({3,4,6,7,9}));
REQUIRE(groups.getSubtree(5) == std::unordered_set<int>({5,8}));
}
SECTION("Test root retieving"){
std::set<int> first_tree = {0,1,2,3,4,6,7,9};
for (int n : first_tree) {
CAPTURE(n);
REQUIRE(groups.getRootId(n) == 2);
}
std::unordered_set<int> second_tree = {5,8};
for (int n : second_tree) {
REQUIRE(groups.getRootId(n) == 5);
}
}
groups.setGroup(3,8);
SECTION("Test leaf nodes 2"){
std::unordered_set<int> nodes = {1,2,3,5,8};
for (int i = 0; i < 10; i++) {
REQUIRE(groups.isLeaf(i) != (nodes.count(i) > 0));
if (nodes.count(i) == 0) {
REQUIRE(groups.getSubtree(i) == std::unordered_set<int>({i}));
REQUIRE(groups.getLeaves(i) == std::unordered_set<int>({i}));
}
}
}
SECTION("Test leaves retrieving 2"){
REQUIRE(groups.getLeaves(1) == std::unordered_set<int>({0}));
REQUIRE(groups.getLeaves(2) == std::unordered_set<int>({0}));
REQUIRE(groups.getLeaves(3) == std::unordered_set<int>({4,6,7,9}));
REQUIRE(groups.getLeaves(5) == std::unordered_set<int>({4,6,7,9}));
REQUIRE(groups.getLeaves(8) == std::unordered_set<int>({4,6,7,9}));
}
SECTION("Test subtree retrieving 2"){
REQUIRE(groups.getSubtree(2) == std::unordered_set<int>({0,1,2}));
REQUIRE(groups.getSubtree(3) == std::unordered_set<int>({3,4,6,7,9}));
REQUIRE(groups.getSubtree(5) == std::unordered_set<int>({5,8,3,4,6,7,9}));
}
SECTION("Test root retieving 2"){
std::set<int> first_tree = {0,1,2};
for (int n : first_tree) {
CAPTURE(n);
REQUIRE(groups.getRootId(n) == 2);
}
std::unordered_set<int> second_tree = {5,8,3,4,6,7,9};
for (int n : second_tree) {
REQUIRE(groups.getRootId(n) == 5);
}
}
groups.setGroup(5,2);
SECTION("Test leaf nodes 3"){
std::unordered_set<int> nodes = {1,2,3,5,8};
for (int i = 0; i < 10; i++) {
REQUIRE(groups.isLeaf(i) != (nodes.count(i) > 0));
if (nodes.count(i) == 0) {
REQUIRE(groups.getSubtree(i) == std::unordered_set<int>({i}));
REQUIRE(groups.getLeaves(i) == std::unordered_set<int>({i}));
}
}
}
SECTION("Test leaves retrieving 3"){
REQUIRE(groups.getLeaves(1) == std::unordered_set<int>({0}));
REQUIRE(groups.getLeaves(2) == std::unordered_set<int>({0,4,6,7,9}));
REQUIRE(groups.getLeaves(3) == std::unordered_set<int>({4,6,7,9}));
REQUIRE(groups.getLeaves(5) == std::unordered_set<int>({4,6,7,9}));
REQUIRE(groups.getLeaves(8) == std::unordered_set<int>({4,6,7,9}));
}
SECTION("Test subtree retrieving 3"){
REQUIRE(groups.getSubtree(2) == std::unordered_set<int>({0,1,2,3,4,5,6,7,8,9}));
REQUIRE(groups.getSubtree(3) == std::unordered_set<int>({3,4,6,7,9}));
REQUIRE(groups.getSubtree(5) == std::unordered_set<int>({5,8,3,4,6,7,9}));
}
SECTION("Test root retieving 3"){
for (int i = 0; i < 10; i++) {
CAPTURE(i);
REQUIRE(groups.getRootId(i) == 2);
}
}
}
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