Editor.cpp
Go to the documentation of this file.
1#include "Editor.h"
2
3#include <utility>
4
7
8namespace armarx
9{
11 Properties properties,
12 std::function<void(objpose::ProvidedObjectPoseSeq&)> pushToMemory,
13 std::function<objpose::ObjectPoseSeq(void)> pullFromMemory) :
14 properties(std::move(properties)),
15 client(client),
16 pushToMemory(std::move(pushToMemory)),
17 pullFromMemory(std::move(pullFromMemory)),
18 isCommitRequired(false),
19 isUpdateRequired(true),
20 isMemoryVizRequired(true),
21 isMetaVizRequired(true)
22 {
23 placeholderOptions.emplace_back("Remove");
24 placeholderOptions.emplace_back(lineString);
25
26 for (auto const& availableObject : this->properties.availableObjects)
27 {
28 placeholderOptions.push_back(armarx::fromIce(availableObject).str());
29 }
30 }
31
32 void
34 {
35 viz::StagedCommit stage = client.stage();
36
37 if (isUpdateRequired)
38 {
39 storedPoses = update();
40
41 isUpdateRequired = false;
42 }
43
44 if (isResetRequired)
45 {
46 reset();
47
48 isResetRequired = false;
49 }
50
51 if (isMemoryVizRequired)
52 {
53 visualizeMemory();
54 stage.add(memoryLayer);
55
56 isMemoryVizRequired = false;
57 }
58
59 if (isMetaVizRequired)
60 {
61 visualizeMeta();
62 stage.add(metaLayer);
63
64 isMetaVizRequired = false;
65 }
66
67 if (isCommitRequired)
68 {
69 commit();
70
71 isCommitRequired = false;
72 }
73
74 observer.requestInteractions(stage);
75
76 viz::CommitResult result = client.commit(stage);
77
78 observer.process(result.interactions());
79 }
80
82 Editor::update()
83 {
84 objpose::ObjectPoseSeq newRequestedPoses = pullFromMemory();
85
86 isMemoryVizRequired = true;
87
88 return newRequestedPoses;
89 }
90
91 void
92 Editor::reset()
93 {
94 changes.clear();
95
96 isMemoryVizRequired = true;
97 }
98
99 void
100 Editor::commit()
101 {
102 changes.moveNewObjectsTo(storedPoses);
103
104 objpose::ProvidedObjectPoseSeq providingPoses;
105 objpose::ObjectPoseSeq remainingPoses;
106
107 remainingPoses.reserve(storedPoses.size());
108 for (objpose::ObjectPose& current : storedPoses)
109 {
110 bool isChanged = changes.applyTo(current);
111
112 if (isChanged)
113 {
114 providingPoses.push_back(current.toProvidedObjectPoseGlobal());
115 objpose::ProvidedObjectPose& providing = providingPoses.back();
116
117 providing.providerName = properties.providerName;
118 providing.timestamp = DateTime::Now();
119 }
120
121 if (current.confidence > properties.confidenceThreshold)
122 {
123 remainingPoses.push_back(current);
124 }
125 }
126
127 pushToMemory(providingPoses);
128
129 changes.clear();
130 storedPoses = remainingPoses;
131
132 isMemoryVizRequired = true;
133 }
134
135 void
136 Editor::visualizeMemory()
137 {
138 observer.clearObservedLayer(memoryLayer);
139
140 for (objpose::ObjectPose& objectPose : storedPoses)
141 {
142 if (objectPose.confidence > properties.confidenceThreshold)
143 {
144 visualizeObject(objectPose);
145 }
146 }
147
148 changes.visualizeNewObjects();
149 }
150
151 void
152 Editor::visualizeObject(objpose::ObjectPose& objectPose)
153 {
154 VisualizationDescription description = changes.buildVisualizationDescription(objectPose);
155
156 viz::InteractionDescription interaction = viz::interaction().selection();
157
158 if (description.allowTransforming)
159 {
161 }
162
163 if (not description.options.empty())
164 {
165 interaction.contextMenu(description.options);
166 }
167
168 viz::Object object = viz::Object(objectPose.objectID.str())
169 .pose(description.transform * objectPose.objectPoseGlobal)
170 .scale(properties.objectScaling)
171 .fileByObjectFinder(objectPose.objectID)
172 .enable(interaction);
173
174 if (description.alpha.has_value())
175 {
176 object.alpha(description.alpha.value());
177 }
178
179 if (description.color.has_value())
180 {
181 object.overrideColor(description.color.value());
182 }
183
184 observer.addObserved(memoryLayer, object)
185 .onContextMenu(description.cloneIndex,
186 [this, &objectPose]
187 {
188 changes.cloneObject(objectPose);
189 isMemoryVizRequired = true;
190 })
191 .onContextMenu(description.deleteIndex,
192 [this, &objectPose]
193 {
194 changes.deleteObject(objectPose);
195 isMemoryVizRequired = true;
196 })
197 .onContextMenu(description.resetIndex,
198 [this, &objectPose]
199 {
200 changes.resetObject(objectPose);
201 isMemoryVizRequired = true;
202 })
203 .onContextMenu(description.prototypeIndex,
204 [this, &objectPose]
205 {
206 const float defaultExtents = 100;
207 simox::OrientedBoxf box(
208 objectPose.objectPoseGlobal,
209 Eigen::Vector3f(defaultExtents, defaultExtents, defaultExtents));
210
211 box = objectPose.oobbGlobal().value_or(box);
212 box = box.transformed(changes.getTransform(objectPose));
213
214 placeholders.addPlaceholder(box);
215 isMetaVizRequired = true;
216 })
217 .onContextMenu(description.commitIndex, [this] { isCommitRequired = true; })
218 .onContextMenu(description.updateIndex, [this] { isUpdateRequired = true; })
219 .onContextMenu(description.resetAllIndex, [this] { isResetRequired = true; })
220 .onTransformEnd(
221 [this, &objectPose](const Eigen::Matrix4f& transform)
222 {
223 changes.moveObject(objectPose, transform);
224 isMemoryVizRequired = true;
225 });
226 }
227
228 void
229 Editor::visualizeMeta()
230 {
231 observer.clearObservedLayer(metaLayer);
232
233 placeholders.visualizePlaceholders();
234 }
235
236 void
237 Editor::visualizePlaceholder(PlaceholderState::Placeholder const& placeholder, size_t id)
238 {
239 viz::InteractionDescription interaction =
241 placeholderOptions);
242
243 viz::Box box = viz::Box("placeholder_" + std::to_string(id))
244 .set(placeholder.box.transformed(placeholder.transform))
245 .color(simox::Color::yellow(255, 128))
246 .enable(interaction);
247
248 auto& observation = observer.addObserved(metaLayer, box)
249 .onContextMenu(0,
250 [this, id]()
251 {
252 placeholders.removePlaceholder(id);
253 isMetaVizRequired = true;
254 })
255 .onTransformEnd(
256 [this, id](Eigen::Matrix4f const& transform)
257 {
258 placeholders.movePlaceholder(id, transform);
259 isMetaVizRequired = true;
260 });
261
262 for (size_t index = 2; index < placeholderOptions.size(); index++)
263 {
264 std::string const& object = placeholderOptions[index];
265
266 observation.onContextMenu(index,
267 [this, id, &object]
268 {
269 placeholders.specifyObject(id, object, changes);
270
271 isMetaVizRequired = true;
272 isMemoryVizRequired = true;
273 });
274 }
275 }
276
277 void
279 {
280 changed.clear();
281 newPoses.clear();
282 }
283
284 void
286 {
287 seq.insert(seq.begin(), newPoses.begin(), newPoses.end());
288 newPoses.clear();
289 }
290
291 bool
293 {
294 auto iterator = changed.find(pose.objectID.str());
295 bool isChanged = iterator != changed.end();
296
297 if (isChanged)
298 {
299 auto& [name, change] = *iterator;
300
301 if (change.kind == DELETE)
302 {
303 pose.confidence = 0;
304 }
305
306 pose.objectPoseGlobal = change.transform * pose.objectPoseGlobal;
307 }
308
309 return isChanged;
310 }
311
312 void
314 {
315 for (objpose::ObjectPose& objectPose : newPoses)
316 {
317 editor->visualizeObject(objectPose);
318 }
319 }
320
323 {
324 VisualizationDescription description;
325
326 auto iterator = changed.find(object.objectID.str());
327 bool isChanged = iterator != changed.end();
328
329 ChangeKind kind = MOVE;
330 if (isChanged)
331 {
332 auto& [name, change] = *iterator;
333 kind = change.kind;
334 }
335
336 description.allowTransforming = kind != DELETE;
337
338 size_t currentIndex = 0;
339
340 if (kind == MOVE)
341 {
342 description.options.emplace_back("Clone");
343 description.cloneIndex = currentIndex++;
344
345 description.options.emplace_back("Delete");
346 description.deleteIndex = currentIndex++;
347 }
348
349 if (isChanged)
350 {
351 auto& [name, change] = *iterator;
352
353 description.options.emplace_back("Reset");
354 description.resetIndex = currentIndex++;
355
356 description.transform = change.transform;
357
358 const float alpha = 0.5;
359
360 switch (kind)
361 {
362 case MOVE:
363 description.alpha = alpha;
364 break;
365
366 case CREATE:
367 description.color = simox::Color(0.0F, 1.0F, 0.0F, alpha);
368 break;
369
370 case DELETE:
371 description.color = simox::Color(1.0F, 0.0F, 0.0F, alpha);
372 break;
373 }
374 }
375
376 description.options.emplace_back("Create Placeholder");
377 description.prototypeIndex = currentIndex++;
378
379 description.options.emplace_back(Editor::lineString);
380 currentIndex++;
381
382 description.options.emplace_back("Commit All Changes");
383 description.commitIndex = currentIndex++;
384
385 description.options.emplace_back("Update Unchanged");
386 description.updateIndex = currentIndex++;
387
388 description.options.emplace_back("Reset All");
389 description.resetAllIndex = currentIndex; // ++
390
391 return description;
392 }
393
394 void
396 {
397 std::string suffix = std::to_string(std::chrono::duration_cast<std::chrono::seconds>(
398 std::chrono::system_clock::now().time_since_epoch())
399 .count());
400
401 objpose::ObjectPose& newPose = newPoses.emplace_back(object);
402 newPose.objectID =
403 object.objectID.withInstanceName(object.objectID.instanceName() + suffix);
404
405 const float minOffset = 100;
406 float offset = minOffset;
407 if (object.localOOBB.has_value())
408 {
409 Eigen::Vector3f size =
410 object.localOOBB.value().corner_max() - object.localOOBB.value().corner_min();
411 float objectOffset = size.maxCoeff() / 2;
412
413 offset = std::max(minOffset, objectOffset);
414 }
415
416 Change& clonedChange = changed[newPose.objectID.str()];
417 clonedChange.kind = CREATE;
418 // Heuristic: Don't shift in Z direction to ease creation in a horizontal plane.
419 clonedChange.transform = Eigen::Affine3f(Eigen::Translation3f(offset, offset, 0)).matrix();
420 clonedChange.iterator = std::prev(newPoses.end());
421
422 auto iterator = changed.find(object.objectID.str());
423 if (iterator != changed.end())
424 {
425 auto& [name, originalChange] = *iterator;
426 clonedChange.transform *= originalChange.transform;
427 }
428 }
429
430 void
431 ChangeState::createObject(const std::string& objectID, const Eigen::Matrix4f& pose)
432 {
433 std::string suffix = std::to_string(std::chrono::duration_cast<std::chrono::seconds>(
434 std::chrono::system_clock::now().time_since_epoch())
435 .count());
436
437 objpose::ObjectPose& newPose = newPoses.emplace_back();
438
439 armarx::ObjectID id(objectID);
440 newPose.objectID = id.withInstanceName(id.instanceName() + suffix);
441
442 newPose.providerName = editor->properties.providerName;
443 newPose.objectType = objpose::ObjectType::KnownObject;
444 newPose.isStatic = true;
445
446 newPose.objectPoseGlobal = pose;
447 newPose.confidence = 1;
448 newPose.timestamp = DateTime::Now();
449
450 std::optional<armarx::ObjectInfo> info = editor->objectFinder.findObject(id);
451 if (info.has_value())
452 {
453 newPose.localOOBB = info->loadOOBB();
454
455 if (newPose.localOOBB.has_value())
456 {
457 newPose.objectPoseGlobal =
458 pose * newPose.localOOBB->transformation_centered().inverse();
459 }
460 }
461
462 Change& createdChange = changed[newPose.objectID.str()];
463 createdChange.kind = CREATE;
464 createdChange.transform = Eigen::Matrix4f::Identity();
465 createdChange.iterator = std::prev(newPoses.end());
466 }
467
468 void
470 {
471 changed[object.objectID.str()].kind = DELETE;
472 }
473
474 void
476 {
477 auto iterator = changed.find(object.objectID.str());
478 if (iterator != changed.end())
479 {
480 auto& [name, change] = *iterator;
481
482 if (change.kind == CREATE)
483 {
484 newPoses.erase(change.iterator);
485 }
486
487 changed.erase(iterator);
488 }
489 }
490
491 void
492 ChangeState::moveObject(const objpose::ObjectPose& object, const Eigen::Matrix4f& transform)
493 {
494 Change& change = changed[object.objectID.str()];
495 change.transform = transform * change.transform;
496 }
497
498 Eigen::Matrix4f
500 {
501 Eigen::Matrix4f transform = Eigen::Matrix4f::Identity();
502
503 auto iterator = changed.find(object.objectID.str());
504 if (iterator != changed.end())
505 {
506 auto& [name, change] = *iterator;
507 transform = change.transform;
508 }
509
510 return transform;
511 }
512
513 void
514 PlaceholderState::addPlaceholder(simox::OrientedBoxf box)
515 {
516 size_t id = getID();
517
518 auto& entry = placeholders[id];
519 entry.placeholder = {.box = std::move(box), .transform = Eigen::Matrix4f::Identity()};
520 entry.isActive = true;
521 }
522
523 void
525 {
526 for (size_t id = 0; id < placeholders.size(); id++)
527 {
528 auto& entry = placeholders[id];
529
530 if (entry.isActive)
531 {
532 editor->visualizePlaceholder(entry.placeholder, id);
533 }
534 }
535 }
536
537 void
538 PlaceholderState::movePlaceholder(size_t id, Eigen::Matrix4f const& transform)
539 {
540 auto& placeholder = placeholders[id].placeholder;
541 placeholder.transform = transform * placeholder.transform;
542 }
543
544 void
546 {
547 placeholders[id].isActive = false;
548
549 unusedIDs.push(id);
550 }
551
552 size_t
553 PlaceholderState::getID()
554 {
555 if (unusedIDs.empty())
556 {
557 size_t id = placeholders.size();
558 placeholders.push_back({Placeholder(), false});
559 return id;
560 }
561
562 size_t id = unusedIDs.top();
563 unusedIDs.pop();
564 return id;
565 }
566
567 void
569 std::string const& objectID,
570 ChangeState& changeState)
571 {
572 auto& placeholder = placeholders[id].placeholder;
573
574 Eigen::Matrix4f pose =
575 placeholder.box.transformed(placeholder.transform).transformation_centered();
576 changeState.createObject(objectID, pose);
577
579 }
580} // namespace armarx
uint8_t index
void moveObject(objpose::ObjectPose const &object, Eigen::Matrix4f const &transform)
Definition Editor.cpp:492
void deleteObject(objpose::ObjectPose const &object)
Definition Editor.cpp:469
Eigen::Matrix4f getTransform(objpose::ObjectPose const &object)
Definition Editor.cpp:499
void createObject(std::string const &objectID, Eigen::Matrix4f const &pose)
Definition Editor.cpp:431
void moveNewObjectsTo(objpose::ObjectPoseSeq &seq)
Definition Editor.cpp:285
void cloneObject(objpose::ObjectPose const &object)
Definition Editor.cpp:395
bool applyTo(objpose::ObjectPose &pose)
Definition Editor.cpp:292
void visualizeNewObjects()
Definition Editor.cpp:313
void resetObject(objpose::ObjectPose const &object)
Definition Editor.cpp:475
VisualizationDescription buildVisualizationDescription(objpose::ObjectPose &object)
Definition Editor.cpp:322
static DateTime Now()
Definition DateTime.cpp:51
Editor(viz::Client &client, Properties properties, std::function< void(objpose::ProvidedObjectPoseSeq &)> pushToMemory, std::function< objpose::ObjectPoseSeq(void)> pullFromMemory)
Definition Editor.cpp:10
void step()
Definition Editor.cpp:33
A known object ID of the form "Dataset/ClassName" or "Dataset/ClassName/InstanceName".
Definition ObjectID.h:11
ObjectID withInstanceName(const std::string &instanceName) const
Definition ObjectID.cpp:83
std::string str() const
Return "dataset/className" or "dataset/className/instanceName".
Definition ObjectID.cpp:60
void addPlaceholder(simox::OrientedBoxf box)
Definition Editor.cpp:514
void specifyObject(size_t id, std::string const &objectID, ChangeState &changeState)
Definition Editor.cpp:568
void movePlaceholder(size_t id, Eigen::Matrix4f const &transform)
Definition Editor.cpp:538
void removePlaceholder(size_t id)
Definition Editor.cpp:545
std::vector< ObjectPose > ObjectPoseSeq
std::vector< ProvidedObjectPose > ProvidedObjectPoseSeq
InteractionDescription interaction()
Definition ElementOps.h:109
This file offers overloads of toIce() and fromIce() functions for STL container types.
auto transform(const Container< InputT, Alloc > &in, OutputT(*func)(InputT const &)) -> Container< OutputT, typename std::allocator_traits< Alloc >::template rebind_alloc< OutputT > >
Convenience function (with less typing) to transform a container of type InputT into the same contain...
Definition algorithm.h:351
std::vector< data::ObjectID > const & availableObjects
Definition Editor.h:120
std::optional< simox::Color > color
Definition Editor.h:24
std::vector< std::string > options
Definition Editor.h:22
Eigen::Matrix4f transform
Definition Editor.h:19
std::optional< float > alpha
Definition Editor.h:23
An object pose as stored by the ObjectPoseStorage.
Definition ObjectPose.h:34
float confidence
Confidence in [0, 1] (1 = full, 0 = none).
Definition ObjectPose.h:96
armarx::ObjectID objectID
The object ID, i.e. dataset, class name and instance name.
Definition ObjectPose.h:56
std::string providerName
Name of the providing component.
Definition ObjectPose.h:59
bool isStatic
Whether object is static. Static objects don't decay.
Definition ObjectPose.h:63
DateTime timestamp
Source timestamp.
Definition ObjectPose.h:98
ObjectType objectType
Known or unknown object.
Definition ObjectPose.h:61
std::optional< simox::OrientedBoxf > localOOBB
Object bounding box in object's local coordinate frame.
Definition ObjectPose.h:102
Eigen::Matrix4f objectPoseGlobal
The object pose in the global frame.
Definition ObjectPose.h:71
InteractionFeedbackRange interactions() const
Definition Client.h:85
Self & contextMenu(std::vector< std::string > const &options)
Definition ElementOps.h:54
A staged commit prepares multiple layers to be committed.
Definition Client.h:30
void add(Layer const &layer)
Stage a layer to be committed later via client.apply(*this)
Definition Client.h:36