1
/*************************************************************************
2
* Copyright (C) 2004 by Olivier Galizzi *
3
* olivier.galizzi@imag.fr *
4
* Copyright (C) 2005 by Janek Kozicki *
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
*************************************************************************/
11
#include"GLViewer.hpp"
12
#include"OpenGLManager.hpp"
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>
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>
31
static unsigned initBlocked(State::DOF_NONE);
33
CREATE_LOGGER(GLViewer);
35
GLLock::GLLock(GLViewer* _glv): boost::try_mutex::scoped_lock(Omega::instance().renderMutex), glv(_glv){
38
GLLock::~GLLock(){ glv->doneCurrent(); }
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;*/ }
45
void GLViewer::closeEvent(QCloseEvent *e){
46
LOG_DEBUG("Will emit closeView for view #"<<viewId);
47
OpenGLManager::self->emitCloseView(viewId);
51
GLViewer::GLViewer(int _viewId, const shared_ptr<OpenGLRenderer>& _renderer, QGLWidget* shareWidget): QGLViewer(/*parent*/(QWidget*)NULL,shareWidget), renderer(_renderer), viewId(_viewId) {
55
timeDispMask=TIME_REAL|TIME_VIRT|TIME_ITER;
58
gridSubdivide = false;
61
if(viewId==0) setWindowTitle("Primary view");
62
else setWindowTitle(("Secondary view #"+boost::lexical_cast<string>(viewId)).c_str());
67
manipulatedClipPlane=-1;
69
if(manipulatedFrame()==0) setManipulatedFrame(new qglviewer::ManipulatedFrame());
71
xyPlaneConstraint=shared_ptr<qglviewer::LocalConstraint>(new qglviewer::LocalConstraint());
72
manipulatedFrame()->setConstraint(NULL);
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");
113
bool GLViewer::isManipulating(){
114
return isMoving || manipulatedClipPlane>=0;
117
void GLViewer::resetManipulation(){
121
manipulatedClipPlane=-1;
124
void GLViewer::startClipPlaneManipulation(int planeNo){
125
assert(planeNo<renderer->numClipPlanes);
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+")"));
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));
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));
157
void GLViewer::keyPressEvent(QKeyEvent *e)
159
last_user_event = boost::posix_time::second_clock::local_time();
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."); }
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(); }
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);
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());}
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");
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);
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
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");
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();}
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.");
235
camera()->setRevolveAroundPoint(sceneCenter());
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);
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);
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();
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));
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...");
269
else if(e->key()==Qt::Key_V){
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; }
274
LOG_INFO("Will save snapshot to "<<nextFrameSnapshotFilename);
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));
295
else if(e->key()!=Qt::Key_Escape && e->key()!=Qt::Key_Space) QGLViewer::keyPressEvent(e);
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);
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.
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.
318
void GLViewer::centerMedianQuartile(){
319
Scene* scene=Omega::instance().getScene().get();
320
if(scene->isPeriodic){ centerPeriodic(); return; }
321
long nBodies=scene->bodies->size();
323
LOG_DEBUG("Less than 4 bodies, median makes no sense; calling centerScene() instead.");
324
return centerScene();
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){
330
for(int i=0; i<3; i++) coords[i].push_back(b->state->pos[i]);
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);
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.);
344
void GLViewer::centerScene(){
345
Scene* rb=Omega::instance().getScene().get();
347
if(rb->isPeriodic){ centerPeriodic(); return; }
348
LOG_INFO("Select with shift, press 'm' to move.");
350
if(not(rb->bound)){ rb->updateBound();}
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){
362
max=max.cwiseMax(b->state->pos);
363
min=min.cwiseMin(b->state->pos);
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");}
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);
378
// new object selected.
379
// set frame coordinates, and isDynamic=false;
380
void GLViewer::postSelection(const QPoint& point)
382
LOG_DEBUG("Selection is "<<selectedName());
383
int selection = selectedName();
386
Body::byId(Body::id_t(last))->state->blockedDOFs=initBlocked; last=-1; Omega::instance().getScene()->selectedBody = -1;}
388
displayMessage("Moving finished"); mouseMovesCamera(); isMoving=false;
389
Omega::instance().getScene()->selectedBody = -1;
393
if(selection>=0 && (*(Omega::instance().getScene()->bodies)).exists(selection)){
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;
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
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?");
415
PyGILState_Release(gstate);
416
// see https://svn.boost.org/trac/boost/ticket/2781 for exception handling
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);
427
string GLViewer::getRealTimeString(){
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();
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;
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);
448
res.appendChild(de2);
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();
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;
462
if(child.tagName()=="timeDisplay" && child.hasAttribute("mask")) timeDispMask=atoi(child.attribute("mask").toAscii());
463
child = child.nextSibling().toElement();
467
boost::posix_time::ptime GLViewer::getLastUserEvent(){return last_user_event;};
470
float YadeCamera::zNear() const
472
float z = distanceToSceneCenter() - zClippingCoefficient()*sceneRadius()*(1.f-2*cuttingDistance);
474
// Prevents negative or null zNear values.
475
const float zMin = zNearCoefficient() * zClippingCoefficient() * sceneRadius();
479
case Camera::PERSPECTIVE :*/ z = zMin; /*break;
480
case Camera::ORTHOGRAPHIC : z = 0.0; break;