/
KinematicForest.h
307 lines (265 loc) · 10.1 KB
/
KinematicForest.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
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/**
* \file IMP/kinematics/KinematicForest.h
* \brief Define and manipulate a kinematic structure over a model.
* \authors Dina Schneidman, Barak Raveh
*
* Copyright 2007-2022 IMP Inventors. All rights reserved.
*/
#ifndef IMPKINEMATICS_KINEMATIC_FOREST_H
#define IMPKINEMATICS_KINEMATIC_FOREST_H
#include <IMP/kinematics/kinematics_config.h>
#include <IMP/Model.h>
#include <IMP/kinematics/KinematicNode.h>
#include <IMP/kinematics/Joint.h>
#include <IMP/kinematics/TransformationJoint.h>
#include <IMP/Object.h>
#include <IMP/Decorator.h>
#include <boost/unordered_set.hpp>
#include <IMP/exception.h>
#include <IMP/Object.h>
#include <IMP/check_macros.h>
#include <IMP/warning_macros.h>
#include <IMP/atom/Hierarchy.h>
#include <queue>
#include <algorithm>
IMPKINEMATICS_BEGIN_NAMESPACE
//! Define and manipulate a kinematic structure over a model.
/** The kinematic structure is defined as a
forest over model particles at a certain hierarchy, such that a
directed edge indicates the propagation of changes in internal
coordinates (joint values) to values of external (Cartesian)
coordinates.
\see Joint
*/
class IMPKINEMATICSEXPORT KinematicForest
: public Object // or ModelObject?
{
public:
IMP_OBJECT_METHODS(KinematicForest);
KinematicForest(Model* m);
// TODO: think about what foldtree scheme to use (star?),
/**
Builds a kinematic forest automatically from a hierarchy that
contains rigid bodies and adds a ScoreState to the model, to
make sure the internal and external coordinates are synced
before model updating
*/
KinematicForest(Model* m, IMP::atom::Hierarchy hierarchy);
/**
Adds a kinematic edge between parent and child,
using a TransformationJoint between them, and
decorating them as KinematicNodes if needed.
*/
// NOTE: must have root on first call
// TODO: verify parent_rb is in tree
Joint* add_edge(IMP::core::RigidBody parent, IMP::core::RigidBody child) {
// create joint and associate it with parent and child
IMP_NEW(TransformationJoint, joint, (parent, child));
add_edge(joint);
return joint;
}
/**
Adds a kinematic edge between the joint parent and child
rigid bodies, decorating them as KinematicNodes if needed.
The joint becomes owned by this KinematicForest, such that
changes to the joint are synchronized with the KinematicForest
@note it is assumed that neither the joint or the rigid bodies in it
were previously added to a kinematic forest (might change in
future IMP versions)
*/
void add_edge(Joint* joint);
/**
adds edges between each pair of consecutive rigid bodies in the list
rbs, using default TransformationJoint joints (transforming from one
rigid body to the next)
@param rbs list of n consecutive rigid bodies
*/
void add_rigid_bodies_in_chain(IMP::core::RigidBodies rbs) {
for (int i = 0; i < (int)rbs.size() - 1; i++) {
add_edge(rbs[i], rbs[i + 1]);
}
}
//! rebuild tree (same topology but change directionality)
void reset_root(IMP::Particle* new_root) {
// TODO: implement
IMP_NOT_IMPLEMENTED;
IMP_UNUSED(new_root);
}
//! updated internal coordinates in the forest based on the current cartesian
//! coordinates and the architecture of joints in the tree
void update_all_internal_coordinates() {
IMP_LOG(VERBOSE, "updating internal coords needed?" << std::endl);
if (is_internal_coords_updated_) {
return;
}
IMP_LOG(VERBOSE, "updating!" << std::endl);
for (unsigned int i = 0; i < joints_.size(); i++) {
joints_[i]->update_joint_from_cartesian_witnesses();
}
is_internal_coords_updated_ = true;
}
//! update all external coordinates of particles in the forest
//! based on their internal coordinates
void update_all_external_coordinates() {
if (is_external_coords_updated_) {
return;
}
// tree BFS traversal from roots
std::queue<KinematicNode> q;
boost::unordered_set<KinematicNode>::iterator it;
for (it = roots_.begin(); it != roots_.end(); it++) {
q.push(*it);
}
while (!q.empty()) {
KinematicNode n = q.front();
q.pop();
JointsTemp out_joints = n.get_out_joints();
for (unsigned int i = 0; i < out_joints.size(); i++) {
Joint* joint_i = out_joints[i];
// TODO: add and implement to joint
joint_i->update_child_node_reference_frame();
q.push(KinematicNode(joint_i->get_child_node()));
}
}
is_external_coords_updated_ = true;
}
//! return joints sorted by BFS traversal
Joints get_ordered_joints() const {
Joints ret;
// tree BFS traversal from roots
std::queue<KinematicNode> q;
boost::unordered_set<KinematicNode>::iterator it;
for (it = roots_.begin(); it != roots_.end(); it++) q.push(*it);
while (!q.empty()) {
KinematicNode n = q.front();
q.pop();
if (n.get_in_joint() != nullptr) ret.push_back(n.get_in_joint());
JointsTemp out_joints = n.get_out_joints();
for (unsigned int i = 0; i < out_joints.size(); i++) {
Joint* joint_i = out_joints[i];
q.push(KinematicNode(joint_i->get_child_node()));
}
}
return ret;
}
//! apply a rigid body transformation to the entire forest
//! safelt, such that the forest will return correct external
//! and internal coordinates if queries through get_coordinates_safe()
//! or get_reference_frame_safe(), or after updating with
//! update_all_external_coordinates() or update_all_internal_coordinates(),
//! respectively.
void transform_safe(IMP::algebra::Transformation3D tr){
for(KinematicNode root : roots_){
IMP::core::transform(root, tr);
mark_internal_coordinates_changed(); // technically, roots reference frames is a part of the internal tree coordinates, so external coordinates will need to be updated at some point
}
}
/**
notifies the tree that joint (internal) coordinates
have changed and therefore external coordinates are not
up to date
*/
void mark_internal_coordinates_changed() {
is_external_coords_updated_ = false;
}
/**
notifies the tree that external Cartesian coordinates
have changed and therefore internal (joint) coordinates are not
up to date
*/
void mark_external_coordinates_changed() {
is_internal_coords_updated_ = false;
}
/**
sets the coordinates of a particle, and makes sure that particles
and joints in the tree will return correct external and internal
coordinates
@param rb a rigid body that was previously added to the tree
@param c new coordinates
*/
void set_coordinates_safe(IMP::core::RigidBody rb, IMP::algebra::Vector3D c) {
IMP_USAGE_CHECK(get_is_member(rb),
"A KinematicForest can only handle particles "
<< " that were previously added to it");
rb.set_coordinates(c);
mark_external_coordinates_changed();
}
/**
*/
IMP::algebra::Vector3D get_coordinates_safe(IMP::core::RigidBody rb) const {
IMP_USAGE_CHECK(get_is_member(rb),
"A KinematicForest can only handle particles "
<< " that were previously added to it");
const_cast<KinematicForest*>(this)->update_all_external_coordinates();
return rb.get_coordinates();
}
/**
*/
bool get_is_member(IMP::core::RigidBody rb) const {
Particle* p = rb.get_particle();
return KinematicNode::get_is_setup(p) &&
nodes_.find(KinematicNode(p)) != nodes_.end();
}
// TODO: do we want to add safe access to rigid body members?
/**
*/
IMP::algebra::ReferenceFrame3D get_reference_frame_safe(
IMP::core::RigidBody rb) const {
IMP_USAGE_CHECK(get_is_member(rb),
"A KinematicForest can only handle particles "
<< " that were previously added to it");
const_cast<KinematicForest*>(this)->update_all_external_coordinates();
return rb.get_reference_frame();
}
/**
sets the reference frame of a rigid body, and makes sure that
particles and joints in the tree will return correct
internal coordinates when queried directly through the
KinematicForest
@param rb a rigid body that was previously added to the tree
@param r new reference frame
*/
void set_reference_frame_safe(IMP::core::RigidBody rb,
IMP::algebra::ReferenceFrame3D r) {
IMP_USAGE_CHECK(get_is_member(rb),
"A KinematicForest can only handle particles "
<< " that were previously added to it");
rb.set_reference_frame(r);
mark_external_coordinates_changed();
}
//! apply a rigid body transformation to the entire forest safely
/** Apply a rigid body transformation to the entire forest
safely, such that the forest will return correct external
and internal coordinates if queries through get_coordinates_safe()
or get_reference_frame_safe(), or after updating with
update_all_external_coordinates() or update_all_internal_coordinates(),
respectively.
*/
void apply_transform_safely(IMP::algebra::Transformation3D tr)
{
for(KinematicNode root : roots_){
IMP::core::transform(root, tr);
mark_internal_coordinates_changed(); // technically, roots reference frames is a part of the internal tree coordinates, so external coordinates will need to be updated at some point
}
}
// TODO: handle derivatives, and safe getting / setting of them
private:
IMP::Particles get_children(IMP::Particle parent) const;
IMP::Particle get_parent(IMP::Particle child) const;
#ifndef SWIG
friend std::ostream& operator<<(std::ostream& s, const KinematicForest& kt);
#endif
private:
Model* m_;
bool is_internal_coords_updated_;
bool is_external_coords_updated_;
/** the root nodes that serves as spatial anchor to the
kinematic trees in the forest */
boost::unordered_set<KinematicNode> roots_;
/** the set of nodes in the tree */
boost::unordered_set<KinematicNode> nodes_;
Joints joints_;
};
IMP_OBJECTS(KinematicForest, KinematicForests);
IMPKINEMATICS_END_NAMESPACE
#endif /* IMPKINEMATICS_KINEMATIC_FOREST_H */