/
SelectionGroupMerger.h
274 lines (220 loc) · 8.87 KB
/
SelectionGroupMerger.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
#pragma once
#include "icomparablenode.h"
#include "imap.h"
#include "iselectiongroup.h"
#include <map>
#include <functional>
#include "NodeUtils.h"
#include "selectionlib.h"
#include "SelectionGroupMergerBase.h"
namespace scene
{
namespace merge
{
/**
* Merger class taking care of adjusting the given target scene such that the
* the groups match what is defined in the source scene.
*
* It tries to keep group links between nodes intact if the nodes are only present
* in the base scene (were not removed during the geometry merge phase).
*/
class SelectionGroupMerger :
public SelectionGroupMergerBase
{
public:
struct Change
{
enum class Type
{
NodeAddedToGroup,
NodeRemovedFromGroup,
BaseGroupCreated,
BaseGroupRemoved,
NodeGroupsReordered,
};
std::size_t groupId;
INodePtr member;
Type type;
};
private:
IMapRootNodePtr _sourceRoot;
IMapRootNodePtr _baseRoot;
selection::ISelectionGroupManager& _sourceManager;
selection::ISelectionGroupManager& _baseManager;
NodeFingerprints _sourceNodes;
NodeFingerprints _baseNodes;
std::vector<std::size_t> _baseGroupIdsToRemove;
std::vector<Change> _changes;
public:
SelectionGroupMerger(const IMapRootNodePtr& sourceRoot, const IMapRootNodePtr& baseRoot) :
_sourceRoot(sourceRoot),
_baseRoot(baseRoot),
_sourceManager(_sourceRoot->getSelectionGroupManager()),
_baseManager(_baseRoot->getSelectionGroupManager())
{}
const IMapRootNodePtr& getSourceRoot() const
{
return _sourceRoot;
}
const IMapRootNodePtr& getBaseRoot() const
{
return _baseRoot;
}
const std::vector<Change>& getChangeLog() const
{
return _changes;
}
void adjustBaseGroups()
{
// Collect all source and base nodes for easier lookup
_sourceNodes = collectNodeFingerprints(_sourceRoot);
_log << "Got " << _sourceNodes.size() << " groups in the source map" << std::endl;
_baseNodes = collectNodeFingerprints(_baseRoot);
_log << "Got " << _baseNodes.size() << " in the base map" << std::endl;
_log << "Start Processing base groups" << std::endl;
// Remove all base groups not present in the source scene, unless we decide to keep it
_baseManager.foreachSelectionGroup(
std::bind(&SelectionGroupMerger::processBaseGroup, this, std::placeholders::_1));
_log << "Start Processing source groups" << std::endl;
_sourceManager.foreachSelectionGroup(
std::bind(&SelectionGroupMerger::processSourceGroup, this, std::placeholders::_1));
_log << "Removing " << _baseGroupIdsToRemove.size() << " base groups that have been marked for removal." << std::endl;
// Remove all base groups that are no longer necessary
for (auto baseGroupId : _baseGroupIdsToRemove)
{
_baseManager.deleteSelectionGroup(baseGroupId);
}
// Run a final pass over the node membership to ensure the group sizes are ascending for each node
// Each group on every node is a superset of any group that was set on that node before
ensureGroupSizeOrder(_baseRoot, [&](const INodePtr& affectedNode)
{
_changes.emplace_back(Change
{
0,
affectedNode,
Change::Type::NodeGroupsReordered
});
});
}
private:
void processBaseGroup(selection::ISelectionGroup& group)
{
// Check if there's a counter-part in the source scene
auto sourceGroup = _sourceManager.getSelectionGroup(group.getId());
// Existing groups are ok, leave them, conflicts will be resolved in processSourceGroup
if (sourceGroup)
{
_log << "Base group " << group.getId() << " is present in source too, skipping." << std::endl;
return;
}
// This base group is no longer present in the source scene,
// we'll remove it, unless it's referenced by nodes which are base-only
// which indicates that the base nodes have explicitly been chosen by the user
// to be kept during the merge operation
std::vector<INodePtr> nodesToRemove;
group.foreachNode([&](const INodePtr& node)
{
auto fingerprint = NodeUtils::GetGroupMemberFingerprint(node);
// All nodes that are also present in the source map are removed from this group
// we only keep the base nodes that are preserved during merge
if (_sourceNodes.count(fingerprint) > 0)
{
nodesToRemove.push_back(node);
}
});
for (const auto& node : nodesToRemove)
{
_changes.emplace_back(Change
{
group.getId(),
node,
Change::Type::NodeRemovedFromGroup
});
_log << "Removing node " << node->name() << " from group " <<
group.getId() << ", since it is not exclusive to the base map." << std::endl;
group.removeNode(node);
}
if (group.size() < 2)
{
_log << "Base group " << group.getId() << " ends up with less than two members and is marked for removal." << std::endl;
_changes.emplace_back(Change
{
group.getId(),
INodePtr(),
Change::Type::BaseGroupRemoved
});
_baseGroupIdsToRemove.push_back(group.getId());
}
}
void processSourceGroup(selection::ISelectionGroup& group)
{
_log << "Processing source group with ID: " << group.getId() << ", size: " << group.size() << std::endl;
// Make sure the group exists in the base
auto baseGroup = _baseManager.getSelectionGroup(group.getId());
if (!baseGroup)
{
_log << "Creating group with ID " << group.getId() << " in the base map" << std::endl;
baseGroup = _baseManager.createSelectionGroup(group.getId());
_changes.emplace_back(Change
{
group.getId(),
INodePtr(),
Change::Type::BaseGroupCreated
});
}
// Ensure the correct members are in the group, if they are available in the map
auto desiredGroupMembers = getGroupMemberFingerprints(group);
auto currentGroupMembers = getGroupMemberFingerprints(*baseGroup);
std::vector<GroupMembers::value_type> membersToBeRemoved;
std::vector<GroupMembers::value_type> membersToBeAdded;
auto compareFingerprint = [](const GroupMembers::value_type& left, const GroupMembers::value_type& right)
{
return left.first < right.first;
};
std::set_difference(currentGroupMembers.begin(), currentGroupMembers.end(),
desiredGroupMembers.begin(), desiredGroupMembers.end(),
std::back_inserter(membersToBeRemoved), compareFingerprint);
std::set_difference(desiredGroupMembers.begin(), desiredGroupMembers.end(),
currentGroupMembers.begin(), currentGroupMembers.end(),
std::back_inserter(membersToBeAdded), compareFingerprint);
_log << "Members to be added: " << membersToBeAdded.size() << ", members to be removed: " << membersToBeRemoved.size() << std::endl;
for (const auto& pair : membersToBeRemoved)
{
// Look up the base node with that fingerprint
auto baseNode = _baseNodes.find(pair.first);
if (baseNode == _baseNodes.end())
{
_log << "Could not lookup the node " << pair.second->name() << " in the base map for removal" << std::endl;
continue;
}
_log << "Removing node " << baseNode->second->name() << " from group " << baseGroup->getId() << std::endl;
baseGroup->removeNode(baseNode->second);
_changes.emplace_back(Change
{
group.getId(),
baseNode->second,
Change::Type::NodeRemovedFromGroup
});
}
for (const auto& pair : membersToBeAdded)
{
// Look up the base node with that fingerprint
auto baseNode = _baseNodes.find(pair.first);
if (baseNode == _baseNodes.end())
{
_log << "Could not lookup the node " << pair.second->name() << " in the base map for addition" << std::endl;
continue;
}
_log << "Adding node " << baseNode->second->name() << " to group " << baseGroup->getId() << std::endl;
baseGroup->addNode(baseNode->second);
_changes.emplace_back(Change
{
group.getId(),
baseNode->second,
Change::Type::NodeAddedToGroup
});
}
}
};
}
}