Content of file AnimationEditor.inl

/******************************************************************************
*                 SOFA, Simulation Open-Framework Architecture                *
*                    (c) 2006 INRIA, USTL, UJF, CNRS, MGH                     *
*                                                                             *
* This program is free software; you can redistribute it and/or modify it     *
* under the terms of the GNU Lesser General Public License as published by    *
* the Free Software Foundation; either version 2.1 of the License, or (at     *
* your option) any later version.                                             *
*                                                                             *
* This program is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
* for more details.                                                           *
*                                                                             *
* You should have received a copy of the GNU Lesser General Public License    *
* along with this program. If not, see <http://www.gnu.org/licenses/>.        *
*******************************************************************************
*                           Plugin SoftRobots                                 *
*                                                                             *
* This plugin is also distributed under the GNU LGPL (Lesser General          *
* Public License) license with the same conditions than SOFA.                 *
*                                                                             *
* Contributors: Defrost team  (INRIA, University of Lille, CNRS,              *
*               Ecole Centrale de Lille)                                      *
*                                                                             *
* Contact information: https://project.inria.fr/softrobot/contact/            *
******************************************************************************/
#pragma once

#include <SoftRobots/component/controller/AnimationEditor.h>
#ifdef SOFA_WITH_DACCORD
#include "../../applications/sofa/gui/SofaGuiCommon/editor/editor.h"
using daccord::current::Editor ;
#endif

#include <sofa/core/objectmodel/KeypressedEvent.h>
#include <sofa/simulation/AnimateBeginEvent.h>
#include <sofa/core/objectmodel/Event.h>

#if SOFTROBOTS_HAVE_SOFA_GL
#include <sofa/gl/template.h>
using sofa::gl::glVertexT;
#endif

#include <fstream>
#include <iomanip>
#include <fstream>


namespace softrobots::controller
{

using sofa::core::objectmodel::ComponentState;

using sofa::core::objectmodel::KeypressedEvent;
using sofa::core::objectmodel::MouseEvent;
using sofa::simulation::AnimateBeginEvent;

using sofa::core::visual::VisualParams;
using sofa::type::Mat;
using sofa::helper::WriteAccessor;
using sofa::helper::ReadAccessor;
using std::endl;
using std::cout;
using std::stringstream;
using sofa::type::RGBAColor;
using sofa::type::Vec3d;
using sofa::type::Vec4d;
using std::ifstream;
using std::ofstream;
using sofa::type::vector;


template<class DataTypes>
AnimationEditor<DataTypes>::AnimationEditor()
    :
      d_maxKeyFrame(initData(&d_maxKeyFrame,(unsigned int)150,"maxKeyFrame","Max >= 1, default 150"))
    , d_filename(initData(&d_filename,string("animation.txt"),"filename","If no filename given, set default to animation.txt"))
    , d_loop(initData(&d_loop,false,"loop","If true, will loop on the animation (only in play mode)."))
    , d_load(initData(&d_load,true,"load","If true, will load the animation at init."))
    , d_dx(initData(&d_dx,0.,"dx","Variation of displacement. You can control the animation on displacement instead of time.\n"
                                  "If dx is set, at each time step, the animation will progress in term of displacement/distance.\n"
                                  "A positive dx means move forward and a negative dx means backward (on the timeline)."))
    , d_frameTime(initData(&d_frameTime,0.01,"frameTime","Frame time."))
    , d_drawTimeline(initData(&d_drawTimeline,true,"drawTimeline",""))
    , d_drawSize(initData(&d_drawSize,0.1,"drawSize",""))
    , d_drawTrajectory(initData(&d_drawTrajectory,false,"drawTrajectory",""))
    , d_cursor(initData(&d_cursor,(unsigned int)0,"cursor","Current frame of the cursor along the timeline"))
    , m_state(nullptr)
    , m_isFrameDirty(false)
    , m_isPlaying(false)
    , m_maxKeyFrameID(0)
    , m_time(0.)
    , m_dx(0.)
{
    d_drawTimeline.setGroup("Visualization");
    d_drawTrajectory.setGroup("Visualization");
    d_drawSize.setGroup("Visualization");
    f_listening.setValue(true);
}


template<class DataTypes>
AnimationEditor<DataTypes>::~AnimationEditor()
{

}


template<class DataTypes>
void AnimationEditor<DataTypes>::init()
{
    d_componentState.setValue(ComponentState::Invalid);

    m_keyFramesID.clear();

    if(d_maxKeyFrame.getValue()<=0)
        d_maxKeyFrame.setValue(1);

    m_state = dynamic_cast<MechanicalState<DataTypes>*>(getContext()->getMechanicalState());
    if(m_state == nullptr)
    {
        msg_error() <<"This component needs a mechanical state in its context with the correct template. Abort.";
        return;
    }

    m_keyFramesID.push_back(0);
    m_animation.resize(1,m_state->readPositions().ref());

    if(d_load.getValue())
        loadAnimation();

    d_componentState.setValue(ComponentState::Valid);
}


template<class DataTypes>
void AnimationEditor<DataTypes>::reinit()
{
    if(d_componentState.getValue() != ComponentState::Valid)
            return ;

    if(d_maxKeyFrame.getValue()<=0)
        d_maxKeyFrame.setValue(1);

    unsigned int nbFrames = m_animation.size();
    if(d_maxKeyFrame.getValue()<nbFrames)
    {
        m_animation.erase(m_animation.begin()+d_maxKeyFrame.getValue()+1,m_animation.end());
        for (unsigned int i=1; i<m_keyFramesID.size(); i++)
            if (m_keyFramesID[i]>d_maxKeyFrame.getValue())
            {
                m_keyFramesID.erase(m_keyFramesID.begin()+i);
                --i;
            }
    }
}


template<class DataTypes>
void AnimationEditor<DataTypes>::bwdInit(){}


template<class DataTypes>
void AnimationEditor<DataTypes>::reset()
{
    if(d_componentState.getValue() != ComponentState::Valid)
            return ;

    d_cursor.setValue(0);
    m_isFrameDirty = true;
    m_time = 0.;
    m_incrKeyFrame = 0;
}


template<class DataTypes>
void AnimationEditor<DataTypes>::loadAnimation()
{
    ifstream file;
    file.open(d_filename.getValue(), ifstream::in);
    if (file.is_open())
    {
        unsigned int nbFrames = 0;
        unsigned int nbPositions = 0;
        string line;
        int lineId = 0;

        while (getline(file,line))
        {
            if(lineId == 0)
            {
                line.erase(line.begin(),line.begin()+6);
                string type = line.c_str();
                if(DataTypes::Name()!=type)
                    msg_error() << "Animation positions are "<< type <<" while mechanical context is templated with "<<DataTypes::Name();
            }

            if(lineId == 1)
            {
                line.erase(line.begin(),line.begin()+17);
                nbFrames = atoi(line.c_str());
            }

            if(lineId == 2)
            {
                line.erase(line.begin(),line.begin()+20);
                nbPositions = atoi(line.c_str());

                if (m_state->getSize() != nbPositions)
                {
                    msg_warning() <<"Size from file does not match mechanical state, abort.";
                    return;
                }
                else
                {
                    msg_info() <<"Loaded animation of "<<nbFrames<<" frames from "<<d_filename.getValue();
                    m_animation.clear();
                    m_animation.resize(nbFrames);
                    m_keyFramesID.clear();
                    d_cursor.setValue(0);
                    if(d_maxKeyFrame.getValue()<nbFrames)
                        d_maxKeyFrame.setValue(nbFrames+nbFrames/2);
                }
            }

            if(lineId == 3)
            {
                line.erase(line.begin(),line.begin()+10);
                string key;
                stringstream s(line);
                for (int i = 0; s >> key; i++)
variable 'i' set but not used
{ int keyId = atoi(key.c_str()); m_keyFramesID.push_back(keyId); } } if(lineId > 3) { stringstream s(line); m_animation[lineId-4].resize(nbPositions); Coord position; unsigned int i=0; while(i<nbPositions) { for (unsigned int j=0; j<position.size(); j++) s >> position[j]; m_animation[lineId-4][i++] = position; } } lineId++; } file.close(); m_isFrameDirty = true; m_maxKeyFrameID = getMaxKeyFrameID(); msg_info() <<"Load file succeeded"; } else msg_warning() <<"Error in attempt to load file"; } template<class DataTypes> void AnimationEditor<DataTypes>::saveAnimation() { msg_info() <<"Saved animation of "<<m_animation.size()<<" frames in "<<d_filename.getValue(); ofstream file; file.open(d_filename.getValue()); if(!file.is_open()) { msg_warning() <<"Failed to open file, abort."; return; } file << "Type: "<< DataTypes::Name() << endl; file << "Number of frame: " << m_animation.size() << endl; file << "Number of effector: " << m_state->getSize() << endl; file << "KeyFrames:"; for(unsigned int i=0; i<m_keyFramesID.size(); i++) file << " " << m_keyFramesID[i]; file << endl; for(unsigned int i=0; i<m_animation.size(); i++) { for(unsigned int j=0; j<m_animation[i].size(); j++) for(unsigned int k=0; k<m_animation[i][j].size(); k++) file << m_animation[i][j][k] << " "; file << endl; } file.close(); } template<class DataTypes> void AnimationEditor<DataTypes>::onBeginAnimationStep(const double dt) { if(d_componentState.getValue() != ComponentState::Valid) return ; if(d_dx.isSet()) { moveCursorWithdx(); } else if(m_isPlaying) { m_time+=dt; if(m_time>=d_frameTime.getValue()) { m_time = 0.; d_cursor.setValue(d_cursor.getValue() + 1); if(d_cursor.getValue() > d_maxKeyFrame.getValue()) { d_cursor.setValue(d_maxKeyFrame.getValue()); m_isPlaying = false; } else m_isFrameDirty = true; if(d_loop.getValue() && d_cursor.getValue()>m_maxKeyFrameID) d_cursor.setValue(0); } } if(m_isFrameDirty) { m_isFrameDirty = false; if(d_cursor.getValue()<m_animation.size()) m_state->write(sofa::core::VecCoordId::position())->setValue(m_animation[d_cursor.getValue()]); } } template<class DataTypes> void AnimationEditor<DataTypes>::onEndAnimationStep(const double dt) { SOFA_UNUSED(dt); } template<class DataTypes> void AnimationEditor<DataTypes>::handleEvent(Event *event) { if(d_componentState.getValue() != ComponentState::Valid) return ; if(!d_dx.isSet()) { if (KeypressedEvent* keyEvent = dynamic_cast<KeypressedEvent*>(event)) { if(keyEvent->getKey() == 18 || keyEvent->getKey() == 20) moveCursor(keyEvent->getKey()); else if(keyEvent->getKey() == 17 || keyEvent->getKey() == 16) moveCursor(keyEvent->getKey()); else if(keyEvent->getKey() == 22 || keyEvent->getKey() == 23) moveCursor(keyEvent->getKey()); else if(keyEvent->getKey() == 'A') addKeyFrame(); // Ctrl+a (add) else if(keyEvent->getKey() == 'D') deleteKeyFrame(); // Ctrl+d (delete) else if(keyEvent->getKey() == 'W') saveAnimation(); // Ctrl+w (write) else if(keyEvent->getKey() == 'C') copyKeyFrame(); // Ctrl+c (copy) else if(keyEvent->getKey() == 'V') pasteKeyFrame(); // Ctrl+v (paste) else if(keyEvent->getKey() == 'X') cutKeyFrame(); // Ctrl+x (cut) else if(keyEvent->getKey() == 'M') m_isPlaying = !m_isPlaying; // Ctrl+m (play/pause) } } if (AnimateBeginEvent::checkEventType(event)) { double dt = getContext()->getDt(); onBeginAnimationStep(dt); } } template<class DataTypes> void AnimationEditor<DataTypes>::moveCursor(const char key) { if(key == 20)//Move cursor to right direction : Ctrl+Right Arrow { m_isFrameDirty = true; d_cursor.setValue(d_cursor.getValue()+1); } if(key == 18)//Move cursor to left direction : Ctrl+Left Arrow { m_isFrameDirty = true; if(d_cursor.getValue()>0) d_cursor.setValue(d_cursor.getValue()-1); } if(key == 17)//Move cursor fast to right direction : Ctrl+Fn+Right Arrow { m_isFrameDirty = true; d_cursor.setValue(d_cursor.getValue()+(d_maxKeyFrame.getValue()/20)); } if(key == 16)//Move cursor fast to left direction : Ctrl+Fn+Left Arrow { m_isFrameDirty = true; if(d_cursor.getValue()>0) d_cursor.setValue(d_cursor.getValue()-(d_maxKeyFrame.getValue()/20)); } if(key == 23)//Move cursor to the next nearest key frame : Ctrl+PgDn { if (d_cursor.getValue() < m_maxKeyFrameID) { unsigned int nextKeyFrame = m_maxKeyFrameID; for(unsigned int i=0; i<m_keyFramesID.size(); i++) if(m_keyFramesID[i]<nextKeyFrame && m_keyFramesID[i]>d_cursor.getValue()) nextKeyFrame = m_keyFramesID[i]; d_cursor.setValue(nextKeyFrame); m_isFrameDirty = true; } } if(key == 22)//Move cursor to the previous nearest key frame : Ctrl+PgUp { if (d_cursor.getValue() > 0) { unsigned int previousKeyFrame = 0; for(unsigned int i=0; i<m_keyFramesID.size(); i++) if(m_keyFramesID[i]>previousKeyFrame && m_keyFramesID[i]<d_cursor.getValue()) previousKeyFrame = m_keyFramesID[i]; d_cursor.setValue(previousKeyFrame); m_isFrameDirty = true; } } if (d_cursor.getValue()==0) { m_isFrameDirty = false; } if (d_cursor.getValue()>d_maxKeyFrame.getValue()) { m_isFrameDirty = false; d_cursor.setValue(d_maxKeyFrame.getValue()); } } template<class DataTypes> void AnimationEditor<DataTypes>::moveCursorWithdx() { ReadAccessor<sofa::Data<double>> dx = d_dx; unsigned int cursor = d_cursor.getValue(); if(dx>0) { if(d_cursor.getValue()<m_maxKeyFrameID) { vector<Coord> positions1 = m_animation[d_cursor.getValue()]; d_cursor.setValue(d_cursor.getValue()+1); vector<Coord> positions2 = m_animation[d_cursor.getValue()]; bool cont = true; while(cont && d_cursor.getValue()<m_maxKeyFrameID) { for(unsigned int i=0; i<positions1.size(); i++) { double displacement = (positions1[i]-positions2[i]).norm(); if(displacement>dx+m_dx) { m_dx += dx; d_cursor.setValue(d_cursor.getValue()-1); cont = false; break; } else m_dx = 0; } if(cont) { d_cursor.setValue(d_cursor.getValue() + 1); positions2 = m_animation[d_cursor.getValue()]; } } } } else if(dx<0) { if(d_cursor.getValue()>0) { vector<Coord> positions1 = m_animation[d_cursor.getValue()]; d_cursor.setValue(d_cursor.getValue() - 1); vector<Coord> positions2 = m_animation[d_cursor.getValue()]; bool cont = true; while(cont && d_cursor.getValue()>0) { for(unsigned int i=0; i<positions1.size(); i++) { double displacement = (positions1[i]-positions2[i]).norm(); if(displacement>-(dx+m_dx)) { m_dx += dx; d_cursor.setValue(d_cursor.getValue() + 1); cont = false; break; } else m_dx = 0; } if(cont) { d_cursor.setValue(d_cursor.getValue()-1); positions2 = m_animation[d_cursor.getValue()]; } } } } if(cursor!=d_cursor.getValue()) m_isFrameDirty = true; } template<class DataTypes> void AnimationEditor<DataTypes>::addKeyFrame() { int index; bool isKey = isCursorKeyFrame(index); if (!isKey) m_keyFramesID.push_back(d_cursor.getValue()); if(d_cursor.getValue()>m_maxKeyFrameID) m_maxKeyFrameID = d_cursor.getValue(); updateAnimation(ST_ADD); } template<class DataTypes> void AnimationEditor<DataTypes>::deleteKeyFrame() { int index = 0; bool isKey = isCursorKeyFrame(index); if (isKey && index!=0) { updateAnimation(ST_DELETE); m_isFrameDirty = true; m_keyFramesID.erase(m_keyFramesID.begin() + index); if(m_maxKeyFrameID==d_cursor.getValue()) m_maxKeyFrameID = getMaxKeyFrameID(); } } template<class DataTypes> void AnimationEditor<DataTypes>::copyKeyFrame() { int index = 0; bool isKey = isCursorKeyFrame(index); if (isKey) m_frameCopy = m_animation[d_cursor.getValue()]; } template<class DataTypes> void AnimationEditor<DataTypes>::cutKeyFrame() { int index = 0; bool isKey = isCursorKeyFrame(index); if (isKey) { m_frameCopy = m_animation[d_cursor.getValue()]; updateAnimation(ST_DELETE); m_keyFramesID.erase(m_keyFramesID.begin() + index); if(m_maxKeyFrameID==d_cursor.getValue()) m_maxKeyFrameID = getMaxKeyFrameID(); } } template<class DataTypes> void AnimationEditor<DataTypes>::pasteKeyFrame() { if(m_frameCopy.size()==0) return; int index = 0; bool isKey = isCursorKeyFrame(index); if (!isKey) { m_keyFramesID.push_back(d_cursor.getValue()); m_isFrameDirty = true; } if(d_cursor.getValue()>m_maxKeyFrameID) m_maxKeyFrameID = d_cursor.getValue(); updateAnimation(ST_PASTE); } template<class DataTypes> bool AnimationEditor<DataTypes>::isCursorKeyFrame(int &index) { bool isKey = false; index = 0; for (unsigned int i=0; i<m_keyFramesID.size(); i++) if (d_cursor.getValue() == m_keyFramesID[i]) { isKey = true; index = int(i); break; } return isKey; } template<class DataTypes> unsigned int AnimationEditor<DataTypes>::getMaxKeyFrameID() { unsigned int maxID = 0; for (unsigned int i=0; i<m_keyFramesID.size(); i++) if (maxID < m_keyFramesID[i]) maxID = m_keyFramesID[i]; return maxID; } template<class DataTypes> void AnimationEditor<DataTypes>::updateAnimation(Status status) { if(m_state == nullptr) { msg_warning() <<"Failed to fetch a mechanical state, abort."; return; } unsigned int nbKeyFrame = m_keyFramesID.size(); unsigned int currentKey = d_cursor.getValue(); unsigned int previousKey = 0; unsigned int nextKey = d_maxKeyFrame.getValue()+1; // Get previous and next keyframe for (unsigned int i=0; i<nbKeyFrame; i++) { if(m_keyFramesID[i]<currentKey && m_keyFramesID[i]>previousKey) previousKey = m_keyFramesID[i]; if(m_keyFramesID[i]>currentKey && m_keyFramesID[i]<nextKey) nextKey = m_keyFramesID[i]; } switch(status) { case ST_DELETE: { if(nextKey != d_maxKeyFrame.getValue()+1) //Key after this one updateAnimationWithInterpolation(previousKey, nextKey); else //No key after this one m_animation.erase(m_animation.begin() + previousKey+1, m_animation.end()); break; } case ST_ADD: { if(nextKey != d_maxKeyFrame.getValue()+1) //Key after this new one { m_animation[currentKey] = m_state->readPositions().ref(); if(currentKey!=0) updateAnimationWithInterpolation(previousKey, currentKey); updateAnimationWithInterpolation(currentKey, nextKey); } else //No key after this new one { m_animation.resize(currentKey+1); m_animation[currentKey] = m_state->readPositions().ref(); if(currentKey!=0) updateAnimationWithInterpolation(previousKey, currentKey); } break; } case ST_PASTE: { if(nextKey != d_maxKeyFrame.getValue()+1) //Key after this new one { m_animation[currentKey] = m_frameCopy; if(currentKey!=0) updateAnimationWithInterpolation(previousKey, currentKey); updateAnimationWithInterpolation(currentKey, nextKey); } else //No key after this new one { m_animation.resize(currentKey+1); m_animation[currentKey] = m_frameCopy; if(currentKey!=0) updateAnimationWithInterpolation(previousKey, currentKey); } break; } default: break; } } template<class DataTypes> void AnimationEditor<DataTypes>::updateAnimationWithInterpolation(const int startKey, const int endKey) { if(m_state == nullptr) { msg_warning() <<"Failed to fetch a mechanical state, abort."; return; } vector<Coord> previousPositions = m_animation[startKey]; vector<Coord> currentPositions = m_animation[endKey]; if (currentPositions.size() != previousPositions.size()) { msg_warning() <<"This component does not handle mechanical state size changes"; return; } int nbPositions = m_state->getSize(); int nbStep = endKey - startKey; for (int i=0; i<nbStep; i++) { vector<Coord> newPositions; for (int k=0; k<nbPositions; k++) { Coord direction = currentPositions[k]- previousPositions[k]; double distance = direction.norm(); double step = distance/nbStep*i; direction.normalize(); newPositions.push_back(previousPositions[k]+direction*step); } m_animation[startKey+i] = newPositions; } } template<class DataTypes> void AnimationEditor<DataTypes>::draw(const VisualParams* vparams) { if(d_componentState.getValue() != ComponentState::Valid) return ; if(d_drawTimeline.getValue()) drawTimeline(vparams); if(d_drawTrajectory.getValue()) drawTrajectory(vparams); } template<class DataTypes> void AnimationEditor<DataTypes>::drawTimeline(const VisualParams* vparams) { #ifdef SOFA_WITH_DACCORD // If the currently selected object is not a time line... we do nothing. if( dynamic_cast<AnimationEditor<DataTypes>*>(Editor::getSelected()) == nullptrptr ) return ; #endif // SOFA_WITH_DACCORD #if SOFTROBOTS_HAVE_SOFA_GL glDisable(GL_LIGHTING); unsigned int ratio = round(vparams->viewport()[2]/d_maxKeyFrame.getValue()); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0, vparams->viewport()[2], 0, vparams->viewport()[3] ,0, 1); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); double yUPos = 50.; double yDPos = 0.; double sizeCursor = yUPos/5.; double xShift = 25.; //////////////////////////////Timeline///////////////////////////// vector<Vec3d> quad; quad.push_back(Vec3d(0,yUPos,0)); //UR quad.push_back(Vec3d(vparams->viewport()[2],yUPos,0)); //UL quad.push_back(Vec3d(vparams->viewport()[2],yDPos,0)); //DL quad.push_back(Vec3d(0,yDPos,0)); //DR vparams->drawTool()->drawQuads(quad,RGBAColor(0.4f,0.4f,0.4f,1.0f)); unsigned int maxKey = 0; for(unsigned int i=0; i<m_keyFramesID.size(); i++) if(m_keyFramesID[i]>maxKey) maxKey = m_keyFramesID[i]; quad.clear(); quad.push_back(Vec3d(ratio+xShift,yUPos,0)); //UR quad.push_back(Vec3d((maxKey+1)*ratio+xShift,yUPos,0)); //UL quad.push_back(Vec3d((maxKey+1)*ratio+xShift,yDPos,0)); //DL quad.push_back(Vec3d(ratio+xShift,yDPos,0)); //DR vparams->drawTool()->drawQuads(quad,RGBAColor(0.0f,0.3f,0.0f,1.0f)); /////////////////////////////////////////////// //////////////Cursor//////////////////////// glBegin(GL_TRIANGLES); //Current cursor vector<Vec3d> triangle; triangle.push_back(Vec3d((d_cursor.getValue()+1)*ratio+xShift, yUPos, 0.)); triangle.push_back(Vec3d(sizeCursor/2.+(d_cursor.getValue()+1)*ratio+xShift, yUPos + sizeCursor, 0.)); triangle.push_back(Vec3d(-sizeCursor/2.+(d_cursor.getValue()+1)*ratio+xShift, yUPos + sizeCursor, 0.)); vparams->drawTool()->drawTriangles(triangle,RGBAColor(0.9f,0.9f,0.9f,1.0f)); /////////////////////////////////////////////////// /// /////////////////////KeyFrames///////////////////// for(unsigned int i=0; i<m_keyFramesID.size(); i++) { vector<Vec3d> line; line.push_back(Vec3d((m_keyFramesID[i]+1)*ratio+xShift,yUPos,0.)); line.push_back(Vec3d((m_keyFramesID[i]+1)*ratio+xShift,yDPos,0.)); vparams->drawTool()->drawLines(line,2,RGBAColor(0.0f,0.5f,0.0f,1.0f)); } vector<Vec3d> line; line.push_back(Vec3d((d_maxKeyFrame.getValue()+1)*ratio+xShift,yUPos,0.)); line.push_back(Vec3d((d_maxKeyFrame.getValue()+1)*ratio+xShift,yDPos,0.)); vparams->drawTool()->drawLines(line,2,RGBAColor(0.6f,0.0f,0.0f,1.0f)); //////////////////////////////////////////////// glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); #endif } template<class DataTypes> void AnimationEditor<DataTypes>::drawTrajectory(const VisualParams* vparams) { vector<Vec3d> points; vector<unsigned int> IDSorted = m_keyFramesID; unsigned int nbKey = m_keyFramesID.size(); std::sort(IDSorted.begin(), IDSorted.begin() + nbKey); for(unsigned int k=0; k<nbKey; k++) { for(unsigned int j=0; j<m_animation[k].size(); j++) points.push_back(m_animation[IDSorted[k]][j]); } vector<Vec3d> lines; for(unsigned int i=0; i<points.size()-1; i++) { lines.push_back(points[i]); lines.push_back(points[i+1]); } vparams->drawTool()->drawPoints(points,d_drawSize.getValue()*5.,RGBAColor(0.4,0.4,0.4,1.)); vparams->drawTool()->drawLines(lines,d_drawSize.getValue()*2.,RGBAColor(0.5,0.5,0.5,1.)); } } // namespace