NCBI C++ ToolKit
phylo_tree_render.cpp
Go to the documentation of this file.
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
Modified on Wed May 23 13:22:19 2012 by modify_doxy.py rev. 337098