NTRT Simulator  Version: Master
 All Classes Namespaces Files Functions Variables Typedefs Friends Pages
TensegrityModel.cpp
Go to the documentation of this file.
1 /*
2  * Copyright © 2012, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
6  * The NASA Tensegrity Robotics Toolkit (NTRT) v1 platform is licensed
7  * under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * http://www.apache.org/licenses/LICENSE-2.0.
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
15  * either express or implied. See the License for the specific language
16  * governing permissions and limitations under the License.
17 */
18 
26 #include "TensegrityModel.h"
27 // C++ Standard Library
28 #include <iostream>
29 #include <stdexcept>
30 // NTRT Core and tgCreator Libraries
31 #include "core/tgBasicActuator.h"
33 #include "core/tgRod.h"
34 #include "core/tgBox.h"
35 #include "core/tgSphere.h"
40 #include "tgcreator/tgRodInfo.h"
41 #include "tgcreator/tgBoxInfo.h"
42 #include "tgcreator/tgSphereInfo.h"
44 
48 TensegrityModel::TensegrityModel(const std::string& structurePath) : tgModel() {
49  topLvlStructurePath = structurePath;
50 }
51 
55 TensegrityModel::TensegrityModel(const std::string& structurePath,
56  bool debugging) : tgModel() {
57  topLvlStructurePath = structurePath;
58  // All places in this file controlled by 'debugging_on' are labelled
59  // with comments with the string DEBUGGING.
60  debugging_on = debugging;
61 }
62 
64 
69 void TensegrityModel::trace(const tgStructure& structure,
70  const tgStructureInfo& structureInfo, tgModel& model)
71 {
72  std::cout << std::endl << "Structure Trace inside TensegrityModel:" << std::endl
73  << structure << std::endl
74  << std::endl << "StructureInfo Trace inside TensegrityModel:" << std::endl
75  << structureInfo << std::endl
76  << std::endl << "tgModel Trace inside Tensegrity Model: " << std::endl
77  << model << std::endl;
78 }
79 
86  // create the build spec that uses tags to turn the structure into a model
87  tgBuildSpec spec;
88 
89  // add default builders (rods, strings, boxes) that match the tags (rods, strings, boxes, spheres)
90  // (these will be overwritten if a different builder is specified for those tags)
91  Yam emptyYam = Yam();
92  addRodBuilder("tgRodInfo", "rod", emptyYam, spec);
93  addBasicActuatorBuilder("tgBasicActuatorInfo", "string", emptyYam, spec);
94  addBoxBuilder("tgBoxInfo", "box", emptyYam, spec);
95  addSphereBuilder("tgSphereInfo", "sphere", emptyYam, spec);
96 
97  tgStructure structure;
98  buildStructure(structure, topLvlStructurePath, spec);
99 
100  tgStructureInfo structureInfo(structure, spec);
101  structureInfo.buildInto(*this, world);
102 
103  // use tgCast::filterto pull out the muscles that we want to control
104  allActuators = tgCast::filter<tgModel, tgSpringCableActuator> (getDescendants());
105 
106  // DEBUGGING: print out the tgStructure, tgStructureInfo, and tgModel.
107  if(debugging_on) {
108  trace(structure, structureInfo, *this);
109  }
110 
111  // notify controllers that setup has finished
112  notifySetup();
113 
114  // actually setup the children
115  tgModel::setup(world);
116 }
117 
118 void TensegrityModel::addChildren(tgStructure& structure, const std::string& structurePath, tgBuildSpec& spec, const Yam& children) {
119  if (!children) return;
120  std::string structureAttributeKeys[] = {"path", "rotation", "translation", "scale", "offset"};
121  std::vector<std::string> structureAttributeKeysVector(structureAttributeKeys, structureAttributeKeys + sizeof(structureAttributeKeys) / sizeof(std::string));
122 
123  // add all the children first
124  for (YAML::const_iterator child = children.begin(); child != children.end(); ++child) {
125  Yam childAttributes = child->second;
126  yamlContainsOnly(childAttributes, structurePath, structureAttributeKeysVector);
127  // multiple children can be defined using the syntax: child1/child2/child3...
128  // (add a slash so that each child is a string with a its name and a slash at the end)
129  std::string childCombos = child->first.as<std::string>() + "/";
130  while (childCombos.find("/") != std::string::npos) {
131  std::string childName = childCombos.substr(0, childCombos.find("/"));
132  addChild(structure, structurePath, childName, childAttributes["path"], spec);
133  childCombos = childCombos.substr(childCombos.find("/") + 1);
134  }
135  }
136 
137  // apply rotation attribute to children
138  for (YAML::const_iterator child = children.begin(); child != children.end(); ++child) {
139  Yam childAttributes = child->second;
140  // multiple children can be defined using the syntax: child1/child2/child3...
141  // (add a slash so that each child is a string with a its name and a slash at the end)
142  std::string childCombos = child->first.as<std::string>() + "/";
143  while (childCombos.find("/") != std::string::npos) {
144  std::string childName = childCombos.substr(0, childCombos.find("/"));
145  tgStructure& childStructure = structure.findChild(childName);
146  addChildRotation(childStructure, childAttributes["rotation"]);
147  childCombos = childCombos.substr(childCombos.find("/") + 1);
148  }
149  }
150 
151  // apply scale, offset and translation attributes to children
152  for (YAML::const_iterator child = children.begin(); child != children.end(); ++child) {
153  Yam childAttributes = child->second;
154  // multiple children can be defined using the syntax: child1/child2/child3...
155  // (add a slash so that each child is a string with a its name and a slash at the end)
156  std::string childCombos = child->first.as<std::string>() + "/";
157  int childComboIndex = 0;
158  while (childCombos.find("/") != std::string::npos) {
159  std::string childName = childCombos.substr(0, childCombos.find("/"));
160  tgStructure& childStructure = structure.findChild(childName);
161  addChildScale(childStructure, childAttributes["scale"]);
162  addChildOffset(childStructure, childComboIndex, childAttributes["offset"]);
163  addChildTranslation(childStructure, childAttributes["translation"]);
164  childCombos = childCombos.substr(childCombos.find("/") + 1);
165  childComboIndex++;
166  }
167  }
168 }
169 
170 void TensegrityModel::addChild(tgStructure& structure, const std::string &parentPath,
171  const std::string& childName, const Yam& childStructurePath, tgBuildSpec& spec) {
172 
173  if (!childStructurePath) return;
174  std::string childPath = childStructurePath.as<std::string>();
175  // if path is relative, use path relative to parent structure
176  if (childPath[0] != '/') {
177  childPath = parentPath.substr(0, parentPath.rfind("/") + 1) + childPath;
178  }
179  tgStructure childStructure = tgStructure(childName);
180  buildStructure(childStructure, childPath, spec);
181  structure.addChild(childStructure);
182 }
183 
184 void TensegrityModel::addChildRotation(tgStructure& childStructure, const Yam& rotation) {
185  if (!rotation) return;
186  Yam reference = rotation["reference"];
187  Yam axis = rotation["axis"];
188  Yam angle = rotation["angle"];
189  if (axis && angle) {
190  double axisX = axis[0].as<double>();
191  double axisY = axis[1].as<double>();
192  double axisZ = axis[2].as<double>();
193  btVector3 axisVector = btVector3(axisX, axisY, axisZ);
194  double angleDegrees = angle.as<double>();
195  double angleRadians = tgUtil::deg2rad(angleDegrees);
196  btVector3 referenceVector;
197  if (reference) {
198  // rotate child around provided reference point
199  double referenceX = reference[0].as<double>();
200  double referenceY = reference[1].as<double>();
201  double referenceZ = reference[2].as<double>();
202  referenceVector = btVector3(referenceX, referenceY, referenceZ);
203  }
204  else {
205  // rotate child around structure's centroid
206  referenceVector = childStructure.getCentroid();
207  }
208  childStructure.addRotation(referenceVector, axisVector, angleRadians);
209  }
210 }
211 
212 void TensegrityModel::addChildScale(tgStructure& childStructure, const Yam& scale) {
213  if (!scale) return;
214  double scaleFactor = scale.as<double>();
215  childStructure.scale(scaleFactor);
216 }
217 
218 void TensegrityModel::addChildOffset(tgStructure& childStructure, int offsetIndex, const Yam& offset) {
219  if (!offset) return;
220  double offsetX = offset[0].as<double>();
221  double offsetY = offset[1].as<double>();
222  double offsetZ = offset[2].as<double>();
223  btVector3 offsetVector = btVector3(offsetX, offsetY, offsetZ);
224  childStructure.move(offsetVector * offsetIndex);
225 }
226 
227 void TensegrityModel::addChildTranslation(tgStructure& childStructure, const Yam& translation) {
228  if (!translation) return;
229  double translationX = translation[0].as<double>();
230  double translationY = translation[1].as<double>();
231  double translationZ = translation[2].as<double>();
232  btVector3 translationVector = btVector3(translationX, translationY, translationZ);
233  childStructure.move(translationVector);
234 }
235 
236 void TensegrityModel::buildStructure(tgStructure& structure, const std::string& structurePath, tgBuildSpec& spec) {
242  Yam root;
243  try
244  {
245  root = YAML::LoadFile(structurePath);
246  }
247  catch( YAML::BadFile badfileexception )
248  {
249  // If a BadFile exception is thrown, output a detailed message first:
250  std::cout << std::endl << "The YAML parser threw a BadFile exception when" <<
251  " trying to load one of your YAML files. " << std::endl <<
252  "The path of the structure that the parser attempted to load is: '" <<
253  structurePath << "'. " << std::endl <<
254  "Check to be sure that the file exists, and " <<
255  "that you didn't spell the path name incorrectly." <<
256  std::endl << std::endl;
257  // Then, throw the exception again, so that the program stops.
258  throw badfileexception;
259  }
260  // Validate YAML
261  std::string rootKeys[] = {"nodes", "pair_groups", "builders", "substructures", "bond_groups"};
262  std::vector<std::string> rootKeysVector(rootKeys, rootKeys + sizeof(rootKeys) / sizeof(std::string));
263  yamlContainsOnly(root, structurePath, rootKeysVector);
264  yamlNoDuplicates(root, structurePath);
265 
266  addChildren(structure, structurePath, spec, root["substructures"]);
267  addBuilders(spec, root["builders"]);
268  addNodes(structure, root["nodes"]);
269  addPairGroups(structure, root["pair_groups"]);
270  addBondGroups(structure, root["bond_groups"], spec);
271 }
272 
273 void TensegrityModel::addNodes(tgStructure& structure, const Yam& nodes) {
274  if (!nodes) return;
275  for (YAML::const_iterator node = nodes.begin(); node != nodes.end(); ++node) {
276  std::string name = node->first.as<std::string>();
277  Yam xyz = node->second;
278  double x = xyz[0].as<double>();
279  double y = xyz[1].as<double>();
280  double z = xyz[2].as<double>();
281  structure.addNode(x, y, z, name);
282  }
283 }
284 
285 void TensegrityModel::addPairGroups(tgStructure& structure, const Yam& pair_groups) {
286  if (!pair_groups) return;
287  for (YAML::const_iterator pair_group = pair_groups.begin(); pair_group != pair_groups.end(); ++pair_group) {
288  std::string tags = pair_group->first.as<std::string>();
289  Yam pairs = pair_group->second;
290  addNodeNodePairs(structure, tags, pairs);
291  }
292 }
293 
294 void TensegrityModel::addBondGroups(tgStructure& structure, const Yam& bond_groups, tgBuildSpec& spec) {
295  if (!bond_groups) return;
296 
297  // go through first level keys
298  for (YAML::const_iterator firstLvl = bond_groups.begin(); firstLvl != bond_groups.end(); ++firstLvl) {
299  std::string bonds;
300  std::string tags;
301  bool bondsSet = false;
302  bool tagsSet = false;
303 
304  std::string firstLvlKey = firstLvl->first.as<std::string>();
305  Yam firstLvlContent = firstLvl->second;
306  // bonds must use the '/' notation
307  if (firstLvlKey.find("/") == std::string::npos) { // firstLvlKey is a tag
308  tags = firstLvlKey;
309  tagsSet = true;
310  }
311  else { // firstLvlKey is a bond
312  bonds = firstLvlKey;
313  bondsSet = true;
314  }
315 
316  // go through second level keys
317  for (YAML::const_iterator secondLvl = firstLvlContent.begin(); secondLvl != firstLvlContent.end(); ++secondLvl) {
318  std::string secondLvlKey = secondLvl->first.as<std::string>();
319  Yam secondLvlContent = secondLvl->second;
320  if (secondLvlKey.find("/") == std::string::npos) { // secondLvlKey is a tag
321  // a bond must be nested inside a tag or vice versa
322  if (tagsSet) throw std::invalid_argument("Invalid nesting of tags: "+ secondLvlKey);
323  tags = secondLvlKey;
324  }
325  else { // secondLvlKey is a bond
326  if (bondsSet) throw std::invalid_argument("Invalid nesting of bonds: " + secondLvlKey);
327  bonds = secondLvlKey;
328  }
329  addBonds(structure, bonds, tags, secondLvlContent, spec);
330  }
331  }
332 }
333 
334 void TensegrityModel::addBonds(tgStructure& structure, const std::string& bonds, const std::string& tags, const Yam& pairs, tgBuildSpec& spec) {
335  if (!pairs) return;
336  // bonds looks like: childName1/childName2/.../bondType
337  std::string bondType = bonds.substr(bonds.rfind("/") + 1);
338  std::string structureCombos = bonds.substr(0, bonds.rfind("/"));
339  if (structureCombos.find("/") == std::string::npos)
340  throw std::invalid_argument("Error: all bond groups must specify a bond between 2 or more structures and a bond type");
341  while (structureCombos.find("/") != std::string::npos) {
342  std::string childStructure1Name = structureCombos.substr(0, structureCombos.find("/"));
343  structureCombos = structureCombos.substr(structureCombos.find("/") + 1);
344  std::string childStructure2Name = structureCombos.substr(0, structureCombos.find("/"));
345  if (bondType == "node_node") {
346  // DEBUGGING: output the bonds that are about to be created.
347  if(debugging_on) {
348  std::cout << "Adding node_node bonds " << tags <<
349  " between structures " << childStructure1Name <<
350  " and " << childStructure2Name << std::endl;
351  }
352  // Add the node_node pairs, regardless of debugging
353  addNodeNodePairs(structure, tags, pairs, &childStructure1Name,
354  &childStructure2Name);
355  }
356  else if (bondType == "node_edge") {
357  addNodeEdgePairs(structure, tags, pairs, &childStructure1Name, &childStructure2Name, spec);
358  }
359  else {
360  throw std::invalid_argument("Unsupported bond type: " + bondType);
361  }
362  }
363 }
364 
365 void TensegrityModel::addNodeNodePairs(tgStructure& structure,
366  const std::string& tags, const Yam& pairs,
367  const std::string* childStructure1Name,
368  const std::string* childStructure2Name) {
369 
370  // NOTE that this method can be called with the childStructureName pointers
371  // equal 0. That's the case if there is no child structure (e.g., this method
372  // is adding a pair to a base structure.)
373 
374  //DEBUGGING: output the node_node pairs about to be created.
375  if(debugging_on) {
376  // This statement is true if the pointer is nonzero.
377  if (childStructure1Name && childStructure2Name) {
378  std::cout << "Adding all the node_node pairs between structures "
379  << *childStructure1Name << " and " << *childStructure2Name
380  << std::endl;
381  }
382  else {
383  std::cout << "Adding all the node_node pairs within a base structure." <<
384  std::endl;
385  }
386  }
387  // Iterate over the pairs and add them
388  for (YAML::const_iterator pairPtr = pairs.begin(); pairPtr != pairs.end(); ++pairPtr) {
389  Yam pair = *pairPtr;
390  std::string node1Path = pair[0].as<std::string>();
391  std::string node2Path = pair[1].as<std::string>();
392  tgNode* node1;
393  tgNode* node2;
394  // This method may add additional tags. So, make a new variable here
395  // and then change it later if needed.
396  std::string pairNewTags = tags;
397  // This statement is true if the pointer is nonzero.
398  if (childStructure1Name && childStructure2Name) {
399  node1 = &getNode(structure.findChild(*childStructure1Name), node1Path);
400  node2 = &getNode(structure.findChild(*childStructure2Name), node2Path);
401  // DEBUGGING: List the specific pairs about to be added
402  if(debugging_on) {
403  std::cout << "Adding node_node pair " << tags << " between structures "
404  << *childStructure1Name << " and " << *childStructure2Name
405  << " for nodes " << *node1 << " and " << *node2
406  << " with original tag " << tags << std::endl;
407  }
408  // Add three additional tags: the names of the two structures that
409  // a pair connects. This is useful for controllers, where tags are used
410  // to designate one actuator from another.
411  // As per tgTaggable, tags are separated by spaces.
412  // Three tags are added: the two connecting structure names, and the
413  // two names connected by a slash, like in the YAML file.
414  pairNewTags = tags + " " + *childStructure1Name + " " + *childStructure2Name
415  + " " + *childStructure1Name + "/" + *childStructure2Name;
416  }
417  else {
418  // Pointers are zero, add directly to this structure and not any
419  // of its children (since there ARE no children!)
420  node1 = &getNode(structure, node1Path);
421  node2 = &getNode(structure, node2Path);
422  }
423  // finally, add the actual pair.
424  //structure.addPair(*node1, *node2, tags);
425  structure.addPair(*node1, *node2, pairNewTags);
426  }
427 }
428 
429 void TensegrityModel::addNodeEdgePairs(tgStructure& structure, const std::string& tags, const Yam& pairs,
430  const std::string* childStructure1Name, const std::string* childStructure2Name, tgBuildSpec& spec) {
431 
432  if (pairs.size() < 3) {
433  throw std::invalid_argument("Error: node_edge bonds must specify at least 3 node_edge pairs");
434  }
435 
436  tgStructure& childStructure1 = structure.findChild(*childStructure1Name);
437  tgStructure& childStructure2 = structure.findChild(*childStructure2Name);
438 
439  // these are used for transformations
440  std::vector<btVector3> structure1RefNodes;
441  std::vector<btVector3> structure2RefNodes;
442 
443  // these are nodes
444  std::vector<tgNode*> ligands;
445  // these are edges
446  std::vector< std::pair<tgNode*, tgNode*> > receptors;
447 
448  // populate refNodes arrays, ligands and receptors
449  parseNodeEdgePairs(childStructure1, childStructure2, structure1RefNodes, structure2RefNodes, ligands, receptors, pairs);
450  rotateAndTranslate(childStructure2, structure1RefNodes, structure2RefNodes);
451 
452  std::vector<tgBuildSpec::RigidAgent*> rigidAgents = spec.getRigidAgents();
453  for (unsigned int i = 0; i < ligands.size(); i++) {
454 
455  // remove old edge connections
456  // try removing from both children since we are not sure which child the pair belongs to
457  // (could add more information to receptors array so we don't have to do this)
458  removePair(childStructure1, receptors[i].first, receptors[i].second, true, rigidAgents, spec);
459  removePair(childStructure2, receptors[i].first, receptors[i].second, true, rigidAgents, spec);
460  for (unsigned int j = 0; j < ligands.size(); j++) {
461  // remove old string connections between nodes/ligands
462  // try removing from both children since we are not sure which child the node belongs to
463  removePair(childStructure1, ligands[i], ligands[j], false, rigidAgents, spec);
464  removePair(childStructure2, ligands[i], ligands[j], false, rigidAgents, spec);
465  }
466  // make new connection from edge -> node -> edge
467  structure.addPair(*(receptors[i].first), *ligands[i], tags);
468  structure.addPair(*ligands[i], *(receptors[i].second), tags);
469 
470  }
471 }
472 
473 void TensegrityModel::parseNodeEdgePairs(tgStructure& childStructure1, tgStructure& childStructure2,
474  std::vector<btVector3>& structure1RefNodes, std::vector<btVector3>& structure2RefNodes,
475  std::vector<tgNode*>& ligands, std::vector< std::pair<tgNode*, tgNode*> >& receptors, const Yam& pairs) {
476 
477  for (YAML::const_iterator pairPtr = pairs.begin(); pairPtr != pairs.end(); ++pairPtr) {
478  Yam pair = *pairPtr;
479 
480  std::string structure1Attachment = pair[0].as<std::string>();
481  std::string structure2Attachment = pair[1].as<std::string>();
482 
483  // node_edge bond must be between a node and an edge (an edge will be defined as 'node1/node2')
484  if (structure1Attachment.find("/") == std::string::npos && structure2Attachment.find("/") == std::string::npos) {
485  throw std::invalid_argument("Invalid node_edge bond: " + structure1Attachment + ", " + structure2Attachment);
486  }
487  if (structure1Attachment.find("/") != std::string::npos && structure2Attachment.find("/") != std::string::npos) {
488  throw std::invalid_argument("Invalid node_edge bond: " + structure1Attachment + ", " + structure2Attachment);
489  }
490  parseAttachmentPoint(childStructure1, structure1Attachment, structure1RefNodes, ligands, receptors);
491  parseAttachmentPoint(childStructure2, structure2Attachment, structure2RefNodes, ligands, receptors);
492  }
493 }
494 
495 void TensegrityModel::parseAttachmentPoint(tgStructure& structure, const std::string& attachment, std::vector<btVector3>& refNodes,
496  std::vector<tgNode*>& ligands, std::vector< std::pair<tgNode*, tgNode*> >& receptors) {
497  if (attachment.find("/") == std::string::npos) { // node
498  tgNode* attachmentNode = &(getNode(structure, attachment));
499  ligands.push_back(attachmentNode);
500  refNodes.push_back(*attachmentNode);
501  }
502  else { // edge
503  std::string attachmentNode1Path = attachment.substr(0, attachment.find("/"));
504  std::string attachmentNode2Path = attachment.substr(attachment.find("/") + 1);
505 
506  tgNode* attachmentNode1 = &(getNode(structure, attachmentNode1Path));
507  tgNode* attachmentNode2 = &(getNode(structure, attachmentNode2Path));
508 
509  receptors.push_back(std::make_pair(attachmentNode1, attachmentNode2));
510  refNodes.push_back((*attachmentNode1 + *attachmentNode2) / 2);
511  }
512 }
513 
515 void TensegrityModel::rotateAndTranslate(tgStructure& childStructure2,
516  std::vector<btVector3>& structure1RefNodes, std::vector<btVector3>& structure2RefNodes) {
517 
518  btVector3 structure1RefNodesCentroid = tgUtil::getCentroid(structure1RefNodes);
519  btVector3 structure2RefNodesCentroid = tgUtil::getCentroid(structure2RefNodes);
520 
521  btVector3 structure1PlaneNormal = ((structure1RefNodes[1] - structure1RefNodes[0]).
522  cross(structure1RefNodes[2] - structure1RefNodes[0])).normalize();
523  btVector3 structure2PlaneNormal = ((structure2RefNodes[1] - structure2RefNodes[0]).
524  cross(structure2RefNodes[2] - structure2RefNodes[0])).normalize();
525 
526  // rotate structure 2 to align normals
527  btVector3 fallBackAxis = (structure2RefNodes[1] - structure2RefNodes[0]).normalize();
528  childStructure2.addRotation(structure2RefNodesCentroid,
529  tgUtil::getQuaternionBetween(structure2PlaneNormal, structure1PlaneNormal, fallBackAxis));
530 
531  // rotate structure 2 ref nodes
532  tgUtil::addRotation(structure2RefNodes[0], structure2RefNodesCentroid,
533  tgUtil::getQuaternionBetween(structure2PlaneNormal, structure1PlaneNormal, fallBackAxis));
534  tgUtil::addRotation(structure2RefNodesCentroid, structure2RefNodesCentroid,
535  tgUtil::getQuaternionBetween(structure2PlaneNormal, structure1PlaneNormal, fallBackAxis));
536 
537  // translate structure 2 to match up centroid points
538  childStructure2.move(structure1RefNodesCentroid - structure2RefNodesCentroid);
539 
540  // translate structure 2 ref nodes
541  structure2RefNodes[0] += structure1RefNodesCentroid - structure2RefNodesCentroid;
542  structure2RefNodesCentroid += structure1RefNodesCentroid - structure2RefNodesCentroid;
543 
544  // rotate structure 2 around structure1PlaneNormal axis to match up node with edge midpoints
545  childStructure2.addRotation(structure1RefNodesCentroid,
546  tgUtil::getQuaternionBetween(structure2RefNodes[0] - structure1RefNodesCentroid,
547  structure1RefNodes[0] - structure1RefNodesCentroid, structure1PlaneNormal));
548 }
549 
550 
551 void TensegrityModel::removePair(tgStructure& structure, const tgNode* from, const tgNode* to, bool isEdgePair,
552  const std::vector<tgBuildSpec::RigidAgent*>& rigidAgents, tgBuildSpec& spec) {
553 
554  tgPair *pair;
555  try {
556  pair = &structure.findPair(*from, *to);
557  } catch (std::invalid_argument e) {
558  return;
559  }
560  for (int i = 0; i < rigidAgents.size(); i++) {
561  tgTagSearch tagSearch = rigidAgents[i]->tagSearch;
562  if (tagSearch.matches(*pair)) {
563  // don't want to remove pairs that are rods
564  if (isEdgePair) {
565  throw std::invalid_argument("Edges in node_edge bonds cannot be rods");
566  }
567  return;
568  }
569  }
570  structure.removePair(*pair);
571 }
572 
573 tgNode& TensegrityModel::getNode(tgStructure& structure, const std::string& nodePath) {
574  // nodePath looks like: 'parentStructure.childStructure.nodeName'
575  if (nodePath.find(".") == std::string::npos) {
576  return structure.findNode(nodePath);
577  }
578  else {
579  std::string structurePath = nodePath.substr(0, nodePath.rfind("."));
580  std::string nodeName = nodePath.substr(nodePath.rfind(".") + 1);
581  tgStructure& targetStructure = getStructure(structure, structurePath);
582  return targetStructure.findNode(nodeName);
583  }
584 }
585 
586 tgStructure& TensegrityModel::getStructure(tgStructure& parentStructure, const std::string& childStructurePath) {
587  // childStructurePath looks like: 'parentStructure.childStructure'
588  if (childStructurePath.find(".") == std::string::npos) {
589  return parentStructure.findChild(childStructurePath);
590  }
591  else {
592  std::string childStructureName = childStructurePath.substr(0, childStructurePath.rfind("."));
593  std::string remainingChildStructurePath = childStructurePath.substr(childStructurePath.rfind(".") + 1);
594  tgStructure& childStructure = parentStructure.findChild(childStructureName);
595  return getStructure(childStructure, remainingChildStructurePath);
596  }
597 }
598 
599 void TensegrityModel::addBuilders(tgBuildSpec& spec, const Yam& builders) {
600  for (YAML::const_iterator builder = builders.begin(); builder != builders.end(); ++builder) {
601  std::string tagMatch = builder->first.as<std::string>();
602  if (!builder->second["class"]) throw std::invalid_argument("Builder class not supplied for tag: " + tagMatch);
603  std::string builderClass = builder->second["class"].as<std::string>();
604  Yam parameters = builder->second["parameters"];
605 
606  if (builderClass == "tgRodInfo") {
607  addRodBuilder(builderClass, tagMatch, parameters, spec);
608  }
609  else if (builderClass == "tgBasicActuatorInfo" || builderClass == "tgBasicContactCableInfo") {
610  addBasicActuatorBuilder(builderClass, tagMatch, parameters, spec);
611  }
612  else if (builderClass == "tgKinematicContactCableInfo" || builderClass == "tgKinematicActuatorInfo") {
613  addKinematicActuatorBuilder(builderClass, tagMatch, parameters, spec);
614  }
615  else if (builderClass == "tgBoxInfo") {
616  addBoxBuilder(builderClass, tagMatch, parameters, spec);
617  }
618  else if (builderClass == "tgSphereInfo") {
619  addSphereBuilder(builderClass, tagMatch, parameters, spec);
620  }
621  // add more builders here if they use a different Config
622  else {
623  throw std::invalid_argument("Unsupported builder class: " + builderClass);
624  }
625  }
626 }
627 
628 void TensegrityModel::addRodBuilder(const std::string& builderClass, const std::string& tagMatch, const Yam& parameters, tgBuildSpec& spec) {
629  // rodParameters
630  std::map<std::string, double> rp;
631  rp["radius"] = rodRadius;
632  rp["density"] = rodDensity;
633  rp["friction"] = rodFriction;
634  rp["roll_friction"] = rodRollFriction;
635  rp["restitution"] = rodRestitution;
636 
637  if (parameters) {
638  for (YAML::const_iterator parameter = parameters.begin(); parameter != parameters.end(); ++parameter) {
639  std::string parameterName = parameter->first.as<std::string>();
640  if (rp.find(parameterName) == rp.end()) {
641  throw std::invalid_argument("Unsupported " + builderClass + " parameter: " + parameterName);
642  }
643  // if defined overwrite default parameter value
644  rp[parameterName] = parameter->second.as<double>();
645  }
646  }
647 
648  const tgRod::Config rodConfig = tgRod::Config(rp["radius"], rp["density"], rp["friction"],
649  rp["roll_friction"], rp["restitution"]);
650  if (builderClass == "tgRodInfo") {
651  // tgBuildSpec takes ownership of the tgRodInfo object
652  spec.addBuilder(tagMatch, new tgRodInfo(rodConfig));
653  }
654  // add more builders that use tgRod::Config here
655 }
656 
657 void TensegrityModel::addBasicActuatorBuilder(const std::string& builderClass, const std::string& tagMatch, const Yam& parameters, tgBuildSpec& spec) {
658  // tgbBasicActuator parameters.
659  // This method assigns default values based on TensegrityModel.h,
660  // then overwrites them if a parameter is specified in the YAML file.
661  // See tgBasicActuator.h for a description of each parameter.
662  // Since tgBasicActuator has both double and boolean config parameters,
663  // store two lists of parameters based on type.
664  // We use the abbreviation "bap" as basic actuator parameters.
665  std::map<std::string, double> bap_doubles;
666  std::map<std::string, bool> bap_booleans;
667  bap_doubles["stiffness"] = stringStiffness;
668  bap_doubles["damping"] = stringDamping;
669  bap_doubles["pretension"] = stringPretension;
670  bap_doubles["history"] = stringHistory;
671  bap_doubles["max_tension"] = stringMaxTension;
672  bap_doubles["target_velocity"] = stringTargetVelocity;
673  bap_doubles["min_actual_length"] = stringMinActualLength;
674  bap_doubles["min_rest_length"] = stringMinRestLength;
675  bap_doubles["rotation"] = stringRotation;
676  bap_booleans["moveCablePointAToEdge"] = stringMoveCablePointAToEdge;
677  bap_booleans["moveCablePointBToEdge"] = stringMoveCablePointBToEdge;
678 
679  // If no parameters are passed in, do not change anything.
680  if (parameters) {
681  // Iterate through all the parameters passed in for this builder.
682  for (YAML::const_iterator parameter = parameters.begin(); parameter != parameters.end(); ++parameter) {
683  // The key for both maps is a string.
684  std::string parameterName = parameter->first.as<std::string>();
685  // However, the value may be either a double or a boolean. Check both
686  // lists, and mark a flag depending on the output.
687  bool paramIsDouble = true;
688  // Check the doubles first:
689  if( bap_doubles.find(parameterName) == bap_doubles.end()) {
690  // The parameter is not a double, since it's not in the doubles map.
691  paramIsDouble = false;
692  // Then, check to see if the parameter is in the booleans map.
693  if( bap_booleans.find(parameterName) == bap_booleans.end()) {
694  // The parameter is in neither list, throw an exception.
695  throw std::invalid_argument("Unsupported " + builderClass + " parameter: " + parameterName);
696  }
697  }
698  // if defined, overwrite default parameter value to the appropriate map.
699  if( paramIsDouble ) {
700  // change the value in the doubles list
701  bap_doubles[parameterName] = parameter->second.as<double>();
702  }
703  else {
704  // the value is in the booleans list.
705  bap_booleans[parameterName] = parameter->second.as<bool>();
706  }
707  }
708  }
709 
710  // Create the config struct.
711  // Note that this calls the constructor for Config, so the parameters
712  // are passed in according to order not name.
713  // @TO-DO: instead, create a Config with nothing passed in, and then change
714  // parameters if defined. That way, no defaults would need to be defined
715  // in TensegrityModel.h. Currently, defaults are defined in BOTH the
716  // actual config struct definition as well as in this .h file.
717  const tgBasicActuator::Config basicActuatorConfig =
718  tgBasicActuator::Config(bap_doubles["stiffness"], bap_doubles["damping"],
719  bap_doubles["pretension"], bap_doubles["history"],
720  bap_doubles["max_tension"],
721  bap_doubles["target_velocity"],
722  bap_doubles["min_actual_length"],
723  bap_doubles["min_rest_length"],
724  bap_doubles["rotation"],
725  bap_booleans["moveCablePointAToEdge"],
726  bap_booleans["moveCablePointBToEdge"]);
727  if (builderClass == "tgBasicActuatorInfo") {
728  // tgBuildSpec takes ownership of the tgBasicActuatorInfo object
729  spec.addBuilder(tagMatch, new tgBasicActuatorInfo(basicActuatorConfig));
730  }
731  else if (builderClass == "tgBasicContactCableInfo") {
732  // tgBuildSpec takes ownership of the tgBasicContactCableInfo object
733  spec.addBuilder(tagMatch, new tgBasicContactCableInfo(basicActuatorConfig));
734  }
735  // add more builders that use tgBasicActuator::Config here
736 }
737 
738 void TensegrityModel::addKinematicActuatorBuilder(const std::string& builderClass, const std::string& tagMatch, const Yam& parameters, tgBuildSpec& spec) {
739  // kinematicActuatorParameters
740  std::map<std::string, double> kap;
741  kap["stiffness"] = stringStiffness;
742  kap["damping"] = stringDamping;
743  kap["pretension"] = stringPretension;
744  kap["radius"] = stringRadius;
745  kap["motor_friction"] = stringMotorFriction;
746  kap["motor_inertia"] = stringMotorInertia;
747  kap["back_drivable"] = stringBackDrivable;
748  kap["history"] = stringHistory;
749  kap["max_tension"] = stringMaxTension;
750  kap["target_velocity"] = stringTargetVelocity;
751  kap["min_actual_length"] = stringMinActualLength;
752  kap["min_rest_length"] = stringMinRestLength;
753  kap["rotation"] = stringRotation;
754 
755  if (parameters) {
756  for (YAML::const_iterator parameter = parameters.begin(); parameter != parameters.end(); ++parameter) {
757  std::string parameterName = parameter->first.as<std::string>();
758  if (kap.find(parameterName) == kap.end()) {
759  throw std::invalid_argument("Unsupported " + builderClass + " parameter: " + parameterName);
760  }
761  // if defined overwrite default parameter value
762  kap[parameterName] = parameter->second.as<double>();
763  }
764  }
765 
766  const tgKinematicActuator::Config kinematicActuatorConfig =
767  tgKinematicActuator::Config(kap["stiffness"], kap["damping"], kap["pretension"], kap["radius"],
768  kap["motor_friction"], kap["motor_inertia"], kap["back_drivable"], kap["history"], kap["max_tension"],
769  kap["target_velocity"],kap["min_actual_length"], kap["min_rest_length"], kap["rotation"]);
770  if (builderClass == "tgKinematicContactCableInfo") {
771  // tgBuildSpec takes ownership of the tgKinematicContactCableInfo object
772  spec.addBuilder(tagMatch, new tgKinematicContactCableInfo(kinematicActuatorConfig));
773  }
774  else if (builderClass == "tgKinematicActuatorInfo") {
775  // tgBuildSpec takes ownership of the tgKinematicActuatorInfo object
776  spec.addBuilder(tagMatch, new tgKinematicActuatorInfo(kinematicActuatorConfig));
777  }
778  // add more builders that use tgKinematicActuator::Config here
779 }
780 
781 void TensegrityModel::addBoxBuilder(const std::string& builderClass, const std::string& tagMatch, const Yam& parameters, tgBuildSpec& spec) {
792  // (1)
793  // Parameters to be used in tgBox::Config. See core/tgBox.h
794  // Boxes are treated like rods with a rectangular cross-section: the box length
795  // is specified by the distance between nodes, and the width and height come from
796  // the Config struct.
797  // Create a map of strings to doubles that will hold the parameters
798  std::map<std::string, double> bp;
799  bp["width"] = boxWidth;
800  bp["height"] = boxHeight;
801  bp["density"] = boxDensity;
802  bp["friction"] = boxFriction;
803  bp["roll_friction"] = boxRollFriction;
804  bp["restitution"] = boxRestitution;
805 
806  if (parameters) {
807  for (YAML::const_iterator parameter = parameters.begin(); parameter != parameters.end(); ++parameter) {
808  std::string parameterName = parameter->first.as<std::string>();
809  if (bp.find(parameterName) == bp.end()) {
810  throw std::invalid_argument("Unsupported " + builderClass + " parameter: " + parameterName);
811  }
812  // if defined overwrite default parameter value
813  bp[parameterName] = parameter->second.as<double>();
814  }
815  }
816 
817  // (3)
818  // this usage is the same as in NTRT v1.0 models.
819  const tgBox::Config boxConfig = tgBox::Config(bp["width"], bp["height"],
820  bp["density"], bp["friction"], bp["roll_friction"], bp["restitution"]);
821  if (builderClass == "tgBoxInfo") {
822  // tgBuildSpec takes ownership of the tgBoxInfo object
823  spec.addBuilder(tagMatch, new tgBoxInfo(boxConfig));
824  }
825 }
826 
827 void TensegrityModel::addSphereBuilder(const std::string& builderClass, const std::string& tagMatch, const Yam& parameters, tgBuildSpec& spec){
838  // (1)
839  // Parameters to be used in tgSphere::Config. See core/tgSphere.h
840  // Spheres are simple: one point, one radius, and then same
841  // rigid body parameters as the rest of the gang.
842  // Create a map of strings to doubles that will hold the sphere parameters
843  std::map<std::string, double> sp;
844  sp["radius"] = sphereRadius;
845  sp["density"] = sphereDensity;
846  sp["friction"] = sphereFriction;
847  sp["roll_friction"] = sphereRollFriction;
848  sp["restitution"] = sphereRestitution;
849 
850  // (2) sub in the new stuff if any exists
851  if (parameters) {
852  for (YAML::const_iterator parameter = parameters.begin(); parameter != parameters.end(); ++parameter) {
853  std::string parameterName = parameter->first.as<std::string>();
854  if (sp.find(parameterName) == sp.end()) {
855  throw std::invalid_argument("Unsupported " + builderClass + " parameter: " + parameterName);
856  }
857  // if defined overwrite default parameter value
858  sp[parameterName] = parameter->second.as<double>();
859  }
860  }
861 
862  // (3)
863  // VALIDATION. We need the node locations to be the same.
864  // Can't create a sphere in two different places.
865  // This is a hack. To-do: refactor the YAML parser so that builders can be
866  // created for a single node, or more than two nodes.
867  // ...actually, we can't do this check here. Makes things more difficult...
868 
869  // (4) add the builder. HOPEFULLY, this will create a builder that only
870  // looks for nodes, not pairs. To do, check tgSphereInfo to see what happens if
871  // it encounters a pair of spheres.
872  const tgSphere::Config sphereConfig = tgSphere::Config(sp["radius"],
873  sp["density"], sp["friction"], sp["roll_friction"], sp["restitution"]);
874  if (builderClass == "tgSphereInfo") {
875  // tgBuildSpec takes ownership of the tgSphereInfo object
876  spec.addBuilder(tagMatch, new tgSphereInfo(sphereConfig));
877  }
878 
879 }
880 
881 void TensegrityModel::yamlNoDuplicates(const Yam& yam, const std::string structurePath) {
882  std::set<std::string> keys;
883  for (YAML::const_iterator iter = yam.begin(); iter != yam.end(); ++iter) {
884  Yam child = iter->second;
885  if (child.Type() == YAML::NodeType::Map) {
886  yamlNoDuplicates(child, structurePath);
887  }
888  std::string keyName = iter->first.as<std::string>();
889  if (keys.find(keyName) == keys.end()) {
890  keys.insert(keyName);
891  }
892  else {
893  throw std::invalid_argument(structurePath.substr(structurePath.rfind("/") + 1) + " contains duplicate key: " + keyName);
894  }
895  }
896 }
897 
898 void TensegrityModel::yamlContainsOnly(const Yam& yam, const std::string structurePath, const std::vector<std::string> keys) {
899  for (YAML::const_iterator key = yam.begin(); key != yam.end(); ++key) {
900  std::string keyName = key->first.as<std::string>();
901  if (std::find(keys.begin(), keys.end(), keyName) == keys.end()) {
902  throw std::invalid_argument(structurePath.substr(structurePath.rfind("/") + 1) + " contains invalid key: " + keyName);
903  }
904  }
905 }
906 
907 void TensegrityModel::step(double timeStep) {
908  if (timeStep <= 0.0) {
909  throw std::invalid_argument("time step is not positive");
910  }
911  else {
912  notifyStep(timeStep);
913  tgModel::step(timeStep);
914  }
915 }
916 
918  tgModel::onVisit(visitor);
919 }
920 
921 const std::vector<tgSpringCableActuator*>& TensegrityModel::getAllActuators() const {
922  return allActuators;
923 }
924 
926  notifyTeardown();
928 }
tgStructure & findChild(const std::string &name)
static btQuaternion getQuaternionBetween(btVector3 a, btVector3 b, const btVector3 &fallbackAxis=btVector3(0, 0, 0))
Definition: tgUtil.h:199
tgNode & findNode(const std::string &name)
Create a box shape as an obstacle or add it to your tensegrity.
virtual void teardown()
Definition: tgModel.cpp:68
virtual void setup(tgWorld &world)
Definition: tgModel.cpp:57
void addChild(tgStructure *child)
Definition of class tgRodInfo.
Definition of class tgSphereInfo.
TensegrityModel(const std::string &structurePath)
static const double rodRadius
virtual void onVisit(tgModelVisitor &visitor)
virtual void step(double timeStep)
Contains the definition of class tgSphere.
virtual void step(double dt)
Definition: tgModel.cpp:84
btVector3 getCentroid() const
static double deg2rad(double degrees)
Definition: tgUtil.h:335
Definition of class tgBasicActuatorInfo.
virtual void onVisit(const tgModelVisitor &r) const
Definition: tgModel.cpp:107
Class that interfaces with Bullet to build the boxes.
const std::vector< tgSpringCableActuator * > & getAllActuators() const
void addPair(int fromNodeIdx, int toNodeIdx, std::string tags="")
Definition: tgStructure.cpp:80
static btVector3 getCentroid(const std::vector< btVector3 > &points)
Definition: tgUtil.h:246
void addRotation(const btVector3 &fixedPoint, const btVector3 &axis, double angle)
Definition of class tgBasicContactCableInfo.
virtual void setup(tgWorld &world)
Contains the definition of class tgBasicActuator.
const bool matches(const tgTags &tags) const
Definition: tgTagSearch.h:62
Definition: tgPair.h:48
Definition: tgNode.h:45
Definition of class tgStructureInfo.
Contains the definition of class tgKinematicActuator.
virtual void teardown()
tgPair & findPair(const btVector3 &from, const btVector3 &to)
virtual ~TensegrityModel()
Definition of class tgKinematicActuatorInfo.
Contains the definition of class tgRod.
static void addRotation(btVector3 &v, const btVector3 &fixedPoint, const btVector3 &axis, double angle)
Definition: tgUtil.h:274
Definition of class tgKinematicContactCableInfo.
std::vector< tgModel * > getDescendants() const
Definition: tgModel.cpp:170
void buildInto(tgModel &model, tgWorld &world)
void addNode(double x, double y, double z, std::string tags="")
Definition: tgStructure.cpp:70