|
NCBI C++ ToolKit
|
00001 /* $Id: phylo_tree_render.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 * 00030 */ 00031 00032 #include <ncbi_pch.hpp> 00033 00034 #include <gui/widgets/phylo_tree/phylo_tree_render.hpp> 00035 00036 00037 #include <gui/widgets/phylo_tree/phylo_tree_ds.hpp> 00038 #include <gui/widgets/gl/mouse_zoom_handler.hpp> 00039 #include <gui/widgets/wx/gui_event.hpp> 00040 #include <gui/widgets/wx/glframebuffer.hpp> 00041 #include <gui/widgets/wx/glcanvas.hpp> 00042 #include <gui/opengl/glhelpers.hpp> 00043 #include <gui/opengl/glexception.hpp> 00044 #include <gui/opengl/glbitmapfont.hpp> 00045 #include <gui/widgets/gl/attrib_menu.hpp> 00046 #include <gui/utils/vect2.hpp> 00047 #include <gui/widgets/gl/mouse_zoom_handler.hpp> 00048 00049 #include <math.h> 00050 #include <algorithm> 00051 #include <limits> 00052 #include <cmath> 00053 00054 #define PTRENDER_EFFECTS_TIMER_ID 63200 00055 00056 BEGIN_NCBI_SCOPE 00057 00058 00059 BEGIN_EVENT_TABLE(IPhyloTreeRenderer, wxEvtHandler) 00060 EVT_LEFT_DOWN(IPhyloTreeRenderer::OnLeftDown) 00061 EVT_LEFT_DCLICK(IPhyloTreeRenderer::OnLeftDblClick) 00062 EVT_LEFT_UP(IPhyloTreeRenderer::OnLeftUp) 00063 EVT_RIGHT_DOWN(IPhyloTreeRenderer::OnRightDown) 00064 EVT_MOTION(IPhyloTreeRenderer::OnMotion) 00065 EVT_TIMER(PTRENDER_EFFECTS_TIMER_ID, IPhyloTreeRenderer::OnTimer) 00066 END_EVENT_TABLE() 00067 00068 IPhyloTreeRenderer :: IPhyloTreeRenderer() 00069 : m_EffectsTimer(this, PTRENDER_EFFECTS_TIMER_ID) 00070 , m_MinimapBuffer(NULL) 00071 { 00072 m_DS = NULL; 00073 m_pLabelFont = NULL; 00074 m_bDistMode = false; 00075 m_bAdaptiveMargins = true; 00076 m_bSimplification = false; 00077 m_bUseSplines = false; 00078 m_bRegenerateTexture = false; 00079 m_bZoomablePrimitives = true; 00080 m_bHighlightCollapsed = false; 00081 m_LabelViewPct = 0.25f; 00082 m_RenderScale = true; 00083 m_bAutosizeLabels = false; 00084 m_State = eIdle; 00085 m_pMouseZoomHandler = NULL; 00086 m_ActiveTooltipNode = NULL; 00087 m_SwitchedLayout = false; 00088 m_ValidLayout = eNeedLayoutAndSize; 00089 00090 // raster rect is setup to default hardcoded values 00091 // it can be changed by Layout() 00092 m_DimX = 10000; 00093 m_DimY = 10000; 00094 m_RasterRect.Init(0, 0, m_DimX, m_DimY); 00095 m_SL.Reset(new CPhyloTreeScheme()); 00096 m_BufferdRenderTime = 0.0f; 00097 00098 #ifdef ATTRIB_MENU_SUPPORT 00099 CAttribMenu& m = CAttribMenuInstance::GetInstance(); 00100 CAttribMenu* sub_menu = m.AddSubMenuUnique("Phylo Render", this); 00101 sub_menu->AddFloatReadOnly("Buffered Render Time", &m_BufferdRenderTime); 00102 sub_menu->AddFloat("Label View Pct", &m_LabelViewPct); 00103 #endif 00104 } 00105 00106 IPhyloTreeRenderer :: IPhyloTreeRenderer(double width, double height) 00107 : m_DimX(width), m_DimY(height) 00108 , m_EffectsTimer(this, PTRENDER_EFFECTS_TIMER_ID) 00109 , m_MinimapBuffer(NULL) 00110 { 00111 m_pPane = NULL; 00112 m_DS = NULL; 00113 m_pLabelFont = NULL; 00114 m_bDistMode = false; 00115 m_bAdaptiveMargins = true; 00116 m_bSimplification = false; 00117 m_bUseSplines = false; 00118 m_bRegenerateTexture = false; 00119 m_bZoomablePrimitives = true; 00120 m_bHighlightCollapsed = false; 00121 m_LabelViewPct = 0.25f; 00122 m_RenderScale = true; 00123 m_bAutosizeLabels = false; 00124 m_State = eIdle; 00125 m_RasterRect.Init(0, 0, m_DimX, m_DimY); 00126 m_SL.Reset(new CPhyloTreeScheme()); 00127 m_ActiveTooltipNode = NULL; 00128 m_SwitchedLayout = false; 00129 m_ValidLayout = eNeedLayoutAndSize; 00130 m_BufferdRenderTime = 0.0f; 00131 00132 #ifdef ATTRIB_MENU_SUPPORT 00133 CAttribMenu& m = CAttribMenuInstance::GetInstance(); 00134 CAttribMenu* sub_menu = m.AddSubMenuUnique("Phylo Render", this); 00135 sub_menu->AddFloatReadOnly("Buffered Render Time", &m_BufferdRenderTime); 00136 sub_menu->AddFloat("Label View Pct", &m_LabelViewPct); 00137 #endif 00138 } 00139 00140 IPhyloTreeRenderer::~IPhyloTreeRenderer() 00141 { 00142 #ifdef ATTRIB_MENU_SUPPORT 00143 CAttribMenuInstance::GetInstance().RemoveMenuR("Phylo Render", this); 00144 #endif 00145 00146 delete m_MinimapBuffer; 00147 m_MinimapBuffer = NULL; 00148 } 00149 00150 void IPhyloTreeRenderer::SetHost(IPhyloTreeRendererHost* pHost) 00151 { 00152 m_pHost = pHost; 00153 } 00154 00155 00156 00157 void IPhyloTreeRenderer::SetPane(CGlPane* pane) 00158 { 00159 // HACK!!! 00160 if (pane) { 00161 m_pPane = pane; 00162 } 00163 } 00164 00165 void IPhyloTreeRenderer::x_RenderSelection(CGlPane& pane) 00166 { 00167 // Draw a stippled line around the currently selected node, if any 00168 if (m_DS->GetCurrentNode() != NULL) { 00169 TVPUnit node_size = x_NodeSize(m_DS->GetCurrentNode()); 00170 if (node_size > 0) { 00171 CGlAttrGuard AttrGuard(GL_POLYGON_BIT | GL_LINE_BIT); 00172 pane.OpenOrtho(); 00173 00174 double x = (**m_DS->GetCurrentNode()).X(); 00175 double y = (**m_DS->GetCurrentNode()).Y(); 00176 00177 glMatrixMode(GL_MODELVIEW); 00178 glPushMatrix(); 00179 glTranslated(x, y, 0.0); 00180 glScaled(pane.GetScaleX(), pane.GetScaleY(), 1.0); 00181 00182 glLineWidth(1.0f); 00183 glColor3f(0.0f, 0.0f, 0.0f); 00184 glLineStipple(3, 0xAAAA); 00185 glEnable(GL_LINE_STIPPLE); 00186 glDisable(GL_LINE_SMOOTH); 00187 00188 node_size += 1; 00189 glBegin(GL_LINE_LOOP); 00190 glVertex2f(-node_size, -node_size); 00191 glVertex2f(node_size, -node_size); 00192 glVertex2f(node_size, node_size); 00193 glVertex2f(-node_size, node_size); 00194 glEnd(); 00195 00196 glDisable(GL_LINE_STIPPLE); 00197 glPopMatrix(); 00198 pane.Close(); 00199 } 00200 } 00201 00202 // Draw the selection rectangle, if user is currently doing a drag-selection 00203 if(m_State == eSelRect) { 00204 _ASSERT(m_pHost); 00205 00206 CGlAttrGuard AttrGuard(GL_POLYGON_BIT | GL_LINE_BIT); 00207 pane.OpenPixels(); 00208 00209 glLineWidth(1.0f); 00210 glColor3f(0.0f, 0.0f, 0.0f); 00211 00212 glLineStipple(1, 0x0F0F); 00213 glEnable(GL_LINE_STIPPLE); 00214 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 00215 00216 glDisable(GL_LINE_SMOOTH); 00217 00218 int x1 = m_StartPoint.x; 00219 int y1 = m_pHost->HMGH_GetVPPosByY(m_StartPoint.y); 00220 int x2 = m_DragPoint.x; 00221 int y2 = m_pHost->HMGH_GetVPPosByY(m_DragPoint.y); 00222 if(x2 < x1) 00223 swap(x1, x2); 00224 if(y2 < y1) 00225 swap(y1, y2); 00226 00227 glBegin(GL_LINES); 00228 glVertex2d(x1, y2); 00229 glVertex2d(x2, y2); 00230 00231 glVertex2d(x2, y2); 00232 glVertex2d(x2, y1); 00233 00234 glVertex2d(x1, y2); 00235 glVertex2d(x1, y1); 00236 00237 glVertex2d(x1, y1); 00238 glVertex2d(x2, y1); 00239 glEnd(); 00240 00241 glDisable(GL_LINE_STIPPLE); 00242 00243 pane.Close(); 00244 } 00245 } 00246 00247 void IPhyloTreeRenderer::x_RenderScaleMarker(CGlPane& pane) 00248 { 00249 // Can turn off scale rendering explicitly, and if we are not in distance 00250 // mode it is also turned off automatically since redering scales without 00251 // distance makes no sense. 00252 if (!m_RenderScale || !m_bDistMode) 00253 return; 00254 00255 // target length in pixels of the distance bar 00256 float target_len = 100.0f; 00257 // actual length of distance bar, after rounding 00258 float final_len = target_len; 00259 00260 float base_xpos; 00261 float base_ypos = 10.0f; 00262 float bar_height = 20.0f; 00263 00264 // Don't try to draw if there is not enough room for 00265 // the error bar. This also avoids some divide-by-0 errors 00266 // for when the viewport width goes to 0. 00267 if (pane.GetViewport().Width() <= target_len || 00268 pane.GetViewport().Height() < bar_height) 00269 return; 00270 00271 CGlAttrGuard AttrGuard(GL_POLYGON_BIT | GL_LINE_BIT); 00272 pane.OpenOrtho(); 00273 00274 if (IsDistanceBarLowerLeft()) { 00275 base_xpos = 10.0f; 00276 } 00277 else { 00278 base_xpos = (float)pane.GetViewport().Width() - 00279 (1.5f*target_len + 10.0f); 00280 base_xpos = std::max(0.0f, base_xpos); 00281 } 00282 00283 // Compute node distance for a fixed pixel offset using 00284 // unproject. m_DimX is a factor used during layout to force 00285 // the graph to project to a virtual window of width m_DimX (labels not 00286 // included) so we divide it out here. GetNormDistance is the 00287 // underlying graph width that gets scaled (at layout) to m_DimX. 00288 double pt1 = pane.UnProjectX(base_xpos); 00289 double pt2 = pane.UnProjectX(base_xpos + target_len); 00290 double default_scale = (pt2 - pt1)*(m_DS->GetNormDistance()/m_DimX); 00291 00292 pane.Close(); 00293 00294 // a 0 value or something very close to it won't work. this 00295 // happens if a tree doesn't have distances specified for nodes. 00296 if (default_scale < std::numeric_limits<double>::epsilon()) 00297 return; 00298 00299 pane.OpenPixels(); 00300 00301 // Represent the distance bar with only 1 significant digit (first non-zero). 00302 // This seems a little simplistic but it seems to work OK. Could also 00303 // compute significance by computing the distance bar length at 00304 // different zoom levels to see how quickly number is changing... 00305 double rounded = default_scale; 00306 double multiplier; 00307 int precision = 0; 00308 if (default_scale < 1.0) { 00309 multiplier = 1.0; 00310 while (rounded < 1.0) { 00311 multiplier *= 10.0; 00312 ++precision; 00313 rounded = default_scale*multiplier; 00314 } 00315 precision = std::max(precision, 1); 00316 rounded = (double)((int)(floor(rounded) + 0.5)); 00317 rounded = rounded / multiplier; 00318 } 00319 else { 00320 precision = 1; 00321 multiplier = 1.0; 00322 while (rounded > 10.0) { 00323 multiplier /= 10.0f; 00324 rounded = default_scale*multiplier; 00325 } 00326 rounded = (double)((int)(floor(rounded) + 0.5)); 00327 rounded = rounded / multiplier; 00328 } 00329 final_len = target_len * rounded/default_scale; 00330 char buf_round[32]; 00331 sprintf(buf_round, "%.*f", precision, rounded); 00332 00333 00334 // Drawing options 00335 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 00336 glDisable(GL_LINE_SMOOTH); 00337 glColor4f(0.9f, 0.9f, 0.9f, 0.93f); 00338 00339 // Draw a quad for the distance bar background 00340 glBegin(GL_QUADS); 00341 glVertex2f(base_xpos-5.0f, base_ypos-5.0f); 00342 glVertex2f(base_xpos + final_len + 5.0f, base_ypos-5.0f); 00343 glVertex2f(base_xpos + final_len + 5.0f, base_ypos + bar_height + 5.0f); 00344 glVertex2f(base_xpos-5.0f, base_ypos + bar_height + 5.0f); 00345 glEnd(); 00346 00347 // Draw the scale (distance) bar 00348 glColor4f(0.0f, 0.0f, 0.0f, 1.0f); 00349 glColor4f(0.0f, 0.0f, 0.0f, 1.0f); 00350 glLineWidth(2.0f); 00351 00352 glBegin(GL_LINES); 00353 glVertex2f(base_xpos, base_ypos); 00354 glVertex2f(base_xpos, base_ypos + bar_height); 00355 00356 glVertex2f(base_xpos, base_ypos + bar_height/2.0f); 00357 glVertex2f(base_xpos + final_len, base_ypos + bar_height/2.0f); 00358 00359 glVertex2f(base_xpos + final_len, base_ypos); 00360 glVertex2f(base_xpos + final_len, base_ypos + bar_height); 00361 glEnd(); 00362 00363 // draw the length represented by the distance bar 00364 const CGlBitmapFont* bestFont = &m_SL->GetFont(); 00365 float tx = base_xpos + 5.0f; 00366 float ty = base_ypos + bar_height/2.0f + 5.0f; 00367 00368 bestFont->TextOut(tx, ty, buf_round); 00369 00370 pane.Close(); 00371 } 00372 00373 void IPhyloTreeRenderer::x_RenderTooltipHints(CGlPane& pane) 00374 { 00375 // Draw a stippled line around the currently selected node, if any 00376 if (m_DS->GetCurrentNode() != NULL) { 00377 TVPUnit node_size = x_NodeSize(m_DS->GetCurrentNode()); 00378 if (node_size > 0) { 00379 CGlAttrGuard AttrGuard(GL_ALL_ATTRIB_BITS); 00380 pane.OpenOrtho(); 00381 00382 double x = (**m_DS->GetCurrentNode()).X(); 00383 double y = (**m_DS->GetCurrentNode()).Y(); 00384 00385 glMatrixMode(GL_MODELVIEW); 00386 glPushMatrix(); 00387 glTranslated(x, y, 0.0); 00388 glScaled(pane.GetScaleX(), pane.GetScaleY(), 1.0); 00389 00390 glLineWidth(1.0f); 00391 glColor3f(0.0f, 0.0f, 0.0f); 00392 glLineStipple(3, 0xAAAA); 00393 glEnable(GL_LINE_STIPPLE); 00394 glDisable(GL_LINE_SMOOTH); 00395 00396 node_size += 1; 00397 glBegin(GL_LINE_LOOP); 00398 glVertex2f(-node_size, -node_size); 00399 glVertex2f(node_size, -node_size); 00400 glVertex2f(node_size, node_size); 00401 glVertex2f(-node_size, node_size); 00402 glEnd(); 00403 00404 glDisable(GL_LINE_STIPPLE); 00405 glPopMatrix(); 00406 pane.Close(); 00407 } 00408 } 00409 00410 // If mouse is currently over tooltip for this node... 00411 if (m_ActiveTooltipNode != NULL) { 00412 float s = (float)x_NodeSize(m_ActiveTooltipNode); 00413 int num_lines = std::max(2, GetDefaultNodeSize(m_ActiveTooltipNode))*10; 00414 00415 CRgbaColor c = m_SL->SetColor(CPhyloTreeScheme::eNode, 00416 CPhyloTreeScheme::eTipSelColor); 00417 00418 CGlAttrGuard AttrGuard(GL_ALL_ATTRIB_BITS); 00419 pane.OpenOrtho(); 00420 00421 double x = (**m_ActiveTooltipNode).X(); 00422 double y = (**m_ActiveTooltipNode).Y(); 00423 00424 glMatrixMode(GL_MODELVIEW); 00425 glPushMatrix(); 00426 glTranslated(x, y, 0.0); 00427 glScaled(pane.GetScaleX(), pane.GetScaleY(), 1.0); 00428 00429 glEnable(GL_BLEND); 00430 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 00431 00432 glColor4fv(c.GetColorArray()); 00433 00434 float s2 = s*3.0f; 00435 if (s2==0.0f) 00436 s2 = 3.0f*(float)GetDefaultNodeSize(m_ActiveTooltipNode); 00437 00438 glBegin(GL_TRIANGLE_FAN); 00439 glVertex2f(0.0f, 0.0f); 00440 00441 float delta = 6.28f/(float)num_lines; 00442 float angle = 0.0f; 00443 for(int i =0; i<=num_lines+1; i++){ 00444 glVertex2f(s2*cosf(angle), s2*sinf(angle)); 00445 angle += delta; 00446 } 00447 glEnd(); 00448 glPopMatrix(); 00449 00450 pane.Close(); 00451 } 00452 00453 00454 // Render temporary arrows, if any currently active 00455 std::vector<NodePointer>::iterator niter, erase_iter = m_NodePointers.end(); 00456 for (niter=m_NodePointers.begin(); niter!=m_NodePointers.end(); ++niter) { 00457 CPhyloTreeNode* node = (*niter).m_Node; 00458 if (node != NULL) { 00459 00460 NodePointer np = *niter; 00461 float alpha = 0.5f; 00462 00463 double x = (**node).X(); 00464 double y = (**node).Y(); 00465 00466 // Check if this arrow has timed out. Can erase 1 at a time (ok because 00467 // there will be very few arrows) 00468 if (np.m_Duration > 0.0) { 00469 if (np.m_Timer.Elapsed() > np.m_Duration) { 00470 alpha = 0.0f; 00471 erase_iter = niter; 00472 break; 00473 } 00474 else alpha *= 1.0f - np.m_Timer.Elapsed()/np.m_Duration; 00475 } 00476 00477 if (alpha > 0.0f) { 00478 CRgbaColor c = m_SL->SetColor(CPhyloTreeScheme::eNode, 00479 CPhyloTreeScheme::eTipSelColor); 00480 00481 glEnable(GL_BLEND); 00482 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 00483 c.SetAlpha(alpha); 00484 glColor4fv(c.GetColorArray()); 00485 00486 // Render in screen space: 00487 glMatrixMode(GL_MODELVIEW); 00488 glPushMatrix(); 00489 glLoadIdentity(); 00490 glMatrixMode(GL_PROJECTION); 00491 glPushMatrix(); 00492 glLoadIdentity(); 00493 glOrtho(0, m_pPane->GetViewport().Width(), 00494 0, m_pPane->GetViewport().Height(), -1.0f, 1.0f); 00495 00496 // Center of the tooltip in screen (pixel) coordinates: 00497 CVect2<float> tip_center(np.m_TipCenter.X(), np.m_TipCenter.Y()); 00498 CVect2<float> node_pos(x, y); 00499 TVPPoint tvp = m_pPane->Project(x, y); 00500 // Node position in screen (pixel) coordinates 00501 CVect2<float> node_proj_pos(tvp.X(), tvp.Y()); 00502 CVect2<float> proj_dir(node_proj_pos - tip_center); 00503 00504 float proj_len = proj_dir.Length(); 00505 proj_dir.Normalize(); 00506 CVect2<float> proj_dir_perp(proj_dir.Y(), -proj_dir.X()); 00507 00508 // Arrowhead that points at node: 00509 CVect2<float> arrowhead_base(node_proj_pos.X() - proj_dir.X()*16.0f, 00510 node_proj_pos.Y() - proj_dir.Y()*16.0f); 00511 00512 // Draw shaft of arrow (unless tip is so close no room for shaft) 00513 float shaft_len = proj_len - 16.0f; 00514 if (shaft_len > 0.0f) { 00515 CVect2<float> dir_minus_arrowhead(proj_dir*shaft_len); 00516 00517 CVect2<float> q1 = tip_center + proj_dir_perp*3.0f; 00518 CVect2<float> q2 = q1 + dir_minus_arrowhead; 00519 CVect2<float> q3 = q2 - proj_dir_perp*6.0f; 00520 CVect2<float> q4 = q3 - dir_minus_arrowhead; 00521 00522 glBegin(GL_QUADS); 00523 glVertex2fv(q1.GetData()); 00524 glVertex2fv(q2.GetData()); 00525 glVertex2fv(q3.GetData()); 00526 glVertex2fv(q4.GetData()); 00527 glEnd(); 00528 } 00529 00530 // Draw arrowhead 00531 CVect2<float> t1(arrowhead_base + proj_dir_perp*8.0f); 00532 CVect2<float> t2(node_proj_pos); 00533 CVect2<float> t3(arrowhead_base - proj_dir_perp*8.0f); 00534 00535 glBegin(GL_TRIANGLES); 00536 glVertex2fv(t1.GetData()); 00537 glVertex2fv(t2.GetData()); 00538 glVertex2fv(t3.GetData()); 00539 glEnd(); 00540 00541 glPopMatrix(); 00542 glMatrixMode(GL_MODELVIEW); 00543 glPopMatrix(); 00544 00545 glDisable(GL_BLEND); 00546 } 00547 } 00548 } 00549 00550 // If a temporary arrow timed out, erase it here: 00551 if (erase_iter != m_NodePointers.end()) 00552 m_NodePointers.erase(erase_iter); 00553 } 00554 00555 00556 //////////////////////////////////////////////////////////////////////////////// 00557 /// event handlers 00558 00559 void IPhyloTreeRenderer::OnLeftDown(wxMouseEvent& event) 00560 { 00561 if (m_DS == NULL) { 00562 event.Skip(); 00563 return; 00564 } 00565 00566 m_bMoveDuringSelection = false; 00567 00568 m_StartPoint = event.GetPosition(); 00569 m_DragPoint = m_StartPoint; 00570 00571 CPhyloTreeNode * current = 00572 GetHoverNode(TVPPoint(event.GetX(), event.GetY())); 00573 00574 // set current or reset it 00575 m_DS->SetCurrentNode(current); 00576 00577 // select point if we hit a node, otherwise select rect if we are in an extended selection mode 00578 // (!eSelectState). 00579 if (m_pMouseZoomHandler->GetPanMode() == CMouseZoomHandler::eLmouse) { 00580 if (current) { 00581 m_State = eSelPoint; 00582 } 00583 else if (CGUIEvent::wxGetSelectState(event) != CGUIEvent::eSelectState) { 00584 m_State = eSelRect; 00585 GetGenericHost()->GHH_CaptureMouse(); 00586 } 00587 else { 00588 m_State = eIdle; 00589 event.Skip(); 00590 } 00591 } 00592 else { 00593 if (current) 00594 m_State = eSelPoint; 00595 else 00596 m_State = eSelRect; 00597 } 00598 00599 if (m_State == eSelPoint) 00600 m_pHost->HMGH_OnRefresh(); 00601 } 00602 00603 void IPhyloTreeRenderer::OnRightDown(wxMouseEvent& event) 00604 { 00605 if (m_DS == NULL) { 00606 event.Skip(); 00607 return; 00608 } 00609 00610 m_State = eSelPoint; 00611 00612 m_StartPoint = event.GetPosition(); 00613 m_DragPoint = m_StartPoint; 00614 00615 CPhyloTreeNode * current = 00616 GetHoverNode(TVPPoint(event.GetX(), event.GetY())); 00617 00618 // set current or reset it 00619 m_DS->SetCurrentNode(current); 00620 00621 m_State = current ? eSelPoint : eIdle; 00622 00623 m_pHost->HMGH_OnRefresh(); 00624 00625 // Skip so that the context menu can come up as well. (Right Down on Mac) 00626 event.Skip(); 00627 } 00628 00629 00630 void IPhyloTreeRenderer::OnLeftDblClick(wxMouseEvent& event) 00631 { 00632 if (m_DS == NULL) { 00633 event.Skip(); 00634 return; 00635 } 00636 00637 CPhyloTreeNode * current = 00638 GetHoverNode(TVPPoint(event.GetX(), event.GetY())); 00639 00640 // set current or reset it 00641 m_DS->SetCurrentNode(current); 00642 00643 if (current) { 00644 switch ((**current).GetChildsDisplay()){ 00645 case IPhyGraphicsNode::eHideChilds: 00646 if (current->CanExpandCollapse(IPhyGraphicsNode::eShowChilds)){ 00647 // Don't try to render again until layout has been called for updated tree. 00648 m_ValidLayout = eNeedLayout; 00649 m_DS->ExpandCollapse(current, IPhyGraphicsNode::eShowChilds); 00650 m_pHost->FireEditEvent(); 00651 } 00652 break; 00653 case IPhyGraphicsNode::eShowChilds: 00654 if (current->CanExpandCollapse(IPhyGraphicsNode::eHideChilds)){ 00655 // Don't try to render again until layout has been called for updated tree. 00656 m_ValidLayout = eNeedLayout; 00657 m_DS->ExpandCollapse(current, IPhyGraphicsNode::eHideChilds); 00658 m_pHost->FireEditEvent(); 00659 } 00660 break; 00661 } 00662 } 00663 } 00664 00665 void IPhyloTreeRenderer::OnMotion(wxMouseEvent& event) 00666 { 00667 if(event.Dragging()) { 00668 m_bMoveDuringSelection = true; 00669 00670 if(m_State == eSelRect) { 00671 wxPoint ms_pos = event.GetPosition(); 00672 00673 if(ms_pos != m_DragPoint) { 00674 m_State = eSelRect; 00675 m_DragPoint = ms_pos; 00676 m_pHost->HMGH_OnRefresh(); 00677 } 00678 } 00679 else { 00680 event.Skip(); 00681 } 00682 } 00683 else { 00684 event.Skip(); 00685 } 00686 00687 m_LastPos = event.GetPosition(); 00688 } 00689 00690 void IPhyloTreeRenderer::OnTimer(wxTimerEvent& /* event */) 00691 { 00692 if (m_NodePointers.size() == 0) { 00693 m_EffectsTimer.Stop(); 00694 return; 00695 } 00696 00697 m_pHost->HMGH_OnChanged(); 00698 } 00699 00700 void IPhyloTreeRenderer::OnLeftUp(wxMouseEvent& event) 00701 { 00702 if (m_DS == NULL) { 00703 event.Skip(); 00704 return; 00705 } 00706 00707 CPhyloTreeNode * current = 00708 GetHoverNode(TVPPoint(event.GetX(), event.GetY())); 00709 00710 CGUIEvent::EGUIState state = 00711 CGUIEvent::wxGetSelectState(event); 00712 00713 // not doing incremental only selection 00714 //bool neg = (state == CGUIEvent::eSelectExtState); 00715 bool inc = (state == CGUIEvent::eSelectIncState); 00716 bool sel = (state == CGUIEvent::eSelectState); 00717 00718 switch(m_State) { 00719 case eSelPoint: 00720 // clear selection if you click with no modifiers on an item 00721 // or if you control-click on empty space (normal click on 00722 // empty space is pan) 00723 if ((sel && current) || (inc && !current)) { 00724 m_DS->SetSelection(m_DS->GetTree(), false, true); 00725 m_SelectedIDs.clear(); 00726 } 00727 00728 if (current) { 00729 x_SelectByPoint(current, inc); 00730 } 00731 00732 m_State = eIdle; 00733 m_pHost->HMGH_OnChanged(); 00734 return; 00735 00736 case eSelRect: { 00737 int dist = std::abs(m_StartPoint.x - m_DragPoint.x) + 00738 std::abs(m_StartPoint.y - m_DragPoint.y); 00739 00740 // Don't select for tiny rectangles. Allow de-selection on mouse up if 00741 // control key was down (if no key was down, we would be panning instead). 00742 if (dist > 4) { 00743 x_SelectByRect(m_DS->GetTree(), inc ); 00744 } 00745 else { 00746 if ((m_pMouseZoomHandler->GetPanMode() == CMouseZoomHandler::eLmouse && inc) || 00747 (m_pMouseZoomHandler->GetPanMode() == CMouseZoomHandler::ePkey && sel)) { 00748 m_DS->SetSelection(m_DS->GetTree(), false, true); 00749 m_SelectedIDs.clear(); 00750 } 00751 } 00752 00753 m_State = eIdle; 00754 00755 GetGenericHost()->GHH_ReleaseMouse(); 00756 00757 m_pHost->HMGH_OnChanged(); 00758 break; 00759 } 00760 default: 00761 // clear selection if user left clicks without moving (panning) the mouse 00762 if ( !m_bMoveDuringSelection && 00763 m_pMouseZoomHandler->GetPanMode() == CMouseZoomHandler::eLmouse && 00764 sel) { 00765 m_DS->SetSelection(m_DS->GetTree(), false, true); 00766 m_SelectedIDs.clear(); 00767 m_pHost->HMGH_OnChanged(); 00768 } 00769 00770 event.Skip(); 00771 } 00772 00773 } 00774 00775 void IPhyloTreeRenderer::x_OnSelectCursor(void) 00776 { 00777 switch(m_State) { 00778 case eIdle: 00779 case eSelPoint: 00780 m_CursorId = wxCURSOR_ARROW; 00781 break; 00782 case eSelRect: 00783 m_CursorId = wxCURSOR_CROSS; 00784 break; 00785 default: 00786 break; 00787 } 00788 GetGenericHost()->GHH_SetCursor(wxCursor(m_CursorId)); 00789 } 00790 00791 IGenericHandlerHost* IPhyloTreeRenderer::GetGenericHost() 00792 { 00793 return dynamic_cast<IGenericHandlerHost*>(m_pHost); 00794 } 00795 00796 00797 /*************************************************************************************/ 00798 00799 00800 int IPhyloTreeRenderer::x_OnMouseMove(void) 00801 { 00802 return (m_State == eIdle) ? 0 : 1; 00803 } 00804 00805 00806 int IPhyloTreeRenderer::x_OnKeyDown(void) 00807 { 00808 return (m_State == eIdle) ? 0 : 1; 00809 } 00810 00811 int IPhyloTreeRenderer::x_OnKeyUp(void) 00812 { 00813 return (m_State == eIdle) ? 0 : 1; 00814 } 00815 00816 00817 00818 void IPhyloTreeRenderer::x_RenderLine(double x1, 00819 double y1, 00820 double x2, 00821 double y2, 00822 IPhyGraphicsNode::TSelectedState state, 00823 string force_color) 00824 { 00825 CRgbaColor color; 00826 switch (state){ 00827 case IPhyGraphicsNode::eTraced: 00828 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 00829 CPhyloTreeScheme::eTraceColor); 00830 break; 00831 case IPhyGraphicsNode::eSelected: 00832 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 00833 CPhyloTreeScheme::eSelColor); 00834 break; 00835 case IPhyGraphicsNode::eShared: 00836 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 00837 CPhyloTreeScheme::eSharedColor); 00838 break; 00839 default: 00840 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 00841 CPhyloTreeScheme::eColor); 00842 } 00843 00844 if (!force_color.empty() && (state!=IPhyGraphicsNode::eSelected)){ 00845 CRgbaColor fColor(force_color); 00846 color = fColor; 00847 } 00848 00849 if (state == IPhyGraphicsNode::eNotSelected && 00850 m_SL->GetSelectionVisibility() == CPhyloTreeScheme::eHighlightSelection) { 00851 color.SetAlpha(m_SL->GetNonSelectedAlpha()); 00852 } 00853 00854 TModelUnit lineWidth = 00855 m_SL->SetSize(CPhyloTreeScheme::eLineWidth); 00856 00857 if (DistanceBetweenNodes() < lineWidth) { 00858 lineWidth = 1; 00859 } 00860 00861 glEnable(GL_BLEND); 00862 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 00863 glLineWidth((GLfloat)lineWidth); 00864 glShadeModel(GL_SMOOTH); 00865 glEnable(GL_LINE_SMOOTH); 00866 00867 glColor4fv(color.GetColorArray()); 00868 00869 glBegin(GL_LINES); 00870 glVertex2d(x1, y1); 00871 glVertex2d(x2, y2); 00872 glEnd(); 00873 00874 if (lineWidth>1){ 00875 glLineWidth(1); 00876 CRgbaColor color2 = color; 00877 color2.Lighten(0.7f); 00878 glColor4fv(color2.GetColorArray()); 00879 glBegin(GL_LINES); 00880 glVertex2d(x1, y1); 00881 glVertex2d(x2, y2); 00882 glEnd(); 00883 } 00884 00885 glDisable(GL_LINE_SMOOTH); 00886 glDisable(GL_BLEND); 00887 } 00888 00889 void IPhyloTreeRenderer::x_RenderLineVbo(CGlVboNode* render_lines, 00890 double x1, 00891 double y1, 00892 double x2, 00893 double y2, 00894 IPhyGraphicsNode::TSelectedState state, 00895 string force_color) 00896 { 00897 CRgbaColor color; 00898 switch (state){ 00899 case IPhyGraphicsNode::eTraced: 00900 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 00901 CPhyloTreeScheme::eTraceColor); 00902 break; 00903 case IPhyGraphicsNode::eSelected: 00904 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 00905 CPhyloTreeScheme::eSelColor); 00906 break; 00907 case IPhyGraphicsNode::eShared: 00908 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 00909 CPhyloTreeScheme::eSharedColor); 00910 break; 00911 default: 00912 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 00913 CPhyloTreeScheme::eColor); 00914 } 00915 00916 if (!force_color.empty() && (state!=IPhyGraphicsNode::eSelected)){ 00917 CRgbaColor fColor(force_color); 00918 color = fColor; 00919 } 00920 00921 if (state == IPhyGraphicsNode::eNotSelected && 00922 m_SL->GetSelectionVisibility() == CPhyloTreeScheme::eHighlightSelection) { 00923 color.SetAlpha(m_SL->GetNonSelectedAlpha()); 00924 } 00925 00926 render_lines->GetColorBuffer().PushBack(color); 00927 render_lines->GetColorBuffer().PushBack(color); 00928 m_DS->GetModel().GetEdges().push_back(CTreeGraphicsEdge(CVect2<float>(x1,y1), CVect2<float>(x2, y2))); 00929 } 00930 00931 void IPhyloTreeRenderer::x_RenderSpline(TPoint pt1, 00932 TPoint pt2, 00933 TPoint pt3, 00934 TPoint pt4, 00935 IPhyGraphicsNode::TSelectedState state, 00936 string force_color, bool bPseudo) 00937 { 00938 CRgbaColor color; 00939 00940 switch (state){ 00941 case IPhyGraphicsNode::eTraced: 00942 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 00943 CPhyloTreeScheme::eTraceColor); 00944 break; 00945 case IPhyGraphicsNode::eSelected: 00946 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 00947 CPhyloTreeScheme::eSelColor); 00948 break; 00949 case IPhyGraphicsNode::eShared: 00950 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 00951 CPhyloTreeScheme::eSharedColor); 00952 break; 00953 default: 00954 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 00955 CPhyloTreeScheme::eColor); 00956 } 00957 00958 if (!force_color.empty() && (state==IPhyGraphicsNode::eNotSelected)){ 00959 CRgbaColor fColor(force_color); 00960 color = fColor; 00961 } 00962 00963 if (state == IPhyGraphicsNode::eNotSelected && 00964 m_SL->GetSelectionVisibility() == CPhyloTreeScheme::eHighlightSelection) { 00965 color.SetAlpha(m_SL->GetNonSelectedAlpha()); 00966 } 00967 00968 TModelUnit lineWidth = 00969 m_SL->SetSize(CPhyloTreeScheme::eLineWidth); 00970 00971 if (DistanceBetweenNodes() < lineWidth) { 00972 lineWidth = 1; 00973 } 00974 00975 glColor4fv(color.GetColorArray()); 00976 glLineWidth((GLfloat)lineWidth); 00977 00978 glEnable(GL_BLEND); 00979 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 00980 glShadeModel(GL_SMOOTH); 00981 00982 if (!bPseudo){ // real spline 00983 glEnable(GL_LINE_SMOOTH); 00984 m_Curve.SetPoint(0, pt1); 00985 m_Curve.SetPoint(1, pt2); 00986 m_Curve.SetPoint(2, pt3); 00987 m_Curve.SetPoint(3, pt4); 00988 m_Curve.Recalc(); 00989 m_Curve.Draw(); 00990 00991 if (lineWidth>1){ 00992 glLineWidth(1); 00993 CRgbaColor color2 = color; 00994 color2.Lighten(0.7f); 00995 glColor4fv(color2.GetColorArray()); 00996 m_Curve.Draw(); 00997 } 00998 glDisable(GL_LINE_SMOOTH); 00999 } 01000 else { 01001 glDisable(GL_LINE_SMOOTH); 01002 01003 // length corrections 01004 double dx=0, dy=0; 01005 if (lineWidth>2){ 01006 dx = floor(lineWidth * 0.5) * m_pPane->GetScaleX(); 01007 dy = floor(lineWidth * 0.5) * m_pPane->GetScaleY() * ((pt1.Y() > pt4.Y())?1:-1); 01008 } 01009 01010 glBegin(GL_LINES); 01011 glVertex2d(pt1.X(), pt1.Y()); 01012 glVertex2d(pt2.X(), pt2.Y()-dy); 01013 glVertex2d(pt3.X()-dx, pt3.Y()); 01014 glVertex2d(pt4.X(), pt4.Y()); 01015 glEnd(); 01016 01017 // inner line 01018 if (lineWidth > 2){ 01019 glLineWidth(1); 01020 CRgbaColor color2 = color; 01021 color2.Lighten(0.7f); 01022 glColor4fv(color2.GetColorArray()); 01023 glBegin(GL_LINE_STRIP); 01024 glVertex2d(pt1.X(), pt1.Y()); 01025 glVertex2d(pt2.X(), pt2.Y()); 01026 glVertex2d(pt3.X(), pt3.Y()); 01027 glVertex2d(pt4.X(), pt4.Y()); 01028 glEnd(); 01029 } 01030 } 01031 glDisable(GL_BLEND); 01032 } 01033 01034 01035 void IPhyloTreeRenderer::x_RenderSplineVbo(vector<CRgbaColor>& edge_colors, 01036 TPoint pt1, 01037 TPoint pt2, 01038 TPoint pt3, 01039 TPoint pt4, 01040 IPhyGraphicsNode::TSelectedState state, 01041 string force_color) 01042 { 01043 CRgbaColor color; 01044 01045 switch (state){ 01046 case IPhyGraphicsNode::eTraced: 01047 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 01048 CPhyloTreeScheme::eTraceColor); 01049 break; 01050 case IPhyGraphicsNode::eSelected: 01051 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 01052 CPhyloTreeScheme::eSelColor); 01053 break; 01054 case IPhyGraphicsNode::eShared: 01055 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 01056 CPhyloTreeScheme::eSharedColor); 01057 break; 01058 default: 01059 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 01060 CPhyloTreeScheme::eColor); 01061 } 01062 01063 if (!force_color.empty() && (state==IPhyGraphicsNode::eNotSelected)){ 01064 CRgbaColor fColor(force_color); 01065 color = fColor; 01066 } 01067 01068 if (state == IPhyGraphicsNode::eNotSelected && 01069 m_SL->GetSelectionVisibility() == CPhyloTreeScheme::eHighlightSelection) { 01070 color.SetAlpha(m_SL->GetNonSelectedAlpha()); 01071 } 01072 01073 01074 //glEnable(GL_LINE_SMOOTH); 01075 m_Curve.SetPoint(0, pt1); 01076 m_Curve.SetPoint(1, pt2); 01077 m_Curve.SetPoint(2, pt3); 01078 m_Curve.SetPoint(3, pt4); 01079 m_Curve.Recalc(); 01080 // 01081 vector<CVect3<float> > buf; 01082 m_Curve.DrawBuffered(buf); 01083 01084 size_t i; 01085 for (i=0; i<buf.size()-1; ++i) { 01086 edge_colors.push_back(color); 01087 edge_colors.push_back(color); 01088 m_DS->GetModel().GetEdges().push_back(CTreeGraphicsEdge( 01089 CVect2<float>(buf[i].X(), buf[i].Y()), 01090 CVect2<float>(buf[i+1].X(), buf[i+1].Y()) )); 01091 } 01092 } 01093 01094 void IPhyloTreeRenderer::x_RenderPseudoSplineVbo(CPhyloTreeNode* child_node, 01095 CGlVboNode* render_lines, 01096 const CVect2<float>& pt1, 01097 const CVect2<float>& mid_point, 01098 const CVect2<float>& pt2, 01099 IPhyGraphicsNode::TSelectedState state) 01100 { 01101 CRgbaColor color(0.0f, 0.0f, 0.0f, 1.0f); 01102 01103 switch (state){ 01104 case IPhyGraphicsNode::eNotSelected: 01105 { 01106 const string& force_color = m_SL->GetColoration()==CPhyloTreeScheme::eClusters? 01107 (**child_node).GetClusterFgColor():(**child_node).GetNodeEdColor(); 01108 01109 if (!force_color.empty()){ 01110 color = CRgbaColor(force_color); 01111 } 01112 else { 01113 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 01114 CPhyloTreeScheme::eColor); 01115 //color.SetAlpha(0.0f); 01116 } 01117 01118 if (m_SL->GetSelectionVisibility() == 01119 CPhyloTreeScheme::eHighlightSelection) { 01120 color.SetAlpha(m_SL->GetNonSelectedAlpha()); 01121 } 01122 } 01123 break; 01124 case IPhyGraphicsNode::eTraced: 01125 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 01126 CPhyloTreeScheme::eTraceColor); 01127 break; 01128 case IPhyGraphicsNode::eSelected: 01129 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 01130 CPhyloTreeScheme::eSelColor); 01131 break; 01132 case IPhyGraphicsNode::eShared: 01133 color = m_SL->SetColor(CPhyloTreeScheme::eLine, 01134 CPhyloTreeScheme::eSharedColor); 01135 break; 01136 } 01137 01138 render_lines->GetColorBuffer().PushBack(color); 01139 render_lines->GetColorBuffer().PushBack(color); 01140 render_lines->GetColorBuffer().PushBack(color); 01141 render_lines->GetColorBuffer().PushBack(color); 01142 01143 m_DS->GetModel().GetEdges().push_back(CTreeGraphicsEdge(pt1, mid_point)); 01144 m_DS->GetModel().GetEdges().push_back(CTreeGraphicsEdge(mid_point, pt2)); 01145 } 01146 01147 01148 void IPhyloTreeRenderer::x_RenderNode(CPhyloTreeNode * node) 01149 { 01150 glPushMatrix(); 01151 01152 double x = (**node).X(); 01153 double y = (**node).Y(); 01154 01155 CRgbaColor color = GetNodeColor(node); 01156 01157 // colors 01158 CRgbaColor col1(color); 01159 CRgbaColor col2(color); 01160 col1.Lighten(0.9f); 01161 01162 TVPUnit node_size = x_NodeSize(node); 01163 // will give good circle - size * approx(2pi) 01164 int num_lines = std::max(2, GetDefaultNodeSize(node))*10; 01165 01166 glTranslated(x, y, 0.0); 01167 01168 if (!m_bZoomablePrimitives) { 01169 glScaled(m_pPane->GetScaleX(), m_pPane->GetScaleY(), 1.0); 01170 } 01171 else { 01172 glScaled(m_pPane->GetScaleX()/m_pPane->GetScaleY(), 1.0, 1.0); 01173 } 01174 01175 glEnable(GL_LINE_SMOOTH); 01176 01177 bool collapsed_node = (**node).GetChildsDisplay() == IPhyGraphicsNode::eHideChilds; 01178 float s = (float)node_size; 01179 01180 glEnable(GL_BLEND); 01181 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 01182 01183 // rendering frame 01184 if (m_DS->GetCurrentNode() == node && node_size > 0) { 01185 CGlAttrGuard AttrGuard(GL_POLYGON_BIT | GL_LINE_BIT); 01186 01187 glLineWidth(1.0f); 01188 glColor3f(0.0f, 0.0f, 0.0f); 01189 glLineStipple(1, 0x0F0F); 01190 glEnable(GL_LINE_STIPPLE); 01191 //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 01192 glDisable(GL_LINE_SMOOTH); 01193 01194 glBegin(GL_LINE_LOOP); 01195 glVertex2d(-node_size, -node_size); 01196 glVertex2d(node_size, -node_size); 01197 glVertex2d(node_size, node_size); 01198 glVertex2d(-node_size, node_size); 01199 glEnd(); 01200 01201 glDisable(GL_LINE_STIPPLE); 01202 } 01203 01204 if (collapsed_node) { 01205 if (m_bHighlightCollapsed) { 01206 float c = GetDefaultNodeSize(node)*2.5f; 01207 CRgbaColor col = col2; 01208 col.SetAlpha(std::min(col.GetAlpha(), 0.5f)); 01209 01210 glBegin(GL_TRIANGLES); 01211 glColor4fv(col.GetColorArray()); glVertex2f(-c, 0); 01212 glColor4fv(col.GetColorArray()); glVertex2f(c, -c); 01213 glColor4fv(col.GetColorArray()); glVertex2f(c, c); 01214 glEnd(); 01215 } 01216 else if (node_size > 0) { 01217 glBegin(GL_TRIANGLE_STRIP); 01218 glColor4fv(col2.GetColorArray()); glVertex2f(-s, 0); 01219 glColor4fv(col2.GetColorArray()); glVertex2f(s, -s); 01220 glColor4fv(col1.GetColorArray()); glVertex2f(0, 0); 01221 glColor4fv(col2.GetColorArray()); glVertex2f(s, s); 01222 glColor4fv(col2.GetColorArray()); glVertex2f(-s, 0); 01223 glEnd(); 01224 } 01225 else { 01226 glBegin(GL_POINTS); 01227 glColor4fv(color.GetColorArray()); 01228 glVertex2d(0, 0); 01229 glEnd(); 01230 } 01231 } 01232 else { 01233 if (node_size > 0) { 01234 glBegin(GL_TRIANGLE_FAN); 01235 glColor4fv(col1.GetColorArray()); 01236 glVertex2d(-s/2.0f, s/2.0f); 01237 01238 float delta = 6.28f/(float)num_lines; 01239 float angle = 0.0f; 01240 for(int i =0; i<=num_lines+1; i++){ 01241 glColor4fv(col2.GetColorArray()); 01242 glVertex2f(s*cosf(angle), s*sinf(angle)); 01243 angle += delta; 01244 } 01245 glEnd(); 01246 } 01247 else { 01248 glBegin(GL_POINTS); 01249 glColor4fv(color.GetColorArray()); 01250 glVertex2d(0, 0); 01251 glEnd(); 01252 } 01253 } 01254 glDisable(GL_BLEND); 01255 01256 // rendering + 01257 /* 01258 if ((**node).GetChildsVisibility()==CPhyloTreeNode::eHideChilds){ 01259 glLineWidth(1.0f); 01260 glColor3f(0.0f, 0.0f, 0.0f); 01261 glBegin(GL_LINES); 01262 glVertex2d(node_ize/2, 0); 01263 glVertex2d(-node_size/2, 0); 01264 glVertex2d(0, -node_size/2); 01265 glVertex2d(0, node_size/2); 01266 glEnd(); 01267 } 01268 */ 01269 01270 float target_alpha = 1.0f; 01271 if (m_SL->GetSelectionVisibility()==CPhyloTreeScheme::eHighlightSelection && 01272 (**node).GetSelectedState()==IPhyGraphicsNode::eNotSelected) { 01273 target_alpha = m_SL->GetNonSelectedAlpha(); 01274 } 01275 01276 if ((**node).GetMarkerColors().size() > 0) { 01277 glEnable(GL_BLEND); 01278 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 01279 01280 size_t color_count = (**node).GetMarkerColors().size(); 01281 float color_countf = (float)color_count; 01282 01283 float marker_size = (**node).GetMarkerSize(); 01284 float s2 = s*marker_size; 01285 if (s2==0.0f) 01286 s2 = marker_size*(float)GetDefaultNodeSize(node); 01287 01288 glBegin(GL_TRIANGLES); 01289 float delta = 6.28f/(float)num_lines; 01290 float angle = 3.14159f/2.0f; 01291 for(int i =0; i<num_lines; i++){ 01292 size_t color_idx = (size_t)((((float)i)/(float)(num_lines)) * color_countf); 01293 color_idx = std::min(color_idx, color_count-1); 01294 CRgbaColor c = (**node).GetMarkerColors()[color_idx]; 01295 c.SetAlpha(c.GetAlpha()*target_alpha); 01296 glColor4fv(c.GetColorArray()); 01297 //glColor4fv((**node).GetMarkerColors()[color_idx].GetColorArray()); 01298 01299 glVertex2d(0.0f, 0.0f); 01300 // add make angle slightly larger to avoid dropout lines. 01301 glVertex2f(s2*cosf(angle+delta+0.005f), s2*sinf(angle+delta+0.005f)); 01302 glVertex2f(s2*cosf(angle), s2*sinf(angle)); 01303 01304 angle += delta; 01305 } 01306 glEnd(); 01307 glDisable(GL_BLEND); 01308 } 01309 01310 glPopMatrix(); 01311 01312 01313 // rendering labels 01314 if (((m_SL->GetLabelVisibility()==CPhyloTreeScheme::eLabelsVisible) || 01315 ((m_SL->GetLabelVisibility()==CPhyloTreeScheme::eLabelsForLeavesOnly)&& 01316 node->IsLeafEx())) && 01317 m_Label.IsVisible(this) ){ 01318 // label fg color 01319 CRgbaColor label_color = GetNodeColor(node, true); 01320 01321 // orientation 01322 bool bLeft = ((**node).GetAngle() < -1.57 || (**node).GetAngle()>1.57); 01323 01324 glEnable(GL_BLEND); 01325 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 01326 01327 // two labels styles 01328 // simple one supports tagging and background (and is never used) 01329 if (true || m_SL->GetLabelStyle()==CPhyloTreeScheme::eSimpleLabels) { 01330 // backround 01331 if ((**node).GetLabelBgColor().size() > 0) { 01332 CRgbaColor c((**node).GetLabelBgColor()); 01333 c.SetAlpha(c.GetAlpha()*target_alpha); 01334 glColorC(c); 01335 01336 CGlRect<double> rect = 01337 m_Label.GetRect(*m_pPane, node, bLeft, this); 01338 01339 glBegin(GL_QUADS); 01340 glVertex2d(rect.Left()-1, rect.Bottom()-1); 01341 glVertex2d(rect.Right()+1, rect.Bottom()-1); 01342 glVertex2d(rect.Right()+1, rect.Top()+1); 01343 glVertex2d(rect.Left()-1, rect.Top()+1); 01344 glEnd(); 01345 } 01346 01347 // drawing label 01348 glColorC(label_color); 01349 m_Label.Render(*m_pPane, node, bLeft, this); 01350 } 01351 else { 01352 01353 CGlRect<double> rect = 01354 m_Label.GetRect(*m_pPane, node, bLeft, this); 01355 01356 double lblWidth = rect.Width() / m_pPane->GetScaleX(); 01357 01358 // adjusting in case of internal node 01359 if (!node->IsLeaf() && !collapsed_node) { 01360 // finding child to get distance, nonlinear also supported this way 01361 CPhyloTreeNode::TNodeList_I fst = node->SubNodeBegin(); 01362 double hole_x = (((***fst).X() - (**node).X()) / m_pPane->GetScaleX())- 01363 node_size*2; 01364 01365 lblWidth = hole_x < lblWidth ? hole_x : lblWidth; 01366 } 01367 01368 if (lblWidth>0) { 01369 // drawing label 01370 glColorC(label_color); 01371 m_Label.Render(*m_pPane, node, bLeft, this); 01372 } 01373 } 01374 01375 glDisable(GL_BLEND); 01376 } 01377 01378 glDisable(GL_LINE_SMOOTH); 01379 } 01380 01381 01382 void IPhyloTreeRenderer::x_RenderNodeVbo(CPhyloTreeNode* node, 01383 CGlVboNode* render_nodes) 01384 { 01385 CVect2<float> node_pos( (**node).XY() ); 01386 01387 CRgbaColor color = GetNodeColor(node); 01388 01389 // colors 01390 CRgbaColor col1(color); 01391 CRgbaColor col2(color); 01392 col1.Lighten(0.9f); 01393 01394 // Default node size 01395 TVPUnit node_size = (TVPUnit)m_SL->SetSize(CPhyloTreeScheme::eNodeSize); 01396 01397 // will give good circle - size * approx(2pi) 01398 int num_lines = std::max(2, GetDefaultNodeSize(node))*10; 01399 01400 bool collapsed_node = (**node).GetChildsDisplay() == IPhyGraphicsNode::eHideChilds; 01401 float s = (float)node_size; 01402 01403 TVPUnit this_node_size = GetDefaultNodeSize(node); 01404 01405 if (collapsed_node) { 01406 if (m_bHighlightCollapsed) { 01407 //float c = GetDefaultNodeSize(node)*2.5f; 01408 CRgbaColor col = col2; 01409 col.SetAlpha(std::min(col.GetAlpha(), 0.5f)); 01410 01411 m_DS->GetModel().GetCollapsedNode()->GetColors().push_back(col); 01412 m_DS->GetModel().GetCollapsedNode()->GetColors().push_back(col); 01413 m_DS->GetModel().GetCollapsedNode()->GetColors().push_back(col); 01414 m_DS->GetModel().GetCollapsedNode()->GetPositions().push_back(node_pos); 01415 m_DS->GetModel().GetCollapsedNode()->GetSizes().push_back(GetDefaultNodeSize(node)*2.5f); 01416 } 01417 else { 01418 m_DS->GetModel().GetCollapsedNode()->GetColors().push_back(col2); 01419 m_DS->GetModel().GetCollapsedNode()->GetColors().push_back(col2); 01420 m_DS->GetModel().GetCollapsedNode()->GetColors().push_back(col2); 01421 m_DS->GetModel().GetCollapsedNode()->GetPositions().push_back(node_pos); 01422 m_DS->GetModel().GetCollapsedNode()->GetSizes().push_back(s); 01423 } 01424 } 01425 else { 01426 float delta = (float)((M_PI*2.0)/(double)num_lines); 01427 float angle = 0.0f; 01428 01429 //Use triangle fan for efficiency 01430 CTreeTriFanNode* node_fan = m_DS->GetModel().GetTriFanNode(); 01431 01432 if (node_fan->GetVertexBuffer2D().GetActiveBufferSize() == 0) { 01433 vector<CVect2<float> > tri_verts; 01434 vector<CRgbaColor> tri_colors; 01435 01436 CRgbaColor center_color(1.0f, 1.0f, 1.0f, 1.0f); 01437 CRgbaColor edge_color(1.0f, 1.0f, 1.0f, 0.0f); 01438 01439 CVect2<float> first_pt(cosf(angle), sinf(angle)); 01440 CVect2<float> current_pt; 01441 01442 // Add center point 01443 tri_verts.push_back(CVect2<float>(-1.0f/2.0f, 1.0f/2.0f)); 01444 tri_colors.push_back(center_color); 01445 01446 // Add first point on circle 01447 tri_verts.push_back(first_pt); 01448 tri_colors.push_back(edge_color); 01449 01450 for (int i=0; i<num_lines; i++) { 01451 angle += delta; 01452 01453 // End exactly where we start to avoid artifacts 01454 if (i == num_lines-1) { 01455 current_pt = first_pt; 01456 } 01457 else { 01458 current_pt.X() = cosf(angle); 01459 current_pt.Y() = sinf(angle); 01460 } 01461 01462 tri_verts.push_back(current_pt); 01463 tri_colors.push_back(edge_color); 01464 } 01465 01466 // If this is the first time, the buffer will be allocated, otherwise 01467 // it'll try to re-use. (but this is a small buffer anyway...) 01468 node_fan->GetVertexBuffer2D().BufferDataResize(tri_verts.size(), 01469 &tri_verts[0], 01470 GL_DYNAMIC_DRAW); 01471 node_fan->GetColorBuffer().BufferDataResize(tri_colors.size(), 01472 &tri_colors[0], 01473 GL_DYNAMIC_DRAW); 01474 node_fan->SetNodeColors(render_nodes->GetColorBuffer().GetBuffer()); 01475 node_fan->ClearPositions(); 01476 node_fan->GetState().Disable(GL_BLEND); 01477 01478 CGlVboNode* node_fan_pass2 = m_DS->GetModel().GetTriFanNodePass2(); 01479 node_fan_pass2->GetVertexBuffer2D().SetBuffer(node_fan->GetVertexBuffer2D().GetBuffer()); 01480 node_fan_pass2->ClearPositions(); 01481 } 01482 01483 render_nodes->GetColorBuffer().PushBack(color); 01484 } 01485 01486 01487 if ((**node).GetMarkerColors().size() > 0) { 01488 m_DS->GetModel().GetMarkerNode()->GetNodes().push_back(node); 01489 } 01490 01491 if ((**node).GetBoundedDisplay()) { 01492 m_DS->GetModel().GetBoundaryNode()->GetNodes().push_back(node); 01493 } 01494 01495 // rendering labels 01496 CRgbaColor label_color = GetNodeColor(node, true); 01497 01498 // Get the label and save it with the label manager object 01499 CVect2<float> so(0.0, 0.0); 01500 CVect2<float> po(0.0, 0.0); 01501 CVect2<float> text_size(0.0, 0.0); 01502 01503 CRgbaColor c = color; 01504 c.Lighten(1.0f - c.GetAlpha()); 01505 01506 string label_txt; 01507 if (((m_SL->GetLabelVisibility()==CPhyloTreeScheme::eLabelsVisible) || 01508 ((m_SL->GetLabelVisibility()==CPhyloTreeScheme::eLabelsForLeavesOnly)&& 01509 node->IsLeafEx()))) { 01510 01511 label_txt = m_Label.x_GetLabel(node); 01512 text_size = m_Label.GetSize(node); 01513 m_Label.GetNodeLabelOffset(node, this, so, po); 01514 CGlRect<float> r(po.X(), po.Y(), po.X() + text_size.X(), po.Y() + text_size.Y()); 01515 01516 // in boundary calcs the node position is added back in. otherwise same?? 01517 (*node)->SetLabelCoords(so, r); 01518 } 01519 01520 CTreeGraphicsNode* label = new CTreeGraphicsNode( 01521 node, 01522 c, 01523 label_color, 01524 label_txt, 01525 (float)this_node_size, 01526 &m_SL->GetFont()); 01527 01528 m_DS->GetModel().GetNodes().push_back(label); 01529 } 01530 01531 01532 void IPhyloTreeRenderer::x_RenderBoundary(CPhyloTreeNode * node) 01533 { 01534 if ((**node).GetBoundedDisplay() == IPhyGraphicsNode::eBounded) { 01535 float alpha_mod = 1.0f; 01536 01537 if ((**node).GetSelectedState()!=IPhyGraphicsNode::eSelected) { 01538 if (m_SL->GetSelectionVisibility() == 01539 CPhyloTreeScheme::eHighlightSelection) { 01540 alpha_mod = m_SL->GetNonSelectedAlpha(); 01541 } 01542 } 01543 01544 CVect2<float> scale(m_pPane->GetScaleX(), m_pPane->GetScaleY()); 01545 (**node).GetSubtreeBoundary()->RenderBoundary(scale, alpha_mod); 01546 } 01547 } 01548 01549 void IPhyloTreeRenderer::x_ComputeNodeBoundary(CPhyloTreeNode* node, 01550 bool compute_boundary, 01551 CBoundaryPoints& boundary_pts, 01552 const string& layout_type) 01553 { 01554 CVect2<float> node_pos((**node).XY()); 01555 01556 // Get pixel rectangle for label 01557 if ( (**node).GetLabel() != "") { 01558 CVect2<float> po, so; 01559 CVect2<float> pixel_size = m_Label.GetSize(node); 01560 m_Label.GetNodeLabelOffset(node, this, so, po); 01561 CGlRect<float> r(po.X(), po.Y(), 01562 po.X() + pixel_size.X(), po.Y() + pixel_size.Y()); 01563 01564 // Scalable offset (so) currently 0 for non-scalable node geom (which it always is...) 01565 (**node).SetLabelCoords(so, r); 01566 } 01567 01568 if (compute_boundary) { 01569 boundary_pts.AddBoundedPoint(node_pos); 01570 01571 // Get pixel rectangle for label 01572 if ( !(**node).GetLabel().empty()) { 01573 boundary_pts.AddPixelRect((**node).GetLabelPos() + node_pos, 01574 (**node).GetLabelRect()); 01575 } 01576 01577 if ((**node).GetBoundedDisplay() == IPhyGraphicsNode::eBounded) { 01578 CVect2<float> scale(m_pPane->GetScaleX(), m_pPane->GetScaleY()); 01579 (**node).GetSubtreeBoundary()->ComputeShapes( 01580 boundary_pts, scale, node_pos, layout_type); 01581 } 01582 } 01583 } 01584 01585 01586 void IPhyloTreeRenderer:: x_DrawBoundingAreas(CPhyloTreeNode * node) 01587 { 01588 // Render boundary areas in a breadth-first approach so that bounding 01589 // areas defined for subtrees appear on top of the (larger) bounding areas 01590 // for their parents. 01591 x_RenderBoundary(node); 01592 01593 // draw childs 01594 for(CPhyloTreeNode::TNodeList_I it = node->SubNodeBeginEx(); 01595 it != node->SubNodeEndEx(); it++ ) { 01596 x_DrawBoundingAreas(static_cast<CPhyloTreeNode*>(*it)); 01597 } 01598 } 01599 01600 01601 CPhyloTreeNode * IPhyloTreeRenderer::x_TestForNode(CPhyloTreeNode * node, wxPoint pos) 01602 { 01603 #ifdef BUFFERED_RENDER 01604 wxUnusedVar(node); 01605 return x_TestForNodeBuffered(pos); 01606 #else 01607 return x_TestForNodeRecursive(node, pos); 01608 #endif 01609 } 01610 01611 CPhyloTreeNode * IPhyloTreeRenderer::x_TestForNodeBuffered(wxPoint pos) 01612 { 01613 CPhyloTreeNode* sel_node = NULL; 01614 01615 float x = (float)m_pPane->UnProjectX(pos.x); 01616 float y = (float)m_pPane->UnProjectY(m_pHost->HMGH_GetVPPosByY(pos.y)); 01617 01618 if (m_pPane) { 01619 sel_node = m_DS->GetModel().GetCollisionData().TestForNode(x, y); 01620 } 01621 01622 return sel_node; 01623 } 01624 01625 CPhyloTreeNode * IPhyloTreeRenderer::x_TestForNodeRecursive(CPhyloTreeNode * node, wxPoint pos) 01626 { 01627 01628 CPhyloTreeNode* selNode = NULL; 01629 01630 if (m_pPane && node != NULL) { 01631 TVPUnit node_size = x_NodeSize(node); 01632 01633 01634 double hit_x = node_size * 2.0 * 01635 (IsPrimitivesScalable()?1:m_pPane->GetScaleX()); 01636 01637 double hit_y = node_size * 2.0 * 01638 (IsPrimitivesScalable()?1:m_pPane->GetScaleY()); 01639 01640 double x1 = m_pPane->UnProjectX(pos.x)-hit_x; 01641 double y1 = m_pPane->UnProjectY(m_pHost->HMGH_GetVPPosByY(pos.y))+hit_y; 01642 double x2 = m_pPane->UnProjectX(pos.x)+hit_x; 01643 double y2 = m_pPane->UnProjectY(m_pHost->HMGH_GetVPPosByY(pos.y))-hit_y; 01644 01645 if (((**node).X() >= x1 && (**node).X() <=x2)&& 01646 ((**node).Y() >= y2 && (**node).Y() <=y1)){ 01647 selNode = node; 01648 } 01649 01650 if (!node->IsLeaf()) { 01651 for(CPhyloTreeNode::TNodeList_I it = node->SubNodeBeginEx(); 01652 it != node->SubNodeEndEx(); it++ ) { 01653 if (!selNode) selNode = x_TestForNode(static_cast<CPhyloTreeNode*>(*it), pos); 01654 } 01655 } 01656 } 01657 01658 return selNode; 01659 } 01660 01661 01662 bool IPhyloTreeRenderer::x_SelectByPoint(CPhyloTreeNode * node, bool toggle) 01663 { 01664 if (m_DS == NULL) 01665 return false; 01666 01667 CPhyloTreeNode * selNode = x_TestForNode(node, m_StartPoint); 01668 if (selNode) { 01669 if (toggle) 01670 m_DS->SetSelection(selNode, !((**selNode).GetSelectedState()==IPhyGraphicsNode::eSelected), true); 01671 else 01672 m_DS->SetSelection(selNode, IPhyGraphicsNode::eSelected, true); 01673 01674 vector<int>::iterator it = find(m_SelectedIDs.begin(), 01675 m_SelectedIDs.end(), 01676 (int)(**selNode).GetId()); 01677 01678 if ((**selNode).GetSelectedState()==IPhyGraphicsNode::eSelected) { 01679 if (it==m_SelectedIDs.end()){ 01680 m_SelectedIDs.push_back((**selNode).GetId()); 01681 } 01682 } 01683 else { 01684 if (it!=m_SelectedIDs.end()){ 01685 m_SelectedIDs.erase(it); 01686 } 01687 } 01688 } 01689 return (selNode!=NULL); 01690 } 01691 01692 bool IPhyloTreeRenderer::x_SelectByRect(CPhyloTreeNode * node, bool toggle) 01693 { 01694 #ifdef BUFFERED_RENDER 01695 // For now use tree-recursive select in toggle mode since selection 01696 // has to occur (in this case) in tree-recursive order to get the 01697 // correct result (because selecting/de-selecting a node will select/ 01698 // de-select parents and children too) 01699 if (toggle) { 01700 x_SelectByRectRecursive(node, toggle); 01701 return true; 01702 } 01703 else { 01704 return x_SelectByRectBuffered(toggle); 01705 } 01706 #else 01707 x_SelectByRectRecursive(node, toggle); 01708 return true; 01709 #endif 01710 } 01711 01712 void IPhyloTreeRenderer::x_SelectByRectRecursive(CPhyloTreeNode * node, bool toggle) 01713 { 01714 float x1 = (float) 01715 m_pPane->UnProjectX(m_StartPoint.x); 01716 float y1 = (float) 01717 m_pPane->UnProjectY(m_pHost->HMGH_GetVPPosByY(m_StartPoint.y)); 01718 float x2 = (float) 01719 m_pPane->UnProjectX(m_DragPoint.x); 01720 float y2 = (float) 01721 m_pPane->UnProjectY(m_pHost->HMGH_GetVPPosByY(m_DragPoint.y)); 01722 01723 bool bSel; 01724 if (toggle) 01725 bSel = !((**node).GetSelectedState()==IPhyGraphicsNode::eSelected); 01726 else 01727 bSel = true; 01728 01729 if (m_DS == NULL) 01730 return; 01731 01732 if (x1 > x2) swap(x1, x2); 01733 if (y2 > y1) swap(y1, y2); 01734 01735 if (((**node).X() >= x1 && (**node).X() <=x2)&& 01736 ((**node).Y() >= y2 && (**node).Y() <=y1)){ 01737 if (toggle) 01738 bSel = !((**node).GetSelectedState()==IPhyGraphicsNode::eSelected); 01739 else 01740 bSel = true; 01741 01742 01743 vector<int>::iterator it = find(m_SelectedIDs.begin(), 01744 m_SelectedIDs.end(), 01745 (int)(**node).GetId()); 01746 m_DS->SetSelection(node, bSel, true); 01747 if (bSel) { 01748 if (it==m_SelectedIDs.end()){ 01749 m_SelectedIDs.push_back((**node).GetId()); 01750 } 01751 } 01752 else { 01753 if (it!=m_SelectedIDs.end()){ 01754 m_SelectedIDs.erase(it); 01755 } 01756 01757 } 01758 // do not descend to childs for selected node 01759 // they will be selected automatically by m_DS->SetSelection(,,true,true) 01760 01761 } else if (!node->IsLeaf()) { 01762 for(CPhyloTreeNode::TNodeList_I it = node->SubNodeBegin(); 01763 it != node->SubNodeEnd(); it++ ) { 01764 x_SelectByRectRecursive(static_cast<CPhyloTreeNode*>(*it), toggle); 01765 } 01766 } 01767 } 01768 01769 bool IPhyloTreeRenderer::x_SelectByRectBuffered(bool toggle) 01770 { 01771 if (m_DS == NULL || m_pPane == NULL) 01772 return false; 01773 01774 float x1 = (float) 01775 m_pPane->UnProjectX(m_StartPoint.x); 01776 float y1 = (float) 01777 m_pPane->UnProjectY(m_pHost->HMGH_GetVPPosByY(m_StartPoint.y)); 01778 float x2 = (float) 01779 m_pPane->UnProjectX(m_DragPoint.x); 01780 float y2 = (float) 01781 m_pPane->UnProjectY(m_pHost->HMGH_GetVPPosByY(m_DragPoint.y)); 01782 01783 01784 if (x1 > x2) swap(x1, x2); 01785 if (y1 > y2) swap(y1, y2); 01786 01787 vector<CPhyloTreeNode*> selected; 01788 01789 selected = m_DS->GetModel().GetCollisionData().SelectNodes(x1, y1, x2, y2); 01790 01791 for (size_t i=0; i<selected.size(); ++i) { 01792 CPhyloTreeNode* node = selected[i]; 01793 01794 bool sel; 01795 if (toggle) 01796 sel = !((**node).GetSelectedState()==IPhyGraphicsNode::eSelected); 01797 else 01798 sel = true; 01799 01800 vector<int>::iterator it = find(m_SelectedIDs.begin(), 01801 m_SelectedIDs.end(), 01802 (int)(**node).GetId()); 01803 m_DS->SetSelection(node, sel, true); 01804 if (sel) { 01805 if (it==m_SelectedIDs.end()){ 01806 m_SelectedIDs.push_back((**node).GetId()); 01807 } 01808 } 01809 else { 01810 if (it!=m_SelectedIDs.end()){ 01811 m_SelectedIDs.erase(it); 01812 } 01813 } 01814 } 01815 01816 return (selected.size() > 0); 01817 } 01818 01819 01820 void IPhyloTreeRenderer::UpdateDataSource(CPhyloTreeDataSource& ds, CGlPane& p) 01821 { 01822 m_pPane = &p; 01823 m_DS = &ds; 01824 } 01825 01826 void IPhyloTreeRenderer::Layout(CPhyloTreeDataSource& ds, CGlPane& p) 01827 { 01828 m_pPane = &p; 01829 m_DS = &ds; 01830 x_Layout(ds); 01831 01832 // Mark the fact that we have a valid layout, which Render function can check. 01833 // Only really need this for first time, since it may not be possible to do 01834 // layout prior to first call to Render(). 01835 m_ValidLayout = eValidLayout; 01836 01837 // New layout establised. (flag is for swtiching between types of layout, 01838 // not updates to current layout like hide/show node.) 01839 m_SwitchedLayout = false; 01840 } 01841 01842 void IPhyloTreeRenderer::x_FindBestSize(CGlPane& pane, 01843 std::vector<ProjectionElement>& xexts, 01844 std::vector<ProjectionElement>& yexts, 01845 TModelRect& limits) 01846 { 01847 float goal = ((float)(limits.Left()-limits.Right()))/1e04f; 01848 goal = std::min(goal, ((float)(limits.Top()-limits.Bottom()))/1e04f); 01849 //float goal = 0.1f; 01850 float delta = 1e10f; 01851 01852 float viewport_x, viewport_y; 01853 float vp_width = (float)pane.GetViewport().Width(); 01854 float vp_height = (float)pane.GetViewport().Height(); 01855 float left = limits.Left(); 01856 float right = limits.Right(); 01857 float top = limits.Top(); 01858 float bottom = limits.Bottom(); 01859 01860 if (left==right) { 01861 left -= 1.0f; 01862 right += 1.0f; 01863 } 01864 if (top==bottom) { 01865 bottom -= 1.0f; 01866 top += 1.0f; 01867 } 01868 01869 int count = 0; 01870 01871 while (delta > goal && ++count < 500) { 01872 01873 // Given current limits (which are used to create the orthographic projection), 01874 // find the max/min x and y viewport positions to which the labels will project. 01875 // This allows us to iteratively search for the optimum limits for which the 01876 // tree will exactly occupy the given viewport. 01877 float viewport_maxx = -1e10f; 01878 float viewport_minx = 1e10f; 01879 float viewport_maxy = -1e10f; 01880 float viewport_miny = 1e10f; 01881 01882 // Viewport projection limits for the nodes of the tree given the 01883 // the current right/left/bottom/top for the visible rect 01884 float node_maxx = -1e10f; 01885 float node_minx = 1e10f; 01886 float node_maxy = -1e10f; 01887 float node_miny = 1e10f; 01888 01889 // We set the limits of the viewing are differently based on the zoom 01890 // behavior. We want to make sure that when the user zooms in 01891 // to the point labels appear, that there is room for the full label 01892 // within the viewing limits. This means that if we are zooming in y 01893 // only, there must be space for the full labels at max. zoom level, 01894 // whereas for zooming in x or proportionally, we don't need the full 01895 // label space until we have zoomed in to the point they are visible. 01896 float label_zoom = 1.0f; 01897 01898 if (m_SL->GetZoomBehavior() == CPhyloTreeScheme::eZoomX || 01899 m_SL->GetZoomBehavior() == CPhyloTreeScheme::eZoomXY) { 01900 // Get the vertical distance between nodes for current projection 01901 // (in pixels) at maximum resolution (visible-rect==model-limits-rect) 01902 float scaley = (top-bottom)/(float)m_pPane->GetViewport().Height(); 01903 float distance_between_nodes = (float)x_DistanceBetweenNodes((TModelUnit)scaley); 01904 01905 // Determine how much zoom (reduction in visible rect), if any, is 01906 // required to get the distance between nodes to a value that allows 01907 // labels to be drawn without overlapping each other (vertically); 01908 if (m_Label.GetCurrFont() != NULL) { 01909 float height = (float)m_Label.GetMinVerticalSeparation(); 01910 label_zoom = std::min(1.0f, distance_between_nodes/height); 01911 } 01912 } 01913 else if (m_SL->GetZoomBehavior() == CPhyloTreeScheme::eZoomY) { 01914 // scale room for label width based on current zoom in horizontal 01915 label_zoom = 1.0f* (((float)pane.GetVisibleRect().Width())/ 01916 (float)pane.GetModelLimitsRect().Width()); 01917 } 01918 01919 01920 // Check the projection in each direction by computing the pixel 01921 // offset as it will computed through the orthographic 01922 // projection-to-viewport mapping. Add to this value the pixel 01923 // offset of the label, "offset". 01924 // 01925 // w = viewport width, h = viewport height, x,y = node position, 01926 // l,r,t,b = limits(left, right, top, bottom) 01927 // 01928 // (scale model to viewport)*(tranlate model to (0,0))*(model point) 01929 // -- -- -- -- -- -- 01930 // | w/(r-l) 0 0 | | 1 0 -l | | x | 01931 // | | | | | | 01932 // | 0 h/(t-b) 0 | | 0 1 -b | | y | 01933 // | | | | | | 01934 // | 0 0 1 | | 0 0 1 | | 1 | 01935 // -- -- -- -- -- -- 01936 for (size_t i=0; i<xexts.size(); ++i) { 01937 // Get position node projects onto viewport: 01938 viewport_x = 01939 (xexts[i].m_NodeCoord*vp_width - left*vp_width)/(right-left); 01940 01941 // In this first (initial) loop over the extents, just compute the 01942 // region (minx..maxx) occupied by the nodes projected onto the viewport. 01943 node_maxx = std::max(viewport_x, node_maxx); 01944 node_minx = std::min(viewport_x, node_minx); 01945 01946 xexts[i].m_ProjectionCood = viewport_x; 01947 } 01948 01949 viewport_maxx = node_maxx; 01950 viewport_minx = node_minx; 01951 01952 // Now that we have the extent that the nodes project onto the viewport 01953 // (node_minx..node_maxx), determine how the viewport needs to be adjusted 01954 // to accomodate pixel-dimensioned (label) attachements to the nodes. 01955 for (size_t i=0; i<xexts.size(); ++i) { 01956 // Get position node projects onto viewport: 01957 viewport_x = xexts[i].m_ProjectionCood; 01958 01959 // Pixl extent (which come from labels) attached to the current node 01960 float offsetx = xexts[i].m_PixelOffset*label_zoom; 01961 01962 // If we do have a pixel attachment it may go either right (+) or left(-). 01963 // Figure out how many pixels of that extent (label) go beyond the right 01964 // or left edge of the overall tree (if the label is on a right-most or 01965 // left-most tree node, that value is 0, otherwise it is the length of the label 01966 // in pixels minus the extent that those pixels overlap with the range 01967 // (node_minx, node_maxx) 01968 if (std::abs(offsetx) > 0.0f) { 01969 float overlap_x; 01970 01971 // overlap_x is the extent of the label that overlaps (in x) 01972 // the tree (node/edges) extent. 01973 if (offsetx > 0.0f) 01974 overlap_x = (node_maxx - viewport_x); 01975 else 01976 overlap_x = (viewport_x - node_minx); 01977 01978 // non_overlap_x is extent of pixel attachment (label) that goes 01979 // beyond the rightmost or leftmost node in the tree 01980 float non_overlap_x = std::max(std::abs(offsetx) - overlap_x, 0.0f); 01981 01982 // Portion of viewport occupied by tree (node_maxx-node_minx) must be 01983 // > or = maximum amount of viewport alloted for labels (non_overlap_x) 01984 float usable_offsetx = std::min(overlap_x, std::abs(offsetx)) + 01985 std::min(non_overlap_x, vp_width*m_LabelViewPct);//node_maxx-node_minx); 01986 offsetx = (offsetx >= 0.0f) ? usable_offsetx : -usable_offsetx; 01987 } 01988 01989 // Add the pixel offset to the overall viewport size. 01990 if (offsetx != 0.0f) { 01991 viewport_x += offsetx; 01992 viewport_maxx = std::max(viewport_x, viewport_maxx); 01993 viewport_minx = std::min(viewport_x, viewport_minx); 01994 } 01995 } 01996 01997 // In y dimension there currently are no (large) pixel attachements 01998 // since all labels are horizontal, but we use the same technique to 01999 // support future requirements. 02000 for (size_t i=0; i<yexts.size(); ++i) { 02001 // Get position node projects onto viewport: 02002 viewport_y = 02003 (yexts[i].m_NodeCoord*vp_height - bottom*vp_height)/(top-bottom); 02004 viewport_maxy = std::max(viewport_y, viewport_maxy); 02005 viewport_miny = std::min(viewport_y, viewport_miny); 02006 02007 // In this first (initial) loop over the extents, just compute the 02008 // region (miny..maxy) occupied by the nodes projected onto the viewport. 02009 node_maxy = std::max(viewport_y, node_maxy); 02010 node_miny = std::min(viewport_y, node_miny); 02011 02012 yexts[i].m_ProjectionCood = viewport_y; 02013 } 02014 02015 viewport_maxy = node_maxy; 02016 viewport_miny = node_miny; 02017 02018 for (size_t i=0; i<yexts.size(); ++i) { 02019 // Get position node projects onto viewport: 02020 viewport_y = yexts[i].m_ProjectionCood; 02021 02022 // Pixl extent (which come from labels) attached to the current node 02023 float offsety = yexts[i].m_PixelOffset*label_zoom; 02024 02025 if (std::abs(offsety) > 0.0f) { 02026 float overlap_y; 02027 02028 if (offsety > 0.0f) 02029 overlap_y = (node_maxy - viewport_y); 02030 else 02031 overlap_y = (viewport_y - node_miny); 02032 02033 float non_overlap_y = std::max(std::abs(offsety) - overlap_y, 0.0f); 02034 02035 // Portion of viewport occupied by tree (node_maxy-node_miny) must be 02036 // > or = maximum amount of viewport alloted for labels (non_overlap_y) 02037 float usable_offsety = std::min(overlap_y, std::abs(offsety)) + 02038 std::min(non_overlap_y, vp_height*m_LabelViewPct); //node_maxy-node_miny); 02039 offsety = (offsety >= 0.0f) ? usable_offsety : -usable_offsety; 02040 } 02041 02042 // Add the pixel offset to the overall viewport size. 02043 if (offsety != 0.0f) { 02044 viewport_y += offsety; 02045 viewport_maxy = std::max(viewport_y, viewport_maxy); 02046 viewport_miny = std::min(viewport_y, viewport_miny); 02047 } 02048 } 02049 02050 ////////////////////////////////////////////////////////////////////////////// 02051 // For the viewport directions that had projections by labels, re-estimate the 02052 // best size for the limits in that dimension. 02053 ////////////////////////////////////////////////////////////////////////////// 02054 delta = 0.0f; 02055 // Pixels could go in both directions (min and max) so we 02056 // just double 02057 if (viewport_maxx != -1e10f) { 02058 float rd = viewport_maxx - (float)pane.GetViewport().Right(); 02059 if (fabs(rd) > 0.0f) { 02060 delta = std::max(delta, (float)fabs(rd)); 02061 float off = (rd/vp_width)*(right-left); 02062 right += off; 02063 } 02064 } 02065 02066 if (viewport_minx != 1e10f) { 02067 float ld = viewport_minx - (float)pane.GetViewport().Left(); 02068 if (fabs(ld) > 0.0f) { 02069 delta = std::max(delta, (float)fabs(ld)); 02070 float off = (ld/vp_width)*(right-left); 02071 left += off; 02072 } 02073 } 02074 02075 if ( viewport_maxy != -1e10f) { 02076 float td = viewport_maxy - (float)pane.GetViewport().Top(); 02077 if (fabs(td) > 0.0f) { 02078 delta = std::max(delta, (float)fabs(td)); 02079 float off = (td/vp_height)*(top-bottom); 02080 top += off; 02081 } 02082 } 02083 02084 if ( viewport_miny != 1e10f) { 02085 float bd = viewport_miny - (float)pane.GetViewport().Bottom(); 02086 if (fabs(bd) > 0.0f) { 02087 delta = std::max(delta, (float)fabs(bd)); 02088 float off = (bd/vp_height)*(top-bottom); 02089 bottom += off; 02090 } 02091 } 02092 } 02093 02094 limits.SetRight(right); 02095 limits.SetLeft(left); 02096 limits.SetBottom(bottom); 02097 limits.SetTop(top); 02098 } 02099 02100 void IPhyloTreeRenderer::CExtentDimension::UpdateExtent(float node_coord, 02101 float pixel_offset, 02102 bool has_label) 02103 { 02104 02105 // If there was no label, the new coordinate can only be inserted 02106 // at the head of the list. It will replace the first element in 02107 // the list if that element also does not have a label (pixel_offset 02108 // is still non-zero however, because all elements have a pixel margin 02109 // around themseleves) 02110 if ( !has_label ) { 02111 // List empty, just add 02112 if (m_Extents.size() == 0) { 02113 m_Extents.insert(m_Extents.begin(), 02114 ProjectionElement(node_coord, pixel_offset)); 02115 } 02116 // List not empty - decide whether or not the first elemeent will 02117 // be replaced 02118 else if (node_coord > m_Extents[0].m_NodeCoord) { 02119 if (m_Extents[0].m_PixelOffset <= pixel_offset) { 02120 m_Extents[0] = ProjectionElement(node_coord, pixel_offset); 02121 } 02122 else { 02123 m_Extents.insert(m_Extents.begin(), 02124 ProjectionElement(node_coord, pixel_offset)); 02125 } 02126 } 02127 } 02128 // Current element has a label. Determine if it should be inserted, 02129 // where to insert it, whether it will overwrite an existing entry, 02130 // and whether elements in the list with smaller coordnates need to 02131 // be erased since they no longer can effect max. projection extent 02132 else { 02133 float max_pixel_offset = -FLT_MAX; 02134 bool resolved = false; 02135 int erase_from = -1; 02136 02137 // Iterate over list until we find where to insert (list is generally 02138 // short (1-10 elements), so a binary search could be longer) 02139 for (size_t ext_idx=0; ext_idx<m_Extents.size() && !resolved; ++ext_idx) { 02140 max_pixel_offset = std::max(max_pixel_offset, m_Extents[ext_idx].m_PixelOffset); 02141 02142 // Coord > than current element in list, so insert it 02143 if (node_coord > m_Extents[ext_idx].m_NodeCoord) { 02144 if (pixel_offset >= max_pixel_offset) { 02145 m_Extents[ext_idx].m_PixelOffset = pixel_offset; 02146 m_Extents[ext_idx].m_NodeCoord = node_coord; 02147 //m_Extents[ext_idx].m_Text = label; 02148 erase_from = ext_idx; 02149 } 02150 else { 02151 ProjectionElement ne(node_coord, pixel_offset); 02152 //ne.m_Text = label; 02153 m_Extents.insert(m_Extents.begin() + ext_idx, ne); 02154 } 02155 resolved = true; 02156 } 02157 // Coord == coord of current list element. Replace the current 02158 // element with the new IF new element has larger pixel offset 02159 else if (node_coord == m_Extents[ext_idx].m_NodeCoord) { 02160 if (pixel_offset > max_pixel_offset) { 02161 //ne.m_Text = label; 02162 m_Extents[ext_idx].m_PixelOffset = pixel_offset; 02163 erase_from = ext_idx; 02164 } 02165 resolved = true; 02166 } 02167 //node_coord < m_Extents[ext_idx].m_NodeCoord 02168 else { 02169 if (pixel_offset <= max_pixel_offset) { 02170 resolved = true; 02171 } 02172 } 02173 } 02174 02175 // Coordinate was smaller than any element in the list, 02176 // but the label (pixel offset) was larger than any element 02177 // in the list so insert the element at the end 02178 if (!resolved && pixel_offset > max_pixel_offset) { 02179 ProjectionElement ne(node_coord, pixel_offset); 02180 //ne.m_Text = label; 02181 m_Extents.push_back(ne); 02182 } 02183 02184 // Element was inserted into the list and its label (pixel offset) was 02185 // larger than all of the elements up to its insertion point. Now erase 02186 // any elements after the newly inserted element that have a pixel offset 02187 // that is smaller than the pixel offset of the inserted element since 02188 // they will no longer effect the projection size (at any zoom level) 02189 if (erase_from >=0) { 02190 size_t erase_to = erase_from + 1; 02191 for (; erase_to < m_Extents.size(); ++erase_to) 02192 if (pixel_offset < m_Extents[erase_to].m_PixelOffset) 02193 break; 02194 02195 if (erase_to > (size_t)(erase_from + 1)) 02196 m_Extents.erase(m_Extents.begin() +(size_t)( erase_from + 1), 02197 m_Extents.begin() + erase_to); 02198 } 02199 } 02200 } 02201 02202 void IPhyloTreeRenderer::CExtentDimension::DumpExtent(const string& dim) { 02203 for (size_t i=0; i<m_Extents.size(); ++i) { 02204 _TRACE("Value: " << 02205 //m_Extents[i].m_Text << 02206 dim << m_Extents[i].m_NodeCoord << " ) Pix: ( " << 02207 m_Extents[i].m_PixelOffset << " ) "); 02208 } 02209 } 02210 02211 02212 void IPhyloTreeRenderer:: 02213 CProjectionExtents::GetTextExtents(const CPhyloTreeScheme& sl, 02214 CPhyloTreeLabel& labeler, 02215 CGlPane& pane, 02216 CPhyloTreeNode * node, 02217 TModelRect& zoomable_extent) 02218 { 02219 CVect2<float> pos = (**node).XY(); 02220 02221 GLdouble top_margin, bottom_margin, left_margin, right_margin; 02222 sl.GetMargins(left_margin, top_margin, right_margin, bottom_margin); 02223 02224 // The zoomable_extent just holds the x/y extents without considering 02225 // labels. 02226 zoomable_extent.SetLeft(std::min(zoomable_extent.Left(), (double)pos.X())); 02227 zoomable_extent.SetRight(std::max(zoomable_extent.Right(), (double)pos.X())); 02228 zoomable_extent.SetBottom(std::min(zoomable_extent.Bottom(), (double)pos.Y())); 02229 zoomable_extent.SetTop(std::max(zoomable_extent.Top(), (double)pos.Y())); 02230 02231 bool label_empty = (**node).GetLabel().empty() && (*node).Expanded(); 02232 // If no label, add to extent without considering label 02233 if ( label_empty || 02234 sl.GetLabelVisibility() == CPhyloTreeScheme::eLabelsHidden || 02235 (sl.GetLabelVisibility() == CPhyloTreeScheme::eLabelsForLeavesOnly && 02236 !node->IsLeafEx()) ) { 02237 CVect2<float> pos = (**node).XY(); 02238 02239 m_MaxX.UpdateExtent(pos.X(), right_margin, false); 02240 m_MaxY.UpdateExtent(pos.Y(), top_margin, false); 02241 m_MinX.UpdateExtent(-pos.X(), left_margin, false); 02242 m_MinY.UpdateExtent(-pos.Y(), bottom_margin, false); 02243 } 02244 // There is a label - add to each extent based on the labels maximum 02245 // projection distance (in pixels) in that direction 02246 else { 02247 CGlRect<float> label_rect = (**node).GetLabelRect(); 02248 02249 //string label = labeler.x_GetLabel(node); 02250 m_MaxX.UpdateExtent(pos.X(), label_rect.Right() + right_margin, true); 02251 m_MaxY.UpdateExtent(pos.Y(), label_rect.Top() + top_margin, true); 02252 // note that we negate minimum values for x,y so that we can use 02253 // the same function (UpdateExtents()) for all 4 directions 02254 m_MinX.UpdateExtent(-pos.X(), -label_rect.Left() + left_margin, true); 02255 m_MinY.UpdateExtent(-pos.Y(), -label_rect.Bottom() + bottom_margin, true); 02256 } 02257 02258 // Recusively visit all other nodes in the tree 02259 if (!(**node).GetChildsDisplay() == IPhyGraphicsNode::eHideChilds) { 02260 for(CPhyloTreeNode::TNodeList_I it = node->SubNodeBeginEx(); 02261 it != node->SubNodeEndEx(); it++) { 02262 CPhyloTreeNode* subnode = static_cast<CPhyloTreeNode*>(*it); 02263 GetTextExtents(sl, labeler, pane, subnode, zoomable_extent); 02264 } 02265 } 02266 } 02267 02268 void IPhyloTreeRenderer:: 02269 CProjectionExtents::ConsolidateExtents(vector<ProjectionElement>& xexts, 02270 vector<ProjectionElement>& yexts) 02271 { 02272 /// Combine m_MaxX and m_MinX. All coordinates in m_MinX are 02273 /// flipped (negated) so we have to reverse that here. 02274 xexts = m_MaxX.m_Extents; 02275 for (size_t i=0; i<m_MinX.m_Extents.size(); ++i) { 02276 ProjectionElement ne(m_MinX.m_Extents[i]); 02277 ne.m_NodeCoord = -ne.m_NodeCoord; 02278 ne.m_PixelOffset = -ne.m_PixelOffset; 02279 02280 xexts.push_back(ne); 02281 } 02282 02283 /// Combine m_MaxY and m_MinY. All coordinates in m_MinY are 02284 /// flipped (negated) so we have to reverse that here. 02285 yexts = m_MaxY.m_Extents; 02286 for (size_t i=0; i<m_MinY.m_Extents.size(); ++i) { 02287 ProjectionElement ne(m_MinY.m_Extents[i]); 02288 ne.m_NodeCoord = -ne.m_NodeCoord; 02289 ne.m_PixelOffset = -ne.m_PixelOffset; 02290 02291 yexts.push_back(ne); 02292 } 02293 } 02294 02295 void IPhyloTreeRenderer::CProjectionExtents::DumpExtents() { 02296 _TRACE(""); 02297 _TRACE("Dumping + X Dimension:"); 02298 m_MaxX.DumpExtent(" X Pos: ( "); 02299 02300 _TRACE(""); 02301 _TRACE("Dumping + Y Dimension:"); 02302 m_MaxY.DumpExtent(" Y Pos: ( "); 02303 02304 _TRACE(""); 02305 _TRACE("Dumping - X Dimension:"); 02306 m_MinX.DumpExtent(" -X Pos: ( "); 02307 02308 _TRACE(""); 02309 _TRACE("Dumping - Y Dimension:"); 02310 m_MinY.DumpExtent(" -Y Pos: ( "); 02311 } 02312 02313 02314 02315 void IPhyloTreeRenderer::ComputeViewingLimits(CGlPane& pane, 02316 bool viewing_only) 02317 { 02318 GLdouble top, bottom, left, right; 02319 m_SL->GetMargins(left, top, right, bottom); 02320 02321 double scalex = m_bZoomablePrimitives?1.0:pane.GetScaleX(); 02322 double scaley = m_bZoomablePrimitives?1.0:pane.GetScaleY(); 02323 02324 // This adds label size to overall viewing area, but does so incorrectly - too much space 02325 // is added for the label probably because the current (visible) rect already includes 02326 // the label from the previous call to this function? Maybe we could differentiate between 02327 // nodes and edges size and that size + text size by saving the text origins with the nodes 02328 // and edges, and using those to compute (at each change) the size with labels... 02329 02330 TModelRect visible = pane.GetVisibleRect(); 02331 TModelRect limits = pane.GetModelLimitsRect(); 02332 02333 CStopWatch t1; 02334 02335 TModelRect extent_limits; 02336 extent_limits.SetBottom(1e10); 02337 extent_limits.SetLeft(1e10f); 02338 extent_limits.SetRight(-1e10); 02339 extent_limits.SetTop(-1e10); 02340 02341 t1.Start(); 02342 // Collect all points and text that may impact the size required for the viewing area 02343 // (these are nodes and text and the min/max x/y positions. There may be multiple 02344 // elements in each direction since text is fixed size regardless of zoom) 02345 CProjectionExtents extents; 02346 extents.GetTextExtents(m_SL.GetNCObject(), m_Label, pane, m_DS->GetTree(), extent_limits); 02347 02348 // Consolidate the extent data into two vectors - one for min/max x values and one 02349 // for min/max y values. 02350 vector<ProjectionElement> xexts, yexts; 02351 extents.ConsolidateExtents(xexts, yexts); 02352 t1.Stop(); 02353 02354 //extents.DumpExtents(); 02355 02356 //_TRACE("Visible Nodes: " << m_DS->GetVisibleNodes() ); 02357 // If there is only 1 node force a small window 02358 bool too_small = false; 02359 if (m_DS->GetVisibleNodes() == 1) { 02360 extent_limits.Inflate(1.0f, 1.0f); 02361 too_small = true; 02362 } 02363 02364 // Get numerical problems as viewport size goes to 0 so we just use the model 02365 // extent in this (not-visually-usable) case: 02366 if (pane.GetViewport().Height() < 20 || 02367 pane.GetViewport().Width() < 20) { 02368 pane.SetModelLimitsRect(extent_limits); 02369 pane.SetVisibleRect(extent_limits); 02370 return; 02371 } 02372 02373 if (!viewing_only) { 02374 if (!too_small) { 02375 t1.Start(); 02376 x_FindBestSize(pane, xexts, yexts, extent_limits); 02377 // Guard against numerical issues (very small extents - maybe nodes are in a line) 02378 if (extent_limits.Width() < 1.0f) 02379 extent_limits.Inflate(1.0f, 0.0f); 02380 if (extent_limits.Height() < 1.0f) 02381 extent_limits.Inflate(0.0f, 1.0f); 02382 t1.Stop(); 02383 02384 //LOG_POST("Limits1: " << extent_limits.ToString() << " T1: " << t1.Elapsed()); 02385 } 02386 02387 scalex = 1.0f/(pane.GetViewport().Width()/(extent_limits.Right() - extent_limits.Left())); 02388 scaley = 1.0f/(pane.GetViewport().Height()/(extent_limits.Top() - extent_limits.Bottom())); 02389 02390 TModelRect mr(extent_limits.Left() - left * scalex, 02391 extent_limits.Bottom() - bottom * scaley, 02392 extent_limits.Right() + right * scalex, 02393 extent_limits.Top() + top * scaley); 02394 02395 pane.SetModelLimitsRect(mr); 02396 02397 float left_pct = 0.0f; 02398 float right_pct = 0.0f; 02399 float bottom_pct = 0.0f; 02400 float top_pct = 0.0f; 02401 02402 if (limits.Width() > 0.0 && limits.Height() > 0.0) { 02403 left_pct = (visible.Left() - limits.Left())/limits.Width(); 02404 right_pct = (visible.Right() - limits.Left())/limits.Width(); 02405 bottom_pct = (visible.Bottom() - limits.Bottom())/limits.Height(); 02406 top_pct = (visible.Top() - limits.Bottom())/limits.Height(); 02407 } 02408 02409 visible.SetLeft(mr.Left() + mr.Width()*left_pct); 02410 visible.SetRight(mr.Left() + mr.Width()*right_pct); 02411 visible.SetBottom(mr.Bottom() + mr.Height()*bottom_pct); 02412 visible.SetTop(mr.Bottom() + mr.Height()*top_pct); 02413 02414 // Set visible rect to fit inside model limits rect. 02415 pane.SetVisibleRect(visible); 02416 } 02417 02418 BufferedRender(pane, *m_DS); 02419 } 02420 02421 void IPhyloTreeRenderer::BufferedRender(CGlPane& pane, CPhyloTreeDataSource& ds) 02422 { 02423 // Depending on initialization order (for a newly created view) this may 02424 // get called before ComputeViewingLimits has been called for the first time. 02425 // Do not render in that case. 02426 if (m_ValidLayout != eValidLayout) 02427 return; 02428 02429 m_pPane = &pane; 02430 m_DS = &ds; 02431 02432 #ifdef BUFFERED_RENDER 02433 CStopWatch render_total; 02434 render_total.Start(); 02435 02436 CGlUtils::CheckGlError(); 02437 m_DS->GetModel().ClearArrays(); 02438 x_RenderVbo(*m_DS); 02439 CGlUtils::CheckGlError(); 02440 02441 /// For large models, track the resolution to be relative to a 02442 /// full screen of nodes with labels since that is our unit of drawing 02443 float collision_resolution = max(ceilf(sqrtf(m_DS->GetSize())), 02444 ((float)m_DS->GetSize())/400.0f); 02445 m_DS->GetModel().GetCollisionData(). 02446 ResizeGrid( CVect2<float>(pane.GetModelLimitsRect().Left(), pane.GetModelLimitsRect().Bottom()), 02447 CVect2<float>(pane.GetModelLimitsRect().Right(), pane.GetModelLimitsRect().Top()), 02448 collision_resolution ); 02449 02450 m_DS->GetModel().GetCollisionData().Sync(&m_DS->GetModel()); 02451 m_DS->GetModel().SyncBuffers(); 02452 02453 // initiate visibility computation 02454 CVect2<float> scale2d((float)m_pPane->GetScaleX(), (float)m_pPane->GetScaleY()); 02455 m_DS->GetModel().GetCollisionData().UpdateScaled(scale2d); 02456 02457 m_BufferdRenderTime = render_total.Elapsed(); 02458 #endif 02459 } 02460 02461 02462 bool IPhyloTreeRenderer::Render(CGlPane& pane, CPhyloTreeDataSource& ds) 02463 { 02464 02465 m_pPane = &pane; 02466 m_DS = &ds; 02467 02468 #ifdef BUFFERED_RENDER 02469 // In this function we just draw. In something like 'soft update' we 02470 // re-create the model. 02471 02472 02473 float x = m_pPane->GetScaleX(); 02474 float y = m_pPane->GetScaleY(); 02475 02476 CVect3<float> scale(x, y, 0.0f); 02477 CVect2<float> scale2d(x, y); 02478 02479 TVPUnit node_size = x_NodeSize(m_DS->GetTree()); 02480 02481 CGlVboNode* geom = m_DS->GetModel().FindGeomNode("HighlightedCollapsedNode"); 02482 if (geom) { 02483 geom->SetScale(scale); 02484 geom->SetVisible(true); 02485 } 02486 geom = m_DS->GetModel().FindGeomNode("CollapsedNode"); 02487 if (geom) { 02488 geom->SetScale(scale); 02489 geom->SetVisible(node_size > 0); 02490 } 02491 02492 // This doesn't handle nodes that are forced to be <0 in size... 02493 CGlVboNode* points = m_DS->GetModel().FindGeomNode("NodePoints"); 02494 if (points) { 02495 points->SetVisible(node_size <= 0); 02496 } 02497 02498 TModelUnit line_width = 02499 m_SL->SetSize(CPhyloTreeScheme::eLineWidth); 02500 02501 if (DistanceBetweenNodes() < line_width) { 02502 line_width = 1.0; 02503 } 02504 02505 CGlVboNode* edge_node = m_DS->GetModel().FindGeomNode("TreeEdges"); 02506 CGlVboNode* narrow_edge_node = m_DS->GetModel().FindGeomNode("NarrowTreeEdges"); 02507 CGlVboNode* filler_points_node = m_DS->GetModel().FindGeomNode("FillerPoints"); 02508 bool filler_points_visible = filler_points_node->IsVisible(); 02509 02510 if (edge_node != NULL && narrow_edge_node != NULL && filler_points_node != NULL) { 02511 edge_node->GetState().SetLineWidth((float)line_width); 02512 02513 // Turn filler points on/off if they are in use for current rendering (and 02514 // reset state after rendering back to 'on') 02515 if (filler_points_visible) { 02516 filler_points_node->GetState().SetPointSize((float)line_width); 02517 filler_points_node->SetVisible(line_width > 1.0f); 02518 } 02519 02520 narrow_edge_node->GetState().SetLineWidth(1.0f); 02521 narrow_edge_node->SetVisible(line_width > 1.0f); 02522 } 02523 02524 CTreeTriFanNode* node_fan = m_DS->GetModel().GetTriFanNode(); 02525 if (node_fan) { 02526 node_fan->SetScale(scale); 02527 TModelRect r = m_pPane->GetVisibleRect(); 02528 node_fan->SetVisibilityInfo(CGlRect<float>((float)r.Left(), (float)r.Bottom(), (float)r.Right(), (float)r.Top()), 02529 (float)m_SL->SetSize(CPhyloTreeScheme::eNodeSize)); 02530 node_fan->SetVisible(node_size > 0); 02531 02532 CGlVboNode* node_fan_pass2 = m_DS->GetModel().GetTriFanNodePass2(); 02533 if (node_fan_pass2 != NULL) { 02534 node_fan_pass2->SetScale(scale); 02535 node_fan_pass2->SetVisible(node_size > 0); 02536 } 02537 02538 if (( (m_SL->GetLabelVisibility()==CPhyloTreeScheme::eLabelsVisible) || 02539 (m_SL->GetLabelVisibility()==CPhyloTreeScheme::eLabelsForLeavesOnly) ) && 02540 m_Label.IsVisible(this) ){ 02541 02542 node_fan->SetLabelVisibility(true); 02543 } 02544 else { 02545 node_fan->SetLabelVisibility(false); 02546 } 02547 02548 // Determines if labels and associated nodes may be visible (if they are in 02549 // the viewing area). Only needed if the nodes or labels are visible. 02550 if (node_fan->IsVisible() || node_fan->GetLabelVisibility()) { 02551 CVect2<float> s = m_DS->GetModel().GetCollisionData().GetScale(); 02552 if (s.X() != scale2d.X() || s.Y() != scale2d.Y()) 02553 m_DS->GetModel().GetCollisionData().UpdateScaled(scale2d); 02554 m_DS->GetModel().GetCollisionData().UpdateVisibility(pane.GetVisibleRect()); 02555 } 02556 } 02557 02558 02559 //m_DS->GetModel().GetLines().SetWidth(line_width); 02560 //m_DS->GetModel().GetNodes().SetScale(...) 02561 CGlUtils::CheckGlError(); 02562 02563 m_DS->GetModel().Render(pane); 02564 02565 // Render selection box 02566 x_RenderSelection(pane); 02567 x_RenderTooltipHints(pane); 02568 // Render distance scale at bottom of screen 02569 x_RenderScaleMarker(pane); 02570 02571 // Want to turn filler points on/off based on zoom, but their 02572 // visibility also depends on rendering style (spline/non-spline) so 02573 // we want to set them visible again if they were visible before (should 02574 // probably capture this somewhere else...) 02575 if (filler_points_node != NULL) 02576 filler_points_node->SetVisible(filler_points_visible); 02577 02578 #else 02579 x_Render(pane, ds); 02580 x_RenderSelection(pane); 02581 x_RenderTooltipHints(pane); 02582 x_RenderScaleMarker(pane); 02583 #endif 02584 02585 return true; 02586 } 02587 02588 void IPhyloTreeRenderer::RenderForHardcopy(CGlPane& pane, CPhyloTreeDataSource& ds) 02589 { 02590 m_pPane = &pane; 02591 m_DS = &ds; 02592 02593 CRgbaColor bgColor(m_SL->SetColor(CPhyloTreeScheme::eTree, 02594 CPhyloTreeScheme::eBgColor)); 02595 02596 glClearColor(bgColor.GetRed(), bgColor.GetGreen(), 02597 bgColor.GetBlue(), bgColor.GetAlpha()); 02598 02599 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 02600 02601 #ifdef BUFFERED_RENDER 02602 Render(pane, ds); 02603 #else 02604 x_Render(pane, ds); 02605 #endif 02606 } 02607 02608 void IPhyloTreeRenderer::Redraw(void) 02609 { 02610 CRgbaColor bgColor(m_SL->SetColor(CPhyloTreeScheme::eTree, 02611 CPhyloTreeScheme::eBgColor)); 02612 02613 glClearColor(bgColor.GetRed(), bgColor.GetGreen(), 02614 bgColor.GetBlue(), bgColor.GetAlpha()); 02615 02616 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 02617 02618 #ifdef BUFFERED_RENDER 02619 Render(*m_pPane, *m_DS); 02620 #else 02621 x_Render(*m_pPane, *m_DS); 02622 #endif 02623 } 02624 02625 void IPhyloTreeRenderer::RemoveCurrentDataSource() 02626 { 02627 m_ValidLayout = eNeedLayoutAndSize; 02628 } 02629 02630 02631 void IPhyloTreeRenderer::x_GenerateTexture(void) 02632 { 02633 GLint w = m_pPane->GetViewport().Width(); 02634 GLint h = m_pPane->GetViewport().Height(); 02635 02636 // Get a power-of-2 size for frame buffer that is the smallest 02637 // power-of-2 size larger than the current viewport 02638 size_t size = std::max(w,h); 02639 size_t buf_dim = 32; 02640 for (int i=0; i<8 && buf_dim<size; ++i) 02641 buf_dim *= 2; 02642 02643 // Some machines have limited framebuffer sizes available. Since we don't 02644 // really *need* a large buffer, make 1024 the max since any machine should 02645 // be able to handle that. 02646 if (buf_dim > 1024) 02647 buf_dim = 1024; 02648 02649 if (m_MinimapBuffer == NULL || 02650 m_MinimapBuffer->GetFrameSize() < buf_dim) { 02651 delete m_MinimapBuffer; 02652 m_MinimapBuffer = new CGLFrameBuffer(buf_dim); 02653 m_MinimapBuffer->SetTextureFiltering(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR); 02654 m_MinimapBuffer->CreateFrameBuffer(); 02655 } 02656 02657 if (m_MinimapBuffer->IsValid()) { 02658 m_MinimapBuffer->MakeCurrent(true); 02659 02660 // Set viewport and draw: 02661 GLint vp_width, vp_height; 02662 if (w > h) { 02663 vp_width = (int)m_MinimapBuffer->GetFrameSize(); 02664 vp_height = (int)(((float)vp_width)*((float)h)/(float)w); 02665 } 02666 else { 02667 vp_height = (int)m_MinimapBuffer->GetFrameSize(); 02668 vp_width = (int)(((float)vp_height)*((float)w)/(float)h); 02669 } 02670 02671 // Get current viewport and visible region so we can 02672 // restore them later 02673 TVPRect saved_viewport = m_pPane->GetViewport(); 02674 TModelRect saved_rect = m_pPane->GetVisibleRect(); 02675 TModelRect saved_limits_rect = m_pPane->GetModelLimitsRect(); 02676 02677 // Set visible rect to draw the whole tree 02678 m_pPane->SetVisibleRect(m_pPane->GetModelLimitsRect()); 02679 m_pPane->GetViewport().Init(0, 0, vp_width, vp_height); 02680 02681 ComputeViewingLimits(*m_pPane); 02682 Redraw(); 02683 02684 // Set viewport and model region back 02685 m_pPane->SetViewport(saved_viewport); 02686 m_pPane->SetVisibleRect(saved_rect); 02687 m_pPane->SetModelLimitsRect(saved_limits_rect); 02688 02689 // Set rendering target back 02690 m_MinimapBuffer->MakeCurrent(false); 02691 m_MinimapBuffer->GenerateMipMaps(); 02692 02693 // Update the minimap texture with the texture id from the framebuffer 02694 m_Texture.Reset(new CGlTexture(m_MinimapBuffer->GetTexture())); 02695 m_Texture->SetFilterMag(m_MinimapBuffer->GetTexMag()); 02696 m_Texture->SetFilterMin(m_MinimapBuffer->GetTexMin()); 02697 m_Texture->SetParams(); 02698 02699 02700 CGlUtils::CheckGlError(); 02701 } 02702 else { 02703 // If system does not support framebuffers, generate a (worse) texture the 02704 // old-fashioned way. 02705 TModelRect saved_rect = m_pPane->GetVisibleRect(); 02706 m_pPane->SetVisibleRect(m_pPane->GetModelLimitsRect()); 02707 Redraw(); 02708 02709 try { 02710 CImage * image = new CImage(w, h, 4); 02711 glPixelStorei(GL_PACK_ALIGNMENT, 1); 02712 02713 glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image->SetData()); 02714 m_Texture.Reset(new CGlTexture(image)); 02715 } 02716 catch(COpenGLException&) 02717 { 02718 m_bRegenerateTexture = false; 02719 } 02720 catch(std::exception&) 02721 { 02722 m_bRegenerateTexture = false; 02723 } 02724 02725 m_pPane->SetVisibleRect(saved_rect); 02726 Redraw(); 02727 } 02728 } 02729 02730 CGlTexture * IPhyloTreeRenderer::GetTexture(float& xcoord_limit, 02731 float& ycoord_limit) 02732 { 02733 if (m_bRegenerateTexture) { 02734 x_GenerateTexture(); 02735 } 02736 02737 // The minimap is created from a square texture, but when we 02738 // draw to the texture the whole texture is not used. The limits 02739 // represent the part of the textture that should be shown. 02740 GLint w = m_pPane->GetViewport().Width(); 02741 GLint h = m_pPane->GetViewport().Height(); 02742 if (w > h) { 02743 xcoord_limit = 1.0f; 02744 ycoord_limit = ((float)h)/(float)w; 02745 } 02746 else { 02747 ycoord_limit = 1.0f; 02748 xcoord_limit = ((float)w)/(float)h; 02749 } 02750 02751 return m_bRegenerateTexture ? m_Texture.GetPointer() : NULL; 02752 } 02753 02754 CPhyloTreeNode * IPhyloTreeRenderer::GetHoverNode(TVPPoint point) 02755 { 02756 if (m_DS != NULL) 02757 return x_TestForNode(m_DS->GetTree(), wxPoint(point.X(), point.Y())); 02758 02759 return NULL; 02760 } 02761 02762 string IPhyloTreeRenderer::TTHH_NeedTooltip(const wxPoint & pt) 02763 { 02764 // Since this can be checked with a timer, we may get 02765 // here while user is using rectangle-select and we don't 02766 // want to do anything in that case. 02767 if (m_DS == NULL || m_State == eSelRect) 02768 return ""; 02769 02770 m_StartPoint = pt; 02771 CPhyloTreeNode* n = x_TestForNode(m_DS->GetTree(), m_StartPoint); 02772 if (n==NULL) 02773 return ""; 02774 02775 return NStr::IntToString(n->GetValue().GetId()); 02776 } 02777 02778 CTooltipInfo IPhyloTreeRenderer::TTHH_GetTooltip(const wxRect & rect) 02779 { 02780 CTooltipInfo tip; 02781 02782 // Should not call this while selecting, but just in case, check m_State 02783 if (m_DS == NULL || m_State == eSelRect) 02784 return tip; 02785 02786 m_StartPoint.x = rect.x; m_StartPoint.y = rect.y; 02787 CPhyloTreeNode * n = x_TestForNode(m_DS->GetTree(), m_StartPoint); 02788 02789 if (n != NULL) { 02790 //std::string tip_text = m_LabelExt.GetLabelForNode(n, m_SL->SetTooltipFormat()); 02791 tip.SetTipID(NStr::IntToString(n->GetValue().GetId())); 02792 02793 std::vector<std::string> arr; 02794 std::string final_text; 02795 02796 // Get tip text, and then remove any lines we don't need. 02797 NStr::Tokenize(m_LabelExt.GetLabelForNode(n, m_SL->SetTooltipFormat()), 02798 "\n", arr); 02799 for (unsigned int i=0; i<arr.size(); ++i) { 02800 if (arr[i].length() > 0) { 02801 // This is in the title 02802 if (arr[i].substr(0, 6) == "label:") 02803 continue; 02804 // This is visually obvious 02805 if (arr[i].substr(0,16) == "$NODE_COLLAPSED:") 02806 continue; 02807 02808 // Remove blank features (no text after first ':')... 02809 std::string::size_type idx1 = arr[i].find_first_of(":"); 02810 if (arr[i].length() > idx1+1) { 02811 std::string::size_type idx2 = arr[i].find_first_not_of(' ', idx1+1); 02812 if (idx2 != std::string::npos) { 02813 final_text += arr[i]; 02814 final_text += '\n'; 02815 } 02816 } 02817 } 02818 } 02819 02820 tip.SetTipText(final_text); 02821 tip.SetTitleText((*n)->GetLabel()); 02822 } 02823 02824 return tip; 02825 } 02826 02827 void IPhyloTreeRenderer::SetActiveTooltipNode(int id) 02828 { 02829 m_ActiveTooltipNode = m_DS->GetNode(id); 02830 } 02831 02832 void IPhyloTreeRenderer::PointToNode(int id, wxRect tip_rect, float sec) 02833 { 02834 if (m_DS == NULL) 02835 return; 02836 02837 // Create an entry for an arrow to point from a tooltip window to node 'id' 02838 CPhyloTreeNode* tip_node = m_DS->GetNode(id); 02839 if (tip_node){ 02840 NodePointer np; 02841 np.m_NodeID = id; 02842 np.m_Node = m_DS->GetNode(id); 02843 02844 _ASSERT(np.m_Node != NULL); 02845 02846 np.m_TipCenter.m_X = (TModelUnit)(tip_rect.x + tip_rect.width/2); 02847 np.m_TipCenter.m_Y = (TModelUnit)(tip_rect.y + tip_rect.height/2); 02848 02849 np.m_TipRect = tip_rect; 02850 02851 np.m_Duration = sec; 02852 if (np.m_Duration != (TModelUnit)-1) 02853 np.m_Timer.Start(); 02854 02855 if (!m_EffectsTimer.IsRunning()) 02856 m_EffectsTimer.Start(30); 02857 02858 // If we are already pointing to this node, reset it 02859 for (unsigned int i=0; i<m_NodePointers.size(); ++i) { 02860 if (m_NodePointers[i].m_NodeID == (IPhyNode::TID)id) { 02861 m_NodePointers[i] = np; 02862 return; 02863 } 02864 } 02865 02866 m_NodePointers.push_back(np); 02867 } 02868 } 02869 02870 void IPhyloTreeRenderer::SetSelectedIDs(const vector<int> & vect) 02871 { 02872 if (m_DS == NULL) 02873 return; 02874 02875 m_SelectedIDs.resize(vect.size()); 02876 copy(vect.begin(), vect.end(), m_SelectedIDs.begin()); 02877 02878 m_DS->SetSelection(m_DS->GetTree(), false, true); 02879 for (vector<int>::const_iterator it=vect.begin(); it!=vect.end(); it++){ 02880 CPhyloTreeNode * node = m_DS->GetNode(*it); 02881 if (node) m_DS->SetSelection(node, true, true); 02882 } 02883 //m_pHost->HMGH_Redraw(); 02884 m_pHost->HMGH_OnChanged(); 02885 } 02886 02887 bool IPhyloTreeRenderer::x_KindOfHuge(void) 02888 { 02889 if (m_DS == NULL) 02890 return false; 02891 02892 TModelUnit modelDist = GetDimY() / m_DS->GetSize(); 02893 TModelUnit node_size = m_SL->SetSize(CPhyloTreeScheme::eNodeSize) * 2; 02894 return modelDist < node_size; 02895 } 02896 02897 TVPUnit IPhyloTreeRenderer::DistanceBetweenNodes(void) 02898 { 02899 // By default, allow labels to be visible to a fairly high resolution 02900 return (TVPUnit)x_DistanceBetweenNodes(m_pPane->GetScaleY()); 02901 } 02902 02903 TModelUnit IPhyloTreeRenderer::x_DistanceBetweenNodes(TModelUnit scaley) 02904 { 02905 return (GetDimY() / (((TModelUnit)m_DS->GetSize())* 0.25 * scaley)); 02906 } 02907 02908 const TModelRect & IPhyloTreeRenderer::GetRasterRect(void) 02909 { 02910 return m_RasterRect; 02911 } 02912 02913 CRgbaColor IPhyloTreeRenderer::GetNodeColor(CPhyloTreeNode * node, bool bLabel) 02914 { 02915 if (!bLabel) { // node 02916 const string & node_color = m_SL->GetColoration()==CPhyloTreeScheme::eClusters?(**node).GetClusterFgColor():(**node).GetNodeFgColor(); 02917 02918 if (!node_color.empty() && (**node).GetSelectedState()!=IPhyGraphicsNode::eSelected) { 02919 CRgbaColor c(node_color); 02920 if (m_SL->GetSelectionVisibility() == 02921 CPhyloTreeScheme::eHighlightSelection) { 02922 c.SetAlpha(m_SL->GetNonSelectedAlpha()); 02923 } 02924 return c; 02925 } 02926 } 02927 else { // label 02928 const string & label_color = (**node).GetLabelFgColor(); 02929 if (!label_color.empty()) { 02930 CRgbaColor c(label_color); 02931 if (m_SL->GetSelectionVisibility() == 02932 CPhyloTreeScheme::eHighlightSelection && 02933 (**node).GetSelectedState()==IPhyGraphicsNode::eNotSelected) { 02934 c.SetAlpha(m_SL->GetNonSelectedAlpha()); 02935 } 02936 return c; 02937 } 02938 } 02939 02940 CRgbaColor color; 02941 02942 if ((**node).GetSelectedState()==IPhyGraphicsNode::eSelected) { 02943 color = 02944 m_SL->SetColor(bLabel?CPhyloTreeScheme::eLabel:CPhyloTreeScheme::eNode, 02945 CPhyloTreeScheme::eSelColor); 02946 } 02947 else if ((**node).GetSelectedState()==IPhyGraphicsNode::eShared) { 02948 color = 02949 m_SL->SetColor(bLabel?CPhyloTreeScheme::eLabel:CPhyloTreeScheme::eNode, 02950 CPhyloTreeScheme::eSharedColor); 02951 } 02952 else if ((**node).GetSelectedState()==IPhyGraphicsNode::eTraced) { 02953 color = 02954 m_SL->SetColor(bLabel?CPhyloTreeScheme::eLabel:CPhyloTreeScheme::eNode, 02955 CPhyloTreeScheme::eTraceColor); 02956 } 02957 else { 02958 color = 02959 m_SL->SetColor(bLabel?CPhyloTreeScheme::eLabel:CPhyloTreeScheme::eNode, 02960 CPhyloTreeScheme::eColor); 02961 if (m_SL->GetSelectionVisibility() == 02962 CPhyloTreeScheme::eHighlightSelection) 02963 color.SetAlpha(m_SL->GetNonSelectedAlpha()); 02964 } 02965 02966 return color; 02967 } 02968 02969 02970 string IPhyloTreeRenderer::GetEdgeColor(CPhyloTreeNode * node) 02971 { 02972 const string & edge_color = m_SL->GetColoration()==CPhyloTreeScheme::eClusters?(**node).GetClusterFgColor():(**node).GetNodeEdColor(); 02973 02974 if (!edge_color.empty() && (**node).GetSelectedState()!=IPhyGraphicsNode::eSelected) { 02975 return CRgbaColor(edge_color).ToString(); 02976 } 02977 return ""; 02978 } 02979 02980 02981 // by default do not allow to drag nodes 02982 bool IPhyloTreeRenderer::CanDragNodes(void) 02983 { 02984 return false; 02985 } 02986 02987 void IPhyloTreeRenderer::SetRenderingOption(TRenderingOption opt, bool bVal) 02988 { 02989 switch (opt) { 02990 case eDistancesRendering: {m_bDistMode = bVal; break;} 02991 case eAdaptiveMargins: {m_bAdaptiveMargins = bVal; break;} 02992 case eSimplification: {m_bSimplification = bVal; break;} 02993 case eSplinesRendering: {m_bUseSplines = bVal; break;} 02994 case eGenerateTextures: {m_bRegenerateTexture = bVal; break;} 02995 case eZoomablePrimitives: {m_bZoomablePrimitives = bVal; break;} 02996 case eAutofitLabels: {m_bAutosizeLabels = bVal; break;} 02997 } 02998 } 02999 03000 TVPRect IPhyloTreeRenderer::GetMinDimensions(CPhyloTreeDataSource & ds, 03001 IPhyloTreeRenderer & rr, 03002 CPhyloTreeScheme & sc) 03003 { 03004 TVPRect rec_rect(0, 0, 0, 0); 03005 03006 // OpenGL limitations in pixels 03007 int max_width = 2000; 03008 int max_height = 2000; 03009 03010 // minimum visible font size 03011 int min_fontsize = CGlBitmapFont::eFontSize_8; 03012 03013 // comfortable space between labels 03014 int min_space = 2; 03015 03016 // retrieving margins 03017 GLdouble ml, mt, mr, mb; 03018 sc.GetMargins(ml, mt, mr, mb); 03019 03020 // height calculation 03021 int height, width; 03022 03023 if (rr.CanDragNodes()) { // unrooted tree 03024 03025 height = width = int (mt + mb + ::sqrt((double)ds.GetSize()*ds.GetWidth()) * 03026 sc.SetSize(CPhyloTreeScheme::eNodeSize)); 03027 } 03028 else { // rooted tree 03029 height = int (mt + mb + (ds.GetSize() + 1) * 03030 (min_fontsize+min_space)); 03031 width = int (ml + mr + (ds.GetWidth() + 1) * 03032 (sc.SetSize(CPhyloTreeScheme::eNodeSize))); 03033 } 03034 03035 // checking for limits 03036 if (height > max_height) height = max_height; 03037 if (width > max_width) width = max_width; 03038 03039 rec_rect.SetSize(width, height); 03040 03041 return rec_rect; 03042 } 03043 03044 03045 wxEvtHandler* IPhyloTreeRenderer::GetEvtHandler() 03046 { 03047 return this; 03048 } 03049 03050 TVPUnit IPhyloTreeRenderer::GetDefaultNodeSize(CPhyloTreeNode * node) 03051 { 03052 int forced_size = (*node)->GetNodeSize(); 03053 TVPUnit defNodeSize = 0; 03054 03055 if (forced_size < 0) { // no hardcoded size 03056 if (m_SL->GetBoaNodes() && node->IsLeafEx()) { 03057 double max_childs_size = m_SL->SetSize(CPhyloTreeScheme::eMaxChildsNmb); 03058 double min_childs_size = m_SL->SetSize(CPhyloTreeScheme::eMinChildsNmb); 03059 double this_childs_size = (**node).GetLeavesNmb() + 1; 03060 03061 double min_node_size = m_SL->SetSize(CPhyloTreeScheme::eMinNodeSize); 03062 double max_node_size = m_SL->SetSize(CPhyloTreeScheme::eMaxNodeSize); 03063 03064 defNodeSize = (TVPUnit) floor( min_node_size + (max_node_size - min_node_size) * 03065 (log(this_childs_size) - log(min_childs_size)) / 03066 (log(max_childs_size) - log(min_childs_size))); 03067 } 03068 else { 03069 defNodeSize = (TVPUnit)m_SL->SetSize(CPhyloTreeScheme::eNodeSize); 03070 } 03071 } 03072 else { 03073 defNodeSize = (TVPUnit)forced_size; 03074 } 03075 03076 03077 return defNodeSize; 03078 } 03079 03080 TVPUnit IPhyloTreeRenderer::x_NodeSize(CPhyloTreeNode * node) 03081 { 03082 TVPUnit nodeSize = 0; 03083 03084 if (m_pPane) { 03085 TVPUnit defNodeSize = GetDefaultNodeSize(node); 03086 03087 // boanodes (whatever that stands for) 03088 // seems to be always false, so this is never executed... 03089 if ((m_SL->GetBoaNodes() && node->IsLeafEx() && !node->IsLeaf()) || 03090 DistanceBetweenNodes()> defNodeSize*2) { 03091 nodeSize = defNodeSize; 03092 } 03093 } 03094 03095 return nodeSize; 03096 } 03097 03098 void IPhyloTreeRenderer::x_DrawNodes(CPhyloTreeNode * node) 03099 { 03100 for(CPhyloTreeNode::TNodeList_I it = node->SubNodeBeginEx(); 03101 it != node->SubNodeEnd(); it++ ) x_DrawNodes(static_cast<CPhyloTreeNode*>(*it)); 03102 03103 x_RenderNode(node); 03104 } 03105 03106 void IPhyloTreeRenderer::x_DrawNodesVbo(CPhyloTreeNode* node, CGlVboNode* render_nodes) 03107 { 03108 for(CPhyloTreeNode::TNodeList_I it = node->SubNodeBeginEx(); 03109 it != node->SubNodeEnd(); it++ ) x_DrawNodesVbo(static_cast<CPhyloTreeNode*>(*it), render_nodes); 03110 03111 x_RenderNodeVbo(node, render_nodes); 03112 } 03113 03114 03115 03116 END_NCBI_SCOPE
1.7.5.1
Modified on Wed May 23 13:22:19 2012 by modify_doxy.py rev. 337098