From 2b8f5c248968747208c8cc44bbebfd1ed6219564 Mon Sep 17 00:00:00 2001 From: Diane Trout Date: Mon, 6 Mar 2006 08:59:35 +0000 Subject: [PATCH] drawing sequence basepairs. 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 | 1 + mussagl.pro | 4 +- py/gl.py | 15 +++-- py/module.mk | 2 +- qui/GlSequence.cxx | 128 ++++++++++++++++++++++++++++++++++++++++- qui/GlSequence.h | 14 ++++- qui/PathScene.cxx | 18 +++--- qui/PathScene.h | 4 +- qui/PathWindow.cxx | 1 + 9 files changed, 157 insertions(+), 30 deletions(-) diff --git a/alg/conserved_path.cxx b/alg/conserved_path.cxx index cdd9392..bf845ad 100644 --- a/alg/conserved_path.cxx +++ b/alg/conserved_path.cxx @@ -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) diff --git a/mussagl.pro b/mussagl.pro index 52bee65..9823fec 100644 --- a/mussagl.pro +++ b/mussagl.pro @@ -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 diff --git a/py/gl.py b/py/gl.py index 4f9ca0d..a9d675b 100644 --- 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() diff --git a/py/module.mk b/py/module.mk index b122d73..d129a1a 100644 --- a/py/module.mk +++ b/py/module.mk @@ -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) diff --git a/qui/GlSequence.cxx b/qui/GlSequence.cxx index 09437b5..de9dfd9 100644 --- a/qui/GlSequence.cxx +++ b/qui/GlSequence.cxx @@ -1,6 +1,9 @@ #include "qui/GlSequence.h" #include +#include +#include +#include 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 Sequence& GlSequence::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; + } } diff --git a/qui/GlSequence.h b/qui/GlSequence.h index 4fb37da..343f9ff 100644 --- a/qui/GlSequence.h +++ b/qui/GlSequence.h @@ -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 Sequence& sequence() 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; diff --git a/qui/PathScene.cxx b/qui/PathScene.cxx index 759014e..e97429c 100644 --- a/qui/PathScene.cxx +++ b/qui/PathScene.cxx @@ -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::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(); } - diff --git a/qui/PathScene.h b/qui/PathScene.h index d836c19..f25c1cd 100644 --- a/qui/PathScene.h +++ b/qui/PathScene.h @@ -27,7 +27,6 @@ public: std::vector 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; diff --git a/qui/PathWindow.cxx b/qui/PathWindow.cxx index b6fc07a..d263b45 100644 --- a/qui/PathWindow.cxx +++ b/qui/PathWindow.cxx @@ -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))); -- 2.30.2