Enhanced curve editor :
- graphical celan up (surrounding frame deleted, histogram realigned) - a feedback tell what point is going to move - on Button press, the point doesn't move to the cursor anymore. Instead, the cursor is hidden during the drag and reappear in the same place it disappeared - by default, a 0.5 factor is used to move the point during the drag (i.e. you have to move the move of 2 pixel to move the point of one pixel) - keyboard can be used to fine tune the point position (subpixel placement) : - CTRL : factor = 0.1 - SHIFT : factor = 0.02 - CTRL+SHIFT : factor = 0.005 In the mean time, i had to create a new "Empty" cursor to hide it. A new method has been created to easily modify the cursor of the main window.
This commit is contained in:
201
rtgui/mycurve.cc
201
rtgui/mycurve.cc
@@ -19,18 +19,17 @@
|
||||
#include <mycurve.h>
|
||||
#include <curves.h>
|
||||
#include <string.h>
|
||||
|
||||
#define RADIUS 3 /* radius of the control points */
|
||||
#define MIN_DISTANCE 8 /* min distance between control points */
|
||||
#include <gdkmm/types.h>
|
||||
|
||||
MyCurve::MyCurve () : listener(NULL), activeParam(-1), bghistvalid(false) {
|
||||
|
||||
cursor_type = Gdk::TOP_LEFT_ARROW;
|
||||
cursor_type = CSArrow;
|
||||
curve.type = Spline;
|
||||
height = 0;
|
||||
grab_point = -1;
|
||||
lit_point = -1;
|
||||
|
||||
add_events(Gdk::EXPOSURE_MASK | Gdk::POINTER_MOTION_MASK | Gdk::POINTER_MOTION_HINT_MASK | Gdk::ENTER_NOTIFY_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::BUTTON1_MOTION_MASK);
|
||||
add_events(Gdk::EXPOSURE_MASK | Gdk::POINTER_MOTION_MASK | Gdk::POINTER_MOTION_HINT_MASK | Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::BUTTON1_MOTION_MASK);
|
||||
signal_event().connect( sigc::mem_fun(*this, &MyCurve::handleEvents) );
|
||||
|
||||
curve.x.push_back(0);
|
||||
@@ -126,7 +125,8 @@ void MyCurve::interpolate (int width, int height) {
|
||||
}
|
||||
}
|
||||
|
||||
void MyCurve::draw (int width, int height) {
|
||||
void MyCurve::draw (int width, int height, int handle) {
|
||||
// width and heigth are the size of the graph
|
||||
|
||||
if (!pixmap)
|
||||
return;
|
||||
@@ -135,7 +135,6 @@ void MyCurve::draw (int width, int height) {
|
||||
if (this->height != height || point.size() != width)
|
||||
interpolate (width, height);
|
||||
|
||||
|
||||
Gtk::StateType state = Gtk::STATE_NORMAL;
|
||||
if (!is_sensitive())
|
||||
state = Gtk::STATE_INSENSITIVE;
|
||||
@@ -159,32 +158,32 @@ void MyCurve::draw (int width, int height) {
|
||||
// draw histogram
|
||||
cr->set_line_width (1.0);
|
||||
double stepSize = (width-1) / 256.0;
|
||||
cr->move_to (0, height-1);
|
||||
cr->move_to (RADIUS, height-1+RADIUS);
|
||||
cr->set_source_rgb (0.75, 0.75, 0.75);
|
||||
for (int i=0; i<256; i++) {
|
||||
double val = bghist[i] * (double)(height-2) / histheight;
|
||||
if (val>height-1)
|
||||
val = height-1;
|
||||
if (i>0)
|
||||
cr->line_to (i*stepSize, height-1-val);
|
||||
cr->line_to (i*stepSize+RADIUS, height-1+RADIUS-val);
|
||||
}
|
||||
cr->line_to (width-1, height-1);
|
||||
cr->line_to (width-1+RADIUS, height-1+RADIUS);
|
||||
cr->fill ();
|
||||
}
|
||||
|
||||
// draw the grid lines:
|
||||
cr->set_line_width (1.0);
|
||||
cr->set_line_width (1.0);
|
||||
c = style->get_dark (state);
|
||||
cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p());
|
||||
cr->set_antialias (Cairo::ANTIALIAS_NONE);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
cr->move_to (RADIUS, i * height / 4 + RADIUS);
|
||||
cr->line_to (width + RADIUS, i * height / 4 + RADIUS);
|
||||
cr->move_to (i * width / 4 + RADIUS, RADIUS);
|
||||
cr->line_to (i * width / 4 + RADIUS, height + RADIUS);
|
||||
cr->move_to (RADIUS, MAX(0,i * height / 4 - 1) + RADIUS);
|
||||
cr->line_to (width + RADIUS, MAX(0,i * height / 4 - 1) + RADIUS);
|
||||
cr->move_to (MAX(0,i * width / 4 - 1) + RADIUS, RADIUS);
|
||||
cr->line_to (MAX(0,i * width / 4 - 1) + RADIUS, height + RADIUS);
|
||||
}
|
||||
cr->stroke ();
|
||||
|
||||
|
||||
// draw f(x)=x line
|
||||
cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p());
|
||||
std::valarray<double> ds (1);
|
||||
@@ -195,6 +194,7 @@ void MyCurve::draw (int width, int height) {
|
||||
cr->stroke ();
|
||||
cr->unset_dash ();
|
||||
|
||||
|
||||
cr->set_antialias (Cairo::ANTIALIAS_SUBPIXEL);
|
||||
cr->set_line_width (1.0);
|
||||
|
||||
@@ -221,10 +221,11 @@ void MyCurve::draw (int width, int height) {
|
||||
// draw bullets
|
||||
if (curve.type!=Parametric)
|
||||
for (int i = 0; i < curve.x.size(); ++i) {
|
||||
cr->set_source_rgb ((i == handle ? 1.0 : 0.0), 0.0, 0.0);
|
||||
double x = ((width-1) * curve.x[i] + 0.5)+RADIUS; // project (curve.x[i], 0, 1, width);
|
||||
double y = height - ((height-1) * curve.y[i] + 0.5)+RADIUS; // project (curve.y[i], 0, 1, height);
|
||||
|
||||
cr->arc (x, y, RADIUS, 0, 2*M_PI);
|
||||
cr->arc (x, y, RADIUS+0.5, 0, 2*M_PI);
|
||||
cr->fill ();
|
||||
}
|
||||
|
||||
@@ -233,13 +234,17 @@ void MyCurve::draw (int width, int height) {
|
||||
|
||||
bool MyCurve::handleEvents (GdkEvent* event) {
|
||||
|
||||
Gdk::CursorType new_type = cursor_type;
|
||||
CursorShape new_type = cursor_type;
|
||||
int src, dst;
|
||||
GdkEventMotion *mevent;
|
||||
std::vector<double>::iterator itx, ity;
|
||||
|
||||
Glib::RefPtr<Gdk::Display> rt_display = Gtk::Widget::get_display();
|
||||
Glib::RefPtr<Gdk::Screen> rt_screen = Gtk::Widget::get_screen();
|
||||
|
||||
bool retval = false;
|
||||
|
||||
/* width and height are the size of the graph */
|
||||
int width = get_allocation().get_width() - RADIUS * 2;
|
||||
int height = get_allocation().get_height() - RADIUS * 2;
|
||||
|
||||
@@ -250,13 +255,13 @@ bool MyCurve::handleEvents (GdkEvent* event) {
|
||||
int tx, ty;
|
||||
Gdk::ModifierType gm;
|
||||
get_window()->get_pointer (tx, ty, gm);
|
||||
int x = CLAMP ((tx - RADIUS), 0, width-1);
|
||||
int y = CLAMP ((ty - RADIUS), 0, height-1);
|
||||
int x = CLAMP ((tx - RADIUS), 0, width-1); // X position of the pointer from the origin of the graph
|
||||
int y = height-1 - CLAMP ((ty - RADIUS), 0, height-1); // Y position of the pointer from the origin of the graph
|
||||
|
||||
unsigned int distance = ~0U;
|
||||
int num = curve.x.size();
|
||||
int closest_point = 0;
|
||||
|
||||
int closest_point = -1;
|
||||
|
||||
if (curve.type!=Parametric) {
|
||||
for (int i = 0; i < num; ++i) {
|
||||
int cx = (int)((width-1) * curve.x[i] + 0.5); //project (c->ctlpoint[i][0], min_x, c->max_x, width);
|
||||
@@ -266,24 +271,30 @@ bool MyCurve::handleEvents (GdkEvent* event) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
switch (event->type) {
|
||||
case Gdk::CONFIGURE:
|
||||
if (pixmap)
|
||||
pixmap.clear ();
|
||||
|
||||
case Gdk::EXPOSE:
|
||||
// When does this event occurs ???
|
||||
if (!pixmap) {
|
||||
pixmap = Gdk::Pixmap::create (get_window(), get_allocation().get_width(), get_allocation().get_height());
|
||||
interpolate (width, height);
|
||||
}
|
||||
draw (width, height);
|
||||
draw (width, height, lit_point);
|
||||
break;
|
||||
|
||||
case Gdk::BUTTON_PRESS:
|
||||
if (curve.type!=Parametric) {
|
||||
add_modal_grab ();
|
||||
new_type = Gdk::PLUS;
|
||||
|
||||
// get cursor position
|
||||
Gdk::ModifierType mod_type;
|
||||
rt_display->get_pointer(cursor_x, cursor_y, mod_type);
|
||||
|
||||
new_type = CSEmpty;
|
||||
if (distance > MIN_DISTANCE) {
|
||||
/* insert a new control point */
|
||||
if (num > 0) {
|
||||
@@ -294,24 +305,30 @@ bool MyCurve::handleEvents (GdkEvent* event) {
|
||||
itx = curve.x.begin();
|
||||
ity = curve.y.begin();
|
||||
for (int i=0; i<closest_point; i++) { itx++; ity++; }
|
||||
curve.x.insert (itx, 0);
|
||||
curve.y.insert (ity, 0);
|
||||
num++;
|
||||
}
|
||||
grab_point = closest_point;
|
||||
curve.x[grab_point] = (double) x / (width-1);
|
||||
curve.y[grab_point] = (double) (height-y) / (height-1);
|
||||
interpolate (width, height);
|
||||
notifyListener ();
|
||||
break;
|
||||
curve.x.insert (itx, 0);
|
||||
curve.y.insert (ity, 0);
|
||||
num++;
|
||||
|
||||
// the graph is refreshed only if a new point is created (snaped to a pixel)
|
||||
curve.x[closest_point] = (double) x / (width-1);
|
||||
curve.y[closest_point] = (double) y / (height-1);
|
||||
interpolate (width, height);
|
||||
draw (width, height, closest_point);
|
||||
}
|
||||
grab_point = closest_point;
|
||||
lit_point = closest_point;
|
||||
ugp_x = curve.x[closest_point];
|
||||
ugp_y = curve.y[closest_point];
|
||||
notifyListener ();
|
||||
break;
|
||||
}
|
||||
draw (width, height);
|
||||
retval = true;
|
||||
break;
|
||||
|
||||
case Gdk::BUTTON_RELEASE:
|
||||
if (curve.type!=Parametric) {
|
||||
remove_modal_grab ();
|
||||
int previous_lit_point = lit_point;
|
||||
/* delete inactive points: */
|
||||
itx = curve.x.begin();
|
||||
ity = curve.y.begin();
|
||||
@@ -330,61 +347,115 @@ bool MyCurve::handleEvents (GdkEvent* event) {
|
||||
curve.x.push_back (0);
|
||||
curve.y.push_back (0);
|
||||
interpolate (width, height);
|
||||
draw (width, height);
|
||||
draw (width, height, lit_point);
|
||||
}
|
||||
}
|
||||
new_type = Gdk::FLEUR;
|
||||
if (distance <= MIN_DISTANCE) {
|
||||
new_type = CSMove;
|
||||
lit_point = closest_point;
|
||||
}
|
||||
else {
|
||||
new_type = CSPlus;
|
||||
lit_point = -1;
|
||||
}
|
||||
if (lit_point != previous_lit_point)
|
||||
draw (width, height, lit_point);
|
||||
grab_point = -1;
|
||||
retval = true;
|
||||
notifyListener ();
|
||||
}
|
||||
break;
|
||||
|
||||
case Gdk::LEAVE_NOTIFY:
|
||||
// Pointer can LEAVE even when dragging the point, so we don't modify the cursor in this case
|
||||
// The cursor will have to LEAVE another time after the drag...
|
||||
if (grab_point == -1)
|
||||
new_type = CSArrow;
|
||||
break;
|
||||
|
||||
case Gdk::MOTION_NOTIFY:
|
||||
mevent = (GdkEventMotion *) event;
|
||||
|
||||
if (curve.type == Linear || curve.type == Spline) {
|
||||
if (grab_point == -1) {
|
||||
int previous_lit_point = lit_point;
|
||||
/* if no point is grabbed... */
|
||||
if (distance <= MIN_DISTANCE)
|
||||
new_type = Gdk::FLEUR;
|
||||
else
|
||||
new_type = Gdk::PLUS;
|
||||
if (distance <= MIN_DISTANCE) {
|
||||
new_type = CSMove;
|
||||
lit_point = closest_point;
|
||||
}
|
||||
else {
|
||||
new_type = CSPlus;
|
||||
lit_point = -1;
|
||||
}
|
||||
if ((new_type != cursor_type) || (lit_point != previous_lit_point))
|
||||
draw (width, height, lit_point);
|
||||
}
|
||||
else {
|
||||
/* drag the grabbed point */
|
||||
new_type = Gdk::FLEUR;
|
||||
int leftbound = -MIN_DISTANCE;
|
||||
if (grab_point > 0)
|
||||
leftbound = (int)((width-1)*curve.x[grab_point-1]+0.5);
|
||||
|
||||
int rightbound = width + RADIUS * 2 + MIN_DISTANCE;
|
||||
if (grab_point + 1 < num)
|
||||
rightbound = (int)((width-1)*curve.x[grab_point+1]+0.5);
|
||||
|
||||
if (tx <= leftbound || tx >= rightbound || ty > height + RADIUS * 2 + MIN_DISTANCE || ty < -MIN_DISTANCE)
|
||||
curve.x[grab_point] = -1.0;
|
||||
else {
|
||||
curve.x[grab_point] = (double) x / (width-1);
|
||||
curve.y[grab_point] = (double) (height-y) / (height-1);
|
||||
int new_cursor_x, new_cursor_y;
|
||||
double factor = 0.5;
|
||||
|
||||
// get cursor position
|
||||
Gdk::ModifierType mod_type;
|
||||
rt_display->get_pointer(new_cursor_x, new_cursor_y, mod_type);
|
||||
|
||||
// set the dragging factor
|
||||
int control_key = gm & GDK_CONTROL_MASK;
|
||||
int shift_key = gm & GDK_SHIFT_MASK;
|
||||
|
||||
// what is the speed factor
|
||||
if (control_key && shift_key) factor = 0.005;
|
||||
else if (shift_key) factor = 0.02;
|
||||
else if (control_key) factor = 0.1;
|
||||
|
||||
// calculate the delta in [0.0 ; 1.0] range
|
||||
double delta_x = (double)(new_cursor_x - cursor_x) * factor / (double)(width-1);
|
||||
double delta_y = (double)(cursor_y - new_cursor_y) * factor / (double)(height-1);
|
||||
|
||||
// modification of the unclamped grabed point
|
||||
ugp_x += delta_x;
|
||||
ugp_y += delta_y;
|
||||
|
||||
// first and last point cannot be deleted anymore (there's no point to do it)
|
||||
// for intermediate points, we look if the point must be deleted
|
||||
if (grab_point > 0 && grab_point < num-1) {
|
||||
double leftbound = curve.x[grab_point-1];
|
||||
double rightbound = curve.x[grab_point+1];
|
||||
double bottombound = (double)(-MIN_DISTANCE) * factor / (double)(height-1);
|
||||
double topbound = (double)1.0 + (double)(MIN_DISTANCE) * factor / (double)(height-1);
|
||||
|
||||
if (ugp_x <= leftbound || ugp_x >= rightbound || ugp_y > topbound || ugp_y < bottombound) {
|
||||
curve.x[grab_point] = -1.0;
|
||||
}
|
||||
}
|
||||
// first and last points are clamped to the [0.0 ; 1.0] range
|
||||
if (curve.x[grab_point] != -1.0) {
|
||||
double new_curve_x = curve.x[grab_point] + delta_x;
|
||||
double new_curve_y = curve.y[grab_point] + delta_y;
|
||||
curve.x[grab_point] = CLAMP(new_curve_x,0.0,1.0);
|
||||
curve.y[grab_point] = CLAMP(new_curve_y,0.0,1.0);
|
||||
}
|
||||
|
||||
interpolate (width, height);
|
||||
draw (width, height);
|
||||
|
||||
// move the cursor back (to avoid being limited by the screen)
|
||||
rt_display->warp_pointer(rt_screen, cursor_x, cursor_y);
|
||||
|
||||
draw (width, height, lit_point);
|
||||
notifyListener ();
|
||||
}
|
||||
}
|
||||
|
||||
if (new_type != cursor_type) {
|
||||
cursor_type = new_type;
|
||||
Gdk::Cursor* cursor = new Gdk::Cursor (get_display(), cursor_type);
|
||||
get_window ()->set_cursor (*cursor);
|
||||
delete cursor;
|
||||
}
|
||||
|
||||
retval = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (new_type != cursor_type) {
|
||||
cursor_type = new_type;
|
||||
cursorManager.setCursor(cursor_type);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user