NCBI C++ ToolKit
phylo_tree_boundary_shapes.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

00001 /*  $Id: phylo_tree_boundary_shapes.cpp 27652 2013-03-20 15:21:49Z falkrb $
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:  Bob Falk
00027  *
00028  * File Description:
00029  *
00030  */
00031 
00032 #include <ncbi_pch.hpp>
00033 #include <gui/widgets/phylo_tree/phylo_tree_boundary_shapes.hpp>
00034 #include <gui/opengl.h>
00035 
00036 #include <cmath>
00037 
00038 BEGIN_NCBI_SCOPE
00039 
00040 
00041 /*********************** IBoundaryShape **************************************/
00042 
00043 IBoundaryShape* IBoundaryShape::CreateBoundary(const string& boundary_type)
00044 {
00045     IBoundaryShape* shape = NULL;
00046 
00047     if (boundary_type == "Rectangle") {
00048         shape = new CBoundaryShapeRect();
00049     }
00050     else if (boundary_type == "RoundedRectangle") {
00051         shape = new CBoundaryShapeRoundedRect();
00052     }
00053     else if (boundary_type == "Triangle") {
00054         shape = new CBoundaryShapeTri();
00055     }
00056     else if (boundary_type == "Ellipse") {
00057         shape = new BoundaryShapeEllipse();
00058     }
00059 
00060     return shape;
00061 }
00062 
00063 void IBoundaryShape::Render(const CVect2<float>& scale, float alpha_mod)
00064 {
00065     if (m_Hidden)
00066         return;
00067 
00068     //
00069     if (m_Parms.GetIncludeTextArea())
00070         ComputeShapeWithLabels(scale);
00071 
00072     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
00073     glEnable(GL_BLEND);
00074     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00075     CRgbaColor c(m_Parms.GetColor());
00076     c.SetAlpha(c.GetAlpha()*alpha_mod);
00077     glColor4fv(c.GetColorArray());
00078    
00079     vector<CVect2<float> > verts;
00080     x_GetTris(verts, scale);
00081 
00082     glBegin(GL_TRIANGLES);
00083     {
00084         for (size_t i=0; i<verts.size(); i+=3) {
00085             glVertex2fv(verts[i].GetData());
00086             glVertex2fv(verts[i+1].GetData());
00087             glVertex2fv(verts[i+2].GetData());
00088         }
00089     }
00090     glEnd();
00091 
00092     //Draw the border around polygons as a line
00093     if (m_Parms.GetDrawBoundaryEdge()) {
00094         glLineWidth(m_Parms.GetBorderWidth());
00095         CRgbaColor c(m_Parms.GetBoundaryEdgeColor());
00096         c.SetAlpha(c.GetAlpha()*alpha_mod);
00097         glColor4fv(c.GetColorArray());
00098                
00099         verts.clear();
00100         x_GetEdges(verts, scale);
00101 
00102         glBegin(GL_LINES);
00103         {
00104             for (size_t i=0; i<verts.size(); i+=2) {
00105                 glVertex2fv(verts[i].GetData());
00106                 glVertex2fv(verts[i+1].GetData());
00107             }
00108         }
00109         glEnd();
00110     }
00111 
00112     glDisable(GL_BLEND);
00113 }
00114 
00115 void IBoundaryShape::RenderVbo(CRef<CGlVboNode>& tri_node,
00116                                CRef<CGlVboNode>& edge_node,                           
00117                                const CVect2<float>& scale, 
00118                                float alpha_mod) 
00119 {
00120     if (m_Hidden)
00121         return;
00122 
00123     // Needed when view is scaled (not necessarily every time)
00124     ComputeShapeWithLabels(scale);     
00125   
00126     //tri_node.Reset(new CGlVboNode(GL_TRIANGLES));
00127     CRgbaColor c(m_Parms.GetColor());
00128     c.SetAlpha(c.GetAlpha()*alpha_mod);  
00129 
00130     tri_node->GetState().ColorC(c);
00131     tri_node->GetState().Enable(GL_BLEND);
00132     tri_node->GetState().BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00133     tri_node->GetState().PolygonMode(GL_FRONT_AND_BACK, GL_FILL);
00134 
00135     vector<CVect2<float> > verts;
00136     x_GetTris(verts, scale);
00137 
00138     tri_node->GetVertexBuffer2D().BufferData(verts.size(), &verts[0], GL_DYNAMIC_DRAW);
00139 
00140     //Draw the border around the outside of the object (if border is given)
00141     if (m_Parms.GetDrawBoundaryEdge()) {
00142         CRgbaColor c(m_Parms.GetBoundaryEdgeColor());
00143         c.SetAlpha(c.GetAlpha()*alpha_mod);
00144 
00145         verts.clear();
00146         x_GetEdges(verts, scale);
00147 
00148         //edge_node.Reset(new CGlVboNode(GL_LINES));
00149         edge_node->GetState().ColorC(c);
00150         edge_node->GetState().Enable(GL_BLEND);
00151         edge_node->GetState().BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00152         edge_node->GetState().LineWidth(m_Parms.GetBorderWidth());
00153         edge_node->GetState().LineJoinStyle(CGlState::eRoundedJoin);
00154         edge_node->GetState().LineCapStyle(CGlState::eRoundCap);
00155 
00156         edge_node->GetVertexBuffer2D().BufferData(verts.size(), &verts[0], GL_DYNAMIC_DRAW);
00157     }
00158 }
00159 
00160 /*********************** CBoundaryShapeRectBase **********************************/
00161 
00162 CBoundaryShapeRectBase::CBoundaryShapeRectBase()
00163 : m_PixelDeltaNegX(0.0f)
00164 , m_PixelDeltaNegY(0.0f)
00165 , m_PixelDeltaPosX(0.0f)
00166 , m_PixelDeltaPosY(0.0f)
00167 {
00168     for (size_t i=0; i<4; ++i)
00169         m_Points[i] = 0.0f;
00170 }
00171 
00172 void CBoundaryShapeRectBase::ComputeShape(const CVect2<float>& /* scale */,
00173                                           const CVect2<float>& /* base_node_pos */,
00174                                           const std::vector<CVect2<float> >& pts,                                      
00175                                           const vector<std::pair<CVect2<float>, CGlRect<float> > >& pixel_pts)
00176 {
00177     float minx = 1e10f;
00178     float miny = 1e10f;
00179     float maxx = -1e10f;
00180     float maxy = -1e10f;
00181 
00182     for (size_t i=0; i<pts.size(); ++i) {
00183         minx = std::min(minx, pts[i].X());
00184         maxx = std::max(maxx, pts[i].X());
00185         miny = std::min(miny, pts[i].Y());
00186         maxy = std::max(maxy, pts[i].Y());
00187     }
00188 
00189     if (m_Parms.GetIncludeTextArea()) {
00190         for (size_t i=0; i<pixel_pts.size(); ++i) {
00191             minx = std::min(minx, pixel_pts[i].first.X());
00192             maxx = std::max(maxx, pixel_pts[i].first.X());
00193             miny = std::min(miny, pixel_pts[i].first.Y());
00194             maxy = std::max(maxy, pixel_pts[i].first.Y());
00195         }
00196     }
00197 
00198     m_Points[0].X() = minx;
00199     m_Points[0].Y() = miny;
00200 
00201     m_Points[1].X() = maxx;
00202     m_Points[1].Y() = miny;
00203 
00204     m_Points[2].X() = maxx;
00205     m_Points[2].Y() = maxy;
00206 
00207     m_Points[3].X() = minx;
00208     m_Points[3].Y() = maxy;
00209 
00210     m_PixelPoints = pixel_pts;
00211 }
00212 
00213 void CBoundaryShapeRectBase::ComputeShapeWithLabels(const CVect2<float>& scale)
00214 {
00215     // This function is needed because when the view scales, text fields
00216     // move to a new position(translate) but do not scale.  This means
00217     // that the size of the bounding area cannot simply be scaled by the
00218     // same amount as the underlying tree.
00219     
00220     // Recompute the size of the boudary taking into account the extent
00221     // of text fields associated with specific nodes (positions) in the tree.
00222     // The contribution of these fields is saved in the m_PixelDelta* vars.
00223     m_PixelDeltaNegX = 0.0f;
00224     m_PixelDeltaNegY = 0.0f;
00225     m_PixelDeltaPosX = 0.0f;    
00226     m_PixelDeltaPosY = 0.0f;
00227 
00228     float sx = scale.X();
00229     float sy = scale.Y();
00230 
00231     // Iterate over the node points and their associated text
00232     // rectangles.  Node positions are in normal (scaled) coordinates
00233     // and text field dimensions are in absolute (pixel) coordinates.
00234     for (size_t i=0; i<m_PixelPoints.size(); ++i) {
00235         // a position in model coordinates:
00236         CVect2<float> base_pos = m_PixelPoints[i].first;
00237         // area of rectangle for the text field (in pixels)
00238         CGlRect<float> pix_rect = m_PixelPoints[i].second;
00239 
00240         // For each of the 4 corners of the text field rectangle, determine
00241         // if it outside the bounds of the current boundary and if so save
00242         // that (scaled) amount in m_PixelDeltaNeg*
00243         float negx = 1e10f;
00244         float negy = 1e10f;
00245         float posx = -1e10f;
00246         float posy = -1e10f;
00247 
00248         for (int i=0; i<4; ++i) {
00249             CGlPoint<float> pix_pos(pix_rect.GetCorner(i));
00250 
00251             negx = std::min(negx, pix_pos.X());
00252             posx = std::max(posx, pix_pos.X());
00253             negy = std::min(negy, pix_pos.Y());
00254             posy = std::max(posy, pix_pos.Y());
00255         }
00256 
00257         m_PixelDeltaNegX = std::max(m_PixelDeltaNegX, m_Points[0].X() - base_pos.X() - negx*sx );
00258         m_PixelDeltaNegY = std::max(m_PixelDeltaNegY, m_Points[0].Y() - base_pos.Y() - negy*sy);
00259         m_PixelDeltaPosX = std::max(m_PixelDeltaPosX, base_pos.X() + posx*sx - m_Points[2].X());
00260         m_PixelDeltaPosY = std::max(m_PixelDeltaPosY, base_pos.Y() + posy*sy - m_Points[2].Y());
00261     }
00262 }
00263 
00264 /*********************** CBoundaryShapeRect **********************************/
00265 
00266 void CBoundaryShapeRect::x_GetTris(vector<CVect2<float> >& tris, 
00267                                    const CVect2<float>& scale)
00268 {
00269     float border_neg_x = m_PixelDeltaNegX + scale.X()*(m_Parms.GetBorderWidth());
00270     float border_neg_y = m_PixelDeltaNegY + scale.Y()*(m_Parms.GetBorderWidth());
00271     float border_pos_x = m_PixelDeltaPosX + scale.X()*(m_Parms.GetBorderWidth());
00272     float border_pos_y = m_PixelDeltaPosY + scale.Y()*(m_Parms.GetBorderWidth());
00273 
00274     tris.push_back(CVect2<float>(m_Points[0].X() - border_neg_x, 
00275                                  m_Points[0].Y() - border_neg_y));
00276     tris.push_back(CVect2<float>(m_Points[1].X() + border_pos_x, 
00277                                  m_Points[1].Y() - border_neg_y));
00278     tris.push_back(CVect2<float>(m_Points[3].X() - border_neg_x, 
00279                                  m_Points[3].Y() + border_pos_y));
00280 
00281     tris.push_back(CVect2<float>(m_Points[1].X() + border_pos_x, 
00282                                  m_Points[1].Y() - border_neg_y));
00283     tris.push_back(CVect2<float>(m_Points[2].X() + border_pos_x, 
00284                                  m_Points[2].Y() + border_pos_y));
00285     tris.push_back(CVect2<float>(m_Points[3].X() - border_neg_x, 
00286                                  m_Points[3].Y() + border_pos_y));
00287 }
00288 
00289 void CBoundaryShapeRect::x_GetEdges(vector<CVect2<float> >& edges, 
00290                                     const CVect2<float>& scale)
00291 {
00292     //Draw the border around the outside of the object (if border is given)
00293     if (m_Parms.GetDrawBoundaryEdge()) {
00294         float border_neg_x = m_PixelDeltaNegX + scale.X()*(m_Parms.GetBorderWidth() + 1);
00295         float border_neg_y = m_PixelDeltaNegY + scale.Y()*(m_Parms.GetBorderWidth() + 1);
00296         float border_pos_x = m_PixelDeltaPosX + scale.X()*(m_Parms.GetBorderWidth() + 1);
00297         float border_pos_y = m_PixelDeltaPosY + scale.Y()*(m_Parms.GetBorderWidth() + 1);
00298 
00299         edges.push_back(CVect2<float>(m_Points[0].X() - border_neg_x, 
00300                                       m_Points[0].Y() - border_neg_y));
00301         edges.push_back(CVect2<float>(m_Points[1].X() + border_pos_x, 
00302                                       m_Points[1].Y() - border_neg_y));
00303 
00304         edges.push_back(CVect2<float>(m_Points[1].X() + border_pos_x, 
00305                                       m_Points[1].Y() - border_neg_y));
00306         edges.push_back(CVect2<float>(m_Points[2].X() + border_pos_x, 
00307                                       m_Points[2].Y() + border_pos_y));
00308 
00309         edges.push_back(CVect2<float>(m_Points[2].X() + border_pos_x, 
00310                                       m_Points[2].Y() + border_pos_y));
00311         edges.push_back(CVect2<float>(m_Points[3].X() - border_neg_x, 
00312                                       m_Points[3].Y() + border_pos_y));
00313 
00314         edges.push_back(CVect2<float>(m_Points[3].X() - border_neg_x, 
00315                                       m_Points[3].Y() + border_pos_y));
00316         edges.push_back(CVect2<float>(m_Points[0].X() - border_neg_x, 
00317                                       m_Points[0].Y() - border_neg_y));
00318     }
00319 }
00320 
00321 
00322 /*********************** CBoundaryShapeRoundedRect ***************************/
00323 
00324 void CBoundaryShapeRoundedRect::x_GetRoudedCornerTris(vector<CVect2<float> >& tris,
00325                                                       const CVect2<float>& pos,
00326                                                       float corner_width_x,
00327                                                       float corner_width_y,
00328                                                       float start_angle)
00329 {     
00330     float tri_count = 10.0f;
00331     float delta = (3.1415926535f/2.0f)/tri_count;
00332     float angle = start_angle;
00333     CVect2<float> prev_pt, pt;
00334 
00335     for(int i =0; i<=tri_count; i++){
00336         prev_pt = pt;
00337         pt.Set(pos.X() + corner_width_x*cosf(angle), 
00338                pos.Y() + corner_width_y*sinf(angle));
00339 
00340         if (i>0) {
00341             tris.push_back(pos);
00342             tris.push_back(prev_pt);
00343             tris.push_back(pt);
00344         }
00345         angle += delta;
00346     }
00347 }
00348 
00349 void CBoundaryShapeRoundedRect::x_GetRoudedCornerEdges(vector<CVect2<float> >& edges,
00350                                                        const CVect2<float>& pos,
00351                                                        float corner_width_x,
00352                                                        float corner_width_y,
00353                                                        float start_angle)
00354 {
00355     float tri_count = 10.0f;
00356     float delta = (3.1415926535f/2.0f)/tri_count;
00357     float angle = start_angle;
00358     CVect2<float> prev_pt, pt;
00359 
00360     for(int i =0; i<=tri_count; i++){
00361         prev_pt = pt;
00362         pt.Set(pos.X() + corner_width_x*cosf(angle), 
00363                pos.Y() + corner_width_y*sinf(angle));
00364 
00365         if (i>0) {
00366             edges.push_back(prev_pt);
00367             edges.push_back(pt);
00368         }
00369         angle += delta;
00370     }
00371 }
00372 
00373 
00374 void CBoundaryShapeRoundedRect::x_GetTris(vector<CVect2<float> >& tris, 
00375                                           const CVect2<float>& scale)
00376 {
00377     // If border is not equal to at least the corner size, then elements may 
00378     // wind up outside the bounded area.
00379     float border_width = std::max(m_Parms.GetBorderWidth(), m_Parms.GetCornerWidth());
00380 
00381     float border_neg_x = m_PixelDeltaNegX + scale.X()*(border_width);
00382     float border_neg_y = m_PixelDeltaNegY + scale.Y()*(border_width);
00383     float border_pos_x = m_PixelDeltaPosX + scale.X()*(border_width);
00384     float border_pos_y = m_PixelDeltaPosY + scale.Y()*(border_width);
00385 
00386     float corner_width_x = scale.X()*m_Parms.GetCornerWidth();
00387     float corner_width_y = scale.Y()*m_Parms.GetCornerWidth();
00388 
00389     // Maximum size for the rounded corners is 1/2 distance in x or y direction
00390     // otherwise we get overlap (which looks bad)
00391     // Get min dimension (x or y)
00392     float xmin = std::min(m_Points[0].X(), m_Points[2].X()) - border_neg_x;
00393     float xmax = std::max(m_Points[0].X(), m_Points[2].X()) + border_pos_x;
00394     float ymin = std::min(m_Points[0].Y(), m_Points[2].Y()) - border_neg_y;
00395     float ymax = std::max(m_Points[0].Y(), m_Points[2].Y()) + border_pos_y;
00396 
00397     float xw = xmax-xmin;
00398     float yw = ymax-ymin;
00399 
00400     if (corner_width_x * 2.0f > xw)
00401         corner_width_x = xw*0.5f;
00402     if (corner_width_y * 2.0f > yw)
00403         corner_width_y = yw*0.5f;
00404 
00405 
00406     /* draw rounded rectangle in 7 parts: 3 rectangles and 4 corners
00407      * 
00408      *     /-----------------------\
00409      *    /4 |                   |7 \
00410      *   |---|    1.             |---|
00411      *   |_2_|                   |3__| 
00412      *    \5 |                   |6 /
00413      *     \-----------------------/
00414      */
00415 
00416     // First quad (two tris, lower left and upper right)
00417     tris.push_back(CVect2<float>(xmin + corner_width_x, ymin));
00418     tris.push_back(CVect2<float>(xmax - corner_width_x, ymin));
00419     tris.push_back(CVect2<float>(xmax - corner_width_x, ymax));
00420 
00421     tris.push_back(CVect2<float>(xmax - corner_width_x, ymax));
00422     tris.push_back(CVect2<float>(xmin + corner_width_x, ymax));
00423     tris.push_back(CVect2<float>(xmin + corner_width_x, ymin));
00424 
00425     // Second quad (as two tris)
00426     tris.push_back(CVect2<float>(xmin, ymin + corner_width_y));
00427     tris.push_back(CVect2<float>(xmin + corner_width_x, ymin + corner_width_y));
00428     tris.push_back(CVect2<float>(xmin + corner_width_x, ymax - corner_width_y));
00429 
00430     tris.push_back(CVect2<float>(xmin + corner_width_x, ymax - corner_width_y));
00431     tris.push_back(CVect2<float>(xmin, ymax - corner_width_y));
00432     tris.push_back(CVect2<float>(xmin, ymin + corner_width_y));
00433 
00434     // third quad (as two tris)
00435     tris.push_back(CVect2<float>(xmax - corner_width_x, ymin + corner_width_y));
00436     tris.push_back(CVect2<float>(xmax, ymin + corner_width_y));
00437     tris.push_back(CVect2<float>(xmax, ymax - corner_width_y));
00438 
00439     tris.push_back(CVect2<float>(xmax, ymax - corner_width_y));
00440     tris.push_back(CVect2<float>(xmax - corner_width_x, ymax - corner_width_y));
00441     tris.push_back(CVect2<float>(xmax - corner_width_x, ymin + corner_width_y));
00442 
00443 
00444     float pi = 3.14159265358979f;
00445 
00446     CVect2<float> pos(xmin + corner_width_x, ymin + corner_width_y);
00447     x_GetRoudedCornerTris(tris, pos, corner_width_x, corner_width_y, pi);
00448 
00449     pos.X() = xmax - corner_width_x;
00450     x_GetRoudedCornerTris(tris, pos, corner_width_x, corner_width_y, (3.0f*pi)/2.0f);
00451     
00452     pos.Y() = ymax - corner_width_y;
00453     x_GetRoudedCornerTris(tris, pos, corner_width_x, corner_width_y, 0.0f);
00454 
00455     pos.X() = xmin + corner_width_x;
00456     x_GetRoudedCornerTris(tris, pos, corner_width_x, corner_width_y, pi/2.0f);
00457 }
00458 
00459 void CBoundaryShapeRoundedRect::x_GetEdges(vector<CVect2<float> >& edges, 
00460                                            const CVect2<float>& scale)
00461 {
00462     // If border is not equal to at least the corner size, then elements may 
00463     // wind up outside the bounded area.
00464     float border_width = std::max(m_Parms.GetBorderWidth(), m_Parms.GetCornerWidth());
00465 
00466     float border_neg_x = m_PixelDeltaNegX + scale.X()*(border_width);
00467     float border_neg_y = m_PixelDeltaNegY + scale.Y()*(border_width);
00468     float border_pos_x = m_PixelDeltaPosX + scale.X()*(border_width);
00469     float border_pos_y = m_PixelDeltaPosY + scale.Y()*(border_width);
00470 
00471     float corner_width_x = scale.X()*m_Parms.GetCornerWidth();
00472     float corner_width_y = scale.Y()*m_Parms.GetCornerWidth();
00473 
00474     // Maximum size for the rounded corners is 1/2 distance in x or y direction
00475     // otherwise we get overlap (which looks bad)
00476     // Get min dimension (x or y)
00477     float xmin = std::min(m_Points[0].X(), m_Points[2].X()) - border_neg_x;
00478     float xmax = std::max(m_Points[0].X(), m_Points[2].X()) + border_pos_x;
00479     float ymin = std::min(m_Points[0].Y(), m_Points[2].Y()) - border_neg_y;
00480     float ymax = std::max(m_Points[0].Y(), m_Points[2].Y()) + border_pos_y;
00481 
00482     float xw = xmax-xmin;
00483     float yw = ymax-ymin;
00484 
00485     if (corner_width_x * 2.0f > xw)
00486         corner_width_x = xw*0.5f;
00487     if (corner_width_y * 2.0f > yw)
00488         corner_width_y = yw*0.5f;
00489 
00490 
00491     /* draw rounded rectangle in 7 parts: 3 rectangles and 4 corners
00492      * 
00493      *     /-----------------------\
00494      *    /4 |                   |7 \
00495      *   |---|    1.             |---|
00496      *   |_2_|                   |3__| 
00497      *    \5 |                   |6 /
00498      *     \-----------------------/
00499      */
00500 
00501     // First quad - top and bottom edges
00502     edges.push_back(CVect2<float>(xmin + corner_width_x, ymin));
00503     edges.push_back(CVect2<float>(xmax - corner_width_x, ymin));
00504 
00505     edges.push_back(CVect2<float>(xmax - corner_width_x, ymax));
00506     edges.push_back(CVect2<float>(xmin + corner_width_x, ymax));
00507 
00508     // Second quad - left edge
00509     edges.push_back(CVect2<float>(xmin , ymax - corner_width_y));
00510     edges.push_back(CVect2<float>(xmin, ymin + corner_width_y));
00511 
00512     // third quad - right edge
00513     edges.push_back(CVect2<float>(xmax, ymin + corner_width_y));
00514     edges.push_back(CVect2<float>(xmax, ymax - corner_width_y));
00515 
00516     float pi = 3.14159265358979f;
00517 
00518     CVect2<float> pos(xmin + corner_width_x, ymin + corner_width_y);
00519     x_GetRoudedCornerEdges(edges, pos, corner_width_x, corner_width_y, pi);
00520 
00521     pos.X() = xmax - corner_width_x;
00522     x_GetRoudedCornerEdges(edges, pos, corner_width_x, corner_width_y, (3.0f*pi)/2.0f);
00523     
00524     pos.Y() = ymax - corner_width_y;
00525     x_GetRoudedCornerEdges(edges, pos, corner_width_x, corner_width_y, 0.0f);
00526 
00527     pos.X() = xmin + corner_width_x;
00528     x_GetRoudedCornerEdges(edges, pos, corner_width_x, corner_width_y, pi/2.0f);
00529 }
00530 
00531 
00532 /*********************** CBoundaryShapeTri ***********************************/
00533 
00534 CBoundaryShapeTri::CBoundaryShapeTri()
00535 {
00536 }
00537 
00538 void CBoundaryShapeTri::ComputeShape(const CVect2<float>& /* scale */,
00539                                      const CVect2<float>& base_node_pos,                                  
00540                                      const std::vector<CVect2<float> >& pts,                    
00541                                      const vector<std::pair<CVect2<float>, CGlRect<float> > >& pixel_pts) 
00542 {
00543     // Pixel points are the points that define text box areas
00544     if (m_Parms.GetIncludeTextArea() || m_Parms.GetTextBox()) {
00545         m_PixelPoints = pixel_pts;
00546     }
00547 
00548     m_NodePoints = pts;
00549     m_BaseNodePos = base_node_pos;
00550 
00551     // Remove the base (apex) node from the main set of nodes since it has a fixed position
00552     // in the triangle and it can mess up the math (gets subtracted from itself and creates
00553     // 0 length vector. Its probably the first node, but check all.
00554     std::vector<CVect2<float> >::iterator iter;
00555     for (iter=m_NodePoints.begin(); iter!=m_NodePoints.end(); ++iter) {
00556         if ( ((*iter)-m_BaseNodePos).Length() < std::numeric_limits<float>::epsilon())
00557             break;
00558     }
00559 
00560     if (iter != m_NodePoints.end() )
00561         m_NodePoints.erase(iter);
00562 }
00563 
00564 void CBoundaryShapeTri::x_ComputeTriParms(const std::vector<CVect2<float> >& pts,  
00565                                           const CVect2<float>& base_pos,
00566                                           const CVect2<float>& dir,
00567                                           const CVect2<float>& perp_dir,
00568                                           float& max_len,
00569                                           float& max_angle_top,
00570                                           float& max_angle_bottom)
00571 {
00572     // Compute angle of all points from base line segment and save result in
00573     // max_angle_top and max_angle_bottom.  Top/bottom is relative to vector
00574     // perpendicular to main direction of triangle (perp_dir)
00575     for (size_t i=0; i<pts.size(); ++i) {     
00576         CVect2<float> pt_dir = pts[i] - base_pos;       
00577         float proj_len = pt_dir.Dot(dir);
00578         max_len = std::max(max_len, proj_len);
00579 
00580         // If the offset from the base position is < 0, then some of the points
00581         // are behind the apex of the triangle, and the shape won't work.  Throw
00582         // out nodes behind the triangle so we can show something (this could occur
00583         // in physically-based graph layout)
00584 
00585         if (max_len > 0.0f) {
00586             pt_dir.Normalize();
00587             float cos_angle = pt_dir.Dot(dir);
00588 
00589             // clamp to -1.0 .. 1.0 to prevent numeric precision problems
00590             cos_angle = cos_angle < -1.0f ? -1.0f : 1.0f < cos_angle ? 1.0f : cos_angle;
00591 
00592             float angle = acosf(cos_angle);
00593             bool pos_angle = (perp_dir.Dot(pt_dir) > 0.0f);
00594             if (pos_angle) {
00595                 max_angle_top = std::max(angle, max_angle_top);
00596                 max_angle_bottom = std::max(-angle, max_angle_bottom);
00597             }
00598             else {
00599                 max_angle_bottom = std::max(angle, max_angle_bottom);
00600                 max_angle_top = std::max(-angle, max_angle_top);
00601             }
00602         }
00603     }
00604 }
00605 
00606 CBoundaryShapeTri::RoundedCorner 
00607 CBoundaryShapeTri::x_ComputeRoundedCorner(const CVect2<float>& pt_in,
00608                                           const CVect2<float>& prev_pt_in,
00609                                           const CVect2<float>& next_pt_in,
00610                                           const CVect2<float>& scale,
00611                                           CVect2<float>& pt_out1,
00612                                           CVect2<float>& pt_out2)
00613 {
00614     CVect2<float> pdir;
00615     CVect2<float> s1, s2;
00616     float c, scale_modifier, theta;
00617     float a = m_Parms.GetCornerWidth();
00618 
00619     // Get vectors going away from the vertex being 'rounded' (these are vectors to
00620     // adjacent vertices).
00621     s1 = prev_pt_in-pt_in;
00622     float s1_len = s1.Length();
00623     s1 /= s1_len;   
00624 
00625     s2 = next_pt_in-pt_in;
00626     float s2_len = s2.Length();
00627     s2 /= s2_len;
00628 
00629     // Compute the angle as it appears in pixel coordinates (scaled) not using model
00630     // coordinates which would be acos(s1.Dot(s2)).   
00631     CVect2<float> s1_scaled = CVect2<float>(s1.X()*1.0f/scale.X(), s1.Y()*1.0f/scale.Y());
00632     CVect2<float> s2_scaled = CVect2<float>(s2.X()*1.0f/scale.X(), s2.Y()*1.0f/scale.Y());    
00633     s1_scaled.Normalize();
00634     s2_scaled.Normalize();
00635     theta = acos(s1_scaled.Dot(s2_scaled));
00636 
00637     RoundedCorner rc;   
00638     
00639     // To round the corner, we find the two points where that same perpendicular
00640     // segment of length 'a' will intersect the two vectors to neighboring 
00641     // vertices.  The distance is based on the right triangle formed by
00642     // the vector to the adjacent vertex, the line segemnt of length 'a'
00643     // and the line besecting the angle of length 'b'.  So the distance
00644     // to the intersection is 'c' from:  sin(theta/2) = (a/2)/c
00645     c = (a/2.0f)/sin(theta/2.0f);
00646 
00647     float scale_modifier1 = 1.0f/CVect2<float>(s1.X()/scale.X(), s1.Y()/scale.Y()).Length();
00648     float scale_modifier2 = 1.0f/CVect2<float>(s2.X()/scale.X(), s2.Y()/scale.Y()).Length();
00649 
00650     if (c*scale_modifier1 > s1_len*0.5f)
00651         c = (s1_len*0.5f)/scale_modifier1;
00652     if (c*scale_modifier2 > s2_len*0.5f)
00653         c = (s2_len*0.5f)/scale_modifier2;
00654    
00655     // Use c (scaled for pixel coordinates) to get intersection along the 
00656     // two vectors that make up the angle:
00657     pt_out1 = pt_in + s1*c*scale_modifier1; 
00658     pt_out2 = pt_in + s2*c*scale_modifier2;
00659 
00660     // We need to find the center and radius of the circle which, when drawn,
00661     // will create the rounded corner.  This is the circle that is tangent to
00662     // the interior of the angle at points pt_out1 and pt_out2, so the vector
00663     // to the center of this circle goes through the midpoint of the line
00664     // segment (pt_out1, pt_out2).  The length of the vector OP where P is
00665     // pt_in and O is the circle center is: cos(theta/2)=c/OP and radius
00666     // is the other side of the triangle: sin(theta/2)=r/OP.
00667     pdir = ((pt_out1+pt_out2)*0.5f)-pt_in;
00668     float OP = c/cosf(theta/2.0f);
00669     float r = sinf(theta/2)*OP;
00670     pdir.Normalize();
00671     scale_modifier = 1.0f/CVect2<float>(pdir.X()/scale.X(), pdir.Y()/scale.Y()).Length();    
00672    
00673     rc.pos = pt_in + pdir*OP*scale_modifier;
00674     rc.initial_vertex = pt_in;
00675     rc.intersection1 = pt_out1;
00676     rc.intersection2 = pt_out2;
00677     rc.corner_width_x = scale.X()*r;
00678     rc.corner_width_y = scale.Y()*r;
00679 
00680     return rc;
00681 }
00682 
00683 
00684 void CBoundaryShapeTri::ComputeShapeWithLabels(const CVect2<float>& scale)
00685 {
00686     //  Shouldn't be called with an empty area
00687     if (m_NodePoints.size() == 0)
00688         return;
00689 
00690     // Get all text positions based on current scale factor
00691     std::vector<CVect2<float> > text_pts;
00692 
00693     // Iterate over the node points and their associated text
00694     // rectangles.  Node positions are in normal (scaled) coordinates
00695     // and text field dimensions are in absolute (pixel) coordinates.
00696     size_t i;
00697 
00698     for (i=0; i<m_PixelPoints.size(); ++i) {
00699         // a position in model coordinates:
00700         CVect2<float> base_pos = m_PixelPoints[i].first;
00701         // area of rectangle for the text field (in pixels)
00702         CGlRect<float> pix_rect = m_PixelPoints[i].second;
00703 
00704         for (int i=0; i<4; ++i) {
00705             CGlPoint<float> pix_pos(pix_rect.GetCorner(i));         
00706             text_pts.push_back(CVect2<float>(base_pos.X() + pix_pos.X()*scale.X(),
00707                                              base_pos.Y() + pix_pos.Y()*scale.Y()));
00708         }
00709     }
00710 
00711     //*************************************************************************
00712     // Need to find main axis of triangle.  If the the triangle is axis aligned
00713     // use the centroid to pick the major axis direction. If not axis aligned,
00714     // pick the axis to be the direction to the parent node (if direction to 
00715     // parent node is not defined, as is the case for the root node of the tree,
00716     // also use the centroid).     
00717     //*************************************************************************
00718     CVect2<float> axis_dir;
00719     CVect2<float> centroid(0.0f);
00720     CVect2<float> minpos(m_BaseNodePos);
00721     CVect2<float> maxpos(m_BaseNodePos);
00722 
00723     for (i=0; i<m_NodePoints.size(); ++i) {
00724         centroid += m_NodePoints[i];
00725         minpos.X() = std::min(minpos.X(), m_NodePoints[i].X());
00726         minpos.Y() = std::min(minpos.Y(), m_NodePoints[i].Y());
00727 
00728         maxpos.X() = std::max(maxpos.X(), m_NodePoints[i].X());
00729         maxpos.Y() = std::max(maxpos.Y(), m_NodePoints[i].Y());
00730     }
00731 
00732     for (i=0; i<text_pts.size(); ++i) {
00733         centroid += text_pts[i];
00734     }
00735 
00736     centroid /= (float)(m_NodePoints.size() + text_pts.size());
00737 
00738     // Compute position of base line segment (non-horizontal case) as b1..b2
00739     axis_dir = (centroid - m_BaseNodePos);
00740     axis_dir.Normalize();
00741 
00742     // find bounding rectangle.  main vertex is at (closest) to top/bottom/left or right
00743     // and primary axis is in opposite direction
00744 
00745     if (m_Parms.GetAxisAligned()) {
00746         // Make the primary axis of the triangle based on whether m_BaseNodePos
00747         // is closer to the top, bottom, left or right side of the triangle.
00748         float d1 = std::abs(m_BaseNodePos.X()-minpos.X());
00749         float d2 = std::abs(m_BaseNodePos.X()-maxpos.X());
00750         float d3 = std::abs(m_BaseNodePos.Y()-minpos.Y());
00751         float d4 = std::abs(m_BaseNodePos.Y()-maxpos.Y());
00752 
00753         if (d1 < d2 && d1 < d3 && d1 < d4) {
00754             axis_dir = CVect2<float>(1.0f, 0.0f);
00755         }
00756         else if (d2 < d3 && d2 < d4) {
00757             axis_dir = CVect2<float>(-1.0f, 0.0f);
00758         }
00759         else if (d3 < d4) {
00760             axis_dir = CVect2<float>(0.0f, 1.0f);
00761         }
00762         else {
00763             axis_dir = CVect2<float>(0.0f, -1.0f);
00764         }
00765     }
00766 
00767     // Get a perpendicular vector that points in a couter-clockwise 
00768     // direction with respect to axis_dir. This is always true if
00769     // we pick (-y,x).
00770     CVect2<float> perp_dir(-axis_dir.Y(), axis_dir.X());
00771 
00772     CVect2<float> base_dir = axis_dir*m_Parms.GetTriOffset();
00773     //float scale_modifier = 1.0f/CVect2<float>(base_dir.X()/scale.X(), base_dir.Y()/scale.Y()).Length();
00774     CVect2<float> base_pos = m_BaseNodePos - base_dir;
00775 
00776 
00777     // Compute angle of all points relative to the axis direction of the
00778     // triangle and return the maximum values in max_angle_[top,bottom].
00779     float max_len = -1e10f;
00780     float max_angle_top = 0.0f;
00781     float max_angle_bottom = 0.0f;
00782     
00783     if (!m_Parms.GetTextBox()) {
00784         x_ComputeTriParms(text_pts, 
00785                           base_pos, 
00786                           axis_dir, 
00787                           perp_dir,
00788                           max_len,
00789                           max_angle_top, 
00790                           max_angle_bottom);
00791     }
00792 
00793     x_ComputeTriParms(m_NodePoints, 
00794                       base_pos, 
00795                       axis_dir, 
00796                       perp_dir,
00797                       max_len,
00798                       max_angle_top,
00799                       max_angle_bottom);
00800     
00801 
00802     CVect2<float> p1, p2, p3;
00803 
00804     p1 = base_pos;  
00805     float a;
00806 
00807     if (m_Parms.GetAxisAligned()) {
00808         float angle = std::max(max_angle_top, max_angle_bottom);
00809 
00810         a = max_len*tanf(angle);
00811         p2 = base_pos + axis_dir*max_len - perp_dir*a;
00812         p3 = base_pos + axis_dir*max_len + perp_dir*a;
00813     }
00814     else  {
00815         a = tanf(max_angle_bottom);
00816         p2 = base_pos + axis_dir - perp_dir*a;
00817 
00818         a = tanf(max_angle_top);   
00819         p3 = base_pos + axis_dir + perp_dir*a;
00820 
00821         CVect2<float> v1 = p3-p1;
00822         v1.Normalize();
00823         CVect2<float> v2 = p2-p1;
00824         v2.Normalize();
00825 
00826         CVect2<float> bisector = ((p1+v1) + (p1+v2))*0.5f - p1;
00827         bisector.Normalize();
00828 
00829         max_len = 0.0f;
00830         for (i=0; i<m_NodePoints.size(); ++i) {     
00831             CVect2<float> pt_dir = m_NodePoints[i] - p1;    
00832             float proj_len = pt_dir.Dot(bisector);
00833             max_len = std::max(max_len, proj_len); 
00834         }
00835 
00836         if (m_Parms.GetIncludeTextArea() && 
00837             !m_Parms.GetTextBox()) {
00838 
00839             for (i=0; i<text_pts.size(); ++i) {     
00840                 CVect2<float> pt_dir = text_pts[i] - p1;    
00841                 float proj_len = pt_dir.Dot(bisector);
00842                 max_len = std::max(max_len, proj_len); 
00843             }
00844         }
00845 
00846         float c = max_len/v1.Dot(bisector);
00847         p3 = p1 + v1*c;
00848 
00849         c = max_len/v2.Dot(bisector);
00850         p2 = p1 + v2*c;
00851     }
00852 
00853 
00854     //*************************************************************************
00855     // Expand triangle by border width
00856     //*************************************************************************
00857     // find tri center and translate
00858     CVect2<float> tri_centroid = (p1 + p2 + p3)*0.3333f;
00859     p1 -= tri_centroid;
00860     p2 -= tri_centroid;
00861     p3 -= tri_centroid;
00862 
00863     m_Center = tri_centroid;
00864 
00865     // Expand each vertex away from the center by 'border' PIXELS (scale
00866     // each vector to convert distance to pixels)
00867     CVect2<float> bdir;
00868     float scaled_border;
00869 
00870     bdir = p1;
00871     bdir.Normalize();
00872     scaled_border = m_Parms.GetBorderWidth()/CVect2<float>(bdir.X()/scale.X(), bdir.Y()/scale.Y()).Length(); 
00873     p1 += bdir*scaled_border;
00874 
00875     bdir = p2;
00876     bdir.Normalize();
00877     scaled_border = m_Parms.GetBorderWidth()/CVect2<float>(bdir.X()/scale.X(), bdir.Y()/scale.Y()).Length();
00878     p2 += bdir*scaled_border;
00879 
00880     bdir = p3;
00881     bdir.Normalize();
00882     scaled_border = m_Parms.GetBorderWidth()/CVect2<float>(bdir.X()/scale.X(), bdir.Y()/scale.Y()).Length();
00883     p3 += bdir*scaled_border;
00884 
00885     p1 += tri_centroid;
00886     p2 += tri_centroid;
00887     p3 += tri_centroid;
00888 
00889     //*************************************************************************
00890     // Define rounded corners for triangle
00891     // Corners will not appear rounded if m_Parms.GetCornerWidth() == 0,
00892     // but we do the same processing either way since the duplicate
00893     // points won't hurt.
00894     //*************************************************************************
00895     m_RoundedCorners.clear();
00896     RoundedCorner rc;
00897     CVect2<float> clipped_corner1, clipped_corner2;
00898 
00899     // If the text is not added, two quads will define bounding area, with each
00900     // vertex split into 2 to that we can optionally have rounded corners.
00901     if (!m_Parms.GetTextBox()) {                              
00902         rc = x_ComputeRoundedCorner(p1, p3, p2, scale, clipped_corner1, clipped_corner2);
00903         m_RoundedCorners.push_back(rc);
00904         m_Rect1[0] = clipped_corner1;
00905         m_Rect1[1] = clipped_corner2;
00906 
00907         rc = x_ComputeRoundedCorner(p2, p1, p3, 
00908                                     scale, clipped_corner1, clipped_corner2);
00909         m_Rect1[2] = clipped_corner1;
00910         m_Rect1[3] = clipped_corner2;
00911         m_Rect2[0] = clipped_corner2;
00912         m_RoundedCorners.push_back(rc);
00913 
00914         rc = x_ComputeRoundedCorner(p3, p2, p1,
00915                                     scale, clipped_corner1, clipped_corner2);
00916         m_RoundedCorners.push_back(rc);
00917         m_Rect2[1] = clipped_corner1;
00918         m_Rect2[2] = clipped_corner2;
00919         m_Rect2[3] = m_Rect1[0];
00920     }
00921     else {
00922         text_pts.push_back(p2);
00923         text_pts.push_back(p3);
00924 
00925         float text_minx = 1e10f;
00926         float text_miny = 1e10f;
00927         float text_maxx = -1e10f;
00928         float text_maxy = -1e10f;
00929 
00930         for (i=0; i<text_pts.size(); ++i) {
00931             text_minx = std::min(text_minx, text_pts[i].X());
00932             text_maxx = std::max(text_maxx, text_pts[i].X());
00933             text_miny = std::min(text_miny, text_pts[i].Y());
00934             text_maxy = std::max(text_maxy, text_pts[i].Y());
00935         }
00936         
00937         float border_x = scale.X()*(m_Parms.GetBorderWidth());
00938         float border_y = scale.Y()*(m_Parms.GetBorderWidth());
00939 
00940         text_minx -= border_x;
00941         text_maxx += border_x;
00942         text_miny -= border_y;
00943         text_maxy += border_y;
00944 
00945         // Try to merge triangle corners (but not the parent nodes corner) with
00946         // the text box corners that are closest. Will not work well for vertically
00947         // oriented trees (should probably not include text in that case)
00948         if (axis_dir.X() >= 0.0f) {
00949             m_TextBox[0].X() = text_minx;
00950             m_TextBox[0].Y() = text_miny;
00951     
00952             m_TextBox[1].X() = text_maxx;
00953             m_TextBox[1].Y() = text_miny;
00954 
00955             m_TextBox[2].X() = text_maxx;
00956             m_TextBox[2].Y() = text_maxy;
00957 
00958             m_TextBox[3].X() = text_minx;
00959             m_TextBox[3].Y() = text_maxy;
00960 
00961             if (text_maxy > p3.Y())
00962                 p3.Y() = text_maxy;
00963             else
00964                 text_maxy = p3.Y();
00965 
00966             if (text_miny < p2.Y())
00967                 p2.Y() = text_miny;
00968             else
00969                  text_miny = p2.Y();
00970                
00971             m_TextBox[0].X() = p2.X();
00972             m_TextBox[3].X() = p3.X();
00973         }
00974         else {
00975             m_TextBox[0].X() = text_maxx;
00976             m_TextBox[0].Y() = text_maxy;
00977     
00978             m_TextBox[1].X() = text_minx;
00979             m_TextBox[1].Y() = text_maxy;
00980 
00981             m_TextBox[2].X() = text_minx;
00982             m_TextBox[2].Y() = text_miny;
00983 
00984             m_TextBox[3].X() = text_maxx;
00985             m_TextBox[3].Y() = text_miny;
00986 
00987             if (text_maxy > p2.Y())
00988                 p2.Y() = text_maxy;
00989             else
00990                 text_maxy = p2.Y();
00991 
00992             if (text_miny < p3.Y())
00993                 p3.Y() = text_miny;
00994             else
00995                  text_miny = p3.Y();
00996                
00997             m_TextBox[0].X() = p2.X();
00998             m_TextBox[3].X() = p3.X();
00999         }
01000       
01001         rc = x_ComputeRoundedCorner(p1, p3, p2, scale, clipped_corner1, clipped_corner2);
01002         m_Rect1[0] = clipped_corner1;
01003         m_Rect1[1] = clipped_corner2;
01004         m_Rect1[2] = p2;
01005         m_Rect1[3] = p3;
01006         m_RoundedCorners.push_back(rc);
01007 
01008         rc = x_ComputeRoundedCorner(m_TextBox[1], m_TextBox[0], m_TextBox[2], 
01009             scale, clipped_corner1, clipped_corner2);
01010         m_Rect2[0] = p2;
01011         m_Rect2[1] = clipped_corner1;
01012         m_Rect2[2] = clipped_corner2;
01013         m_RoundedCorners.push_back(rc);      
01014 
01015         rc = x_ComputeRoundedCorner(m_TextBox[2], m_TextBox[1], m_TextBox[3], 
01016             scale, clipped_corner1, clipped_corner2);
01017         m_Rect2[3] = clipped_corner1;
01018         m_Rect3[0] = clipped_corner1;
01019         m_Rect3[1] = clipped_corner2;
01020         m_Rect3[2] = p3;
01021         m_Rect3[3] = p2;
01022         m_RoundedCorners.push_back(rc); 
01023     }    
01024 
01025     // Compute the center and area of the shape, including text box if 
01026     // applicable.  This information is used to scale the shape when 
01027     // drawing the (optional) border
01028     float minx = 1e10f;
01029     float miny = 1e10f;
01030     float maxx = -1e10f;
01031     float maxy = -1e10f;
01032 
01033     for (int idx=0; idx<4; ++idx) {
01034         minx = std::min(minx, m_Rect1[idx].X());
01035         minx = std::min(minx, m_Rect2[idx].X());          
01036         maxx = std::max(maxx, m_Rect1[idx].X());
01037         maxx = std::max(maxx, m_Rect2[idx].X());
01038 
01039         miny = std::min(miny, m_Rect1[idx].Y());
01040         miny = std::min(miny, m_Rect2[idx].Y());          
01041         maxy = std::max(maxy, m_Rect1[idx].Y());
01042         maxy = std::max(maxy, m_Rect2[idx].Y());
01043 
01044         if (m_Parms.GetTextBox()) {
01045             minx = std::min(minx, m_Rect3[idx].X());
01046             maxx = std::max(maxx, m_Rect3[idx].X());
01047 
01048             miny = std::min(miny, m_Rect3[idx].Y());
01049             maxy = std::max(maxy, m_Rect3[idx].Y()); 
01050         }
01051     }
01052     m_Center.X() = (maxx + minx)/2.0f;
01053     m_Center.Y() = (maxy + miny)/2.0f;
01054     m_Width = maxx-minx;
01055     m_Height = maxy-miny;
01056 
01057 }
01058 
01059 
01060 
01061 void CBoundaryShapeTri::x_GetTris(vector<CVect2<float> >& tris, 
01062                                   const CVect2<float>& scale)
01063 {
01064     // Front quad with clipped front tri if we have a text box.
01065     // If not, this quad has the 4 vertices formed by the clipped
01066     // polygon tip and clipped lower corner with top of front
01067     // clip being first vertex.
01068     //  /|
01069     // | |
01070     //  \|
01071     tris.push_back(CVect2<float>(m_Rect1[0].X(), m_Rect1[0].Y()));
01072     tris.push_back(CVect2<float>(m_Rect1[1].X(), m_Rect1[1].Y()));
01073     tris.push_back(CVect2<float>(m_Rect1[2].X(), m_Rect1[2].Y()));
01074 
01075     tris.push_back(CVect2<float>(m_Rect1[2].X(), m_Rect1[2].Y()));
01076     tris.push_back(CVect2<float>(m_Rect1[3].X(), m_Rect1[3].Y()));
01077     tris.push_back(CVect2<float>(m_Rect1[0].X(), m_Rect1[0].Y()));
01078 
01079     // With a text box, this is the the lower right
01080     // triangle (with lower right corner clipped to 
01081     // make it a quad) of the text box.
01082     // without a text box it is the upper (large) quad
01083     // of the main clipped triangle (3 clips make 6 vertices)
01084     tris.push_back(CVect2<float>(m_Rect2[0].X(), m_Rect2[0].Y()));
01085     tris.push_back(CVect2<float>(m_Rect2[1].X(), m_Rect2[1].Y()));
01086     tris.push_back(CVect2<float>(m_Rect2[2].X(), m_Rect2[2].Y()));
01087 
01088     tris.push_back(CVect2<float>(m_Rect2[2].X(), m_Rect2[2].Y()));
01089     tris.push_back(CVect2<float>(m_Rect2[3].X(), m_Rect2[3].Y()));
01090     tris.push_back(CVect2<float>(m_Rect2[0].X(), m_Rect2[0].Y()));
01091 
01092     // This is the upper left 'triangle' of the text box.  Acutally 
01093     // a quad because the included upper right corner is clipped
01094     if (m_Parms.GetTextBox()) {         
01095         tris.push_back(CVect2<float>(m_Rect3[0].X(), m_Rect3[0].Y()));
01096         tris.push_back(CVect2<float>(m_Rect3[1].X(), m_Rect3[1].Y()));
01097         tris.push_back(CVect2<float>(m_Rect3[2].X(), m_Rect3[2].Y()));
01098 
01099         tris.push_back(CVect2<float>(m_Rect3[2].X(), m_Rect3[2].Y()));
01100         tris.push_back(CVect2<float>(m_Rect3[3].X(), m_Rect3[3].Y()));
01101         tris.push_back(CVect2<float>(m_Rect3[0].X(), m_Rect3[0].Y()));
01102     }   
01103 
01104     // If corners not rounded, return.
01105     if (m_Parms.GetCornerWidth() == 0.0f)
01106         return;
01107 
01108     // Draw the rounded corners.  If there is a text box,
01109     // two corners are the back of the text box.     
01110     for (size_t i=0; i<m_RoundedCorners.size(); ++i) {
01111         RoundedCorner c = m_RoundedCorners[i];
01112 
01113         vector<CVect2<float> > arc;        
01114         x_GetArc(c, scale, arc);
01115 
01116         CVect2<float>  base((c.intersection1+c.intersection2)*0.5f);
01117 
01118         // arc returns edge pairs usable as GL_LINES     
01119         for (size_t i=0; i<arc.size(); i+=2) {
01120             tris.push_back(base);
01121             tris.push_back(arc[i]);
01122             tris.push_back(arc[i+1]);
01123         }
01124     }
01125 }
01126 
01127 void CBoundaryShapeTri::x_GetEdges(vector<CVect2<float> >& edges, 
01128                                    const CVect2<float>& scale)
01129 {
01130     // Front quad with clipped front tri if we have a text box.
01131     // If not, this quad has the 4 vertices formed by the clipped
01132     // polygon tip and clipped lower corner with top of front
01133     // clip being first vertex.
01134     //  /|
01135     // | |
01136     //  \|
01137     if (m_Parms.GetTextBox()) { 
01138         edges.push_back(m_Rect1[1]);
01139         edges.push_back(m_Rect1[2]);
01140         edges.push_back(m_Rect1[3]);
01141         edges.push_back(m_Rect1[0]);
01142     }
01143     else {
01144         edges.push_back(m_Rect1[1]);
01145         edges.push_back(m_Rect1[2]);
01146     }
01147 
01148 
01149     // With a text box, this is the the lower right
01150     // triangle (with lower right corner clipped to 
01151     // make it a quad) of the text box.
01152     // without a text box it is the upper (large) quad
01153     // of the main clipped triangle (3 clips make 6 vertices) 
01154     if (m_Parms.GetTextBox()) { 
01155         edges.push_back(m_Rect2[0]);
01156         edges.push_back(m_Rect2[1]);
01157 
01158         edges.push_back(m_Rect2[2]);
01159         edges.push_back(m_Rect2[3]);
01160     }
01161     else {
01162         edges.push_back(m_Rect2[0]);
01163         edges.push_back(m_Rect2[1]);
01164 
01165         edges.push_back(m_Rect2[2]);
01166         edges.push_back(m_Rect2[3]);
01167     }
01168 
01169     // This is the upper left 'triangle' of the text box.  Acutally 
01170     // a quad because the included upper right corner is clipped
01171     if (m_Parms.GetTextBox()) {  
01172         edges.push_back(m_Rect3[1]);
01173         edges.push_back(m_Rect3[2]);
01174     }
01175 
01176     // If corners not rounded, return.
01177     if (m_Parms.GetCornerWidth() == 0.0f)
01178         return;
01179 
01180     // Draw Lines around the rounded corners  
01181     for (size_t i=0; i<m_RoundedCorners.size(); ++i) {
01182         RoundedCorner c = m_RoundedCorners[i];
01183 
01184         x_GetArc(m_RoundedCorners[i], scale, edges);
01185     }
01186 }
01187 
01188 void CBoundaryShapeTri::x_GetArc(const RoundedCorner& c,
01189                                  const CVect2<float>& scale,
01190                                  vector<CVect2<float> >& edges)
01191 {
01192     vector<CVect2<float> > arc;
01193 
01194     float corner_x = c.pos.X();
01195     float corner_y = c.pos.Y();
01196    
01197     CVect2<float> p0 = c.intersection1;
01198     CVect2<float> p1 = c.intersection2;
01199 
01200     // Get directions to start and stop of arc
01201     CVect2<float> dir0 = (c.intersection1-c.pos);
01202     CVect2<float> dir1 = (c.intersection2-c.pos);
01203     dir0.X() /= scale.X();
01204     dir0.Y() /= scale.Y();
01205     dir1.X() /= scale.X();
01206     dir1.Y() /= scale.Y();
01207 
01208     dir0.Normalize();
01209     dir1.Normalize();
01210 
01211     // Get angles for start and stop of arc, and put those angles
01212     // in order so that a1>a0 
01213     float a0 = atan2f(dir0.Y(), dir0.X());
01214     if (a0 < 0.0f) 
01215         a0 = 2.0f*3.14159f + a0;
01216     float a1 = atan2f(dir1.Y(), dir1.X());
01217     if (a1 < 0.0f) 
01218         a1 = 2.0f*3.14159f + a1;
01219 
01220     if (a0 > a1) {
01221         std::swap(a0, a1);
01222         std::swap(p0, p1);
01223     }
01224 
01225     // special case if a0/a1 cross 0-boundary (all angles here
01226     // will be < 180).  Add 2pi to a0 and swap a1, a0.
01227     if (a1-a0 > 3.14149f) {
01228         a0 += 2.0f*3.14159f;
01229         std::swap(a0, a1);
01230         std::swap(p0, p1);
01231     }
01232 
01233     int vert_count = 10;
01234     float delta = (a1-a0)/(float)vert_count;      
01235     float angle = a0;
01236 
01237     // Add arc. Push back vertices in pairs so they
01238     // can be drawn as GL_LINEs
01239     CVect2<float> prev_vert;
01240 
01241     for(int i=0; i<=vert_count; i++){     
01242         if (i==0) {
01243             prev_vert = p0;
01244         }
01245         else if (i==vert_count) {
01246             edges.push_back(prev_vert);
01247             edges.push_back(p1);            
01248         }
01249         else {
01250             edges.push_back(prev_vert);
01251             edges.push_back(CVect2<float>(corner_x + c.corner_width_x*cosf(angle), 
01252                                           corner_y + c.corner_width_y*sinf(angle)));
01253             prev_vert = edges[edges.size()-1];
01254         }
01255         angle += delta;
01256     }
01257 }
01258 
01259 /*********************** CBoundaryPoints ***********************************/
01260 void CBoundaryPoints::AddBoundedPoint(const CVect2<float>& pt)
01261 {
01262     m_GraphPoints.push_back(pt);
01263 }
01264 
01265 void CBoundaryPoints::AddPixelRect(const CVect2<float>& pt, 
01266                                    const CGlRect<float>& offset)
01267 {
01268     m_PixelPoints.push_back(std::pair<CVect2<float>, CGlRect<float> >(pt, offset));
01269 }
01270 
01271 void CBoundaryPoints::AddBoundedPoints(CBoundaryPoints& pts)
01272 {
01273     m_GraphPoints.insert(m_GraphPoints.begin(), 
01274                          pts.m_GraphPoints.begin(), 
01275                          pts.m_GraphPoints.end());
01276     m_PixelPoints.insert(m_PixelPoints.begin(), 
01277                          pts.m_PixelPoints.begin(), 
01278                          pts.m_PixelPoints.end());
01279 
01280 
01281     pts.ClearBoundedPoints();
01282 }
01283 
01284 void CBoundaryPoints::ClearBoundedPoints() 
01285 {
01286     m_GraphPoints.clear(); 
01287     m_PixelPoints.clear();
01288 }
01289 
01290 /*********************** CSubtreeBoundary ***********************************/
01291 CSubtreeBoundary::~CSubtreeBoundary()
01292 {
01293 }
01294 
01295 
01296 void CSubtreeBoundary::RenderBoundary(const CVect2<float>& scale, float alpha_mod)
01297 {
01298     if (!m_BoundaryShape.Empty())
01299         m_BoundaryShape->Render(scale, alpha_mod);
01300 }
01301 
01302 void CSubtreeBoundary::RenderBoundaryVbo(CRef<CGlVboNode>& tri_node,
01303                                          CRef<CGlVboNode>& edge_node,
01304                                          const CVect2<float>& scale,
01305                                          float alpha_mod)
01306 {
01307     if (!m_BoundaryShape.Empty())
01308         m_BoundaryShape->RenderVbo(tri_node, edge_node, scale, alpha_mod);
01309 }
01310 
01311 void CSubtreeBoundary::ComputeShapes(const CBoundaryPoints& boundary_pts,
01312                                      const CVect2<float>& scale,
01313                                      const CVect2<float>& base_node_pos,
01314                                      const string& layout_type)
01315 {
01316     if (m_Shapes.find(layout_type) != m_Shapes.end())
01317         m_BoundaryShape = m_Shapes[layout_type];
01318     else
01319         m_BoundaryShape = m_Shapes["Default"];
01320 
01321     if (!m_BoundaryShape.Empty()) {
01322         m_BoundaryShape->ComputeShape(scale, 
01323                                       base_node_pos,                                   
01324                                       boundary_pts.GetGraphPoints(), 
01325                                       boundary_pts.GetPixelPoints());
01326         m_BoundaryShape->Hide(false);
01327     }
01328 }
01329 
01330 void CSubtreeBoundary::Hide(bool b) 
01331 {
01332     if (!m_BoundaryShape.Empty()) {
01333         m_BoundaryShape->Hide(b);
01334     }
01335 }
01336 
01337 string CSubtreeBoundary::x_GetParameter(const string& features, const string& parm)
01338 {
01339     string parm_lower = parm;
01340     parm_lower = NStr::ToLower(parm_lower) + "=";
01341 
01342     string features_lower = features;
01343     features_lower = NStr::ToLower(features_lower);
01344 
01345     size_t idx = features_lower.find(parm_lower);
01346     if (idx != string::npos) {
01347         size_t end_idx = features.find_first_of(" ,\t", idx);
01348         if (end_idx == string::npos)
01349             end_idx = features.size();
01350 
01351         return features.substr(idx + parm_lower.length(), end_idx-(idx+parm_lower.length()));
01352     }
01353 
01354     return "";
01355 }
01356 
01357 bool CSubtreeBoundary::x_GetColorParameter(const string& features, 
01358                                            const string& parm,
01359                                            bool parm_required,
01360                                            CRgbaColor& c)
01361 {
01362     string parm_lower = parm;
01363     parm_lower = NStr::ToLower(parm_lower) + "=[";
01364 
01365     string features_lower = features;
01366     features_lower = NStr::ToLower(features_lower);
01367 
01368     string::size_type idx1 = string::npos;
01369     
01370     if (!parm_required) {
01371         if (features[0] == '[')
01372             idx1 = 0;
01373         else {
01374             idx1 = features.find(" [");
01375             if (idx1 != string::npos) 
01376                 idx1 += 2;
01377         }
01378     }
01379 
01380     if (idx1 == string::npos)  {
01381         idx1 = features_lower.find(parm_lower);
01382         if (idx1 != string::npos) {
01383             idx1 += parm_lower.length();
01384         }
01385     }
01386 
01387     if (idx1 != string::npos) {
01388         string::size_type idx2 = features.find(']', idx1 + 2);
01389         if (idx2 != string::npos) {
01390             // There is a color array.  
01391             string color_str = features.substr(idx1, idx2-idx1);
01392             c.FromString(color_str);
01393 
01394             // Was the alpha value provided? if not use default.
01395            // list<string> toks;
01396            // NStr::Split(color_str.substr(1, idx2-idx1-1), ", ", toks);
01397            // if (toks.size() < 4)
01398            //     c.SetAlpha(0.25f);
01399 
01400             return true;
01401         }
01402     }
01403 
01404     return false;
01405 }
01406 
01407 
01408 void CSubtreeBoundary::x_AddBoundaryType(const string& boundary_type,
01409                                          const string& layout_type)
01410 {
01411     if (boundary_type != "") {       
01412         IBoundaryShape* s = IBoundaryShape::CreateBoundary(boundary_type);
01413         if (s != NULL) {
01414             s->SetBoundaryParms(m_Parms);         
01415             CRef<IBoundaryShape> sr(s);
01416             m_Shapes[layout_type] = sr;
01417         }
01418     }
01419 }
01420 
01421 
01422 void CSubtreeBoundary::CreateShapes(const std::string& features)
01423 {
01424     // default to red if color not provided
01425     m_Parms.GetColor().Set(1.0f, 0.0f, 0.0f, 0.25f);
01426 
01427     // find color string in feature in form [...]
01428     // may be given without a name or as color=[...]
01429     string color_str;
01430     
01431     //string::size_type idx1 = features.find_first_of('[');
01432 
01433     // If there is a qualifier name of the form name=[..] and 
01434     // the qualifier is "color=" then this is the color we want.
01435     // Also if there is no qualifier (no name=), then that is
01436     // the color we want.
01437     x_GetColorParameter(features, "Color", false, m_Parms.GetColor());
01438 
01439     //
01440     // shape=RoundedRect CladogramShape=Rect RadialShape=Ellipse ForceShape=Triangle
01441     // Color=[128, 128, 128, 32]
01442     // Border=22
01443     // 
01444     //map<string, CRef<IBoundaryShape> > m_Shapes;
01445     m_Shapes.clear();
01446 
01447     string parm;
01448     
01449     parm = x_GetParameter(features, "Border");
01450     if (parm != "")
01451         m_Parms.SetBorderWidth(NStr::StringToDouble(parm, NStr::fConvErr_NoThrow));
01452 
01453     parm = x_GetParameter(features, "Corner");
01454     if (parm != "")
01455         m_Parms.SetCornerWidth(NStr::StringToDouble(parm, NStr::fConvErr_NoThrow));
01456 
01457     // valid values: true t yes y | false f no n
01458     try {
01459         parm = x_GetParameter(features, "DrawEdge");
01460         if (parm != "")
01461             m_Parms.SetDrawBoundaryEdge(NStr::StringToBool(parm));
01462     }
01463     catch (CStringException&) {
01464         // just use default
01465     };
01466 
01467     x_GetColorParameter(features, "EdgeColor", true, m_Parms.GetBoundaryEdgeColor());
01468 
01469     try {
01470         parm = x_GetParameter(features, "IncludeText");
01471         if (parm != "")
01472             m_Parms.SetIncludeTextArea(NStr::StringToBool(parm));
01473     }
01474     catch (CStringException&) {
01475         // just use default
01476     };
01477 
01478     try {
01479         parm = x_GetParameter(features, "AxisAligned");
01480         if (parm != "")
01481             m_Parms.SetAxisAligned(NStr::StringToBool(parm));
01482     }
01483     catch (CStringException&) {
01484         // just use default
01485     };
01486 
01487     try {
01488         parm = x_GetParameter(features, "TextBox");
01489         if (parm != "")
01490             m_Parms.SetTextBox(NStr::StringToBool(parm));
01491     }
01492     catch (CStringException&) {
01493         // just use default
01494     };
01495 
01496     parm = x_GetParameter(features, "TriOffset");
01497     if (parm != "")
01498         m_Parms.SetTriOffset(NStr::StringToDouble(parm, NStr::fConvErr_NoThrow));
01499 
01500     parm = x_GetParameter(features, "Shape");
01501     if (parm == "")
01502         parm = "RoundedRectangle";
01503 
01504     IBoundaryShape* s = IBoundaryShape::CreateBoundary(parm);
01505     if (s != NULL) {
01506         s->SetBoundaryParms(m_Parms);
01507 
01508         CRef<IBoundaryShape> sr(s);
01509         m_Shapes["Default"] = sr;
01510     }
01511 
01512     x_AddBoundaryType(x_GetParameter(features, "SlantedCladogram"), 
01513                       "SlantedCladogram");
01514     x_AddBoundaryType(x_GetParameter(features, "RectCladogram"), 
01515                       "RectCladogram");
01516     x_AddBoundaryType(x_GetParameter(features, "Radial"), 
01517                       "Radial");
01518     x_AddBoundaryType(x_GetParameter(features, "ForceLayout"), 
01519                       "ForceLayout");
01520 
01521     m_BoundaryShape = m_Shapes["Default"];
01522 }
01523 
01524 END_NCBI_SCOPE
Modified on Sun Jul 13 17:39:17 2014 by modify_doxy.py rev. 426318