SceneEditor.h
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package RobotAPI::ArmarXObjects::SceneEditor
17 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
18 * GNU General Public License
19 */
20
21#pragma once
22
23#include <filesystem>
24#include <map>
25#include <mutex>
26#include <optional>
27#include <string>
28#include <unordered_map>
29#include <vector>
30
31#include <Eigen/Core>
32#include <Eigen/Geometry>
33
34#include <SimoxUtility/shapes/AxisAlignedBoundingBox.h>
35
38
42
49
50namespace armarx
51{
57
58 /**
59 * @defgroup Component-SceneEditor SceneEditor
60 * @ingroup RobotAPI-Components
61 *
62 * An interactive editor for ArmarX object scenes.
63 *
64 * Objects from the standard ArmarX object database (found via
65 * `armarx::ObjectFinder`) can be spawned, dragged in ArViz, and saved as
66 * a scene JSON file (`armarx::objects::Scene` format).
67 *
68 * Objects are locked to a ground plane: they can only be translated in
69 * x/y and rotated continuously in yaw. The ground plane is defined by a
70 * scene object designated as ground (its bounding box top) or by a
71 * manually set height. Objects can be flipped by 90° around each axis
72 * via the RemoteGui or the context menu in ArViz. Setting coordinates
73 * manually in the RemoteGui overrides the ground locking. Moves whose
74 * collision meshes would intersect another object are rejected.
75 */
77 virtual public Component,
78 virtual public ArVizComponentPluginUser,
80 {
81 public:
82 std::string getDefaultName() const override;
83
84 protected:
86
87 void onInitComponent() override;
88 void onConnectComponent() override;
89 void onDisconnectComponent() override;
90 void onExitComponent() override;
91
92 void RemoteGui_update() override;
93 void remoteGuiUpdate();
94
95 private:
96 /// One object instance in the edited scene.
97 struct Entry
98 {
100 /// If true, the pose was set manually and the entry is not locked to the ground.
101 bool manualPose = false;
102 /// Instance name of the ground object this entry is grounded to
103 /// ("" = the default ground plane). Assigned when the entry is
104 /// created or locked; changing the global ground selection later
105 /// does not affect it.
106 std::string groundRef;
107 };
108
109 struct RemoteGuiTab : RemoteGui::Client::Tab
110 {
112 RemoteGui::Client::ComboBox objectClass;
114
115 RemoteGui::Client::ComboBox groundObject;
117
118 RemoteGui::Client::Label selectedInfo;
120 RemoteGui::Client::LineEdit instanceName;
121 RemoteGui::Client::Button renameObject;
128 RemoteGui::Client::Button lockToGround;
132 RemoteGui::Client::Button deleteObject;
133
134 RemoteGui::Client::ComboBox alignTarget;
135 RemoteGui::Client::CheckBox alignOutside;
140
144 RemoteGui::Client::Button clearScene;
146 };
147
148 /// (Re)creates the RemoteGui tab. Combo box options (datasets,
149 /// objects of the current dataset, scene instances for the ground
150 /// and align reference selection) are baked into the tab, so it is
151 /// rebuilt when they change.
152 void createRemoteGuiTab();
153
154 /// ArViz visualization and interaction loop.
155 void run();
156
157 // All methods below must be called with mutex_ held.
158
159 Entry* findEntry(const std::string& instanceName);
160 Entry& addObject(const std::string& classId);
161 /// Renames the entry and updates all references to it (ground
162 /// references, caches, selection). The new name is also used when
163 /// the scene is saved.
164 void renameEntry(Entry& entry, const std::string& newName);
165 void deleteEntry(const std::string& instanceName);
166 void flipEntry(Entry& entry, const Eigen::Vector3f& axis);
167 void handleInteraction(const viz::InteractionFeedback& interaction);
168 void applyPendingTransform(const std::string& instanceName);
169 bool applyManualPose(Entry& entry, const Eigen::Vector3f& position, float yawDeg);
170 void selectEntry(const std::string& instanceName);
171 void setGroundZ(float groundZ);
172 /// Aligns the selected object's bounding box face with the same face
173 /// of the align tab's reference object: `axis` 0 aligns yz-planes
174 /// (x direction), `axis` 1 aligns xz-planes (y direction); `minSide`
175 /// selects the front (min) face, otherwise the back (max) face.
176 void alignToPlane(int axis, bool minSide, const std::string& planeName);
177
178 /// Height of the ground referenced by `ref`: the top of that
179 /// object's bounding box, or the default ground plane height for "".
180 float groundHeightOf(const std::string& ref);
181 float groundHeightFor(const Entry& entry);
182 /// Re-seats all locked entries grounded to `ref` onto their ground.
183 void reseatEntriesGroundedTo(const std::string& ref);
184 /// Call after an entry's pose changed: re-seats entries grounded to
185 /// it and refreshes the ground height display if needed.
186 void afterEntryPoseChanged(const Entry& entry);
187
188 /// Returns the z coordinate for `entry`'s position such that the
189 /// lowest point of its bounding box (at `candidatePose`, which
190 /// determines the orientation) touches the ground plane. Falls back
191 /// to placing the origin on the ground if there is no bounding box.
192 float groundSnappedZ(const Entry& entry, const Eigen::Matrix4f& candidatePose);
193
194 /// The object's local-frame AABB (from the object database's
195 /// aabb.json, like e.g. armem_objects uses), cached per class.
196 const std::optional<simox::AxisAlignedBoundingBox>& localAabbOf(const Entry& entry);
197
198 /// The global axis-aligned bounding box of `entry` at `pose`: the
199 /// corners of the local AABB transformed into the world frame.
200 std::optional<simox::AxisAlignedBoundingBox> globalAabb(const Entry& entry,
201 const Eigen::Matrix4f& pose);
202
203 /// Returns the instance name of the first entry whose collision mesh
204 /// intersects `entry` at `candidatePose`, or "" if there is none.
205 std::string collidesWith(const Entry& entry, const Eigen::Matrix4f& candidatePose);
206 /// Checks whether moving `entry` to `candidatePose` is allowed and
207 /// sets a status message if not. Moves of already-colliding entries
208 /// are always allowed so that collisions can be resolved.
209 bool isMoveAllowed(Entry& entry, const Eigen::Matrix4f& candidatePose);
210 VirtualRobot::ObstaclePtr collisionModelOf(const Entry& entry);
211
212 void saveScene(const std::string& fileArg);
213 void loadScene(const std::string& fileArg);
214 /// Resolves the (absolute) path of a scene `.json` file. `name` may be
215 /// absolute (used verbatim) or relative; relative names are placed
216 /// directly into SceneStorageDirectory (if set) or the ScenesPackage's
217 /// scenes directory, and resolved to an absolute path.
218 std::filesystem::path resolveScenePath(std::string name) const;
219 /// Path of the grounding-info file accompanying `scenePath`: the same
220 /// path with the last "scenes" directory component replaced by
221 /// "groundings" (same file name), or a "<name>.groundings.json"
222 /// sibling if the scene path has no "scenes" component.
223 std::filesystem::path groundingPathFor(const std::filesystem::path& scenePath) const;
224 std::string makeUniqueInstanceName(const std::string& classId);
225
226 void rebuildSceneLayer();
227 void rebuildGroundLayer();
228
229 static float yawOf(const Eigen::Quaternionf& q);
230 static Eigen::Matrix4f poseOf(const objects::SceneObject& obj);
231
232 private:
233 RemoteGuiTab tab_;
235
236 std::mutex mutex_;
237
238 std::vector<Entry> entries_;
239 std::unordered_map<std::string, Eigen::Matrix4f> pendingTransforms_;
240 std::map<std::string, VirtualRobot::ObstaclePtr> collisionModels_;
241 std::map<std::string, std::optional<simox::AxisAlignedBoundingBox>> localAabbs_;
242 std::string selected_;
243
244 viz::Layer sceneLayer_;
245 viz::Layer groundLayer_;
246 bool sceneLayerDirty_ = true;
247 bool groundLayerDirty_ = true;
248 bool syncGui_ = false;
249 bool tabRebuildNeeded_ = false;
250 bool guiInitialized_ = false;
251 std::string status_;
252
253 /// Height of the default ground plane.
254 float groundZ_ = 0.0f;
255
256 std::map<std::string, std::vector<std::string>> objectsByDataset_;
257 std::string currentDataset_;
258
259 std::string objectsPackage_;
260 std::string scenesPackage_;
261 /// Base directory for scene storage. If empty, the ScenesPackage's
262 /// data directory is used.
263 std::string sceneStorageDirectory_;
264 std::string initialSceneFile_;
265 std::string initialGroundClass_;
266 int nextId_ = 1;
267
268 ObjectFinder objectFinder_;
269 };
270} // namespace armarx
ComponentPropertyDefinitions(std::string prefix, bool hasObjectNameParameter=true)
Definition Component.cpp:46
Component()
Protected default constructor. Used for virtual inheritance. Use createManagedIceObject() instead.
Definition Component.cpp:66
Used to find objects in the ArmarX objects repository [1] (formerly [2]).
std::string prefix
Prefix of the properties such as namespace, domain, component name, etc.
IceUtil::Handle< RunningTask< T > > pointer_type
Shared pointer type for convenience.
SceneEditorPropertyDefinitions(std::string prefix)
void onInitComponent() override
Pure virtual hook for the subclass.
void onDisconnectComponent() override
Hook for subclass.
void RemoteGui_update() override
SceneEditor()
All widgets exposed by this plugin are added in the constructor via calls to addWidget()
void onConnectComponent() override
Pure virtual hook for the subclass.
PropertyDefinitionsPtr createPropertyDefinitions() override
void onExitComponent() override
Hook for subclass.
std::string getDefaultName() const override
Retrieve default name of component.
#define q
Quaternion< float, 0 > Quaternionf
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.