Skip to content

Commit

Permalink
Fix #4581: Grouping of child nodes do not persist through func_static…
Browse files Browse the repository at this point in the history
… cloning.

Now the group mapping code is invoked for every child node that is cloned throughout the source hierarchy.
  • Loading branch information
codereader committed Jul 22, 2017
1 parent 1c46a76 commit d071948
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 35 deletions.
1 change: 0 additions & 1 deletion radiant/map/Map.cpp
Expand Up @@ -36,7 +36,6 @@
#include "map/StartupMapLoader.h"
#include "map/RootNode.h"
#include "map/MapResource.h"
#include "map/algorithm/Clone.h"
#include "map/algorithm/Merge.h"
#include "map/algorithm/Traverse.h"
#include "map/algorithm/MapExporter.h"
Expand Down
59 changes: 42 additions & 17 deletions radiant/map/algorithm/Clone.h
@@ -1,7 +1,8 @@
#ifndef CLONEALLWALKER_H_
#define CLONEALLWALKER_H_
#pragma once

#include "inode.h"
#include "iscenegraph.h"
#include <functional>

namespace map
{
Expand All @@ -20,37 +21,55 @@ inline scene::INodePtr cloneSingleNode(const scene::INodePtr& node)
scene::CloneablePtr cloneable = Node_getCloneable(node);

// Return an empty node if not cloneable
return (cloneable != NULL) ? cloneable->clone() : scene::INodePtr();
return cloneable ? cloneable->clone() : scene::INodePtr();
}

// Function that is invoked after a node has been cloned, called with <sourceNode, clonedNode>
typedef std::function<void(const scene::INodePtr&, const scene::INodePtr&)> PostCloneCallback;

class CloneAll :
public scene::NodeVisitor
{
private:
scene::Path _path;

PostCloneCallback _postCloneCallback;

public:
CloneAll(const scene::INodePtr& root) :
_path(root)
CloneAll(const scene::INodePtr& root, const PostCloneCallback& callback) :
_path(root),
_postCloneCallback(callback)
{}

bool pre(const scene::INodePtr& node)
bool pre(const scene::INodePtr& node) override
{
if (node->isRoot()) {
if (node->isRoot())
{
return false;
}

// Insert the cloned node or NULL if not cloneable
_path.push(cloneSingleNode(node));
scene::INodePtr cloned = cloneSingleNode(node);

if (cloned && _postCloneCallback)
{
_postCloneCallback(node, cloned);
}

// Insert the cloned node or an empty ptr if not cloneable
_path.push(cloned);

return true;
}

void post(const scene::INodePtr& node)
void post(const scene::INodePtr& node) override
{
if (node->isRoot()) {
if (node->isRoot())
{
return;
}

if (_path.top() != NULL) {
if (_path.top())
{
// Cloning was successful, add to parent
_path.parent()->addChildNode(_path.top());
}
Expand All @@ -61,14 +80,22 @@ class CloneAll :

/**
* greebo: Attempts to clone the given node and all cloneable child nodes.
*
* The PostCloneCallback will be invoked for every node that is successfully cloned,
* to give the calling code an opportunity for post-processing.
*
* @returns: the cloned node (which might own cloned children).
*/
inline scene::INodePtr Node_Clone(const scene::INodePtr& node)
inline scene::INodePtr cloneNodeIncludingDescendants(const scene::INodePtr& node,
const PostCloneCallback& callback)
{
scene::INodePtr clone = cloneSingleNode(node);

CloneAll visitor(clone);
if (callback)
{
callback(node, clone);
}

CloneAll visitor(clone, callback);
node->traverseChildren(visitor);

// Cloned child nodes are assigned the layers of the source nodes
Expand All @@ -80,5 +107,3 @@ inline scene::INodePtr Node_Clone(const scene::INodePtr& node)
}

} // namespace map

#endif /*CLONEALLWALKER_H_*/
40 changes: 23 additions & 17 deletions radiant/selection/algorithm/Transformation.cpp
@@ -1,5 +1,7 @@
#include "Transformation.h"

#include <functional>

#include "i18n.h"
#include <string>
#include <map>
Expand Down Expand Up @@ -159,32 +161,36 @@ class SelectionCloner :
if (Node_isSelected(node))
{
// Clone the current node
scene::INodePtr clone = map::Node_Clone(node);
scene::INodePtr clone = map::cloneNodeIncludingDescendants(node,
sigc::mem_fun(*this, &SelectionCloner::postProcessClonedNode));

// Add the cloned node and its parent to the list
_cloned.insert(Map::value_type(clone, node->getParent()));

// Insert this node in the root
_cloneRoot->addChildNode(clone);
}
}

// Collect and add the group IDs of the source node
std::shared_ptr<IGroupSelectable> groupSelectable = std::dynamic_pointer_cast<IGroupSelectable>(node);
void postProcessClonedNode(const scene::INodePtr& sourceNode, const scene::INodePtr& clonedNode)
{
// Collect and add the group IDs of the source node
std::shared_ptr<IGroupSelectable> groupSelectable = std::dynamic_pointer_cast<IGroupSelectable>(sourceNode);

if (groupSelectable)
if (groupSelectable)
{
const IGroupSelectable::GroupIds& groupIds = groupSelectable->getGroupIds();

// Get the Groups the source node was assigned to, and add the
// cloned node to the mapped group, one by one, keeping the order intact
for (std::size_t id : groupIds)
{
const IGroupSelectable::GroupIds& groupIds = groupSelectable->getGroupIds();

// Get the Groups the source node was assigned to, and add the
// cloned node to the mapped group, one by one, keeping the order intact
for (std::size_t id : groupIds)
{
// Try to insert the ID, ignore if already exists
// Get a new mapping for the given group ID
const ISelectionGroupPtr& mappedGroup = getMappedGroup(id);

// Assign the new group ID to this clone
mappedGroup->addNode(clone);
}
// Try to insert the ID, ignore if already exists
// Get a new mapping for the given group ID
const ISelectionGroupPtr& mappedGroup = getMappedGroup(id);

// Assign the new group ID to this clone
mappedGroup->addNode(clonedNode);
}
}
}
Expand Down

0 comments on commit d071948

Please sign in to comment.