~ubuntu-branches/debian/sid/yade/sid

« back to all changes in this revision

Viewing changes to .pc/04_libqglviewer.patch/gui/qt4/GLViewer.cpp

  • Committer: Package Import Robot
  • Author(s): Anton Gladky
  • Date: 2015-07-04 14:09:45 UTC
  • Revision ID: package-import@ubuntu.com-20150704140945-1upfis32oiavxo0j
Tags: 1.14.0-3
* [dcc15ea] Fix compilation against new libqglviewer. (Closes: #790627)
* [2a34be9] Update d/copyright.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*************************************************************************
 
2
*  Copyright (C) 2004 by Olivier Galizzi                                 *
 
3
*  olivier.galizzi@imag.fr                                               *
 
4
*  Copyright (C) 2005 by Janek Kozicki                                   *
 
5
*  cosurgi@berlios.de                                                    *
 
6
*                                                                        *
 
7
*  This program is free software; it is licensed under the terms of the  *
 
8
*  GNU General Public License v2 or later. See file LICENSE for details. *
 
9
*************************************************************************/
 
10
 
 
11
#include"GLViewer.hpp"
 
12
#include"OpenGLManager.hpp"
 
13
 
 
14
#include<lib/opengl/OpenGLWrapper.hpp>
 
15
#include<core/Body.hpp>
 
16
#include<core/Scene.hpp>
 
17
#include<core/Interaction.hpp>
 
18
#include<core/DisplayParameters.hpp>
 
19
#include<boost/algorithm/string.hpp>
 
20
#include<sstream>
 
21
#include<iomanip>
 
22
#include<boost/algorithm/string/case_conv.hpp>
 
23
#include<lib/serialization/ObjectIO.hpp>
 
24
#include<lib/pyutil/gil.hpp>
 
25
#include<QtGui/qevent.h>
 
26
 
 
27
#ifdef YADE_GL2PS
 
28
        #include<gl2ps.h>
 
29
#endif
 
30
 
 
31
static unsigned initBlocked(State::DOF_NONE);
 
32
 
 
33
CREATE_LOGGER(GLViewer);
 
34
 
 
35
GLLock::GLLock(GLViewer* _glv): boost::try_mutex::scoped_lock(Omega::instance().renderMutex), glv(_glv){
 
36
        glv->makeCurrent();
 
37
}
 
38
GLLock::~GLLock(){ glv->doneCurrent(); }
 
39
 
 
40
 
 
41
#define _W3 setw(3)<<setfill('0')
 
42
#define _W2 setw(2)<<setfill('0')
 
43
GLViewer::~GLViewer(){ /* get the GL mutex when closing */ GLLock lock(this); /* cerr<<"Destructing view #"<<viewId<<endl;*/ }
 
44
 
 
45
void GLViewer::closeEvent(QCloseEvent *e){
 
46
        LOG_DEBUG("Will emit closeView for view #"<<viewId);
 
47
        OpenGLManager::self->emitCloseView(viewId);
 
48
        e->accept();
 
49
}
 
50
 
 
51
GLViewer::GLViewer(int _viewId, const shared_ptr<OpenGLRenderer>& _renderer, QGLWidget* shareWidget): QGLViewer(/*parent*/(QWidget*)NULL,shareWidget), renderer(_renderer), viewId(_viewId) {
 
52
        isMoving=false;
 
53
        drawGrid=0;
 
54
        drawScale=true;
 
55
        timeDispMask=TIME_REAL|TIME_VIRT|TIME_ITER;
 
56
        cut_plane = 0;
 
57
        cut_plane_delta = -2;
 
58
        gridSubdivide = false;
 
59
        resize(550,550);
 
60
        last=-1;
 
61
        if(viewId==0) setWindowTitle("Primary view");
 
62
        else setWindowTitle(("Secondary view #"+boost::lexical_cast<string>(viewId)).c_str());
 
63
 
 
64
        show();
 
65
        
 
66
        mouseMovesCamera();
 
67
        manipulatedClipPlane=-1;
 
68
 
 
69
        if(manipulatedFrame()==0) setManipulatedFrame(new qglviewer::ManipulatedFrame());
 
70
 
 
71
        xyPlaneConstraint=shared_ptr<qglviewer::LocalConstraint>(new qglviewer::LocalConstraint());
 
72
        manipulatedFrame()->setConstraint(NULL);
 
73
 
 
74
        setKeyDescription(Qt::Key_Return,"Run simulation.");
 
75
        setKeyDescription(Qt::Key_A,"Toggle visibility of global axes.");
 
76
        setKeyDescription(Qt::Key_C,"Set scene center so that all bodies are visible; if a body is selected, center around this body.");
 
77
        setKeyDescription(Qt::Key_C & Qt::AltModifier,"Set scene center to median body position (same as space)");
 
78
        setKeyDescription(Qt::Key_D,"Toggle time display mask");
 
79
        setKeyDescription(Qt::Key_G,"Toggle grid visibility; g turns on and cycles");
 
80
        setKeyDescription(Qt::Key_G & Qt::ShiftModifier ,"Hide grid.");
 
81
        setKeyDescription(Qt::Key_M, "Move selected object.");
 
82
        setKeyDescription(Qt::Key_X,"Show the xz [shift: xy] (up-right) plane (clip plane: align normal with +x)");
 
83
        setKeyDescription(Qt::Key_Y,"Show the yx [shift: yz] (up-right) plane (clip plane: align normal with +y)");
 
84
        setKeyDescription(Qt::Key_Z,"Show the zy [shift: zx] (up-right) plane (clip plane: align normal with +z)");
 
85
        setKeyDescription(Qt::Key_Period,"Toggle grid subdivision by 10");
 
86
        setKeyDescription(Qt::Key_S,"Save QGLViewer state to /tmp/qglviewerState.xml");
 
87
        setKeyDescription(Qt::Key_T,"Switch orthographic / perspective camera");
 
88
        setKeyDescription(Qt::Key_O,"Set narrower field of view");
 
89
        setKeyDescription(Qt::Key_P,"Set wider field of view");
 
90
        setKeyDescription(Qt::Key_R,"Revolve around scene center");
 
91
        setKeyDescription(Qt::Key_V,"Save PDF of the current view to /tmp/yade-snapshot-0001.pdf (whichever number is available first). (Must be compiled with the gl2ps feature.)");
 
92
        setPathKey(-Qt::Key_F1);
 
93
        setPathKey(-Qt::Key_F2);
 
94
        setKeyDescription(Qt::Key_Escape,"Manipulate scene (default)");
 
95
        setKeyDescription(Qt::Key_F1,"Manipulate clipping plane #1");
 
96
        setKeyDescription(Qt::Key_F2,"Manipulate clipping plane #2");
 
97
        setKeyDescription(Qt::Key_F3,"Manipulate clipping plane #3");
 
98
        setKeyDescription(Qt::Key_1,"Make the manipulated clipping plane parallel with plane #1");
 
99
        setKeyDescription(Qt::Key_2,"Make the manipulated clipping plane parallel with plane #2");
 
100
        setKeyDescription(Qt::Key_2,"Make the manipulated clipping plane parallel with plane #3");
 
101
        setKeyDescription(Qt::Key_1 & Qt::AltModifier,"Add/remove plane #1 to/from the bound group");
 
102
        setKeyDescription(Qt::Key_2 & Qt::AltModifier,"Add/remove plane #2 to/from the bound group");
 
103
        setKeyDescription(Qt::Key_3 & Qt::AltModifier,"Add/remove plane #3 to/from the bound group");
 
104
        setKeyDescription(Qt::Key_0,"Clear the bound group");
 
105
        setKeyDescription(Qt::Key_7,"Load [Alt: save] view configuration #0");
 
106
        setKeyDescription(Qt::Key_8,"Load [Alt: save] view configuration #1");
 
107
        setKeyDescription(Qt::Key_9,"Load [Alt: save] view configuration #2");
 
108
        setKeyDescription(Qt::Key_Space,"Center scene (same as Alt-C); clip plane: activate/deactivate");
 
109
 
 
110
        centerScene();
 
111
}
 
112
 
 
113
bool GLViewer::isManipulating(){
 
114
        return isMoving || manipulatedClipPlane>=0;
 
115
}
 
116
 
 
117
void GLViewer::resetManipulation(){
 
118
        mouseMovesCamera();
 
119
        setSelectedName(-1);
 
120
        isMoving=false;
 
121
        manipulatedClipPlane=-1;
 
122
}
 
123
 
 
124
void GLViewer::startClipPlaneManipulation(int planeNo){
 
125
        assert(planeNo<renderer->numClipPlanes);
 
126
        resetManipulation();
 
127
        mouseMovesManipulatedFrame(xyPlaneConstraint.get());
 
128
        manipulatedClipPlane=planeNo;
 
129
        const Se3r se3(renderer->clipPlaneSe3[planeNo]);
 
130
        manipulatedFrame()->setPositionAndOrientation(qglviewer::Vec(se3.position[0],se3.position[1],se3.position[2]),qglviewer::Quaternion(se3.orientation.x(),se3.orientation.y(),se3.orientation.z(),se3.orientation.w()));
 
131
        string grp=strBoundGroup();
 
132
        displayMessage("Manipulating clip plane #"+boost::lexical_cast<string>(planeNo+1)+(grp.empty()?grp:" (bound planes:"+grp+")"));
 
133
}
 
134
 
 
135
string GLViewer::getState(){
 
136
        QString origStateFileName=stateFileName();
 
137
        string tmpFile=Omega::instance().tmpFilename();
 
138
        setStateFileName(QString(tmpFile.c_str())); saveStateToFile(); setStateFileName(origStateFileName);
 
139
        LOG_WARN("State saved to temp file "<<tmpFile);
 
140
        // read tmp file contents and return it as string
 
141
        // this will replace all whitespace by space (nowlines will disappear, which is what we want)
 
142
        ifstream in(tmpFile.c_str()); string ret; while(!in.eof()){string ss; in>>ss; ret+=" "+ss;}; in.close();
 
143
        boost::filesystem::remove(boost::filesystem::path(tmpFile));
 
144
        return ret;
 
145
}
 
146
 
 
147
void GLViewer::setState(string state){
 
148
        string tmpFile=Omega::instance().tmpFilename();
 
149
        std::ofstream out(tmpFile.c_str());
 
150
        if(!out.good()){ LOG_ERROR("Error opening temp file `"<<tmpFile<<"', loading aborted."); return; }
 
151
        out<<state; out.close();
 
152
        LOG_WARN("Will load state from temp file "<<tmpFile);
 
153
        QString origStateFileName=stateFileName(); setStateFileName(QString(tmpFile.c_str())); restoreStateFromFile(); setStateFileName(origStateFileName);
 
154
        boost::filesystem::remove(boost::filesystem::path(tmpFile));
 
155
}
 
156
 
 
157
void GLViewer::keyPressEvent(QKeyEvent *e)
 
158
{
 
159
        last_user_event = boost::posix_time::second_clock::local_time();
 
160
 
 
161
        if(false){}
 
162
        /* special keys: Escape and Space */
 
163
        else if(e->key()==Qt::Key_A){ toggleAxisIsDrawn(); return; }
 
164
        else if(e->key()==Qt::Key_Escape){
 
165
                if(!isManipulating()){ setSelectedName(-1); return; }
 
166
                else { resetManipulation(); displayMessage("Manipulating scene."); }
 
167
        }
 
168
        else if(e->key()==Qt::Key_Space){
 
169
                if(manipulatedClipPlane>=0) {displayMessage("Clip plane #"+boost::lexical_cast<string>(manipulatedClipPlane+1)+(renderer->clipPlaneActive[manipulatedClipPlane]?" de":" ")+"activated"); renderer->clipPlaneActive[manipulatedClipPlane]=!renderer->clipPlaneActive[manipulatedClipPlane]; }
 
170
                else{ centerMedianQuartile(); }
 
171
        }
 
172
        /* function keys */
 
173
        else if(e->key()==Qt::Key_F1 || e->key()==Qt::Key_F2 || e->key()==Qt::Key_F3 /* || ... */ ){
 
174
                int n=0; if(e->key()==Qt::Key_F1) n=1; else if(e->key()==Qt::Key_F2) n=2; else if(e->key()==Qt::Key_F3) n=3; assert(n>0); int planeId=n-1;
 
175
                if(planeId>=renderer->numClipPlanes) return;
 
176
                if(planeId!=manipulatedClipPlane) startClipPlaneManipulation(planeId);
 
177
        }
 
178
        /* numbers */
 
179
        else if(e->key()==Qt::Key_0 && (e->modifiers() & Qt::AltModifier)) { boundClipPlanes.clear(); displayMessage("Cleared bound planes group.");}
 
180
        else if(e->key()==Qt::Key_1 || e->key()==Qt::Key_2 || e->key()==Qt::Key_3 /* || ... */ ){
 
181
                int n=0; if(e->key()==Qt::Key_1) n=1; else if(e->key()==Qt::Key_2) n=2; else if(e->key()==Qt::Key_3) n=3; assert(n>0); int planeId=n-1;
 
182
                if(planeId>=renderer->numClipPlanes) return; // no such clipping plane
 
183
                if(e->modifiers() & Qt::AltModifier){
 
184
                        if(boundClipPlanes.count(planeId)==0) {boundClipPlanes.insert(planeId); displayMessage("Added plane #"+boost::lexical_cast<string>(planeId+1)+" to the bound group: "+strBoundGroup());}
 
185
                        else {boundClipPlanes.erase(planeId); displayMessage("Removed plane #"+boost::lexical_cast<string>(planeId+1)+" from the bound group: "+strBoundGroup());}
 
186
                }
 
187
                else if(manipulatedClipPlane>=0 && manipulatedClipPlane!=planeId) {
 
188
                        const Quaternionr& o=renderer->clipPlaneSe3[planeId].orientation;
 
189
                        manipulatedFrame()->setOrientation(qglviewer::Quaternion(o.x(),o.y(),o.z(),o.w()));
 
190
                        displayMessage("Copied orientation from plane #1");
 
191
                }
 
192
        }
 
193
        else if(e->key()==Qt::Key_7 || e->key()==Qt::Key_8 || e->key()==Qt::Key_9){
 
194
                int nn=-1; if(e->key()==Qt::Key_7)nn=0; else if(e->key()==Qt::Key_8)nn=1; else if(e->key()==Qt::Key_9)nn=2; assert(nn>=0); size_t n=(size_t)nn;
 
195
                if(e->modifiers() & Qt::AltModifier) saveDisplayParameters(n);
 
196
                else useDisplayParameters(n);
 
197
        }
 
198
        /* letters alphabetically */
 
199
        else if(e->key()==Qt::Key_C && (e->modifiers() & Qt::AltModifier)){ displayMessage("Median centering"); centerMedianQuartile(); }
 
200
        else if(e->key()==Qt::Key_C){
 
201
                // center around selected body
 
202
                if(selectedName() >= 0 && (*(Omega::instance().getScene()->bodies)).exists(selectedName())) setSceneCenter(manipulatedFrame()->position());
 
203
                // make all bodies visible
 
204
                else centerScene();
 
205
        }
 
206
        else if(e->key()==Qt::Key_D &&(e->modifiers() & Qt::AltModifier)){ Body::id_t id; if((id=Omega::instance().getScene()->selectedBody)>=0){ const shared_ptr<Body>& b=Body::byId(id); b->setDynamic(!b->isDynamic()); LOG_INFO("Body #"<<id<<" now "<<(b->isDynamic()?"":"NOT")<<" dynamic"); } }
 
207
        else if(e->key()==Qt::Key_D) {timeDispMask+=1; if(timeDispMask>(TIME_REAL|TIME_VIRT|TIME_ITER))timeDispMask=0; }
 
208
        else if(e->key()==Qt::Key_G) { if(e->modifiers() & Qt::ShiftModifier){ drawGrid=0; return; } else drawGrid++; if(drawGrid>=8) drawGrid=0; }
 
209
        else if (e->key()==Qt::Key_M && selectedName() >= 0){ 
 
210
                if(!(isMoving=!isMoving)){
 
211
                        displayMessage("Moving done.");
 
212
                        if (last>=0) {Body::byId(Body::id_t(last))->state->blockedDOFs=initBlocked; last=-1;} mouseMovesCamera();}
 
213
                else{   displayMessage("Moving selected object");
 
214
                        
 
215
                        long selection = Omega::instance().getScene()->selectedBody;
 
216
                        initBlocked=Body::byId(Body::id_t(selection))->state->blockedDOFs; last=selection;
 
217
                        Body::byId(Body::id_t(selection))->state->blockedDOFs=State::DOF_ALL;
 
218
                        Quaternionr& q = Body::byId(selection)->state->ori;
 
219
                        Vector3r&    v = Body::byId(selection)->state->pos;
 
220
                        manipulatedFrame()->setPositionAndOrientation(qglviewer::Vec(v[0],v[1],v[2]),qglviewer::Quaternion(q.x(),q.y(),q.z(),q.w()));
 
221
                        mouseMovesManipulatedFrame();}
 
222
        }
 
223
        else if (e->key() == Qt::Key_T) camera()->setType(camera()->type()==qglviewer::Camera::ORTHOGRAPHIC ? qglviewer::Camera::PERSPECTIVE : qglviewer::Camera::ORTHOGRAPHIC);
 
224
        else if(e->key()==Qt::Key_O) camera()->setFieldOfView(camera()->fieldOfView()*0.9);
 
225
        else if(e->key()==Qt::Key_P) camera()->setFieldOfView(camera()->fieldOfView()*1.1);
 
226
        else if(e->key()==Qt::Key_R){ // reverse the clipping plane; revolve around scene center if no clipping plane selected
 
227
                if(manipulatedClipPlane>=0 && manipulatedClipPlane<renderer->numClipPlanes){
 
228
                        /* here, we must update both manipulatedFrame orientation and renderer->clipPlaneSe3 orientation in the same way */
 
229
                        Quaternionr& ori=renderer->clipPlaneSe3[manipulatedClipPlane].orientation;
 
230
                        ori=Quaternionr(AngleAxisr(Mathr::PI,Vector3r(0,1,0)))*ori; 
 
231
                        manipulatedFrame()->setOrientation(qglviewer::Quaternion(qglviewer::Vec(0,1,0),Mathr::PI)*manipulatedFrame()->orientation());
 
232
                        displayMessage("Plane #"+boost::lexical_cast<string>(manipulatedClipPlane+1)+" reversed.");
 
233
                }
 
234
                else {
 
235
                        camera()->setRevolveAroundPoint(sceneCenter());
 
236
                }
 
237
        }
 
238
        else if(e->key()==Qt::Key_S){
 
239
                LOG_INFO("Saving QGLViewer state to /tmp/qglviewerState.xml");
 
240
                setStateFileName("/tmp/qglviewerState.xml"); saveStateToFile(); setStateFileName(QString::null);
 
241
        }
 
242
        else if(e->key()==Qt::Key_L){
 
243
                LOG_INFO("Loading QGLViewer state from /tmp/qglviewerState.xml");
 
244
                setStateFileName("/tmp/qglviewerState.xml"); restoreStateFromFile(); setStateFileName(QString::null);
 
245
        }
 
246
        else if(e->key()==Qt::Key_X || e->key()==Qt::Key_Y || e->key()==Qt::Key_Z){
 
247
                int axisIdx=(e->key()==Qt::Key_X?0:(e->key()==Qt::Key_Y?1:2));
 
248
                if(manipulatedClipPlane<0){
 
249
                        qglviewer::Vec up(0,0,0), vDir(0,0,0);
 
250
                        bool alt=(e->modifiers() && Qt::ShiftModifier);
 
251
                        up[axisIdx]=1; vDir[(axisIdx+(alt?2:1))%3]=alt?1:-1;
 
252
                        camera()->setViewDirection(vDir);
 
253
                        camera()->setUpVector(up);
 
254
                        centerMedianQuartile();
 
255
                }
 
256
                else{ // align clipping normal plane with world axis
 
257
                        // x: (0,1,0),pi/2; y: (0,0,1),pi/2; z: (1,0,0),0
 
258
                        qglviewer::Vec axis(0,0,0); axis[(axisIdx+1)%3]=1; Real angle=axisIdx==2?0:Mathr::PI/2;
 
259
                        manipulatedFrame()->setOrientation(qglviewer::Quaternion(axis,angle));
 
260
                }
 
261
        }
 
262
        else if(e->key()==Qt::Key_Period) gridSubdivide = !gridSubdivide;
 
263
        else if(e->key()==Qt::Key_Return){
 
264
                if (Omega::instance().isRunning()) Omega::instance().pause();
 
265
                else Omega::instance().run();
 
266
                LOG_INFO("Running...");
 
267
        }
 
268
#ifdef YADE_GL2PS
 
269
        else if(e->key()==Qt::Key_V){
 
270
                for(int i=0; ;i++){
 
271
                        std::ostringstream fss; fss<<"/tmp/yade-snapshot-"<<setw(4)<<setfill('0')<<i<<".pdf";
 
272
                        if(!boost::filesystem::exists(fss.str())){ nextFrameSnapshotFilename=fss.str(); break; }
 
273
                }
 
274
                LOG_INFO("Will save snapshot to "<<nextFrameSnapshotFilename);
 
275
        }
 
276
#endif
 
277
#if 0
 
278
        else if( e->key()==Qt::Key_Plus ){
 
279
                        cut_plane = std::min(1.0, cut_plane + std::pow(10.0,(double)cut_plane_delta));
 
280
                        static_cast<YadeCamera*>(camera())->setCuttingDistance(cut_plane);
 
281
                        displayMessage("Cut plane: "+boost::lexical_cast<std::string>(cut_plane));
 
282
        }else if( e->key()==Qt::Key_Minus ){
 
283
                        cut_plane = std::max(0.0, cut_plane - std::pow(10.0,(double)cut_plane_delta));
 
284
                        static_cast<YadeCamera*>(camera())->setCuttingDistance(cut_plane);
 
285
                        displayMessage("Cut plane: "+boost::lexical_cast<std::string>(cut_plane));
 
286
        }else if( e->key()==Qt::Key_Slash ){
 
287
                        cut_plane_delta -= 1;
 
288
                        displayMessage("Cut plane increment: 1e"+(cut_plane_delta>0?std::string("+"):std::string(""))+boost::lexical_cast<std::string>(cut_plane_delta));
 
289
        }else if( e->key()==Qt::Key_Asterisk ){
 
290
                        cut_plane_delta = std::min(1+cut_plane_delta,-1);
 
291
                        displayMessage("Cut plane increment: 1e"+(cut_plane_delta>0?std::string("+"):std::string(""))+boost::lexical_cast<std::string>(cut_plane_delta));
 
292
        }
 
293
#endif
 
294
 
 
295
        else if(e->key()!=Qt::Key_Escape && e->key()!=Qt::Key_Space) QGLViewer::keyPressEvent(e);
 
296
        updateGL();
 
297
}
 
298
/* Center the scene such that periodic cell is contained in the view */
 
299
void GLViewer::centerPeriodic(){
 
300
        Scene* scene=Omega::instance().getScene().get();
 
301
        assert(scene->isPeriodic);
 
302
        Vector3r center=.5*scene->cell->getSize();
 
303
        Vector3r halfSize=.5*scene->cell->getSize();
 
304
        float radius=std::max(halfSize[0],std::max(halfSize[1],halfSize[2]));
 
305
        LOG_DEBUG("Periodic scene center="<<center<<", halfSize="<<halfSize<<", radius="<<radius);
 
306
        setSceneCenter(qglviewer::Vec(center[0],center[1],center[2]));
 
307
        setSceneRadius(radius*1.5);
 
308
        showEntireScene();
 
309
}
 
310
 
 
311
/* Calculate medians for x, y and z coordinates of all bodies;
 
312
 *then set scene center to median position and scene radius to 2*inter-quartile distance.
 
313
 *
 
314
 * This function eliminates the effect of lonely bodies that went nuts and enlarge
 
315
 * the scene's Aabb in such a way that fitting the scene to see the Aabb makes the
 
316
 * "central" (where most bodies is) part very small or even invisible.
 
317
 */
 
318
void GLViewer::centerMedianQuartile(){
 
319
        Scene* scene=Omega::instance().getScene().get();
 
320
        if(scene->isPeriodic){ centerPeriodic(); return; }
 
321
        long nBodies=scene->bodies->size();
 
322
        if(nBodies<4) {
 
323
                LOG_DEBUG("Less than 4 bodies, median makes no sense; calling centerScene() instead.");
 
324
                return centerScene();
 
325
        }
 
326
        std::vector<Real> coords[3];
 
327
        for(int i=0;i<3;i++)coords[i].reserve(nBodies);
 
328
        FOREACH(shared_ptr<Body> b, *scene->bodies){
 
329
                if(!b) continue;
 
330
                for(int i=0; i<3; i++) coords[i].push_back(b->state->pos[i]);
 
331
        }
 
332
        Vector3r median,interQuart;
 
333
        for(int i=0;i<3;i++){
 
334
                sort(coords[i].begin(),coords[i].end());
 
335
                median[i]=*(coords[i].begin()+nBodies/2);
 
336
                interQuart[i]=*(coords[i].begin()+3*nBodies/4)-*(coords[i].begin()+nBodies/4);
 
337
        }
 
338
        LOG_DEBUG("Median position is"<<median<<", inter-quartile distance is "<<interQuart);
 
339
        setSceneCenter(qglviewer::Vec(median[0],median[1],median[2]));
 
340
        setSceneRadius(2*(interQuart[0]+interQuart[1]+interQuart[2])/3.);
 
341
        showEntireScene();
 
342
}
 
343
 
 
344
void GLViewer::centerScene(){
 
345
        Scene* rb=Omega::instance().getScene().get();
 
346
        if (!rb) return;
 
347
        if(rb->isPeriodic){ centerPeriodic(); return; }
 
348
        LOG_INFO("Select with shift, press 'm' to move.");
 
349
        Vector3r min,max;       
 
350
        if(not(rb->bound)){ rb->updateBound();}
 
351
        
 
352
        min=rb->bound->min; max=rb->bound->max;
 
353
        bool hasNan=(isnan(min[0])||isnan(min[1])||isnan(min[2])||isnan(max[0])||isnan(max[1])||isnan(max[2]));
 
354
        Real minDim=std::min(max[0]-min[0],std::min(max[1]-min[1],max[2]-min[2]));
 
355
        if(minDim<=0 || hasNan){
 
356
                // Aabb is not yet calculated...
 
357
                LOG_DEBUG("scene's bound not yet calculated or has zero or nan dimension(s), attempt get that from bodies' positions.");
 
358
                Real inf=std::numeric_limits<Real>::infinity();
 
359
                min=Vector3r(inf,inf,inf); max=Vector3r(-inf,-inf,-inf);
 
360
                FOREACH(const shared_ptr<Body>& b, *rb->bodies){
 
361
                        if(!b) continue;
 
362
                        max=max.cwiseMax(b->state->pos);
 
363
                        min=min.cwiseMin(b->state->pos);
 
364
                }
 
365
                if(isinf(min[0])||isinf(min[1])||isinf(min[2])||isinf(max[0])||isinf(max[1])||isinf(max[2])){ LOG_DEBUG("No min/max computed from bodies either, setting cube (-1,-1,-1)×(1,1,1)"); min=-Vector3r::Ones(); max=Vector3r::Ones(); }
 
366
        } else {LOG_DEBUG("Using scene's Aabb");}
 
367
 
 
368
        LOG_DEBUG("Got scene box min="<<min<<" and max="<<max);
 
369
        Vector3r center = (max+min)*0.5;
 
370
        Vector3r halfSize = (max-min)*0.5;
 
371
        float radius=std::max(halfSize[0],std::max(halfSize[1],halfSize[2])); if(radius<=0) radius=1;
 
372
        LOG_DEBUG("Scene center="<<center<<", halfSize="<<halfSize<<", radius="<<radius);
 
373
        setSceneCenter(qglviewer::Vec(center[0],center[1],center[2]));
 
374
        setSceneRadius(radius*1.5);
 
375
        showEntireScene();
 
376
}
 
377
 
 
378
// new object selected.
 
379
// set frame coordinates, and isDynamic=false;
 
380
void GLViewer::postSelection(const QPoint& point) 
 
381
{
 
382
        LOG_DEBUG("Selection is "<<selectedName());
 
383
        int selection = selectedName();
 
384
        if(selection<0){
 
385
                if (last>=0) {
 
386
                        Body::byId(Body::id_t(last))->state->blockedDOFs=initBlocked; last=-1; Omega::instance().getScene()->selectedBody = -1;}
 
387
                if(isMoving){
 
388
                        displayMessage("Moving finished"); mouseMovesCamera(); isMoving=false;
 
389
                        Omega::instance().getScene()->selectedBody = -1;
 
390
                }
 
391
                return;
 
392
        }
 
393
        if(selection>=0 && (*(Omega::instance().getScene()->bodies)).exists(selection)){
 
394
                resetManipulation();
 
395
                if (last>=0) {Body::byId(Body::id_t(last))->state->blockedDOFs=initBlocked; last=-1;}
 
396
                if(Body::byId(Body::id_t(selection))->isClumpMember()){ // select clump (invisible) instead of its member
 
397
                        LOG_DEBUG("Clump member #"<<selection<<" selected, selecting clump instead.");
 
398
                        selection=Body::byId(Body::id_t(selection))->clumpId;                   
 
399
                }
 
400
                
 
401
                setSelectedName(selection);
 
402
                LOG_DEBUG("New selection "<<selection);
 
403
                displayMessage("Selected body #"+boost::lexical_cast<string>(selection)+(Body::byId(selection)->isClump()?" (clump)":""));
 
404
                Omega::instance().getScene()->selectedBody = selection;
 
405
                        PyGILState_STATE gstate;
 
406
                        gstate = PyGILState_Ensure();
 
407
      boost::python::object main=boost::python::import("__main__");
 
408
      boost::python::object global=main.attr("__dict__");
 
409
                                // the try/catch block must be properly nested inside PyGILState_Ensure and PyGILState_Release
 
410
                                try{
 
411
          boost::python::eval(string("onBodySelect("+boost::lexical_cast<string>(selection)+")").c_str(),global,global);
 
412
                                } catch (boost::python::error_already_set const &) {
 
413
                                        LOG_DEBUG("unable to call onBodySelect. Not defined?");
 
414
                                }
 
415
                        PyGILState_Release(gstate);
 
416
                        // see https://svn.boost.org/trac/boost/ticket/2781 for exception handling
 
417
        }
 
418
}
 
419
 
 
420
// maybe new object will be selected.
 
421
// if so, then set isDynamic of previous selection, to old value
 
422
void GLViewer::endSelection(const QPoint &point){
 
423
        manipulatedClipPlane=-1;
 
424
        QGLViewer::endSelection(point);
 
425
}
 
426
 
 
427
string GLViewer::getRealTimeString(){
 
428
        ostringstream oss;
 
429
  boost::posix_time::time_duration t=Omega::instance().getRealTime_duration();
 
430
        unsigned d=t.hours()/24,h=t.hours()%24,m=t.minutes(),s=t.seconds();
 
431
        oss<<"clock ";
 
432
        if(d>0) oss<<d<<"days "<<_W2<<h<<":"<<_W2<<m<<":"<<_W2<<s;
 
433
        else if(h>0) oss<<_W2<<h<<":"<<_W2<<m<<":"<<_W2<<s;
 
434
        else oss<<_W2<<m<<":"<<_W2<<s;
 
435
        return oss.str();
 
436
}
 
437
#undef _W2
 
438
#undef _W3
 
439
 
 
440
// cut&paste from QGLViewer::domElement documentation
 
441
QDomElement GLViewer::domElement(const QString& name, QDomDocument& document) const{
 
442
        QDomElement de=document.createElement("grid");
 
443
        string val; if(drawGrid & 1) val+="x"; if(drawGrid & 2)val+="y"; if(drawGrid & 4)val+="z";
 
444
        de.setAttribute("normals",val.c_str());
 
445
        QDomElement de2=document.createElement("timeDisplay"); de2.setAttribute("mask",timeDispMask);
 
446
        QDomElement res=QGLViewer::domElement(name,document);
 
447
        res.appendChild(de);
 
448
        res.appendChild(de2);
 
449
        return res;
 
450
}
 
451
 
 
452
// cut&paste from QGLViewer::initFromDomElement documentation
 
453
void GLViewer::initFromDOMElement(const QDomElement& element){
 
454
        QGLViewer::initFromDOMElement(element);
 
455
        QDomElement child=element.firstChild().toElement();
 
456
        while (!child.isNull()){
 
457
                if (child.tagName()=="gridXYZ" && child.hasAttribute("normals")){
 
458
                        string val=child.attribute("normals").toLower().toStdString();
 
459
                        drawGrid=0;
 
460
                        if(val.find("x")!=string::npos) drawGrid+=1; if(val.find("y")!=string::npos)drawGrid+=2; if(val.find("z")!=string::npos)drawGrid+=4;
 
461
                }
 
462
                if(child.tagName()=="timeDisplay" && child.hasAttribute("mask")) timeDispMask=atoi(child.attribute("mask").toAscii());
 
463
                child = child.nextSibling().toElement();
 
464
        }
 
465
}
 
466
 
 
467
boost::posix_time::ptime GLViewer::getLastUserEvent(){return last_user_event;};
 
468
 
 
469
 
 
470
float YadeCamera::zNear() const
 
471
{
 
472
  float z = distanceToSceneCenter() - zClippingCoefficient()*sceneRadius()*(1.f-2*cuttingDistance);
 
473
 
 
474
  // Prevents negative or null zNear values.
 
475
  const float zMin = zNearCoefficient() * zClippingCoefficient() * sceneRadius();
 
476
  if (z < zMin)
 
477
/*    switch (type())
 
478
      {
 
479
      case Camera::PERSPECTIVE  :*/ z = zMin; /*break;
 
480
      case Camera::ORTHOGRAPHIC : z = 0.0;  break;
 
481
      }*/
 
482
  return z;
 
483
}
 
484
 
 
485