drawing sequence basepairs.
authorDiane Trout <diane@caltech.edu>
Mon, 6 Mar 2006 08:59:35 +0000 (08:59 +0000)
committerDiane Trout <diane@caltech.edu>
Mon, 6 Mar 2006 08:59:35 +0000 (08:59 +0000)
OpenGL is not very text friendly. But I can draw AGCTs with something
that's vaguely readable. I tried to use FTGL (a freetype based GL font
rendering library, but I couldn't get it to control the width of fonts
and so they ended up rendering on top of each other.) I used an example
out of the opengl redbook, to hand code glyphs that are at least passable.

The current block of code manages to turn on text rendering only when there's
enough space to make them legible. However I'm not completely convinced that
the text is actually in the right place.

There's also a problem with the python to C++ interface that I worked around
if you create a Sequence object and point a GlSequence to it, if the
python copy goes away, the copy in GlSequence dies, which makes rendering
sequence data a bit challenging.

alg/conserved_path.cxx
mussagl.pro
py/gl.py
py/module.mk
qui/GlSequence.cxx
qui/GlSequence.h
qui/PathScene.cxx
qui/PathScene.h
qui/PathWindow.cxx

index cdd9392a7d1c419b70709dbb1e9e70a069937dfc..bf845ad9a9cf70ab9334abf402c4b67622baeba6 100644 (file)
@@ -73,6 +73,7 @@ std::ostream& operator<<(std::ostream& out, const ConservedPath& path)
     // if we had no elements just close the block
     out << "/>";
   }
+  return out;
 }
 
 void ConservedPath::push_back(const ConservedPath::path_element& element)
index 52bee654ab3ff888fb411ddd5db4a7007bfc0e84..9823fec5cbd4386bbfdf0e2f6087081a9bf3008a 100644 (file)
@@ -8,7 +8,7 @@ DEPENDPATH += . \
               alg \
               qui 
 #              test 
-INCLUDEPATH += . alg qui
+INCLUDEPATH += . alg qui 
 
 # Input
 HEADERS += mussa_exceptions.hh \
@@ -47,5 +47,5 @@ SOURCES += mussagl.cxx \
 #           test/test_nway.cxx \
 #           test/test_sequence.cxx 
 
-LIBS += -lm -lboost_program_options
+LIBS += -lm -lboost_program_options 
 QT += opengl
index 4f9ca0d1e0c7dd647d1aea2132a6851dd6f3e024..a9d675b7afa5140676cb1d0841d7db43e694bdde 100644 (file)
--- a/py/gl.py
+++ b/py/gl.py
@@ -9,26 +9,25 @@ import mussa
   
 class pyMussaGL:
   def __init__(self):
-    seq = mussa.Sequence()
-    s = "AAGGCCTTAAGGCCTT" * 100
-    seq = mussa.Sequence(s)
-    assert len(s) == seq.size() == len(seq)
-    self.glseq = mussa.GlSequence(seq)
+    self.s = "AAGGCCTT" * 5
+    self.seq = mussa.Sequence(self.s)
+    self.glseq = mussa.GlSequence(self.seq)
+    print self.glseq.width
 
     glutInit(sys.argv)
     glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH )
     glutCreateWindow('mussa')
     glClearColor(1,1,1,0)
 
-    glOrtho(-50, len(seq)+50, -50, 50, -50, 50)
+    glOrtho(-10, len(self.s)+10, -50, 50, -50, 50)
 
     glutDisplayFunc(self.display)
     print "init done"
   
   def display(self):
     glClear(GL_COLOR_BUFFER_BIT)
-    self.glseq.draw()
-    glutSwapBuffers()
+    self.glseq.draw(-50, self.glseq.width+50)
+    #glutSwapBuffers()
 
 if __name__ == "__main__":
   p = pyMussaGL()
index b122d73a78ba7d9188e3e4c30a9ad6dd1f4cb1f9..d129a1ab4a3ac39774d8a06311cef3845b0ec0c5 100644 (file)
@@ -13,7 +13,7 @@ MUSSA_PY_OBJ := $(MUSSA_PY_SRC:.cxx=$(OBJEXT))
 SRC += $(MUSSA_PY_SRC)
 CXXFLAGS += 
 
-MUSSAPY := $(BASEDIR)/mussa.so
+MUSSAPY := $(CURDIR)/mussa.so
 TARGETBINS += $(MUSSAPY)
 
 $(MUSSAPY): $(MUSSA_PY_OBJ) $(MUSSA_ALG_OBJ) $(MUSSA_ALG_GL_OBJ)
index 09437b54bbafa6dae1e4065b16112cf2c9dfb758..de9dfd9ddc22c8e7abeaefa3f58429c72f88dfe0 100644 (file)
@@ -1,6 +1,9 @@
 #include "qui/GlSequence.h"
 
 #include <iostream>
+#include <cassert>
+#include <math.h>
+#include <stdexcept>
 using namespace std;
 
 GlSequence::GlSequence(const Sequence &s) 
@@ -9,8 +12,9 @@ GlSequence::GlSequence(const Sequence &s)
     seq_y(0.0), 
     seq_z(1.0), 
     seq_width(s.size()),
-    seq_height(gl_track_height) 
+    seq_height(gl_track_height)
 {
+  assert (seq.size() == seq_width);
 }
 
 GlSequence::GlSequence(const GlSequence &s) 
@@ -21,6 +25,7 @@ GlSequence::GlSequence(const GlSequence &s)
     seq_width(s.seq_width),
     seq_height(s.seq_height)
 {
+  assert (seq.size() == seq_width);
 }
 
 GlSequence &GlSequence::operator=(const GlSequence & s)
@@ -36,7 +41,7 @@ GlSequence &GlSequence::operator=(const GlSequence & s)
   return *this;
 }
 
-const Sequence &GlSequence::sequence() const
+const SequenceGlSequence::sequence() const
 {
   return seq;
 }
@@ -72,7 +77,7 @@ GLfloat GlSequence::width() const
 }
 
 
-void GlSequence::draw() const
+void GlSequence::draw(GLfloat left, GLfloat right) const
 {
   glLineWidth(seq_height);
   glColor3f(0.0, 0.0, 0.0);
@@ -96,4 +101,121 @@ void GlSequence::draw() const
       glVertex3f(annot_itor->end  , seq_y, annotation_z);
     glEnd();
   }
+  draw_sequence(left, right);
+}
+
+const int PT = 1;
+const int STROKE = 2;
+const int END =3;
+
+typedef struct charpoint {
+  GLfloat x, y;
+  int type;
+} CP;
+
+CP Adata[] = {
+  {0, 0, PT}, {0, 9, PT}, {1, 10, PT}, {4, 10, PT}, 
+  {5, 9, PT}, {5, 0, STROKE}, {0, 5, PT}, {5, 5, END}
+};
+
+CP Tdata[] = {
+  {2.5, 0, PT}, {2.5,10, STROKE}, {0, 10, PT}, {5,10, END}
+};
+
+CP Gdata[] = {
+  {5, 9, PT}, {4, 10, PT}, {1, 10, PT}, {0, 9, PT}, {0, 1, PT},
+  {1, 0, PT}, {5, 0, PT}, {5, 4, PT}, {3.5, 4, END}
+};
+
+CP Cdata[] = {
+  {5, 8, PT}, {3, 10, PT}, {2, 10, PT}, {0, 8, PT}, {0, 2, PT},
+  {2, 0, PT}, {3, 0, PT}, {5, 2, END}
+};
+
+CP Xdata[] = {{ 0, 10, PT}, {5,0,STROKE},{0,0,PT},{5,10,END}};
+CP Ndata[] = {{ 0, 0, PT}, {0, 10, PT}, {5, 0, PT}, {5, 10, END}};
+
+static void drawLetter(CP *l)
+{
+  glBegin(GL_LINE_STRIP);
+  while(1) {
+    switch (l->type) {
+      case PT:
+        glVertex2fv(&l->x);
+        break;
+      case STROKE:
+        glVertex2fv(&l->x);
+        glEnd();
+        glBegin(GL_LINE_STRIP);
+        break;
+      case END:
+        glVertex2fv(&l->x);
+        glEnd();
+        return;
+        break;
+      default:
+         throw runtime_error("data structure failure");
+    }
+    l++;
+  }
+}
+
+void GlSequence::draw_sequence(GLfloat left, GLfloat right) const
+{
+  int viewport[4];
+  glGetIntegerv(GL_VIEWPORT, viewport);
+  int port_width = viewport[3]; // grab the viewport width
+  GLfloat world_width = right - left;
+  int left_base = (left > 0) ? (int)ceil(left) : 0;
+  int right_base = (int)floor(right);
+  int render_count = (right_base < seq.size()) ? right_base : seq.size(); 
+  render_count -= left_base;
+
+  glLineWidth(0.01);
+  GLfloat char_width = 1.1;
+  GLfloat pixels_needed = char_width * world_width;
+
+  cout << "seq width needed " << pixels_needed 
+       << " port width " << port_width 
+       << " count " << render_count 
+       << " left " << left_base 
+       << " right " << right_base 
+       << " size " << seq.size() << endl;
+  // if the number of pixels taken up by rendering the characters 
+  // that'd show up in the current ortho width is less than the window
+  // width we can actually draw something 
+  if (pixels_needed < port_width and render_count > 0) {
+    cout << "rendering: ";
+    string bases = seq.subseq(left_base, render_count);
+    glColor3f(0.1, 0.1, 0.1);
+    for (string::size_type basepair = 0; basepair != bases.size(); ++basepair) 
+    {
+      glPushMatrix();
+      glTranslatef( left_base + basepair, seq_y+20, 1.0 );
+      glScalef(0.1, 1.0, 1.0);
+      switch (bases[basepair]) {
+        case 'A': case 'a':
+          drawLetter(Adata);
+        break;
+        case 'T': case 't':
+          drawLetter(Tdata);
+        break;
+        case 'G': case 'g':
+          drawLetter(Gdata);
+        break;
+        case 'C': case 'c':
+          drawLetter(Cdata);
+        break;
+        case 'N': case 'n':
+          drawLetter(Ndata);
+        break;
+        default:
+          drawLetter(Xdata);
+        break;
+      }
+      cout << bases[basepair];
+      glPopMatrix();
+    }
+    cout << endl;
+  }
 }
index 4fb37da049457784e11da11d9435a52e0d8ac4f7..343f9ff29a76aad96127a21e1e8c409ae626c4a0 100644 (file)
@@ -15,9 +15,12 @@ public:
   GlSequence(const GlSequence & s);
   GlSequence &operator=(const GlSequence &s);
 
-  void draw() const;
+  //! draw a track 
+  /*! left and right are the current edges of the viewable world
+   */
+  void draw(GLfloat left, GLfloat right) const;
 
-  const Sequence &sequence() const;
+  const Sequencesequence() const;
   void setX(GLfloat);
   GLfloat x() const;
   void setY(GLfloat);
@@ -25,13 +28,18 @@ public:
   void setWidth(GLfloat);
   GLfloat width() const;
 
-private:
+protected:
   const Sequence& seq;
   GLfloat seq_x;
   GLfloat seq_y;
   GLfloat seq_z;
   GLfloat seq_width;
   GLfloat seq_height;
+
+  //! render a sequence (if we have enough space
+  /*! left and right are the current edges of the viewable world
+   */
+  void draw_sequence(GLfloat, GLfloat) const;
 };
 
 const float gl_track_height = 10.0;
index 759014ec6835ddcb5e61107d6f7833c8bf2c0846..e97429cef7d0285f8551d89ba8dbc7d83f4fcdf4 100644 (file)
@@ -19,7 +19,8 @@ using namespace std;
 
 PathScene::PathScene(Mussa* analysis, QWidget *parent) : 
   QGLWidget(parent),
-  X(0),
+  viewport_height(0),
+  viewport_width(0),
   clipZ(30.0),
   zoom(0),
   maxOrtho2d(-50.0, -50, 3000000.0, 300.0),
@@ -44,14 +45,6 @@ QSize PathScene::sizeHint() const
   return QSize(400, 400);
 }
 
-void PathScene::setX(int newX)
-{
-  if (X != newX) {
-    X = newX;
-    update();
-  }
-}
-
 static float max(float a, float b)
 {
   if ( a < b) 
@@ -178,6 +171,10 @@ void PathScene::initializeGL()
 
 void PathScene::resizeGL(int width, int height)
 {
+  viewport_width = width;
+  viewport_height = height;
+  assert (geometry().width() == width);
+  assert (geometry().height() == height);
   glViewport(0, 0, (GLsizei)width, (GLsizei)height);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
@@ -379,7 +376,7 @@ void PathScene::draw_tracks() const
   for (vector<GlSequence>::size_type i = 0; i != tracks.size(); ++i )
   {
     glLoadName(i);
-    tracks[i].draw();
+    tracks[i].draw(curOrtho2d.left(), curOrtho2d.right());
   }
   glPopName();
   glPopMatrix();
@@ -486,4 +483,3 @@ void PathScene::mussaesque()
   glPopName();
 }
 
-
index d836c1917e0cfb3fa06b092854bdead1b4ed5720..f25c1cd617c404cc9c995c0335bc97a9086c7e8e 100644 (file)
@@ -27,7 +27,6 @@ public:
   std::vector<GlSequence> tracks;
 
 public slots:
-  void setX(int x);
   void setClipPlane(int z);
   void setZoom(int);
   //! load a mussa parameter file (which specifies an analysis to run)
@@ -44,7 +43,8 @@ signals:
   void analysisUpdated();
 
 protected:
-  int X;
+  int viewport_height;
+  int viewport_width;
   double clipZ;
   int zoom;
   QRectF maxOrtho2d;
index b6fc07a5e3a37e72a198fdc8f56396fa56336c3d..d263b4590e68ba437b93f200b646c21a56ba5e6b 100644 (file)
@@ -37,6 +37,7 @@ PathWindow::PathWindow(Mussa *analysis, QWidget *) :
 
   QSpinBox *zoom = new QSpinBox();
   zoom->setWhatsThis("zoom magnification factor");
+  zoom->setRange(0,1000);
   mussaViewTB->addWidget(zoom);
   connect(zoom, SIGNAL(valueChanged(int)), scene, SLOT(setZoom(int)));