iCub-main
Loading...
Searching...
No Matches
compensationThread.cpp
Go to the documentation of this file.
1
2/*
3 * Copyright (C) 2009 RobotCub Consortium, European Commission FP6 Project IST-004370
4 * Authors: Andrea Del Prete, Alexander Schmitz
5 * email: andrea.delprete@iit.it, alexander.schmitz@iit.it
6 * website: www.robotcub.org
7 * Permission is granted to copy, distribute, and/or modify this program
8 * under the terms of the GNU General Public License, version 2 or any
9 * later version published by the Free Software Foundation.
10 *
11 * A copy of the license can be found at
12 * http://www.robotcub.org/icub/license/gpl.txt
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
17 * Public License for more details
18 */
19#include <yarp/os/Time.h>
20#include <yarp/math/Math.h>
21#include "math.h"
22#include "memory.h"
24
25#define FOR_ALL_PORTS(i) for(unsigned int i=0;i<portNum;i++)
26
27using namespace std;
28using namespace yarp::os;
29using namespace yarp::sig;
30using namespace yarp::math;
31using namespace iCub::skinManager;
32
33
34CompensationThread::CompensationThread(string name, ResourceFinder* rf, string robotName, double _compensationGain, double _contactCompensationGain,
35 int addThreshold, float minBaseline, bool zeroUpRawData,
36 int period, bool binarization, bool smoothFilter, float smoothFactor)
37 :
38 PeriodicThread((double)period/1000.0), moduleName(name), compensationGain(_compensationGain),
39 contactCompensationGain(_contactCompensationGain),
40 ADD_THRESHOLD(addThreshold), robotName(robotName),
41 binarization(binarization), smoothFilter(smoothFilter), smoothFactor(smoothFactor)
42{
43 this->rf = rf;
44 this->minBaseline = minBaseline;
45 this->zeroUpRawData = zeroUpRawData;
46 initializationFinished = false;
47}
48
50{
51 yTrace("[CompensationThread] threadInit()\n");
52
53 /* initialize variables and create data-structures if needed */
54 readErrorCounter = 0;
56 calibrationCounter = 0;
57 CAL_SAMPLES = (int)((double)CAL_TIME/getPeriod()); // samples needed for calibration
58
59 // open the output ports for communicating with the gui
60 string monitorPortName = "/" + moduleName + "/monitor:o"; // output streaming data
61 string infoPortName = "/" + moduleName + "/info:o"; // output occasional data
62 if(!monitorPort.open(monitorPortName.c_str())){
63 stringstream msg; msg<< "Unable to open port " << monitorPortName << endl;
64 sendErrorMsg(msg.str());
65 initializationFinished = true;
66 return false;
67 }
68 if(!infoPort.open(infoPortName.c_str())){
69 stringstream msg; msg<< "Unable to open port " << infoPortName << endl;
70 sendErrorMsg(msg.str());
71 initializationFinished = true;
72 return false;
73 }
74
75 // open the input and output ports for the skin data
76 if(!rf->check("outputPorts") || !rf->check("inputPorts")){
77 stringstream msg; msg<< "Input ports and/or output ports missing. Closing the module.";
78 sendErrorMsg(msg.str());
79 initializationFinished = true;
80 return false;
81 }
82
83 Bottle* outputPortList = rf->find("outputPorts").asList();
84 Bottle* inputPortList = rf->find("inputPorts").asList();
85 portNum = outputPortList->size();
86 if(portNum<=0 || portNum!=inputPortList->size()){
87 stringstream msg;
88 msg<< "No input port specified or mismatching number of input and output ports ("
89 << portNum<< " out ports; "<< inputPortList->size()<< " in ports).";
90 sendErrorMsg(msg.str());
91 initializationFinished = true;
92 return false;
93 }
94
95 compensators.resize(portNum);
96 compWorking.resize(portNum);
97 compEnable.resize(portNum, true);
98 compensatorCounter = portNum;
99 SKIN_DIM = 0;
100 cout<< portNum<< " input ports found in the configuration file\n";
101 FOR_ALL_PORTS(i){
102 string outputPortName = outputPortList->get(i).asString().c_str();
103 string inputPortName = inputPortList->get(i).asString().c_str();
104 // cout << "Input port: "<< inputPortName<< " -> Output port: "<< outputPortName<< endl;
105 yInfo("Input port: %s -> Output port: %s",inputPortName.c_str(),outputPortName.c_str());
106 stringstream name;
107 name<< moduleName<< i;
108 compensators[i] = new Compensator(name.str(), robotName, outputPortName, inputPortName, &infoPort,
109 compensationGain, contactCompensationGain, ADD_THRESHOLD, minBaseline, zeroUpRawData, binarization,
110 smoothFilter, smoothFactor);
111 SKIN_DIM += compensators[i]->getNumTaxels();
112 }
113
114 // remove the compensators that did not open correctly
115 FOR_ALL_PORTS(i){
116 compWorking[i] = compensators[i]->isWorking();
117 if(!compWorking[i]){
118 stringstream msg;
119 msg<< " Compensator "<< compensators[i]->getInputPortName().c_str()
120 << " did not open correctly. Removing the port.";
121 sendErrorMsg(msg.str());
122 if(compensatorCounter==1){
123 msg.str("");
124 msg<< "No input port left. Stopping the thread.";
125 sendErrorMsg(msg.str());
126 this->threadRelease();
127 initializationFinished = true;
128 return false;
129 }
130 compensatorCounter--;
131 SKIN_DIM -= compensators[i]->getNumTaxels();
132 }
133 }
134
135
136 // configure the SKIN_EVENT if the corresponding section exists
137 skinEventsOn = false;
138 Bottle &skinEventsConf = rf->findGroup("SKIN_EVENTS");
139 if(!skinEventsConf.isNull()){
140 yDebug("SKIN_EVENTS section found");
141 string eventPortName = "/" + moduleName + "/skin_events:o"; // output skin events
142 if(!skinEventsPort.open(eventPortName.c_str()))
143 sendErrorMsg("Unable to open port "+eventPortName);
144 else
145 skinEventsOn = true;
146
147 if(skinEventsConf.check("skinParts")){
148 Bottle* skinPartList = skinEventsConf.find("skinParts").asList();
149 if(skinPartList->size() != portNum){
150 stringstream msg;
151 msg<< "ERROR: the number of skin part ids is not equal to the number of input ports ("
152 << skinPartList->size()<< "!="<< portNum<< "). Skin parts will not be set.";
153 sendDebugMsg(msg.str());
154 }else{
155 FOR_ALL_PORTS(i){
156 // cout<< "Skin part "<< SkinPart_s[skinPartList->get(i).asInt32()]<< endl;
157 yDebug("Skin part %s",SkinPart_s[skinPartList->get(i).asInt32()].c_str());
158 compensators[i]->setSkinPart((SkinPart)skinPartList->get(i).asInt32());
159 }
160 }
161 }
162
163 if(skinEventsConf.check("taxelPositionFiles")){
164 Bottle *taxelPosFiles = skinEventsConf.find("taxelPositionFiles").asList();
165 if(portNum!=taxelPosFiles->size()){
166 stringstream msg;
167 msg<< "Mismatching number of taxel position files and input ports ("
168 <<portNum<< " in ports; "<< taxelPosFiles->size()<< " taxel position files). ";
169 msg<< "Taxel positions will not be set.";
170 msg<< ". Taxel position file list: "<< taxelPosFiles->toString().c_str();
171 sendDebugMsg(msg.str());
172 }
173 else{
174 maxNeighDist = skinEventsConf.check("maxNeighborDist", Value(MAX_NEIGHBOR_DISTANCE)).asFloat64();
175
176 yInfo("Max neighbor distance: %f m\n", maxNeighDist);
177 FOR_ALL_PORTS(i){
178 if(compWorking[i]){
179 string taxelPosFile = taxelPosFiles->get(i).asString().c_str();
180 string filePath(rf->findFile(taxelPosFile.c_str()));
181 compensators[i]->setMaxNeighborDistance(maxNeighDist);
182 compensators[i]->setTaxelPosesFromFile(filePath.c_str());
183 }
184 }
185 }
186 }
187 }
188 if(skinEventsOn)
189 sendDebugMsg("Skin events ENABLED.");
190 else
191 sendDebugMsg("Skin events DISABLED.");
192
193 initializationFinished = true;
194 return true;
195}
196
198 lock_guard<mutex> lck(stateSem);
199 if(state != calibration){
201 calibrationCounter = 0;
202 }
203}
204
206 stateSem.lock();
207
208 if( state == compensation){
209 // It reads the raw data, computes the difference between the read values and the baseline
210 // and outputs these values
211 FOR_ALL_PORTS(i){
212 if(compWorking[i]){
213 if(compensators[i]->readRawAndWriteCompensatedData()){
214 //If the read succeeded, update the baseline
215 compensators[i]->updateBaseline();
216 }
217 }
218 }
219
220 if(skinEventsOn){
221 sendSkinEvents();
222 }
223 }
224 else if(state == calibration){
225 FOR_ALL_PORTS(i){
226 if(compWorking[i]){
227 if(calibrationCounter==0)
228 compensators[i]->calibrationInit();
229
230 compensators[i]->calibrationDataCollection();
231
232 if(calibrationCounter==CAL_SAMPLES){
233 compensators[i]->calibrationFinish();
235 }
236 }
237 }
238 calibrationCounter++;
239 }
240 else{
241 stateSem.unlock();
242 sendDebugMsg("[ERROR] Unknown state in CompensationThread. Suspending the thread.\n");
243 this->suspend();
244 return;
245 }
246 stateSem.unlock();
247 sendMonitorData();
248 checkErrors();
249}
250
251void CompensationThread::sendSkinEvents(){
252 skinContactList &skinEvents = skinEventsPort.prepare();
253 skinEvents.clear();
254
255 skinContactList temp;
256 Stamp timestamp;
257 FOR_ALL_PORTS(i){
258 if(compWorking[i] && compEnable[i]){
259 temp = compensators[i]->getContacts();
260 timestamp = compensators[i]->getTimestamp();
261 skinEvents.insert(skinEvents.end(), temp.begin(), temp.end());
262 }
263 }
264#ifdef _DEBUG
265 if(skinEvents.size()>0)
266 printf("SkinContacts size: %d\n", skinEvents.size());
267 /*printf("SkinContacts:\n%s\n", skinEvents.toString().c_str());*/
268#endif
269
270 skinEventsPort.setEnvelope(timestamp);
271 skinEventsPort.write(); // send something anyway (if there is no contact the bottle is empty)
272}
273
274void CompensationThread::checkErrors(){
275 FOR_ALL_PORTS(i){
276 if(compWorking[i]){
277 compWorking[i] = compensators[i]->isWorking();
278 if(!compWorking[i]){ // read failed too many times in a row, remove the port
279 if(compensatorCounter==1){
280 fprintf(stderr, "No input port left. Stopping the compensation thread\n");
281 this->threadRelease();
282 this->suspend();
283 return;
284 }
285
286 compensatorCounter--;
287 SKIN_DIM -= compensators[i]->getNumTaxels(); // remove the taxel from the total count
288 }
289 }
290 }
291
292 unsigned int taxInd, compInd;
293 double baseline, initialBaseline;
294 if(doesBaselineExceed(compInd, taxInd, baseline, initialBaseline)){
295 stringstream msg;
296 msg<< "Baseline of the taxel "<< taxInd<< " of port "<< compensators[compInd]->getInputPortName()
297 << " saturated (current baseline="<< baseline<< "; initial baseline="<< initialBaseline<<
298 ")! A skin calibration is suggested.";
299 sendDebugMsg(msg.str());
300 }
301}
302
303bool CompensationThread::doesBaselineExceed(unsigned int &compInd, unsigned int &taxInd, double &baseline, double &initialBaseline){
304 stateSem.lock();
305 CompensationThreadState currentState = state;
306 stateSem.unlock();
307 if(currentState==compensation){
308 FOR_ALL_PORTS(i){
309 if(compWorking[i] && compensators[i]->doesBaselineExceed(taxInd, baseline, initialBaseline)){
310 compInd = i;
311 return true;
312 }
313 }
314 }
315 return false;
316}
317
319{
320 FOR_ALL_PORTS(i){
321 delete compensators[i];
322 }
323 portNum = 0;
324 state = compensation; // to prevent the GUI from looping calling isCalibrating() on the thread
325
326 monitorPort.interrupt();
327 infoPort.interrupt();
328 monitorPort.close();
329 infoPort.close();
330}
331
332// send the data on the monitor port
333void CompensationThread::sendMonitorData(){
334 // send the monitor data (if there is at least a connection)
335 if(monitorPort.getOutputCount()>0){
336 int originalSkinDim = 0;
338 originalSkinDim += compensators[i]->getNumTaxels();
339
340 Vector &b = monitorPort.prepare();
341 b.clear();
342 b.resize(1+ 2*originalSkinDim);
343 b[0] = 1.0/getEstimatedPeriod(); // thread frequency
344
345 stateSem.lock();
346 if(state==compensation){ // during calibration don't send this data
347 // for each taxel add how much the baseline has changed so far (i.e. the drift)
348 int index = 1;
349 FOR_ALL_PORTS(i){
350 if(compWorking[i]){
351 b.setSubvector(index, compensators[i]->getCompensation());
352 }
353 index += compensators[i]->getNumTaxels();
354 }
355 FOR_ALL_PORTS(i){
356 if(compWorking[i]){
357 b.setSubvector(index, compensators[i]->getRawData());
358 }
359 index += compensators[i]->getNumTaxels();
360 }
361 }
362 stateSem.unlock();
363 //printf("Writing %d data on monitor port\n", b.size());
364 monitorPort.write();
365 }
366}
367
368void CompensationThread::sendDebugMsg(string msg){
369 //printf("\n");
370 yDebug("[CompensationThread] %s", msg.c_str());
371 Bottle& b = infoPort.prepare();
372 b.clear();
373 b.addString(msg.c_str());
374 infoPort.write(true);
375}
376
377void CompensationThread::sendErrorMsg(string msg){
378 //printf("\n");
379 yError("[CompensationThread] %s", msg.c_str());
380 Bottle& b = infoPort.prepare();
381 b.clear();
382 b.addString(msg.c_str());
383 infoPort.write(true);
384}
385
387 Bottle res;
388 if(initializationFinished){ // check whether the thread has been initialized
389 Bottle& nameB = res.addList();
390 nameB.addString("Name: ");
391 nameB.addString(moduleName.c_str());
392 Bottle& robotB = res.addList();
393 robotB.addString("Robot Name: ");
394 robotB.addString(robotName.c_str());
395 Bottle& portB = res.addList();
396 string compName;
397 FOR_ALL_PORTS(i){
398 compName = compensators[i]->getInputPortName().c_str();
399 if(!compWorking[i])
400 compName = compName + " (NOT WORKING)";
401 portB.addString(compName.c_str());
402 portB.addInt32(compensators[i]->getNumTaxels());
403 }
404 }else{
405 res.addString("Module initialization has not been completed yet.");
406 }
407 return res;
408}
409
411 binarization = value;
412 FOR_ALL_PORTS(i){
413 compensators[i]->setBinarization(value);
414 }
415}
417 if(smoothFilter != value){
418 lock_guard<mutex> lck(stateSem);
419 smoothFilter = value;
420 FOR_ALL_PORTS(i){
421 compensators[i]->setSmoothFilter(value);
422 }
423 }
424}
426 if(value<0 || value>1)
427 return false;
428 if(value==1)
429 value = 0.99f; // otherwise with 1 the values don't update
430 smoothFactor = value;
431 FOR_ALL_PORTS(i){
432 compensators[i]->setSmoothFactor(value);
433 }
434 return true;
435}
437 bool res = true;
438 FOR_ALL_PORTS(i){
439 res = res && compensators[i]->setAddThreshold(thr);
440 }
441 if(res)
442 ADD_THRESHOLD = thr;
443 return res;
444}
445
447 bool res = true;
448 FOR_ALL_PORTS(i){
449 res = res && compensators[i]->setCompensationGain(gain);
450 }
451 if(res)
452 compensationGain = gain;
453 return res;
454}
455
457 bool res = true;
458 FOR_ALL_PORTS(i){
459 res = res && compensators[i]->setContactCompensationGain(gain);
460 }
461 if(res)
462 contactCompensationGain = gain;
463 return res;
464}
466 bool res = true;
467 FOR_ALL_PORTS(i){
468 res = res && compensators[i]->setMaxNeighborDistance(dist);
469 }
470 if(res)
471 maxNeighDist = dist;
472 return res;
473}
474bool CompensationThread::setTaxelPosition(SkinPart sp, unsigned int taxelId, const Vector &position){
475 FOR_ALL_PORTS(i){
476 if(compensators[i]->getSkinPart()==sp){
477 return compensators[i]->setTaxelPosition(taxelId, position);
478 }
479 }
480 return false;
481}
482bool CompensationThread::setTaxelPositions(SkinPart sp, const Vector &positions){
483 FOR_ALL_PORTS(i){
484 if(compensators[i]->getSkinPart()==sp){
485 return compensators[i]->setTaxelPositions(positions);
486 }
487 }
488 return false;
489}
490bool CompensationThread::setTaxelOrientation(SkinPart sp, unsigned int taxelId, const Vector &orientation){
491 FOR_ALL_PORTS(i){
492 if(compensators[i]->getSkinPart()==sp){
493 return compensators[i]->setTaxelOrientation(taxelId, orientation);
494 }
495 }
496 return false;
497}
498bool CompensationThread::setTaxelOrientations(SkinPart sp, const vector<Vector> &orientations){
499 FOR_ALL_PORTS(i){
500 if(compensators[i]->getSkinPart()==sp){
501 return compensators[i]->setTaxelOrientations(orientations);
502 }
503 }
504 return false;
505}
506bool CompensationThread::setTaxelPose(SkinPart sp, unsigned int taxelId, const Vector &pose){
507 FOR_ALL_PORTS(i){
508 if(compensators[i]->getSkinPart()==sp){
509 return compensators[i]->setTaxelPose(taxelId, pose);
510 }
511 }
512 return false;
513}
514bool CompensationThread::setTaxelPoses(SkinPart sp, const vector<Vector> &poses){
515 FOR_ALL_PORTS(i){
516 if(compensators[i]->getSkinPart()==sp){
517 return compensators[i]->setTaxelPoses(poses);
518 }
519 }
520 return false;
521}
522bool CompensationThread::setTaxelPoses(SkinPart sp, const Vector &poses){
523 FOR_ALL_PORTS(i){
524 if(compensators[i]->getSkinPart()==sp){
525 unsigned int numTax = compensators[i]->getNumTaxels();
526 if(poses.size()==6*numTax){
527 //return false;
528 vector<Vector> p(numTax);
529 for(unsigned int j=0; j<numTax; j++){
530 p[j] = poses.subVector(6*j, 6*j+5);
531 }
532 return compensators[i]->setTaxelPoses(p);
533 }
534 else if(poses.size()==7*numTax){ //check if there is also a confidence value of the estimation
535 vector<Vector> p(numTax);
536 for(unsigned int j=0; j<numTax; j++){
537 p[j] = poses.subVector(7*j, 7*j+6);
538 }
539 return compensators[i]->setTaxelPoses(p);
540 }
541 else
542 return false;
543 }
544 }
545 return false;
546}
547
548//************************************************************************************************************
549//************************************************************************************************************
550// GET METHODS
551//************************************************************************************************************
552//************************************************************************************************************
554 Vector res(SKIN_DIM);
555 int currentDim=0;
556 FOR_ALL_PORTS(i){
557 if(compWorking[i]){
558 Vector temp = compensators[i]->getTouchThreshold();
559 memcpy(res.data()+currentDim, temp.data(), temp.size()*sizeof(double));
560 currentDim += temp.size();
561 }
562 }
563 return res;
564}
565
567 return ADD_THRESHOLD;
568}
570 return compensationGain;
571}
573 return contactCompensationGain;
574}
576 return binarization;
577}
579 return smoothFilter;
580}
582 lock_guard<mutex> lck(stateSem);
583 bool res = state==calibration;
584 return res;
585}
587 return smoothFactor;
588}
589Vector CompensationThread::getTaxelPosition(SkinPart sp, unsigned int taxelId){
590 FOR_ALL_PORTS(i){
591 if(compensators[i]->getSkinPart()==sp){
592 return compensators[i]->getTaxelPosition(taxelId);
593 }
594 }
595 return zeros(3);
596}
598 if(sp==SKIN_PART_ALL){
599 vector<Vector> res;
600 FOR_ALL_PORTS(i){
601 vector<Vector> temp = compensators[i]->getTaxelPositions();
602 res.insert(res.end(), temp.begin(), temp.end());
603 }
604 return res;
605 }
606 FOR_ALL_PORTS(i){
607 if(compensators[i]->getSkinPart()==sp){
608 return compensators[i]->getTaxelPositions();
609 }
610 }
611 return vector<Vector>();
612}
613Vector CompensationThread::getTaxelOrientation(SkinPart sp, unsigned int taxelId){
614 FOR_ALL_PORTS(i){
615 if(compensators[i]->getSkinPart()==sp){
616 return compensators[i]->getTaxelOrientation(taxelId);
617 }
618 }
619 return zeros(3);
620}
622 if(sp==SKIN_PART_ALL){
623 vector<Vector> res;
624 FOR_ALL_PORTS(i){
625 vector<Vector> temp = compensators[i]->getTaxelOrientations();
626 res.insert(res.end(), temp.begin(), temp.end());
627 }
628 return res;
629 }
630 FOR_ALL_PORTS(i){
631 if(compensators[i]->getSkinPart()==sp){
632 return compensators[i]->getTaxelOrientations();
633 }
634 }
635 return vector<Vector>();
636}
637Vector CompensationThread::getTaxelPose(SkinPart sp, unsigned int taxelId){
638 FOR_ALL_PORTS(i){
639 if(compensators[i]->getSkinPart()==sp){
640 return compensators[i]->getTaxelPose(taxelId);
641 }
642 }
643 return zeros(3);
644}
646 if(sp==SKIN_PART_ALL){
647 vector<Vector> res;
648 FOR_ALL_PORTS(i){
649 vector<Vector> temp = compensators[i]->getTaxelPoses();
650 res.insert(res.end(), temp.begin(), temp.end());
651 }
652 return res;
653 }
654 FOR_ALL_PORTS(i){
655 if(compensators[i]->getSkinPart()==sp){
656 return compensators[i]->getTaxelPoses();
657 }
658 }
659 return vector<Vector>();
660}
661
662double CompensationThread::getPoseConfidence(SkinPart sp, unsigned int taxelId){
663 FOR_ALL_PORTS(i){
664 if(compensators[i]->getSkinPart()==sp){
665 return compensators[i]->getPoseConfidence(taxelId);
666 }
667 }
668 return 0.0;
669}
670
672 FOR_ALL_PORTS(i){
673 if(compensators[i]->getSkinPart()==sp){
674 return compensators[i]->getPoseConfidences();
675 }
676 }
677 return zeros(0);
678}
679
681 vector<SkinPart> res(compensators.size());
683 res[i] = compensators[i]->getSkinPart();
684 return res;
685}
688 if(compensators[i]->getSkinPart()==sp){
689 compEnable[i] = true;
690 return true;
691 }
692 return false;
693}
696 if(compensators[i]->getSkinPart()==sp){
697 compEnable[i] = false;
698 return true;
699 }
700 return false;
701}
704 if(compensators[i]->getSkinPart()==sp)
705 return compEnable[i];
706 return false;
707}
Class representing a list of external contacts acting on the iCub' skin.
bool setTaxelPosition(SkinPart sp, unsigned int taxelId, const Vector &position)
Vector getTaxelPose(SkinPart sp, unsigned int taxelId)
Vector getTaxelOrientation(SkinPart sp, unsigned int taxelId)
bool setTaxelPose(SkinPart sp, unsigned int taxelId, const Vector &pose)
double getPoseConfidence(SkinPart sp, unsigned int taxelId)
vector< Vector > getTaxelPoses(SkinPart sp=SKIN_PART_ALL)
bool setTaxelPositions(SkinPart sp, const Vector &positions)
CompensationThread(string name, ResourceFinder *rf, string robotName, double _compensationGain, double _contactCompensationGain, int addThreshold, float minBaseline, bool zeroUpRawData, int period, bool binarization, bool smoothFilter, float smoothFactor)
bool setTaxelPoses(SkinPart sp, const vector< Vector > &poses)
Vector getTaxelPosition(SkinPart sp, unsigned int taxelId)
bool setTaxelOrientation(SkinPart sp, unsigned int taxelId, const Vector &orientation)
bool setTaxelOrientations(SkinPart sp, const vector< Vector > &orientations)
vector< Vector > getTaxelPositions(SkinPart sp=SKIN_PART_ALL)
vector< Vector > getTaxelOrientations(SkinPart sp=SKIN_PART_ALL)
#define FOR_ALL_PORTS(i)
zeros(2, 2) eye(2
const std::string SkinPart_s[]
Definition common.h:64
static const double MAX_NEIGHBOR_DISTANCE
fprintf(fid,'\n')