iMSTK
Interactive Medical Simulation Toolkit
imstkProgrammableClient.cpp
1 /*
2 ** This file is part of the Interactive Medical Simulation Toolkit (iMSTK)
3 ** iMSTK is distributed under the Apache License, Version 2.0.
4 ** See accompanying NOTICE for details.
5 */
6 
7 #include "imstkProgrammableClient.h"
8 #include "imstkLogger.h"
9 #include "imstkCapsule.h"
10 
11 #include <iostream>
12 
13 namespace imstk
14 {
16 {
17  for (auto cmd : m_commands)
18  {
19  delete cmd;
20  }
21  m_commands.clear();
22 }
23 
24 double
26 {
27  double total = 0;
28  for (auto cmd : m_commands)
29  {
30  total += cmd->duration;
31  }
32  return total;
33 }
34 
35 void
37 {
38  if (m_complete)
39  {
40  return;
41  }
42  if (m_dt <= 0)
43  {
44  LOG(WARNING) << "warning: No update period set";
45  return;
46  }
47 
48  size_t active = 0;
49  size_t complete = 0;
50  m_currentTime += m_dt;
51  for (auto cmd : m_commands)
52  {
53  if (cmd->state == CommandState::WAITING && m_currentTime > cmd->startTime)
54  {
55  cmd->activate(*this);
56  }
57  else if (cmd->state == CommandState::ACTIVE && m_currentTime >= cmd->startTime + cmd->duration)
58  {
59  cmd->complete(*this);
60  }
61 
62  if (cmd->state == CommandState::ACTIVE)
63  {
64  active++;
65  cmd->updateDevice(*this);
66  }
67  else if (cmd->state == CommandState::COMPLETE)
68  {
69  complete++;
70  }
71  }
72 
73  // if (active > 1)
74  // LOG(WARNING) << "More than 1 command is active for a Programmable Client";
75  if (complete == m_commands.size())
76  {
77  m_complete = true;
78  }
79 }
80 
81 bool
82 ProgrammableClient::addLinearMovement(Vec3d startPos, Vec3d stopPos, double startTime, double duration)
83 {
84  if (startPos == stopPos)
85  {
86  LOG(WARNING) << "warning: No travel set";
87  return false;
88  }
89  if (duration <= 0)
90  {
91  LOG(WARNING) << "warning: No duration set";
92  return false;
93  }
94 
95  LinearMovement* move = new LinearMovement();
96  move->startPosition = startPos;
97  move->stopPosition = stopPos;
98  move->startTime = startTime;
99  move->duration = duration;
100  m_commands.push_back(move);
101  return true;
102 }
103 
104 void
105 ProgrammableClient::LinearMovement::activate(ProgrammableClient& pc)
106 {
107  Command::activate(pc);
108  pc.m_position = startPosition;
109  pc.m_velocity = ((stopPosition - startPosition) / duration);
110 }
111 
112 void
113 ProgrammableClient::LinearMovement::updateDevice(ProgrammableClient& pc)
114 {
115  pc.m_position += (pc.getVelocity() * pc.m_dt);
116 }
117 
118 void
119 ProgrammableClient::LinearMovement::complete(ProgrammableClient& pc)
120 {
121  Command::complete(pc);
122  pc.m_position = stopPosition;
123 }
124 
125 bool
126 ProgrammableClient::addCircularMovement(Vec3d startPos, Vec3d centerPos, double startTime, double duration)
127 {
128  if (duration <= 0)
129  {
130  LOG(WARNING) << "warning: No duration set";
131  return false;
132  }
133 
134  CircularMovement* move = new CircularMovement();
135  move->startPosition = startPos;
136  move->centerPosition = centerPos;
137  move->radius = (startPos - centerPos).norm();
138  move->angle = acos((startPos[0] / move->radius) - centerPos[0]);
139  move->angleStep = 2 * PI / (duration / m_dt);
140  move->startTime = startTime;
141  move->duration = duration;
142  m_commands.push_back(move);
143  return true;
144 }
145 
146 void
147 ProgrammableClient::CircularMovement::activate(ProgrammableClient& pc)
148 {
149  Command::activate(pc);
150  pc.m_position[0] = centerPosition[0] + (cos(angle) * radius);
151  pc.m_position[1] = centerPosition[1];
152  pc.m_position[2] = centerPosition[2] + (sin(angle) * radius);
153  pc.m_angularVelocity = Vec3d::Zero();
154 }
155 
156 void
157 ProgrammableClient::CircularMovement::updateDevice(ProgrammableClient& pc)
158 {
159  angle += angleStep;
160  pc.m_position[0] = centerPosition[0] + (cos(angle) * radius);
161  pc.m_position[1] = centerPosition[1];
162  pc.m_position[2] = centerPosition[2] + (sin(angle) * radius);
163  // TODO Compute angular velocity
164 }
165 
166 void
167 ProgrammableClient::CircularMovement::complete(ProgrammableClient& pc)
168 {
169  Command::complete(pc);
170  pc.m_position[0] = centerPosition[0] + (cos(0) * radius);
171  pc.m_position[1] = centerPosition[1];
172  pc.m_position[2] = centerPosition[2] + (sin(0) * radius);
173  pc.m_angularVelocity = Vec3d::Zero();
174 }
175 
176 bool
177 ProgrammableClient::addGrasping(std::shared_ptr<PbdObject> tool, std::shared_ptr<PbdObjectGrasping> objectGrasping, double startTime, double duration)
178 {
179  GraspAction* grasp = new GraspAction();
180  grasp->tool = tool;
181  grasp->objectGrasping = objectGrasping;
182  grasp->startTime = startTime;
183  grasp->duration = duration;
184  m_commands.push_back(grasp);
185  return true;
186 }
187 
188 void
189 ProgrammableClient::GraspAction::activate(ProgrammableClient& pc)
190 {
191  Command::activate(pc);
192  objectGrasping->beginVertexGrasp(std::dynamic_pointer_cast<Capsule>(tool->getPhysicsGeometry()));
193 }
194 
195 void
196 ProgrammableClient::GraspAction::complete(ProgrammableClient& pc)
197 {
198  Command::complete(pc);
199  objectGrasping->endGrasp();
200 }
201 
202 bool
204  std::shared_ptr<PbdObject> object,
205  std::vector<int> vertexIds,
206  Vec3d translation,
207  std::vector<bool> pin,
208  double startTime,
209  double duration)
210 {
211  if (translation == Vec3d::Zero())
212  {
213  LOG(WARNING) << "warning: No travel set";
214  }
215  if (duration <= 0)
216  {
217  LOG(WARNING) << "warning: No duration set";
218  return false;
219  }
220 
222  move->object = object;
223  move->translation = translation;
224  move->startTime = startTime;
225  move->duration = duration;
226  move->vertexIds = vertexIds;
227  move->pin = pin;
228 
229  if (move->vertexIds.empty())
230  {
231  LOG(WARNING) << "warning: Invalid initial position for vertex";
232  return false;
233  }
234 
235  m_commands.push_back(move);
236  return true;
237 }
238 
239 void
240 ProgrammableClient::LinearVertexMovement::activate(ProgrammableClient& pc)
241 {
242  Command::activate(pc);
243  pc.m_velocity = translation / duration;
244  for (int i = 0; i < vertexIds.size(); i++)
245  {
246  currPos.push_back((*object->getPbdBody()->vertices)[vertexIds[i]]);
247  }
248 }
249 
250 void
251 ProgrammableClient::LinearVertexMovement::updateDevice(ProgrammableClient& pc)
252 {
253  for (int i = 0; i < vertexIds.size(); i++)
254  {
255  for (int dim = 0; dim < 3; dim++)
256  {
257  if (pin[dim])
258  {
259  currPos[i][dim] += (pc.m_velocity[dim] * pc.m_dt);
260  }
261  else
262  {
263  currPos[i][dim] = (*object->getPbdBody()->vertices)[vertexIds[i]][dim];
264  }
265  }
266 
267  (*object->getPbdBody()->vertices)[vertexIds[i]] = currPos[i];
268  (*object->getPbdBody()->velocities)[vertexIds[i]] = Vec3d::Zero();// pc.m_velocity;
269  }
270 }
271 
272 void
273 ProgrammableClient::LinearVertexMovement::complete(ProgrammableClient& pc)
274 {
275  Command::complete(pc);
276 }
277 
278 bool
280  std::shared_ptr<PbdObject> object,
281  std::vector<int> vertexIds,
282  double strain,
283  DeformationType defType,
284  double poisson,
285  std::vector<bool> pin,
286  double startTime, double duration)
287 {
288  if (duration <= 0)
289  {
290  LOG(WARNING) << "warning: No duration set in addDeformation for ProgrammableClient";
291  return false;
292  }
293 
294  Deformation* deform = new Deformation();
295  deform->object = object;
296  deform->strain = strain;
297  deform->startTime = startTime;
298  deform->duration = duration;
299  deform->vertexIds = vertexIds;
300  deform->pin = pin;
301  deform->type = defType;
302  deform->poissons = poisson;
303 
304  if (deform->vertexIds.empty())
305  {
306  LOG(WARNING) << "warning: Invalid initial position for vertex";
307  return false;
308  }
309 
310  m_commands.push_back(deform);
311  return true;
312 }
313 
314 void
315 ProgrammableClient::Deformation::activate(ProgrammableClient& pc)
316 {
317  Command::activate(pc);
318  strainRate = strain / duration;
319 }
320 
321 void
322 ProgrammableClient::Deformation::updateDevice(ProgrammableClient& pc)
323 {
324  auto mesh = std::dynamic_pointer_cast<PointSet>(object->getPhysicsGeometry());
325  std::shared_ptr<VecDataArray<double, 3>> initVerticesPtr = mesh->getInitialVertexPositions();
326  const VecDataArray<double, 3>& initVertices = *initVerticesPtr;
327 
328  std::shared_ptr<VecDataArray<double, 3>> verticesPtr = mesh->getVertexPositions();
329  VecDataArray<double, 3>& vertices = *verticesPtr;
330 
331  double volFac = poissons * 2.0;
332 
333  if (type == DeformationType::Compression)
334  {
335  double isoCompression = sqrt(1.0 / (1.0 - strainRate * pc.m_dt)) - 1.0;
336  Mat3d compression{
337  { isoCompression* volFac, 0.0, 0.0 },
338  { 0.0, -strainRate * pc.m_dt, 0.0 },
339  { 0.0, 0.0, isoCompression* volFac } };
340 
341  defGrad += compression;
342  }
343  else if (type == DeformationType::Tension)
344  {
345  double isoTension = sqrt(1.0 / (1.0 + strainRate * pc.m_dt)) - 1.0;
346 
347  Mat3d tension{
348  { isoTension* volFac, 0.0, 0.0 },
349  { 0.0, strainRate* pc.m_dt, 0.0 },
350  { 0.0, 0.0, isoTension* volFac } };
351 
352  defGrad += tension;
353  }
354  else if (type == DeformationType::SimpleShear)
355  {
356  double gamma = strainRate * pc.m_dt;
357  Mat3d simpleShear{
358  { 0.0, gamma, 0.0 },
359  { 0.0, 0.0, 0.0 },
360  { 0.0, 0.0, 0.0 } };
361 
362  defGrad += simpleShear;
363  }
364  else if (type == DeformationType::PureShear)
365  {
366  double gamma = strainRate * pc.m_dt;
367  Mat3d pureShear{
368  { 0.0, gamma, 0.0 },
369  { gamma, 0.0, 0.0 },
370  { 0.0, 0.0, 0.0 } };
371 
372  defGrad += pureShear;
373  }
374 
375  for (int i = 0; i < vertexIds.size(); i++)
376  {
377  vertices[vertexIds[i]] = defGrad * initVertices[vertexIds[i]];
378  (*object->getPbdBody()->vertices)[vertexIds[i]] = vertices[vertexIds[i]];
379  }
380 }
381 
382 void
383 ProgrammableClient::Deformation::complete(ProgrammableClient& pc)
384 {
385  Command::complete(pc);
386 }
387 
388 std::vector<int>
389 ProgrammableClient::findVertex(std::shared_ptr<PointSet> mesh, std::vector<Vec3d> initPos)
390 {
391  std::vector<int> ids;
392  for (Vec3d pos : initPos)
393  {
394  for (int i = 0; i < mesh->getNumVertices(); i++)
395  {
396  if (pos.isApprox(mesh->getInitialVertexPosition(i)))
397  {
398  ids.push_back(i);
399  }
400  }
401  }
402 
403  return ids;
404 }
405 
406 bool
407 ProgrammableClient::addWaitCommand(double startTime, double duration)
408 {
409  if (duration <= 0)
410  {
411  LOG(WARNING) << "warning: No duration set";
412  return false;
413  }
414 
415  WaitCommand* wait = new WaitCommand();
416  wait->startTime = startTime;
417  wait->duration = duration;
418  m_commands.push_back(wait);
419  return true;
420 }
421 
422 bool
423 ProgrammableClient::addHoldCommand(std::shared_ptr<PbdObject> object, double startTime, double duration, std::vector<int> vertexIds)
424 {
425  if (duration <= 0)
426  {
427  LOG(WARNING) << "warning: No duration set";
428  return false;
429  }
430 
431  if (vertexIds.size() <= 0)
432  {
433  LOG(WARNING) << "warning: Vertices to hold in addHoldCommand";
434  return false;
435  }
436 
437  HoldCommand* hold = new HoldCommand();
438  hold->object = object;
439  hold->vertexIds = vertexIds;
440  hold->startTime = startTime;
441  hold->duration = duration;
442 
443  m_commands.push_back(hold);
444  return true;
445 }
446 
447 void
448 ProgrammableClient::HoldCommand::activate(ProgrammableClient& pc)
449 {
450  Command::activate(pc);
451 
452  // auto mesh = std::dynamic_pointer_cast<PointSet>(object->getPhysicsGeometry());
453  if (object->getPhysicsGeometry()->isMesh())
454  {
455  auto mesh = std::dynamic_pointer_cast<PointSet>(object->getPhysicsGeometry());
456 
457  std::shared_ptr<VecDataArray<double, 3>> verticesPtr = mesh->getVertexPositions();
458 
459  for (int vert = 0; vert < vertexIds.size(); vert++)
460  {
461  // holdPosition.push_back((*object->getPbdBody()->vertices)[vertexIds[vert]]);
462  //(*ptBody.invMasses)[0]
463  auto body = object->getPbdBody();
464  (*body->invMasses)[vertexIds[vert]] = 0.0;
465  // (*(object->getPbdBody()).invMasses)[vertexIds[vert]] = 0.0;
466  Vec3d pos = verticesPtr->at(vertexIds[vert]);
467  holdPosition.push_back(pos);
468  }
469  }
470  else
471  {
472  auto analyticalGeo = std::dynamic_pointer_cast<AnalyticalGeometry>(object->getPhysicsGeometry());
473  auto body = object->getPbdBody();
474  (*body->invMasses)[vertexIds[0]] = 0.0;
475  // (*(object->getPbdBody()).invMasses)[vertexIds[vert]] = 0.0;
476  Vec3d pos = analyticalGeo->getPosition();
477  holdPosition.push_back(pos);
478  }
479 }
480 
481 void
482 ProgrammableClient::HoldCommand::updateDevice(ProgrammableClient&)
483 {
484  if (object->getPhysicsGeometry()->isMesh())
485  {
486  auto mesh = std::dynamic_pointer_cast<PointSet>(object->getPhysicsGeometry());
487 
488  std::shared_ptr<VecDataArray<double, 3>> verticesPtr = mesh->getVertexPositions();
489  VecDataArray<double, 3>& vertices = *verticesPtr;
490 
491  for (int i = 0; i < vertexIds.size(); i++)
492  {
493  vertices[vertexIds[i]] = holdPosition[i];
494  (*object->getPbdBody()->velocities)[vertexIds[i]] = Vec3d::Zero();
495  }
496  }
497  else
498  {
499  auto analyticalGeo = std::dynamic_pointer_cast<AnalyticalGeometry>(object->getPhysicsGeometry());
500 
501  analyticalGeo->setPosition(holdPosition[0]);
502  (*object->getPbdBody()->velocities)[vertexIds[0]] = Vec3d::Zero();
503  }
504 }
505 
506 void
507 ProgrammableClient::HoldCommand::complete(ProgrammableClient& pc)
508 {
509  Command::complete(pc);
510 }
511 
512 /*
513  void ProgrammableClient::updateAngularOrientation()
514  {
515  // Quatd newOrientation = getOrientation();
516  if (this->startPointSet)
517  {
518 
519  std::cout << "Before:\n" << newOrientation << std::endl;
520  newOrientation = newOrientation.slerp(0, Quatd(.707, 1, 1, 1));
521  setOrientation(newOrientation);
522  startPointSet = false;
523  std::cout << "After:\n" << newOrientation << std::endl;
524  return;
525  }
526  double temp = this->time / this->dt;
527 
528  std::cout << newOrientation << std::endl;
529  Quatd temp = Quatd(.707, 0, 0, .707);
530  setOrientation(temp);
531  }*/
532 } // namespace imstk
Base class for any analytical geometrical representation.
~ProgrammableClient() override
Destructor.
Command for holding a subset of vertices at a specific position.
Base class for all geometries represented by discrete points and elements The pointsets follow a pipe...
Definition: imstkPointSet.h:25
bool addGrasping(std::shared_ptr< PbdObject > tool, std::shared_ptr< PbdObjectGrasping > objectGrasping, double startTime, double duration)
Add a grasp command to run.
Vec3d m_angularVelocity
Angular velocity of the end effector.
Command for linear movement of a subset of vertices from an object.
bool addHoldCommand(std::shared_ptr< PbdObject > object, double startTime, double duration, std::vector< int > vertexIds)
Add a hold command to run.
double getTotalDuration()
Returns to total duration of all commands.
Vec3d getPosition(DataType type=DataType::PostTransform)
Get the local or global position (post transformed)
Vec3d getVelocity()
Get the device velocity.
bool addWaitCommand(double startTime, double duration)
Add a wait command to run.
Compound Geometry.
bool addCircularMovement(Vec3d startPos, Vec3d centerPos, double startTime, double duration)
Add a circular movement command to run.
bool addLinearMovement(Vec3d startPos, Vec3d stopPos, double startTime, double duration)
Add a linear movement command to run.
DeformationType
Enum for type of deformation.
bool addDeformation(std::shared_ptr< PbdObject > object, std::vector< int > initPos, double strain, DeformationType defType, double poisson, std::vector< bool > pin, double startTime, double duration)
Add a defomation command to run.
Command for linear movement of an analytical geometry.
void setPosition(const Vec3d p)
Set the local position.
Vec3d m_velocity
Linear velocity of end effector.
Vec3d m_position
Position of end effector.
std::shared_ptr< VecDataArray< double, 3 > > getVertexPositions(DataType type=DataType::PostTransform) const
Returns the vector of current positions of the mesh vertices.
Command for waiting. Used to let system continue running with no active commands. ...
bool addLinearVertexMovement(std::shared_ptr< PbdObject > object, std::vector< int > vertexIds, Vec3d translation, std::vector< bool > pin, double startTime, double duration)
Add a linear vertex movement command to run.
void update() override
Update all commands.
Command for grasping an object.
std::shared_ptr< VecDataArray< double, 3 > > getInitialVertexPositions() const
Returns the vector of initial positions of the mesh vertices.
Definition: imstkPointSet.h:62
Command for applying deformationt to a subset of vertices from an object.
Command for circular movement of an analytical geometry.
Allows setting the pose of the device from external caller without a real device connected.