|
NCBI C++ ToolKit
|
00001 /* $Id: phylo_tree_force.cpp 25384 2012-03-06 19:27:20Z ucko $ 00002 * =========================================================================== 00003 * 00004 * PUBLIC DOMAIN NOTICE 00005 * National Center for Biotechnology Information 00006 * 00007 * This software/database is a "United States Government Work" under the 00008 * terms of the United States Copyright Act. It was written as part of 00009 * the author's official duties as a United States Government employee and 00010 * thus cannot be copyrighted. This software/database is freely available 00011 * to the public for use. The National Library of Medicine and the U.S. 00012 * Government have not placed any restriction on its use or reproduction. 00013 * 00014 * Although all reasonable efforts have been taken to ensure the accuracy 00015 * and reliability of the software and data, the NLM and the U.S. 00016 * Government do not and cannot warrant the performance or results that 00017 * may be obtained by using this software or data. The NLM and the U.S. 00018 * Government disclaim all warranties, express or implied, including 00019 * warranties of performance, merchantability or fitness for any particular 00020 * purpose. 00021 * 00022 * Please cite the author in any work or product based on this material. 00023 * 00024 * =========================================================================== 00025 * 00026 * Authors: Vladimir Tereshkov 00027 * 00028 * File Description: 00029 * Force tree layout 00030 */ 00031 00032 #include <ncbi_pch.hpp> 00033 #include <corelib/ncbitime.hpp> 00034 #include <corelib/ncbistl.hpp> 00035 00036 #include <gui/widgets/phylo_tree/phylo_tree_force.hpp> 00037 #include <gui/widgets/phylo_tree/phylo_tree_pane.hpp> 00038 #include <gui/widgets/phylo_tree/phylo_tree_ds.hpp> 00039 #include <gui/widgets/phylo_tree/phylo_tree_ps.hpp> 00040 #include <gui/opengl/glhelpers.hpp> 00041 #include <gui/graph/igraph_utils.hpp> 00042 //#include <gui/widgets/phylo_tree/phylo_tree_render.hpp> 00043 00044 #include <gui/widgets/gl/attrib_menu.hpp> 00045 00046 #include <gui/utils/event_translator.hpp> 00047 #include <gui/framework/app_task_service.hpp> 00048 #include <gui/utils/app_job_impl.hpp> 00049 #include <gui/utils/view_event.hpp> 00050 00051 00052 #include <math.h> 00053 00054 BEGIN_NCBI_SCOPE 00055 00056 #define PHYLO_RENDER_TIMER_ID 63001 00057 00058 BEGIN_EVENT_MAP(CPhyloForce, CEventHandler) 00059 ON_MESSAGE(CAppJobNotification, CAppJobNotification::eStateChanged, 00060 &CPhyloForce::OnAppJobNotification) 00061 ON_MESSAGE(CAppJobNotification, CAppJobNotification::eProgress, 00062 &CPhyloForce::OnAppJobProgress) 00063 END_EVENT_MAP() 00064 00065 BEGIN_EVENT_TABLE(CPhyloForce, CPhyloRadial) 00066 EVT_TIMER(PHYLO_RENDER_TIMER_ID, CPhyloForce::OnTimer) 00067 END_EVENT_TABLE() 00068 00069 00070 CPhyloForce::CPhyloForce() 00071 : m_Timer(this, PHYLO_RENDER_TIMER_ID) 00072 , m_MainThreadPS(NULL) 00073 , m_UseMainThreadPhysics(true) 00074 { 00075 m_JobID = CAppJobDispatcher::eInvalidJobID; 00076 00077 // If rendering within gbench, use thread for force calculations, 00078 // otherwise we are probably cgi and should use main thread. 00079 if (CGuiRegistry::GetInstance().HasField("GBENCH")) { 00080 m_UseMainThreadPhysics = false; 00081 } 00082 } 00083 00084 CPhyloForce::CPhyloForce(double w, double h) 00085 : CPhyloRadial(w, h) 00086 , m_Timer(this, PHYLO_RENDER_TIMER_ID) 00087 , m_MainThreadPS(NULL) 00088 , m_UseMainThreadPhysics(true) 00089 { 00090 m_JobID = CAppJobDispatcher::eInvalidJobID; 00091 00092 // If rendering within gbench, use thread for force calculations, 00093 // otherwise we are probably cgi and should use main thread. 00094 if (CGuiRegistry::GetInstance().HasField("GBENCH")) { 00095 m_UseMainThreadPhysics = false; 00096 } 00097 00098 #ifdef ATTRIB_MENU_SUPPORT 00099 // CAttribMenu& m = CAttribMenuInstance::GetInstance(); 00100 00101 // CAttribMenu* sub_menu = m.AddSubMenuUnique("Force ext", this); 00102 00103 // sub_menu->AddFloatReadOnly("Vis Left", &vis_left); 00104 // sub_menu->AddFloatReadOnly("Vis Right", &vis_right); 00105 // sub_menu->AddFloatReadOnly("Vis Bottom", &vis_bottom); 00106 // sub_menu->AddFloatReadOnly("Vis Top", &vis_top); 00107 00108 // sub_menu->AddFloatReadOnly("Ext Left", &ext_left); 00109 // sub_menu->AddFloatReadOnly("Ext Right", &ext_right); 00110 // sub_menu->AddFloatReadOnly("Ext Bottom", &ext_bottom); 00111 // sub_menu->AddFloatReadOnly("Ext top", &ext_top); 00112 #endif 00113 00114 } 00115 00116 00117 CPhyloForce::~CPhyloForce() 00118 { 00119 if (m_JobID != CAppJobDispatcher::eInvalidJobID) { 00120 CAppJobDispatcher& disp = CAppJobDispatcher::GetInstance(); 00121 disp.DeleteJob(m_JobID); 00122 } 00123 00124 m_Timer.Stop(); 00125 } 00126 00127 void CPhyloForce::x_Layout(CPhyloTreeDataSource& ds) 00128 { 00129 m_pRoot = ds.GetTree(); 00130 00131 bool distMode = m_bDistMode; 00132 m_bDistMode = false; 00133 00134 // It is more efficient not to re-run the radial layout each time 00135 // if, for example, the user simply collapsed some nodes. Unfortuately 00136 // some user operations wind up setting all node position values to 0, 00137 // so be be sure we have to compute the radial tree first each time. 00138 //if (m_SwitchedLayout) 00139 CPhyloRadial::x_Layout(ds); 00140 m_bDistMode = distMode; 00141 00142 Int4 leafs = ds.GetSize(); 00143 Int4 width = ds.GetWidth(); 00144 00145 m_xStep = m_DimX / width; 00146 m_yStep = m_DimY / ((leafs>1)?(leafs-1):2); 00147 00148 if (ds.GetNormDistance() > 0){ 00149 m_NormDistance = m_DimX / ds.GetNormDistance(); 00150 } 00151 00152 m_Timer.Stop(); 00153 00154 CPhyloTreePS::PhysicsParms p; 00155 bool retain_parms = false; 00156 00157 // Set drawing options that are specific to the force layout 00158 CGlVboNode* edge_node = ds.GetModel().FindGeomNode("TreeEdges"); 00159 CGlVboNode* narrow_edge_node = ds.GetModel().FindGeomNode("NarrowTreeEdges"); 00160 00161 if (edge_node != NULL && narrow_edge_node != NULL) { 00162 edge_node->GetState().Enable(GL_LINE_SMOOTH); 00163 edge_node->GetState().Enable(GL_BLEND); 00164 edge_node->GetState().SetBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 00165 00166 narrow_edge_node->GetState().Enable(GL_LINE_SMOOTH); 00167 narrow_edge_node->GetState().Enable(GL_BLEND); 00168 narrow_edge_node->GetState().SetBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 00169 narrow_edge_node->GetState().SetColor(CRgbaColor(1.0f, 1.0f, 1.0f, 0.7f)); 00170 } 00171 00172 CGlVboNode* filler_points_node = m_DS->GetModel().FindGeomNode("FillerPoints"); 00173 if (filler_points_node != NULL) 00174 filler_points_node->SetVisible(false); 00175 00176 // Special case: no need for particle system if there is only 00177 // one node. 00178 if (m_pRoot->IsLeafEx()) { 00179 TModelRect newRect = ds.GetBoundRect(); 00180 00181 newRect.Inflate(1.0f, 1.0f); 00182 m_RasterRect = newRect; 00183 00184 // This handles single node case too. 00185 ComputeViewingLimits(*m_pPane, false); 00186 00187 return; 00188 } 00189 00190 if (!m_UseMainThreadPhysics) { 00191 00192 try { 00193 CForceJob* j = m_Job.GetPointerOrNull(); 00194 00195 // for debugging, copy the particle system parameters from the current 00196 // system to the new one (parameters may be updated via attribute 00197 // menus). 00198 if (j != NULL) { 00199 p = j->GetPS()->getPhysicParms(); 00200 retain_parms = true; 00201 } 00202 00203 if (m_JobID != CAppJobDispatcher::eInvalidJobID) { 00204 CAppJobDispatcher& disp = CAppJobDispatcher::GetInstance(); 00205 IAppJob::EJobState state = disp.GetJobState(m_JobID); 00206 if (state == IAppJob::eRunning) 00207 disp.CancelJob(m_JobID); 00208 else 00209 disp.DeleteJob(m_JobID); 00210 00211 m_Job.Reset(); 00212 } 00213 00214 j = m_Job.GetPointerOrNull(); 00215 00216 if (j == NULL) { 00217 // This is for debugging - if you run the force update several times, 00218 // you may change parameters each time and this retains those updates, 00219 // which probably occured through attribute menus, between updates. 00220 CForceJob *fj = new CForceJob(ds.GetTree(), ds); 00221 00222 if (retain_parms) { 00223 fj->GetPS()->SetPhysicsParms(p); 00224 00225 00226 #ifdef ATTRIB_MENU_SUPPORT 00227 CAttribMenuInstance::GetInstance().SynchToUserValue(); 00228 #endif 00229 00230 } 00231 00232 m_Job.Reset(fj); 00233 m_JobID = CAppJobDispatcher::GetInstance().StartJob(*m_Job, "ThreadPool", *this, 1, true); 00234 00235 m_Timer.Start(100); 00236 } 00237 } 00238 // Unable to start a job - try to do physics in foreground 00239 catch (CAppJobException&) { 00240 m_UseMainThreadPhysics = true; 00241 } 00242 } 00243 else { 00244 if (!m_MainThreadPS) { 00245 m_MainThreadPS = new CPhyloTreePS(ds); 00246 // This makes the system end more quickly since the velocity threshold 00247 // below which we can stop is raised. 00248 m_MainThreadPS->SetVelocityThresholdK(0.2f); 00249 } else { 00250 m_MainThreadPS->Init(ds); 00251 } 00252 00253 //m_Timer.Start(50); // no good for cgi... 00254 00255 // This has to work for a batch, CGI version too - so there are 00256 // no progressive updates - we just want to iterate until it stabilizes 00257 // or we reach a maximum iteration count. 00258 for (int i=0; i<200 && !m_MainThreadPS->IsDone(); ++i) 00259 x_SingleThreadPhysics(); 00260 00261 CBoundaryPoints boundary_pts; 00262 x_CalculateBoundary(m_pRoot, boundary_pts, false); 00263 ComputeViewingLimits(*m_pPane, false); 00264 00265 // This is the rect for viewing limits used by cgi. 00266 m_RasterRect = m_pPane->GetModelLimitsRect(); 00267 00268 return; 00269 } 00270 } 00271 00272 void CPhyloForce::RemoveCurrentDataSource() 00273 { 00274 IPhyloTreeRenderer::RemoveCurrentDataSource(); 00275 00276 try { 00277 m_Timer.Stop(); 00278 00279 if (m_JobID != CAppJobDispatcher::eInvalidJobID) { 00280 CAppJobDispatcher& disp = CAppJobDispatcher::GetInstance(); 00281 IAppJob::EJobState state = disp.GetJobState(m_JobID); 00282 00283 if (state == IAppJob::eRunning) { 00284 disp.CancelJob(m_JobID); 00285 00286 // Wait until job is really done (current iteration is finished) 00287 // to move on: 00288 int count = 0; 00289 while (count++<20 && disp.GetJobState(m_JobID) == IAppJob::eRunning) { 00290 wxMilliSleep(100); 00291 } 00292 } 00293 else { 00294 disp.DeleteJob(m_JobID); 00295 } 00296 00297 m_JobID = CAppJobDispatcher::eInvalidJobID; 00298 m_Job.Reset(); 00299 } 00300 } 00301 catch (CAppJobException&) { 00302 _TRACE("Error deleting force layout job"); 00303 } 00304 00305 m_JobID = CAppJobDispatcher::eInvalidJobID; 00306 m_Job.Reset(); 00307 } 00308 00309 bool CPhyloForce::Render(CGlPane& pane, CPhyloTreeDataSource& ds) 00310 { 00311 m_pPane = &pane; 00312 m_DS = &ds; 00313 00314 // Is a job running? If so make sure we don't draw while the 00315 // particle system is syncing with the graphics model. Otherwise 00316 // just draw. 00317 if (m_JobID != CAppJobDispatcher::eInvalidJobID) { 00318 00319 CForceJob* j = m_Job.GetPointerOrNull(); 00320 if (j != NULL ) { 00321 00322 if (j->DataSynched() == CForceJob::eDataSynched) { 00323 j->SetDataSynched(CForceJob::eRenderInProgress); 00324 00325 #ifdef BUFFERED_RENDER 00326 /// Need only to drop scale marker for force layout... 00327 IPhyloTreeRenderer::Render(pane,ds); 00328 #else 00329 x_Render(pane, ds); 00330 #endif 00331 j->SetDataSynched(CForceJob::eDataSynched); 00332 } 00333 // Unable to render since force thread is busy synchrnizing 00334 // the particle system data with the graphics data. 00335 else { 00336 return false; 00337 } 00338 } 00339 } 00340 else { 00341 #ifdef BUFFERED_RENDER 00342 /// Need only to drop scale marker for force layout... 00343 IPhyloTreeRenderer::Render(pane,ds); 00344 #else 00345 x_Render(pane, ds); 00346 #endif 00347 } 00348 00349 return true; 00350 } 00351 00352 void CPhyloForce::x_SingleThreadPhysics() 00353 { 00354 if (!m_MainThreadPS) 00355 return; 00356 00357 if (!m_MainThreadPS->IsDone()) { 00358 for (int j=0; j<10; ++j) { 00359 m_MainThreadPS->Update(); 00360 } 00361 00362 m_MainThreadPS->UpdateAndSynch(); 00363 } 00364 else { 00365 m_Timer.Stop(); 00366 } 00367 } 00368 00369 void CPhyloForce::x_Render(CGlPane& pane, CPhyloTreeDataSource& ds) 00370 { 00371 CRgbaColor bgColor(m_SL->SetColor(CPhyloTreeScheme::eTree, 00372 CPhyloTreeScheme::eBgColor)); 00373 00374 glClearColor(bgColor.GetRed(), bgColor.GetGreen(), 00375 bgColor.GetBlue(), bgColor.GetAlpha()); 00376 00377 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 00378 00379 pane.OpenOrtho(); 00380 00381 x_DrawBoundingAreas(ds.GetTree()); 00382 x_DrawTree( ds.GetTree() ); 00383 x_DrawNodes( ds.GetTree() ); 00384 00385 pane.Close(); 00386 00387 x_RenderSelection(pane); 00388 } 00389 00390 void CPhyloForce::x_DrawTree(CPhyloTreeNode * node) 00391 { 00392 // drawing leaf node 00393 if (node->GetParent()){ 00394 x_RenderLine(node->GetParent()->GetValue().X(), 00395 node->GetParent()->GetValue().Y(), 00396 node->GetValue().X(), node->GetValue().Y(), 00397 (**node).GetSelectedState(), 00398 GetEdgeColor(node)); 00399 } 00400 for(CPhyloTreeNode::TNodeList_I it = node->SubNodeBeginEx(); 00401 it != node->SubNodeEndEx(); it++ ){ 00402 x_DrawTree(static_cast<CPhyloTreeNode*>(*it)); 00403 } 00404 } 00405 00406 00407 void CPhyloForce::x_CalculateBoundary(CPhyloTreeNode * node, 00408 CBoundaryPoints& parent_boundary_pts, 00409 bool compute_boundary) 00410 { 00411 if ((**node).GetBoundedDisplay() == IPhyGraphicsNode::eBounded) 00412 compute_boundary = true; 00413 00414 CBoundaryPoints node_boundary_pts; 00415 00416 if (!node->IsLeafEx()) { 00417 for(CPhyloTreeNode::TNodeList_I it = node->SubNodeBeginEx(); 00418 it != node->SubNodeEnd(); it++) { 00419 CPhyloTreeNode* subnode = static_cast<CPhyloTreeNode*>(*it); 00420 x_CalculateBoundary(subnode, node_boundary_pts, compute_boundary); 00421 } 00422 } 00423 00424 x_ComputeNodeBoundary(node, compute_boundary, node_boundary_pts, "ForceLayout"); 00425 00426 // Each node that computes a boundary has to add in the points (and text rectangles) 00427 // for all nodes below it in the tree 00428 if (compute_boundary) { 00429 parent_boundary_pts.AddBoundedPoints(node_boundary_pts); 00430 } 00431 } 00432 00433 void CPhyloForce::StartRendering(bool b) 00434 { 00435 IPhyloTreeRenderer::StartRendering(b); 00436 00437 if (!b) { 00438 CAppJobDispatcher& disp = CAppJobDispatcher::GetInstance(); 00439 disp.DeleteJob(m_JobID); 00440 m_Timer.Stop(); 00441 } 00442 } 00443 00444 00445 /// handles "state changed" notification from CAppJobDispatcher 00446 void CPhyloForce::OnAppJobNotification(CEvent* evt) 00447 { 00448 CAppJobNotification* notn = dynamic_cast<CAppJobNotification*>(evt); 00449 _ASSERT(notn); 00450 00451 00452 IAppJob::EJobState state = IAppJob::eInvalid; 00453 int job_id = 0; 00454 00455 if(notn) { 00456 job_id = notn->GetJobID(); 00457 if(job_id) { 00458 state = notn->GetState(); 00459 } 00460 } 00461 00462 if (state != IAppJob::eRunning) { 00463 // Only redraw if job completed normally - not if it was cancellled. 00464 if (state == IAppJob::eCompleted) { 00465 CBoundaryPoints boundary_pts; 00466 x_CalculateBoundary(m_pRoot, boundary_pts, false); 00467 ComputeViewingLimits(*m_pPane, false); 00468 if (m_pHost != NULL) 00469 m_pHost->HMGH_UpdateLimits(true); 00470 00471 x_Render(*m_pPane, *m_DS); 00472 } 00473 00474 // Stop timer if job is not running. Can't just use job status since 00475 // when layout it called it stops the current job but then immediately 00476 // starts a new one, so this can be called from the cancelled job 00477 // even though the timer is needed for the newly created job. 00478 if (job_id == m_JobID) { 00479 m_Timer.Stop(); 00480 m_JobID = CAppJobDispatcher::eInvalidJobID; 00481 m_Job.Reset(); 00482 } 00483 } 00484 } 00485 00486 void CPhyloForce::OnTimer(wxTimerEvent& /* evt */) 00487 { 00488 // If there is a valid (deform) job running and it has 00489 // synchronized the visual data with the physics data 00490 // since the last time called, recompute the tree boundaries 00491 // and redraw. 00492 if (!m_UseMainThreadPhysics && 00493 m_JobID != CAppJobDispatcher::eInvalidJobID) { 00494 00495 CAppJobDispatcher& disp = CAppJobDispatcher::GetInstance(); 00496 IAppJob::EJobState state = disp.GetJobState(m_JobID); 00497 00498 CForceJob* j = m_Job.GetPointerOrNull(); 00499 if (j != NULL ) { 00500 CMutexGuard guard(j->GetMutex()); 00501 if (state == IAppJob::eRunning && 00502 j->DataSynched() == CForceJob::eDataRefreshed) { 00503 j->SetDataSynched(CForceJob::eRenderInProgress); 00504 CBoundaryPoints boundary_pts; 00505 x_CalculateBoundary(m_pRoot, boundary_pts, false); 00506 ComputeViewingLimits(*m_pPane, false); 00507 m_pHost->HMGH_UpdateLimits(true); 00508 j->SetDataSynched(CForceJob::eDataSynched); 00509 } 00510 } 00511 else if (m_UseMainThreadPhysics) { 00512 x_SingleThreadPhysics(); 00513 00514 CBoundaryPoints boundary_pts; 00515 x_CalculateBoundary(m_pRoot, boundary_pts, false); 00516 ComputeViewingLimits(*m_pPane, false); 00517 if (m_pHost != NULL) 00518 m_pHost->HMGH_UpdateLimits(true); 00519 } 00520 } 00521 } 00522 00523 00524 /////////////////////////////////////////////////////////////////////////////// 00525 // 00526 // Background job for running force update on a separate thread 00527 // 00528 /////////////////////////////////////////////////////////////////////////////// 00529 CForceJob::CForceJob(CPhyloTreeNode * node, CPhyloTreeDataSource& ds) 00530 : m_Node(node) 00531 , m_PS(NULL) 00532 , m_DataSynched(eDataSynched) 00533 , m_UpdatesSincLastSync(0) 00534 { 00535 m_PS = new CPhyloTreePS(ds); 00536 } 00537 00538 CForceJob::~CForceJob() 00539 { 00540 delete m_PS; 00541 m_PS = NULL; 00542 } 00543 00544 IAppJob::EJobState CForceJob::Run() 00545 { 00546 // Loop a large number of times. Ideally, system will stop when 00547 // velocity drops (checked in x_Calculate) 00548 for (int i=0; i<20000 && !Cancelled(); i++) 00549 x_Calculate(); 00550 00551 return eCompleted; 00552 } 00553 00554 void CForceJob::x_Calculate() 00555 { 00556 if (Cancelled()) { 00557 return; 00558 } 00559 00560 // updates should be timer based, but make sure that 00561 // a minimum number happen (otherwise it will slow down 00562 // progress of the particle system) 00563 double i = 0.0f; 00564 00565 CStopWatch sw; 00566 sw.Start(); 00567 00568 // Update the system a fixed number of times or until 0.5 seconds 00569 // has passed. 00570 while (i * sw.Elapsed() < 1.0) { 00571 m_PS->Update(); 00572 i += 2.0; 00573 ++m_UpdatesSincLastSync; 00574 } 00575 00576 // Do a special update that synchronizes the tree data with the particle 00577 // system. To keep rendering (kind of ) smooth during updates of larger 00578 // trees, don't do this if there were less than a (low) number of iterations 00579 // since synch blocks rendering. 00580 if (!Cancelled() && 00581 m_DataSynched != eRenderInProgress && 00582 m_UpdatesSincLastSync > 5) { 00583 m_UpdatesSincLastSync = 0; 00584 CMutexGuard guard(m_Mutex); 00585 m_DataSynched = eDataNotSynched; 00586 m_PS->UpdateAndSynch(); 00587 m_DataSynched = eDataRefreshed; 00588 00589 /// If velocity has dropped low enough, stop system. 00590 if (m_PS->IsDone()) 00591 RequestCancel(); 00592 } 00593 } 00594 00595 void CForceJob::RequestCancel() 00596 { 00597 CMutexGuard guard(m_Mutex); 00598 m_StopRequested = true; 00599 } 00600 00601 CForceJob::TDataSynchStatus CForceJob::DataSynched() 00602 { 00603 return m_DataSynched; 00604 } 00605 00606 void CForceJob::SetDataSynched(TDataSynchStatus s) 00607 { 00608 CMutexGuard guard(m_Mutex); 00609 m_DataSynched = s; 00610 } 00611 00612 CConstIRef<IAppJobProgress> CForceJob::GetProgress() 00613 { 00614 return CConstIRef<IAppJobProgress>(new CAppJobProgress(1.0, "Finished")); 00615 } 00616 00617 CRef<CObject> CForceJob::GetResult() 00618 { 00619 CMutexGuard guard(m_Mutex); 00620 CRef<CObject> result(new CObject()); 00621 return result; 00622 } 00623 00624 CConstIRef<IAppJobError> CForceJob::GetError() 00625 { 00626 return CConstIRef<IAppJobError>(new CAppJobError("No errors")); 00627 } 00628 00629 string CForceJob::GetDescr() const 00630 { 00631 return "Building Force Layout"; 00632 } 00633 00634 void CForceJob::x_CheckCancelled() const 00635 { 00636 CMutexGuard guard(m_Mutex); 00637 if (Cancelled()) 00638 NCBI_THROW(CException, eUnknown, "Job Cancelled"); 00639 } 00640 00641 void CPhyloForce::OnAppJobProgress(CEvent* evt) 00642 { 00643 CAppJobNotification* notn = 00644 dynamic_cast<CAppJobNotification*>(evt); 00645 _ASSERT(notn); 00646 00647 if(notn) { 00648 00649 // Could redraw on this with code below, but we use a separate timer 00650 // since this is called to infrequently to be helpful 00651 // (only called around 1/sec) 00652 /* 00653 CForceJob* j = m_Job.GetPointerOrNull(); 00654 if (j != NULL) { 00655 00656 CMutexGuard guard(j->GetMutex()); 00657 00658 CBoundaryPoints boundary_pts; 00659 x_CalculateBoundary(m_pRoot, boundary_pts, false); 00660 ComputeViewingLimits(*m_pPane, false); 00661 m_pHost->HMGH_UpdateLimits(true); 00662 //x_GetParent()->GetPort().SetModelLimitsRect(m_MatrixPane.GetModelLimitsRect()); 00663 //x_GetParent()->GetPort().SetVisibleRect(m_MatrixPane.GetVisibleRect()); 00664 m_pPane->OpenOrtho(); 00665 00666 x_DrawBoundingAreas(m_DS->GetTree()); 00667 x_DrawTree( m_DS->GetTree() ); 00668 x_DrawNodes( m_DS->GetTree() ); 00669 00670 m_pPane->Close(); 00671 } 00672 */ 00673 } 00674 00675 } 00676 00677 00678 00679 00680 END_NCBI_SCOPE
1.7.5.1
Modified on Wed May 23 13:10:06 2012 by modify_doxy.py rev. 337098