# HG changeset patch # User John Tsiombikas # Date 1419953318 -7200 # Node ID 71b479ffb9f7da7d62af71a2a156165fbfa78bba # Parent f22be47a35723c1ca98db8e5987c4fb7dcd9e8ad curve manipulation works diff -r f22be47a3572 -r 71b479ffb9f7 Makefile --- a/Makefile Tue Dec 30 06:22:54 2014 +0200 +++ b/Makefile Tue Dec 30 17:28:38 2014 +0200 @@ -5,7 +5,7 @@ bin = dicomview -opt = -O3 -ffast-math +#opt = -O3 -ffast-math dbg = -g def = -DUSE_GLUT #inc = diff -r f22be47a3572 -r 71b479ffb9f7 src/curve.cc --- a/src/curve.cc Tue Dec 30 06:22:54 2014 +0200 +++ b/src/curve.cc Tue Dec 30 17:28:38 2014 +0200 @@ -43,7 +43,7 @@ CurvePoint *Curve::get_point(int idx) { - if(idx < 0 || idx >= cp.size()) { + if(idx < 0 || idx >= (int)cp.size()) { return 0; } return &cp[idx]; @@ -51,7 +51,7 @@ const CurvePoint *Curve::get_point(int idx) const { - if(idx < 0 || idx >= cp.size()) { + if(idx < 0 || idx >= (int)cp.size()) { return 0; } return &cp[idx]; diff -r f22be47a3572 -r 71b479ffb9f7 src/dicomview.h --- a/src/dicomview.h Tue Dec 30 06:22:54 2014 +0200 +++ b/src/dicomview.h Tue Dec 30 17:28:38 2014 +0200 @@ -19,6 +19,7 @@ void redisplay(); void quit(); void get_window_size(int *xsz, int *ysz); +unsigned int get_modifiers(); #ifdef __cplusplus } diff -r f22be47a3572 -r 71b479ffb9f7 src/main.cc --- a/src/main.cc Tue Dec 30 06:22:54 2014 +0200 +++ b/src/main.cc Tue Dec 30 17:28:38 2014 +0200 @@ -21,6 +21,7 @@ static void motion(int x, int y); static int win_width, win_height; +static unsigned int mod; int main(int argc, char **argv) { @@ -40,6 +41,7 @@ glutKeyboardUpFunc(key_up); glutMouseFunc(mouse); glutMotionFunc(motion); + glutPassiveMotionFunc(motion); glewInit(); @@ -73,6 +75,11 @@ *ysz = win_height; } +unsigned int get_modifiers() +{ + return mod; +} + static void display() { ev_display(); @@ -87,16 +94,19 @@ static void key_down(unsigned char key, int x, int y) { + mod = glutGetModifiers(); ev_keyboard(key, 1, x, y); } static void key_up(unsigned char key, int x, int y) { + mod = glutGetModifiers(); ev_keyboard(key, 0, x, y); } static void mouse(int bn, int state, int x, int y) { + mod = glutGetModifiers(); ev_mouse_button(bn - GLUT_LEFT_BUTTON, state == GLUT_DOWN ? 1 : 0, x, y); } diff -r f22be47a3572 -r 71b479ffb9f7 src/rend_fast.cc --- a/src/rend_fast.cc Tue Dec 30 06:22:54 2014 +0200 +++ b/src/rend_fast.cc Tue Dec 30 17:28:38 2014 +0200 @@ -3,8 +3,6 @@ #include "rend_fast.h" #include "sdr.h" -static inline float smoothstep(float a, float b, float x); - #define XFER_MAP_SZ 1024 static unsigned int sdr; @@ -117,6 +115,7 @@ if(xfer) { xfer->map(x, pptr); + pptr[3] = std::max(pptr[0], std::max(pptr[1], pptr[2])); } else { pptr[0] = pptr[1] = pptr[2] = pptr[3] = x; } @@ -168,12 +167,3 @@ glMatrixMode(GL_MODELVIEW); glPopMatrix(); } - -static inline float smoothstep(float a, float b, float x) -{ - if(x < a) return 0.0; - if(x >= b) return 1.0; - - x = (x - a) / (b - a); - return x * x * (3.0 - 2.0 * x); -} diff -r f22be47a3572 -r 71b479ffb9f7 src/xfer_view.cc --- a/src/xfer_view.cc Tue Dec 30 06:22:54 2014 +0200 +++ b/src/xfer_view.cc Tue Dec 30 17:28:38 2014 +0200 @@ -1,11 +1,14 @@ #include +#include #include "opengl.h" #include "xfer_view.h" #include "dicomview.h" static TransferFunc *xfer; -static int act_color = 3; +static int act_color = -1; +static int grabbed_handle = -1; +static int mousex, mousey; bool xfview_init(TransferFunc *xferarg) { @@ -25,6 +28,8 @@ glMatrixMode(GL_PROJECTION); glLoadIdentity(); + glOrtho(0, 1, 0, 1, -1, 1); + glMatrixMode(GL_MODELVIEW); glLoadIdentity(); @@ -34,7 +39,8 @@ // paint the background a faint version of the selected color glBegin(GL_QUADS); - glColor3f(line_color[act_color][0] * 0.1, line_color[act_color][1] * 0.1, line_color[act_color][2] * 0.1); + int cidx = act_color == -1 ? 3 : act_color; + glColor3f(line_color[cidx][0] * 0.1, line_color[cidx][1] * 0.1, line_color[cidx][2] * 0.1); glVertex2f(-1, -1); glVertex2f(1, -1); glVertex2f(1, 1); @@ -46,15 +52,105 @@ glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // draw handles on the selected curve + TransferWindow *xfwin = dynamic_cast(xfer); + if(xfwin) { + float dx = 1.0 / (float)xsz; + float cursor = (float)mousex / (float)xsz; + float low[3], high[3]; + xfwin->get_interval_rgba(low, high); + + if(act_color == -1) { // all curves + int nearest = xfwin->nearest_handle(-1, cursor); + + if(grabbed_handle != -1) { + glBegin(GL_LINES); + glColor3f(0.8, 0.8, 0.8); + for(int i=0; i<3; i++) { + float x = xfwin->get_handle(i, nearest); + float rad = xfwin->get_soft_radius(); + glVertex2f(x - rad, 0.5); + glVertex2f(x + rad, 0.5); + glVertex2f(x - rad, 0.4); + glVertex2f(x - rad, 0.6); + glVertex2f(x + rad, 0.4); + glVertex2f(x + rad, 0.6); + } + glEnd(); + } + + // draw handles on all lines, highlighting the nearest side of all of them + glBegin(GL_QUADS); + glColor3f(1, 1, 1); + + for(int i=0; i<3; i++) { + float x = nearest == TransferWindow::HANDLE_LOW ? low[i] : high[i]; + glVertex2f(x - 2.0 * dx, -1); + glVertex2f(x + 2.0 * dx, -1); + glVertex2f(x + 2.0 * dx, 1); + glVertex2f(x - 2.0 * dx, 1); + } + for(int i=0; i<3; i++) { + glColor3fv(line_color[i]); + glVertex2f(low[i] - dx, -1); + glVertex2f(low[i] + dx, -1); + glVertex2f(low[i] + dx, 1); + glVertex2f(low[i] - dx, 1); + glVertex2f(high[i] - dx, -1); + glVertex2f(high[i] + dx, -1); + glVertex2f(high[i] + dx, 1); + glVertex2f(high[i] - dx, 1); + } + glEnd(); + + } else { + int nearest = xfwin->nearest_handle(act_color, cursor); + float x = nearest == TransferWindow::HANDLE_LOW ? low[act_color] : high[act_color]; + + if(grabbed_handle != -1) { + float x = xfwin->get_handle(act_color, nearest); + float rad = xfwin->get_soft_radius(); + + glBegin(GL_LINES); + glColor3f(0.8, 0.8, 0.8); + glVertex2f(x - rad, 0.5); + glVertex2f(x + rad, 0.5); + glVertex2f(x - rad, 0.4); + glVertex2f(x - rad, 0.6); + glVertex2f(x + rad, 0.4); + glVertex2f(x + rad, 0.6); + glEnd(); + } + + + glBegin(GL_QUADS); + glColor3f(1, 1, 1); + glVertex2f(x - 2.0 * dx, -1); + glVertex2f(x + 2.0 * dx, -1); + glVertex2f(x + 2.0 * dx, 1); + glVertex2f(x - 2.0 * dx, 1); + + glColor3fv(line_color[act_color]); + for(int i=0; i<2; i++) { + glVertex2f(x - dx, -1); + glVertex2f(x + dx, -1); + glVertex2f(x + dx, 1); + glVertex2f(x - dx, 1); + x = nearest == TransferWindow::HANDLE_LOW ? high[act_color] : low[act_color]; + } + glEnd(); + } + } + // draw curve glLineWidth(2.0); - for(int i=0; i<3; i++) { + for(int i=0; i<4; i++) { int idx; - if(act_color < 3) { - idx = (i + act_color + 1) % 3; + if(act_color == -1) { + idx = i; } else { - idx = i; + idx = (i + act_color + 1) % 4; } glColor3fv(line_color[idx]); @@ -63,9 +159,14 @@ for(int j=0; jmap(t, vval); - glVertex2f(t * 2.0 - 1.0, vval[i] * 2.0 - 1.0); + if(idx < 3) { + xfer->map(t, vval); + } else { + vval[3] = xfer->map(t); + } + + glVertex2f(t, vval[idx]); } glEnd(); } @@ -73,21 +174,64 @@ glDisable(GL_BLEND); } +static int prev_x, prev_y; + void xfview_button(int bn, int press, int x, int y) { - if(bn == 2 && press) { - act_color = (act_color + 1) % 4; + prev_x = x; + prev_y = y; + + if(bn == 2 && press && grabbed_handle == -1) { + act_color = (act_color + 2) % 4 - 1; redisplay(); return; } - if(bn == 1) { - if(press) { + if(bn == 0) { + int xsz, ysz; + get_window_size(&xsz, &ysz); + + TransferWindow *xfwin = dynamic_cast(xfer); + if(xfwin && press) { + float cursor = (float)x / (float)xsz; + float low[3], high[3]; + xfwin->get_interval_rgba(low, high); + + // grab the nearest handle + grabbed_handle = xfwin->nearest_handle(act_color, cursor); } else { + grabbed_handle = -1; } + redisplay(); } } void xfview_motion(int x, int y) { + mousex = x; + mousey = y; + + int dx = x - prev_x; + int dy = y - prev_y; + prev_x = x; + prev_y = y; + + if(grabbed_handle != -1) { + TransferWindow *xfwin = dynamic_cast(xfer); + if(!xfwin) return; + + int xsz, ysz; + get_window_size(&xsz, &ysz); + + if(get_modifiers()) { + float soft = xfwin->get_soft_radius() + dy * 0.01; + if(soft < 0.0) soft = 0.0; + xfwin->set_soft_radius(soft); + } else { + float pos = (float)x / (float)xsz; + xfwin->set_handle(act_color, grabbed_handle, pos); + } + } + + redisplay(); } diff -r f22be47a3572 -r 71b479ffb9f7 src/xfermap.cc --- a/src/xfermap.cc Tue Dec 30 06:22:54 2014 +0200 +++ b/src/xfermap.cc Tue Dec 30 17:28:38 2014 +0200 @@ -1,3 +1,4 @@ +#include #include #include "xfermap.h" @@ -11,18 +12,55 @@ TransferWindow::TransferWindow() { soft_rad = 0.5; - for(int i=0; i<4; i++) { + for(int i=0; i<3; i++) { low[i] = 0.5; - high[i] = 2.0; + high[i] = 1.5; } } +void TransferWindow::set_handle(int channel, int handle, float val) +{ + float *dest = handle == HANDLE_LOW ? low : high; + + if(channel == -1) { + dest[0] = dest[1] = dest[2] = val; + } else { + dest[channel] = val; + } +} + +float TransferWindow::get_handle(int channel, int handle) const +{ + const float *src = handle == HANDLE_LOW ? low : high; + + if(channel == -1) { + return src[0]; // XXX this doens't make much sense + } + return src[channel]; +} + +int TransferWindow::nearest_handle(int channel, float pos) const +{ + float ldist = 0, hdist = 0; + + if(channel == -1) { + for(int i=0; i<3; i++) { + ldist += fabs(low[i] - pos); + hdist += fabs(high[i] - pos); + } + } else { + ldist = fabs(low[channel] - pos); + hdist = fabs(high[channel] - pos); + } + return ldist <= hdist ? HANDLE_LOW : HANDLE_HIGH; +} + void TransferWindow::set_interval(float a, float b) { float v0 = std::min(a, b); float v1 = std::max(a, b); - for(int i=0; i<4; i++) { + for(int i=0; i<3; i++) { low[i] = v0; high[i] = v1; } @@ -30,7 +68,7 @@ void TransferWindow::set_interval(float *rgba_low, float *rgba_high) { - for(int i=0; i<4; i++) { + for(int i=0; i<3; i++) { low[i] = std::min(rgba_low[i], rgba_high[i]); high[i] = std::max(rgba_low[i], rgba_high[i]); } @@ -42,6 +80,26 @@ high[channel] = std::max(a, b); } +void TransferWindow::get_interval(float *aptr, float *bptr) const +{ + *aptr = low[0]; + *bptr = high[0]; +} + +void TransferWindow::get_interval_rgba(float *rgba_low, float *rgba_high) const +{ + for(int i=0; i<3; i++) { + rgba_low[i] = low[i]; + rgba_high[i] = high[i]; + } +} + +void TransferWindow::get_interval_rgba(int channel, float *aptr, float *bptr) const +{ + *aptr = low[channel]; + *bptr = high[channel]; +} + void TransferWindow::set_soft_radius(float s) { soft_rad = s; @@ -63,15 +121,17 @@ float TransferWindow::map(float x) const { - return smoothstep(low[3] - soft_rad, high[3] - soft_rad, x) * - (1.0 - smoothstep(low[3] + soft_rad, high[3] + soft_rad, x)); + float rgb[3]; + map(x, rgb); + + return std::max(rgb[0], std::max(rgb[1], rgb[2])); } -void TransferWindow::map(float x, float *rgba_value) const +void TransferWindow::map(float x, float *rgb_value) const { - for(int i=0; i<4; i++) { - float val = smoothstep(low[i] - soft_rad, high[i] - soft_rad, x); - val *= 1.0 - smoothstep(low[i] + soft_rad, high[i] + soft_rad, x); - rgba_value[i] = val; + for(int i=0; i<3; i++) { + float val = smoothstep(low[i] - soft_rad, low[i] + soft_rad, x); + val *= 1.0 - smoothstep(high[i] - soft_rad, high[i] + soft_rad, x); + rgb_value[i] = val; } } diff -r f22be47a3572 -r 71b479ffb9f7 src/xfermap.h --- a/src/xfermap.h Tue Dec 30 06:22:54 2014 +0200 +++ b/src/xfermap.h Tue Dec 30 17:28:38 2014 +0200 @@ -12,11 +12,20 @@ class TransferWindow : public TransferFunc { private: float soft_rad; - float low[4], high[4]; // rgb + float low[3], high[3]; // rgb public: + enum { HANDLE_LOW = 0, HANDLE_HIGH = 1}; + TransferWindow(); + // handle: 0 or HANDLE_LOW is low, 1 or HANDLE_HIGH is high + // if channel == -1, change all channels simultaneously + void set_handle(int channel, int handle, float val); + float get_handle(int channel, int handle) const; + + int nearest_handle(int channel, float pos) const; + void set_interval(float a, float b); void set_interval(float *rgba_low, float *rgba_high); void set_interval_rgba(int channel, float a, float b);