| Directory: | ./ |
|---|---|
| File: | src/device.cc |
| Date: | 2025-05-04 12:09:19 |
| Exec | Total | Coverage | |
|---|---|---|---|
| Lines: | 202 | 305 | 66.2% |
| Branches: | 171 | 530 | 32.3% |
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // | ||
| 2 | // Copyright (c) 2016 CNRS | ||
| 3 | // Author: NMansard | ||
| 4 | // | ||
| 5 | // | ||
| 6 | |||
| 7 | // Redistribution and use in source and binary forms, with or without | ||
| 8 | // modification, are permitted provided that the following conditions are | ||
| 9 | // met: | ||
| 10 | // | ||
| 11 | // 1. Redistributions of source code must retain the above copyright | ||
| 12 | // notice, this list of conditions and the following disclaimer. | ||
| 13 | // | ||
| 14 | // 2. Redistributions in binary form must reproduce the above copyright | ||
| 15 | // notice, this list of conditions and the following disclaimer in the | ||
| 16 | // documentation and/or other materials provided with the distribution. | ||
| 17 | // | ||
| 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 22 | // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | ||
| 29 | // DAMAGE. | ||
| 30 | |||
| 31 | #include <pinocchio/fwd.hpp> | ||
| 32 | |||
| 33 | // include pinocchio first | ||
| 34 | |||
| 35 | #include <coal/BV/AABB.h> | ||
| 36 | #include <coal/BVH/BVH_model.h> | ||
| 37 | |||
| 38 | #include <Eigen/Core> | ||
| 39 | #include <boost/foreach.hpp> | ||
| 40 | #include <boost/serialization/export.hpp> | ||
| 41 | #include <hpp/pinocchio/device.hh> | ||
| 42 | #include <hpp/pinocchio/fwd.hh> | ||
| 43 | #include <hpp/pinocchio/joint-collection.hh> | ||
| 44 | #include <hpp/util/serialization.hh> | ||
| 45 | #include <pinocchio/algorithm/geometry.hpp> | ||
| 46 | #include <pinocchio/algorithm/joint-configuration.hpp> // ::pinocchio::details::Dispatch | ||
| 47 | #include <pinocchio/algorithm/model.hpp> | ||
| 48 | #include <pinocchio/multibody/geometry.hpp> | ||
| 49 | #include <pinocchio/multibody/model.hpp> | ||
| 50 | #include <pinocchio/serialization/model.hpp> | ||
| 51 | // #include <hpp/pinocchio/distance-result.hh> | ||
| 52 | #include <hpp/pinocchio/body.hh> | ||
| 53 | #include <hpp/pinocchio/collision-object.hh> | ||
| 54 | #include <hpp/pinocchio/extra-config-space.hh> | ||
| 55 | #include <hpp/pinocchio/gripper.hh> | ||
| 56 | #include <hpp/pinocchio/joint.hh> | ||
| 57 | #include <hpp/pinocchio/liegroup-space.hh> | ||
| 58 | #include <hpp/pinocchio/liegroup.hh> | ||
| 59 | #include <hpp/pinocchio/serialization.hh> | ||
| 60 | |||
| 61 | namespace hpp { | ||
| 62 | namespace pinocchio { | ||
| 63 | const ::pinocchio::FrameType all_joint_type = | ||
| 64 | (::pinocchio::FrameType)(::pinocchio::JOINT | ::pinocchio::FIXED_JOINT); | ||
| 65 | |||
| 66 | 30 | Device::Device(const std::string& name) | |
| 67 |
4/8✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 30 times.
✗ Branch 6 not taken.
✓ Branch 11 taken 30 times.
✗ Branch 12 not taken.
✓ Branch 16 taken 30 times.
✗ Branch 17 not taken.
|
30 | : AbstractDevice(), name_(name), weakPtr_() { |
| 68 | 30 | invalidate(); | |
| 69 |
1/2✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
|
30 | createData(); |
| 70 |
1/2✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
|
30 | createGeomData(); |
| 71 | |||
| 72 |
1/2✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
|
30 | numberDeviceData(1); |
| 73 | 30 | } | |
| 74 | |||
| 75 | ✗ | Device::Device(const Device& other) | |
| 76 | ✗ | : AbstractDevice(other.model_, other.geomModel_), | |
| 77 | ✗ | d_(other.d_), | |
| 78 | ✗ | name_(other.name_), | |
| 79 | ✗ | grippers_(), | |
| 80 | ✗ | extraConfigSpace_(other.extraConfigSpace_), | |
| 81 | ✗ | weakPtr_(), | |
| 82 | ✗ | datas_() { | |
| 83 | ✗ | numberDeviceData(other.numberDeviceData()); | |
| 84 | } | ||
| 85 | |||
| 86 | 118 | Device::~Device() { datas_.clear(); } | |
| 87 | |||
| 88 | 155 | void Device::numberDeviceData(const size_type& s) { | |
| 89 | // Delete current device datas | ||
| 90 |
1/2✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
|
155 | datas_.clear(); |
| 91 | // Create new device datas | ||
| 92 |
1/2✓ Branch 2 taken 155 times.
✗ Branch 3 not taken.
|
155 | std::vector<DeviceData*> datas((std::size_t)s); |
| 93 |
2/2✓ Branch 0 taken 100 times.
✓ Branch 1 taken 155 times.
|
255 | for (std::size_t i = 0; i < (std::size_t)s; ++i) |
| 94 |
2/4✓ Branch 1 taken 100 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 100 times.
✗ Branch 5 not taken.
|
100 | datas[i] = new DeviceData(d_); |
| 95 |
1/2✓ Branch 3 taken 155 times.
✗ Branch 4 not taken.
|
155 | datas_.push_back(datas.begin(), datas.end()); |
| 96 | 155 | } | |
| 97 | |||
| 98 | 124 | size_type Device::numberDeviceData() const { return (size_type)datas_.size(); } | |
| 99 | |||
| 100 | // static method | ||
| 101 | 28 | DevicePtr_t Device::create(const std::string& name) { | |
| 102 |
2/4✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 28 times.
✗ Branch 6 not taken.
|
28 | DevicePtr_t res = DevicePtr_t(new Device(name)); // init shared ptr |
| 103 |
1/2✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
|
28 | res->init(res); |
| 104 | 28 | return res; | |
| 105 | } | ||
| 106 | |||
| 107 | // static method | ||
| 108 | ✗ | DevicePtr_t Device::createCopy(const DevicePtr_t& other) { | |
| 109 | ✗ | DevicePtr_t res = DevicePtr_t(new Device(*other)); // init shared ptr | |
| 110 | ✗ | res->initCopy(res, *other); | |
| 111 | ✗ | return res; | |
| 112 | } | ||
| 113 | |||
| 114 | // static method | ||
| 115 | ✗ | DevicePtr_t Device::createCopyConst(const DeviceConstPtr_t& device) { | |
| 116 | ✗ | DevicePtr_t res = Device::create(device->name()); // init shared ptr | |
| 117 | /* The copy of Pinocchio::Model is not implemented yet. */ | ||
| 118 | /* We need this feature to finish the implementation of this method. */ | ||
| 119 | ✗ | assert(false && "TODO: createCopyConst is not implemented yet."); | |
| 120 | return res; | ||
| 121 | } | ||
| 122 | |||
| 123 | 30 | void Device::init(const DeviceWkPtr_t& weakPtr) { | |
| 124 | 30 | weakPtr_ = weakPtr; | |
| 125 | 30 | d_.devicePtr_ = weakPtr; | |
| 126 | 30 | } | |
| 127 | |||
| 128 | ✗ | void Device::initCopy(const DeviceWkPtr_t& weakPtr, const Device& other) { | |
| 129 | ✗ | init(weakPtr); | |
| 130 | ✗ | grippers_.resize(other.grippers_.size()); | |
| 131 | ✗ | for (std::size_t i = 0; i < grippers_.size(); ++i) | |
| 132 | ✗ | grippers_[i] = Gripper::createCopy(other.grippers_[i], weakPtr); | |
| 133 | } | ||
| 134 | |||
| 135 | 60 | void Device::createData() { | |
| 136 |
1/2✓ Branch 3 taken 60 times.
✗ Branch 4 not taken.
|
60 | d_.data_ = DataPtr_t(new Data(model())); |
| 137 | // We assume that model is now complete and state can be resized. | ||
| 138 | 60 | resizeState(); | |
| 139 | 60 | } | |
| 140 | |||
| 141 | 60 | void Device::createGeomData() { | |
| 142 |
2/4✓ Branch 3 taken 60 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 60 times.
✗ Branch 8 not taken.
|
60 | d().geomData_ = GeomDataPtr_t(new GeomData(geomModel())); |
| 143 | 60 | ::pinocchio::computeBodyRadius(model(), geomModel(), geomData()); | |
| 144 | 60 | d().invalidate(); | |
| 145 |
1/2✓ Branch 2 taken 60 times.
✗ Branch 3 not taken.
|
60 | numberDeviceData(numberDeviceData()); |
| 146 | 60 | } | |
| 147 | |||
| 148 | 1 | void Device::removeJoints(const std::vector<std::string>& jointNames, | |
| 149 | Configuration_t referenceConfig) { | ||
| 150 |
1/2✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
1 | std::vector<JointIndex> joints(jointNames.size()); |
| 151 |
1/2✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | std::transform(jointNames.begin(), jointNames.end(), joints.begin(), |
| 152 | 4 | [this](const std::string& n) { | |
| 153 | 2 | auto id = model_->getJointId(n); | |
| 154 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
|
2 | if (id >= model_->joints.size()) |
| 155 | ✗ | throw std::invalid_argument("Device " + name_ + | |
| 156 | " does not have " | ||
| 157 | ✗ | "any joint named " + | |
| 158 | ✗ | n); | |
| 159 | 2 | return id; | |
| 160 | }); | ||
| 161 | |||
| 162 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
|
1 | ModelPtr_t nModel(new Model); |
| 163 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
|
1 | GeomModelPtr_t nGeomModel(new GeomModel); |
| 164 |
1/2✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
1 | ::pinocchio::buildReducedModel(*model_, *geomModel_, joints, |
| 165 |
1/2✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
1 | referenceConfig.head(model_->nq), *nModel, |
| 166 | 1 | *nGeomModel); | |
| 167 | 1 | model_ = nModel; | |
| 168 | 1 | geomModel_ = nGeomModel; | |
| 169 | |||
| 170 | // update the grippers | ||
| 171 |
1/2✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | std::transform(grippers_.begin(), grippers_.end(), grippers_.begin(), |
| 172 | ✗ | [this](GripperPtr_t g) { | |
| 173 | ✗ | return Gripper::create(g->name(), this->weakPtr_); | |
| 174 | }); | ||
| 175 | |||
| 176 | 1 | invalidate(); | |
| 177 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | createData(); |
| 178 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | createGeomData(); |
| 179 | |||
| 180 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | numberDeviceData(numberDeviceData()); |
| 181 | 1 | } | |
| 182 | |||
| 183 | /* ---------------------------------------------------------------------- */ | ||
| 184 | /* --- JOINT ------------------------------------------------------------ */ | ||
| 185 | /* ---------------------------------------------------------------------- */ | ||
| 186 | |||
| 187 | 18 | JointPtr_t Device::rootJoint() const { | |
| 188 |
1/2✓ Branch 3 taken 18 times.
✗ Branch 4 not taken.
|
18 | return Joint::create(weakPtr_.lock(), 1); |
| 189 | } | ||
| 190 | |||
| 191 | ✗ | Frame Device::rootFrame() const { | |
| 192 | ✗ | return Frame(weakPtr_.lock(), | |
| 193 | ✗ | model().getFrameId("root_joint", all_joint_type)); | |
| 194 | } | ||
| 195 | |||
| 196 | 81 | size_type Device::nbJoints() const { | |
| 197 | 81 | return size_type(model().joints.size() - 1); | |
| 198 | } | ||
| 199 | |||
| 200 | 80 | JointPtr_t Device::jointAt(const size_type& i) const { | |
| 201 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 80 times.
|
80 | assert(i < nbJoints()); |
| 202 |
1/2✓ Branch 3 taken 80 times.
✗ Branch 4 not taken.
|
80 | return Joint::create(weakPtr_.lock(), JointIndex(i + 1)); |
| 203 | } | ||
| 204 | |||
| 205 | ✗ | JointPtr_t Device::getJointAtConfigRank(const size_type& r) const { | |
| 206 | // BOOST_FOREACH( const JointModel & j, // Skip "universe" joint | ||
| 207 | // std::make_pair(model_->joints.begin()+1,model_->joints.end()) ) | ||
| 208 | ✗ | BOOST_FOREACH (const JointModel& j, model().joints) { | |
| 209 | ✗ | if (j.id() == 0) continue; // Skip "universe" joint | |
| 210 | ✗ | const size_type iq = r - j.idx_q(); | |
| 211 | ✗ | if (0 <= iq && iq < j.nq()) return Joint::create(weakPtr_.lock(), j.id()); | |
| 212 | } | ||
| 213 | ✗ | assert(false && "The joint at config rank has not been found"); | |
| 214 | return JointPtr_t(); | ||
| 215 | } | ||
| 216 | |||
| 217 | ✗ | JointPtr_t Device::getJointAtVelocityRank(const size_type& r) const { | |
| 218 | ✗ | BOOST_FOREACH (const JointModel& j, model().joints) { | |
| 219 | ✗ | if (j.id() == 0) continue; // Skip "universe" joint | |
| 220 | ✗ | const size_type iv = r - j.idx_v(); | |
| 221 | ✗ | if (0 <= iv && iv < j.nv()) return Joint::create(weakPtr_.lock(), j.id()); | |
| 222 | } | ||
| 223 | ✗ | assert(false && "The joint at velocity rank has not been found"); | |
| 224 | return JointPtr_t(); | ||
| 225 | } | ||
| 226 | |||
| 227 | 30 | JointPtr_t Device::getJointByName(const std::string& name) const { | |
| 228 |
2/2✓ Branch 2 taken 2 times.
✓ Branch 3 taken 28 times.
|
30 | if (!model().existJointName(name)) |
| 229 |
3/6✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
|
6 | throw std::runtime_error("Device " + name_ + |
| 230 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
8 | " does not have any joint named " + name); |
| 231 | 28 | JointIndex id = model().getJointId(name); | |
| 232 |
1/2✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
|
28 | return Joint::create(weakPtr_.lock(), id); |
| 233 | } | ||
| 234 | |||
| 235 | 12 | JointPtr_t Device::getJointByBodyName(const std::string& name) const { | |
| 236 |
3/4✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
✓ Branch 5 taken 2 times.
|
12 | if (model().existFrame(name)) { |
| 237 |
1/2✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
|
10 | FrameIndex bodyId = model().getFrameId(name); |
| 238 |
1/2✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
|
10 | if (model().frames[bodyId].type == ::pinocchio::BODY) { |
| 239 | 10 | JointIndex jointId = model().frames[bodyId].parent; | |
| 240 | // assert(jointId>=0); | ||
| 241 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
|
10 | assert((std::size_t)jointId < model().joints.size()); |
| 242 |
1/2✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
|
10 | return Joint::create(weakPtr_.lock(), jointId); |
| 243 | } | ||
| 244 | } | ||
| 245 |
3/6✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
|
6 | throw std::runtime_error("Device " + name_ + |
| 246 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
8 | " has no joint with body of name " + name); |
| 247 | } | ||
| 248 | |||
| 249 | 8 | Frame Device::getFrameByName(const std::string& name) const { | |
| 250 |
2/4✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
|
8 | if (!model().existFrame(name)) |
| 251 | ✗ | throw std::logic_error("Device " + name_ + | |
| 252 | ✗ | " does not have any frame named " + name); | |
| 253 |
1/2✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | FrameIndex id = model().getFrameId(name); |
| 254 |
1/2✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
|
8 | return Frame(weakPtr_.lock(), id); |
| 255 | } | ||
| 256 | |||
| 257 | 168 | size_type Device::configSize() const { | |
| 258 | 168 | return model().nq + extraConfigSpace_.dimension(); | |
| 259 | } | ||
| 260 | |||
| 261 | 171 | size_type Device::numberDof() const { | |
| 262 | 171 | return model().nv + extraConfigSpace_.dimension(); | |
| 263 | } | ||
| 264 | |||
| 265 | /* ---------------------------------------------------------------------- */ | ||
| 266 | /* --- CONFIG ----------------------------------------------------------- */ | ||
| 267 | /* ---------------------------------------------------------------------- */ | ||
| 268 | |||
| 269 | 62 | void Device::resizeState() { | |
| 270 | 62 | d_.invalidate(); | |
| 271 | 62 | d_.currentConfiguration_ = neutralConfiguration(); | |
| 272 |
1/2✓ Branch 3 taken 62 times.
✗ Branch 4 not taken.
|
62 | d_.currentVelocity_ = vector_t::Zero(numberDof()); |
| 273 |
1/2✓ Branch 3 taken 62 times.
✗ Branch 4 not taken.
|
62 | d_.currentAcceleration_ = vector_t::Zero(numberDof()); |
| 274 | 62 | d_.jointJacobians_.resize((std::size_t)model().njoints); | |
| 275 | |||
| 276 | 62 | configSpace_ = LiegroupSpace::empty(); | |
| 277 | 62 | configSpaceRnxSOn_ = LiegroupSpace::empty(); | |
| 278 | 62 | const Model& m(model()); | |
| 279 |
2/2✓ Branch 1 taken 557 times.
✓ Branch 2 taken 62 times.
|
619 | for (JointIndex i = 1; i < m.joints.size(); ++i) { |
| 280 |
3/6✓ Branch 2 taken 557 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 557 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 557 times.
✗ Branch 11 not taken.
|
557 | *configSpace_ *= Joint(weakPtr_, i).configurationSpace(); |
| 281 |
3/6✓ Branch 2 taken 557 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 557 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 557 times.
✗ Branch 11 not taken.
|
557 | *configSpaceRnxSOn_ *= Joint(weakPtr_, i).RnxSOnConfigurationSpace(); |
| 282 | } | ||
| 283 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 59 times.
|
62 | if (extraConfigSpace_.dimension() > 0) { |
| 284 | LiegroupSpacePtr_t extra = | ||
| 285 |
1/2✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | LiegroupSpace::create(extraConfigSpace_.dimension()); |
| 286 |
1/2✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
|
3 | *configSpace_ *= extra; |
| 287 |
1/2✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
|
3 | *configSpaceRnxSOn_ *= extra; |
| 288 | 3 | } | |
| 289 | |||
| 290 |
1/2✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
|
62 | numberDeviceData(numberDeviceData()); |
| 291 | 62 | } | |
| 292 | |||
| 293 | 82 | Configuration_t Device::neutralConfiguration() const { | |
| 294 | 82 | const Model& m(model()); | |
| 295 |
1/2✓ Branch 2 taken 82 times.
✗ Branch 3 not taken.
|
82 | Configuration_t n(configSize()); |
| 296 |
2/4✓ Branch 1 taken 82 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 82 times.
✗ Branch 5 not taken.
|
82 | ::pinocchio::neutral(m, n.head(m.nq)); |
| 297 |
2/4✓ Branch 2 taken 82 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 82 times.
✗ Branch 6 not taken.
|
82 | n.tail(extraConfigSpace_.dimension()).setZero(); |
| 298 | 82 | return n; | |
| 299 | } | ||
| 300 | |||
| 301 | 13 | void Device::addJointConstraint(JointLinearConstraint constraint) { | |
| 302 |
2/4✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
|
13 | assert(constraint.joint && constraint.reference); |
| 303 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 13 times.
|
13 | assert(constraint.joint->configSize() == 1); |
| 304 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 13 times.
|
13 | assert(constraint.reference->configSize() == 1); |
| 305 | |||
| 306 | 13 | jointConstraints_.push_back(constraint); | |
| 307 | 13 | } | |
| 308 | |||
| 309 | ✗ | std::ostream& Device::print(std::ostream& os) const { | |
| 310 | ✗ | os << model() << iendl; | |
| 311 | // Print frame names | ||
| 312 | ✗ | os << "Frames" << std::endl; | |
| 313 | ✗ | for (FrameIndex i = 0; i < (FrameIndex)model().nframes; ++i) { | |
| 314 | ✗ | const ::pinocchio::Frame& frame(model().frames[i]); | |
| 315 | ✗ | os << frame.name << "\t parent:" << model().names[frame.parent] | |
| 316 | ✗ | << std::endl; | |
| 317 | } | ||
| 318 | ✗ | if (jointConstraints_.size() > 0) | |
| 319 | ✗ | os << "Joint linear constraints:" << incindent; | |
| 320 | ✗ | for (std::size_t i = 0; i < jointConstraints_.size(); ++i) | |
| 321 | ✗ | os << iendl << jointConstraints_[i].joint->name() << " = " | |
| 322 | ✗ | << jointConstraints_[i].multiplier << " * " | |
| 323 | ✗ | << jointConstraints_[i].reference->name() << " + " | |
| 324 | ✗ | << jointConstraints_[i].offset; | |
| 325 | ✗ | return os << decindent << std::endl; | |
| 326 | } | ||
| 327 | |||
| 328 | /* ---------------------------------------------------------------------- */ | ||
| 329 | /* --- COLLISIONS ------------------------------------------------------- */ | ||
| 330 | /* ---------------------------------------------------------------------- */ | ||
| 331 | |||
| 332 | ✗ | BodyPtr_t Device::obstacles() const { | |
| 333 | ✗ | return BodyPtr_t(new Body(weakPtr_.lock(), 0)); | |
| 334 | } | ||
| 335 | |||
| 336 | ✗ | size_type Device::nbObjects() const { | |
| 337 | ✗ | return (size_type)geomModel().geometryObjects.size(); | |
| 338 | } | ||
| 339 | |||
| 340 | ✗ | CollisionObjectPtr_t Device::objectAt(const size_type& i) const { | |
| 341 | ✗ | assert(i < nbObjects()); | |
| 342 | return CollisionObjectPtr_t( | ||
| 343 | ✗ | new CollisionObject(weakPtr_.lock(), (GeomIndex)i)); | |
| 344 | } | ||
| 345 | |||
| 346 | ✗ | bool Device::collisionTest(const bool stopAtFirstCollision) { | |
| 347 | /* Following hpp::model API, the forward kinematics (joint placement) is | ||
| 348 | * supposed to have already been computed. */ | ||
| 349 | ✗ | updateGeometryPlacements(); | |
| 350 | ✗ | return ::pinocchio::computeCollisions(geomModel(), geomData(), | |
| 351 | ✗ | stopAtFirstCollision); | |
| 352 | } | ||
| 353 | |||
| 354 | ✗ | void Device::computeDistances() { | |
| 355 | /* Following hpp::model API, the forward kinematics (joint placement) is | ||
| 356 | * supposed to have already been computed. */ | ||
| 357 | ✗ | updateGeometryPlacements(); | |
| 358 | ✗ | ::pinocchio::computeDistances(geomModel(), geomData()); | |
| 359 | } | ||
| 360 | |||
| 361 | ✗ | const DistanceResults_t& Device::distanceResults() const { | |
| 362 | ✗ | return geomData().distanceResults; | |
| 363 | } | ||
| 364 | |||
| 365 | /* ---------------------------------------------------------------------- */ | ||
| 366 | /* --- Bounding box ----------------------------------------------------- */ | ||
| 367 | /* ---------------------------------------------------------------------- */ | ||
| 368 | |||
| 369 | struct AABBStep : public ::pinocchio::fusion::JointUnaryVisitorBase<AABBStep> { | ||
| 370 | typedef boost::fusion::vector<const Model&, Configuration_t, bool, | ||
| 371 | coal::AABB&> | ||
| 372 | ArgsType; | ||
| 373 | |||
| 374 | template <typename JointModel> | ||
| 375 | 6 | static void algo(const ::pinocchio::JointModelBase<JointModel>& jmodel, | |
| 376 | const Model& model, Configuration_t q, bool initializeAABB, | ||
| 377 | coal::AABB& aabb) { | ||
| 378 | typedef typename RnxSOnLieGroupMap::operation<JointModel>::type LG_t; | ||
| 379 | /* | ||
| 380 | if (LG_t::NT == 0) { | ||
| 381 | aabb.min_.setZero(); | ||
| 382 | aabb.max_.setZero(); | ||
| 383 | return; | ||
| 384 | } | ||
| 385 | */ | ||
| 386 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
6 | typename JointModel::JointDataDerived data(jmodel.createData()); |
| 387 | // Set configuration to lower bound. | ||
| 388 |
3/6✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
|
6 | jmodel.jointConfigSelector(q).template head<LG_t::NT>() = |
| 389 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
6 | jmodel.jointConfigSelector(model.lowerPositionLimit) |
| 390 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
6 | .template head<LG_t::NT>(); |
| 391 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
6 | jmodel.calc(data, q); |
| 392 |
2/5✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
|
6 | vector3_t min = data.M_accessor().translation(); |
| 393 | // Set configuration to upper bound. | ||
| 394 |
3/6✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
|
6 | jmodel.jointConfigSelector(q).template head<LG_t::NT>() = |
| 395 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
6 | jmodel.jointConfigSelector(model.upperPositionLimit) |
| 396 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
6 | .template head<LG_t::NT>(); |
| 397 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
6 | jmodel.calc(data, q); |
| 398 |
2/5✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
|
6 | vector3_t max = data.M_accessor().translation(); |
| 399 | |||
| 400 | // This should not be required as it should be done in | ||
| 401 | // AABB::operator+=(Vec3f) for(int i = 0; i < 3; ++i) { if (min[i] > max[i]) | ||
| 402 | // std::swap(min[i], max[i]); | ||
| 403 | // } | ||
| 404 | // aabb.min_ = min; | ||
| 405 | // aabb.max_ = max; | ||
| 406 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
6 | if (initializeAABB) |
| 407 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
6 | aabb = coal::AABB(min); |
| 408 | else | ||
| 409 | ✗ | aabb += min; | |
| 410 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
6 | aabb += max; |
| 411 | } | ||
| 412 | }; | ||
| 413 | |||
| 414 | 3 | coal::AABB Device::computeAABB() const { | |
| 415 | // TODO check that user has called | ||
| 416 | |||
| 417 | 3 | const Model& m(model()); | |
| 418 | |||
| 419 | // Compute maximal distance to parent joint. | ||
| 420 |
1/2✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
|
3 | std::vector<value_type> maxDistToParent(m.joints.size(), 0); |
| 421 |
2/2✓ Branch 1 taken 81 times.
✓ Branch 2 taken 3 times.
|
84 | for (JointIndex i = 1; i < m.joints.size(); ++i) { |
| 422 |
1/2✓ Branch 3 taken 81 times.
✗ Branch 4 not taken.
|
162 | Joint joint(weakPtr_.lock(), i); |
| 423 |
1/2✓ Branch 1 taken 81 times.
✗ Branch 2 not taken.
|
81 | joint.computeMaximalDistanceToParent(); |
| 424 |
1/2✓ Branch 1 taken 81 times.
✗ Branch 2 not taken.
|
81 | maxDistToParent[i] = joint.maximalDistanceToParent(); |
| 425 | 81 | } | |
| 426 | |||
| 427 | // Compute maximal distance to root joint. | ||
| 428 |
1/2✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
|
3 | std::vector<value_type> maxDistToRoot(m.joints.size(), 0); |
| 429 |
1/2✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
|
3 | std::vector<value_type> distances(m.joints.size(), 0); |
| 430 | |||
| 431 | 3 | std::vector<JointIndex> rootIdxs; | |
| 432 | 3 | std::vector<value_type> maxRadius; | |
| 433 |
2/2✓ Branch 1 taken 81 times.
✓ Branch 2 taken 3 times.
|
84 | for (JointIndex i = 1; i < m.joints.size(); ++i) { |
| 434 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 78 times.
|
81 | if (m.parents[i] == 0) // Moving child of universe |
| 435 | { | ||
| 436 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | rootIdxs.push_back(i); |
| 437 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | maxRadius.push_back(0); |
| 438 | // This is the root of a subtree. | ||
| 439 | // maxDistToRoot[i] = 0; // Already zero by initialization | ||
| 440 | } else { | ||
| 441 | 78 | maxDistToRoot[i] = maxDistToRoot[m.parents[i]] + maxDistToParent[i]; | |
| 442 | } | ||
| 443 | |||
| 444 |
1/2✓ Branch 3 taken 81 times.
✗ Branch 4 not taken.
|
162 | Body body(weakPtr_.lock(), i); |
| 445 |
1/2✓ Branch 1 taken 81 times.
✗ Branch 2 not taken.
|
81 | distances[i] = body.radius() + maxDistToRoot[i]; |
| 446 | |||
| 447 | 81 | maxRadius.back() = std::max(maxRadius.back(), distances[i]); | |
| 448 | 81 | } | |
| 449 | |||
| 450 | // Compute AABB | ||
| 451 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | Configuration_t q(neutralConfiguration()); |
| 452 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | coal::AABB aabb; |
| 453 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
|
6 | for (std::size_t k = 0; k < rootIdxs.size(); ++k) { |
| 454 | 3 | JointIndex i = rootIdxs[k]; | |
| 455 | 3 | value_type radius = maxRadius[k]; | |
| 456 | |||
| 457 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | coal::AABB aabb_subtree; |
| 458 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
|
3 | AABBStep::run(m.joints[i], AABBStep::ArgsType(m, q, true, aabb_subtree)); |
| 459 | |||
| 460 | // Move AABB | ||
| 461 |
2/4✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
|
3 | coal::rotate(aabb_subtree, m.jointPlacements[i].rotation()); |
| 462 |
2/4✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
|
3 | coal::translate(aabb_subtree, m.jointPlacements[i].translation()); |
| 463 | |||
| 464 | // Add radius | ||
| 465 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
3 | aabb_subtree.min_.array() -= radius; |
| 466 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
3 | aabb_subtree.max_.array() += radius; |
| 467 | |||
| 468 | // Merge back into previous one. | ||
| 469 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (k == 0) |
| 470 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | aabb = aabb_subtree; |
| 471 | else | ||
| 472 | ✗ | aabb += aabb_subtree; | |
| 473 | } | ||
| 474 | 6 | return aabb; | |
| 475 | 3 | } | |
| 476 | |||
| 477 | ✗ | void replaceGeometryByConvexHull(GeomModel& gmodel, | |
| 478 | const std::vector<std::string>& gnames) { | ||
| 479 | ✗ | for (std::size_t i = 0; i < gnames.size(); ++i) { | |
| 480 | ✗ | if (!gmodel.existGeometryName(gnames[i])) | |
| 481 | ✗ | throw std::invalid_argument("Geometry " + gnames[i] + | |
| 482 | " does not " | ||
| 483 | ✗ | "exist."); | |
| 484 | ✗ | GeomIndex gid = gmodel.getGeometryId(gnames[i]); | |
| 485 | ✗ | GeometryObject& go = gmodel.geometryObjects[gid]; | |
| 486 | ✗ | if (go.geometry->getObjectType() == coal::OT_BVH) { | |
| 487 | coal::BVHModelBase* bvh = | ||
| 488 | ✗ | dynamic_cast<coal::BVHModelBase*>(go.geometry.get()); | |
| 489 | ✗ | assert(bvh != NULL); | |
| 490 | ✗ | bvh->buildConvexHull(false, "Qx"); | |
| 491 | ✗ | go.geometry = bvh->convex; | |
| 492 | } | ||
| 493 | } | ||
| 494 | } | ||
| 495 | |||
| 496 | template <typename To, typename Ti, typename UnaryOp> | ||
| 497 | inline std::vector<To> serialize_to(const std::vector<Ti>& in, UnaryOp op) { | ||
| 498 | std::vector<To> out; | ||
| 499 | out.reserve(in.size()); | ||
| 500 | std::transform(in.begin(), in.end(), out.begin(), op); | ||
| 501 | return out; | ||
| 502 | } | ||
| 503 | |||
| 504 | template <class Archive> | ||
| 505 | 2 | void Device::save(Archive& ar, const unsigned int version) const { | |
| 506 | (void)version; | ||
| 507 | |||
| 508 | 2 | auto* har = hpp::serialization::cast(&ar); | |
| 509 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(name_); |
| 510 | // write it is not present in the HPP archive. | ||
| 511 | 2 | bool written = | |
| 512 |
3/6✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
2 | (!har || har->template get<hpp::pinocchio::Device>(name_, false) != this); |
| 513 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(written); |
| 514 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
2 | if (written) { |
| 515 | // AbstractDevice | ||
| 516 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(model_); |
| 517 | // ar & BOOST_SERIALIZATION_NVP(geomModel_); | ||
| 518 | |||
| 519 | // Device | ||
| 520 | // - grippers_ | ||
| 521 | 2 | std::vector<FrameIndex> grippers; | |
| 522 | 2 | std::transform( | |
| 523 | grippers_.begin(), grippers_.end(), grippers.begin(), | ||
| 524 | ✗ | [](const GripperPtr_t& g) -> FrameIndex { return g->frameId(); }); | |
| 525 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(grippers); |
| 526 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(jointConstraints_); |
| 527 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(weakPtr_); |
| 528 | |||
| 529 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(extraConfigSpace_.dimension_); |
| 530 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(extraConfigSpace_.lowerBounds_); |
| 531 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(extraConfigSpace_.upperBounds_); |
| 532 | |||
| 533 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
2 | size_type nbDeviceData = numberDeviceData(); |
| 534 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(nbDeviceData); |
| 535 | } | ||
| 536 | } | ||
| 537 | |||
| 538 | template <class Archive> | ||
| 539 | 2 | void Device::load(Archive& ar, const unsigned int version) { | |
| 540 | (void)version; | ||
| 541 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(name_); |
| 542 | bool written; | ||
| 543 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(written); |
| 544 | |||
| 545 | 2 | auto* har = hpp::serialization::cast(&ar); | |
| 546 | 2 | bool dummyDevice = | |
| 547 |
3/6✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
2 | har && har->template get<hpp::pinocchio::Device>(name_, false); |
| 548 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
2 | if (written) { |
| 549 | // AbstractDevice | ||
| 550 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(model_); |
| 551 | // ar & BOOST_SERIALIZATION_NVP(geomModel_); | ||
| 552 | |||
| 553 | // Device | ||
| 554 | // - grippers_ | ||
| 555 | 2 | std::vector<FrameIndex> grippers; | |
| 556 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(grippers); |
| 557 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(jointConstraints_); |
| 558 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(weakPtr_); |
| 559 | |||
| 560 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(extraConfigSpace_.dimension_); |
| 561 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(extraConfigSpace_.lowerBounds_); |
| 562 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(extraConfigSpace_.upperBounds_); |
| 563 | |||
| 564 | size_type nbDeviceData; | ||
| 565 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | ar& BOOST_SERIALIZATION_NVP(nbDeviceData); |
| 566 | |||
| 567 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
2 | if (!dummyDevice) { // If this device will not be thrown away, initialize |
| 568 | // it cleanly. | ||
| 569 | ✗ | grippers_.reserve(grippers.size()); | |
| 570 | ✗ | std::transform(grippers.begin(), grippers.end(), grippers_.begin(), | |
| 571 | ✗ | [this](FrameIndex i) -> GripperPtr_t { | |
| 572 | ✗ | return Gripper::create(model_->frames[i].name, weakPtr_); | |
| 573 | }); | ||
| 574 | ✗ | createData(); | |
| 575 | ✗ | createGeomData(); | |
| 576 | ✗ | numberDeviceData(nbDeviceData); | |
| 577 | } | ||
| 578 |
0/2✗ Branch 1 not taken.
✗ Branch 2 not taken.
|
2 | } else if (!dummyDevice) |
| 579 | ✗ | throw std::logic_error( | |
| 580 | "This archive does not contain a valid Device " | ||
| 581 | "of name " + | ||
| 582 | ✗ | name_); | |
| 583 | } | ||
| 584 | |||
| 585 | HPP_SERIALIZATION_SPLIT_IMPLEMENT(Device); | ||
| 586 | } // namespace pinocchio | ||
| 587 | } // namespace hpp | ||
| 588 | |||
| 589 | namespace boost { | ||
| 590 | namespace serialization { | ||
| 591 | template <class Archive> | ||
| 592 | ✗ | void serialize(Archive& ar, hpp::pinocchio::Device::JointLinearConstraint& c, | |
| 593 | const unsigned int version) { | ||
| 594 | (void)version; | ||
| 595 | ✗ | ar& make_nvp("offset", c.offset); | |
| 596 | ✗ | ar& make_nvp("multiplier", c.multiplier); | |
| 597 | ✗ | ar& make_nvp("joint", c.joint); | |
| 598 | ✗ | ar& make_nvp("reference", c.reference); | |
| 599 | } | ||
| 600 | } // namespace serialization | ||
| 601 | } // namespace boost | ||
| 602 |