Implement logarithmic slider mode.

This commit is contained in:
Jonathan Moore Liles 2013-10-11 23:07:38 -07:00
parent 50a5f66fff
commit de6c22ac46
3 changed files with 252 additions and 23 deletions

View File

@ -21,6 +21,8 @@
#include <FL/Fl.H>
#include <FL/fl_draw.H>
#include <math.h>
void
Fl_SliderX::draw ( int X, int Y, int W, int H)
{
@ -28,32 +30,14 @@ Fl_SliderX::draw ( int X, int Y, int W, int H)
if (damage()&FL_DAMAGE_ALL) draw_box();
double val;
if (minimum() == maximum())
val = 0.5;
else {
val = (value()-minimum())/(maximum()-minimum());
if (val > 1.0) val = 1.0;
else if (val < 0.0) val = 0.0;
}
int ww = (horizontal() ? W : H);
int hh = (horizontal() ? H : W);
int xx, S;
/* S = int(val*ww+.5); */
/* if (minimum()>maximum()) {S = ww-S; xx = ww-S;} */
/* else xx = 0; */
xx = slider_position( value(), ww );
{
//S = //int(ww+.5);
S = hh;
int T = (horizontal() ? H : W)/2+1;
// if (type()==FL_VERT_NICE_SLIDER || type()==FL_HOR_NICE_SLIDER) T += 4;
if (S < T) S = T;
xx = int(val*(ww-S)+.5);
}
S = (horizontal() ? H : W );
int xsl, ysl, wsl, hsl;
if (horizontal()) {
@ -131,3 +115,229 @@ Fl_SliderX::draw ( int X, int Y, int W, int H)
/* h()-Fl::box_dh(box())); */
}
/** return a value between 0.0 and 1.0 which represents the current slider position. */
int
Fl_SliderX::slider_position ( double value, int w )
{
double A = minimum();
double B = maximum();
if (B == A) return 0;
bool flip = B < A;
if (flip) {A = B; B = minimum();}
if (!horizontal()) flip = !flip;
// if both are negative, make the range positive:
if (B <= 0) {flip = !flip; double t = A; A = -B; B = -t; value = -value;}
double fraction;
if (!log()) {
// linear slider
fraction = (value-A)/(B-A);
} else if (A > 0) {
// logatithmic slider
if (value <= A) fraction = 0;
else fraction = (::log(value)-::log(A))/(::log(B)-::log(A));
} else if (A == 0) {
// squared slider
if (value <= 0) fraction = 0;
else fraction = sqrt(value/B);
} else {
// squared signed slider
if (value < 0) fraction = (1-sqrt(value/A))*.5;
else fraction = (1+sqrt(value/B))*.5;
}
if (flip) fraction = 1-fraction;
w -= int(slider_size()*w+.5); if (w <= 0) return 0;
if (fraction >= 1) return w;
else if (fraction <= 0) return 0;
else return int(fraction*w+.5);
}
double
Fl_SliderX::slider_value ( int X, int w )
{
w -= int(slider_size()*w+.5); if (w <= 0) return minimum();
double A = minimum();
double B = maximum();
bool flip = B < A;
if (flip) {A = B; B = minimum();}
if (!horizontal()) flip = !flip;
if (flip) X = w-X;
double fraction = double(X)/w;
if (fraction <= 0) return A;
if (fraction >= 1) return B;
// if both are negative, make the range positive:
flip = (B <= 0);
if (flip) {double t = A; A = -B; B = -t; fraction = 1-fraction;}
double value;
double derivative;
if (!log()) {
// linear slider
value = fraction*(B-A)+A;
derivative = (B-A)/w;
} else if (A > 0) {
// log slider
double d = (::log(B)-::log(A));
value = exp(fraction*d+::log(A));
derivative = value*d/w;
} else if (A == 0) {
// squared slider
value = fraction*fraction*B;
derivative = 2*fraction*B/w;
} else {
// squared signed slider
fraction = 2*fraction - 1;
if (fraction < 0) B = A;
value = fraction*fraction*B;
derivative = 4*fraction*B/w;
}
// find nicest multiple of 10,5, or 2 of step() that is close to 1 pixel:
if (step() && derivative > step()) {
double w = log10(derivative);
double l = ceil(w);
int num = 1;
int i; for (i = 0; i < l; i++) num *= 10;
int denom = 1;
for (i = -1; i >= l; i--) denom *= 10;
if (l-w > 0.69897) denom *= 5;
else if (l-w > 0.30103) denom *= 2;
value = floor(value*denom/num+.5)*num/denom;
}
if (flip) return -value;
return value;
}
int Fl_SliderX::handle(int event, int X, int Y, int W, int H) {
// Fl_Widget_Tracker wp(this);
switch (event) {
case FL_PUSH: {
Fl_Widget_Tracker wp(this);
if (!Fl::event_inside(X, Y, W, H)) return 0;
handle_push();
if (wp.deleted()) return 1; }
// fall through ...
case FL_DRAG: {
static int offcenter;
int ww = (horizontal() ? W : H);
if ( event == FL_PUSH )
{
int x = slider_position( value(), ww );
offcenter = (Fl::event_x()-X) - x;
}
try_again:
int mx = (horizontal() ? Fl::event_x()-X : Fl::event_y()-Y) - offcenter;
double v = slider_value( mx, ww );
if (event == FL_PUSH && v == value()) {
offcenter = int(slider_size()*ww+0.5)/2;
event = FL_DRAG;
goto try_again;
}
handle_drag(clamp(v));
} return 1;
case FL_RELEASE:
handle_release();
return 1;
case FL_KEYBOARD:
{ Fl_Widget_Tracker wp(this);
switch (Fl::event_key()) {
case FL_Up:
if (horizontal()) return 0;
handle_push();
if (wp.deleted()) return 1;
handle_drag(clamp(increment(value(),-1)));
if (wp.deleted()) return 1;
handle_release();
return 1;
case FL_Down:
if (horizontal()) return 0;
handle_push();
if (wp.deleted()) return 1;
handle_drag(clamp(increment(value(),1)));
if (wp.deleted()) return 1;
handle_release();
return 1;
case FL_Left:
if (!horizontal()) return 0;
handle_push();
if (wp.deleted()) return 1;
handle_drag(clamp(increment(value(),-1)));
if (wp.deleted()) return 1;
handle_release();
return 1;
case FL_Right:
if (!horizontal()) return 0;
handle_push();
if (wp.deleted()) return 1;
handle_drag(clamp(increment(value(),1)));
if (wp.deleted()) return 1;
handle_release();
return 1;
default:
return 0;
}
}
// break not required because of switch...
case FL_FOCUS :
case FL_UNFOCUS :
if (Fl::visible_focus()) {
redraw();
return 1;
} else return 0;
case FL_ENTER :
case FL_LEAVE :
return 1;
case FL_MOUSEWHEEL :
{
if ( this != Fl::belowmouse() )
return 0;
if (Fl::e_dy==0)
return 0;
const int steps = Fl::event_ctrl() ? 128 : 16;
const float step = fabs( maximum() - minimum() ) / (float)steps;
int dy = Fl::e_dy;
/* slider is in 'upside down' configuration, invert meaning of mousewheel */
if ( minimum() > maximum() )
dy = 0 - dy;
handle_drag(clamp(value() + step * dy));
return 1;
}
default:
return 0;
}
}
int Fl_SliderX::handle(int event) {
if (event == FL_PUSH && Fl::visible_focus()) {
Fl::focus(this);
redraw();
}
return handle(event,
x()+Fl::box_dx(box()),
y()+Fl::box_dy(box()),
w()-Fl::box_dw(box()),
h()-Fl::box_dh(box()));
}
void
Fl_SliderX::resize ( int X, int Y, int W, int H )
{
Fl_Slider::resize(X,Y,W,H);
slider_size( horizontal() ? H / (float)W : W / (float)H );
}

View File

@ -23,15 +23,31 @@
class Fl_SliderX : public Fl_Slider
{
bool _log;
public:
void log ( bool v ) { _log = v; }
bool log ( void ) const { return _log; }
Fl_SliderX( int X, int Y, int W, int H, const char *L=0 ) : Fl_Slider(X,Y,W,H,L)
{
_log = 0;
}
virtual ~Fl_SliderX ( ) { };
virtual int slider_position ( double value, int w );
virtual double slider_value ( int X, int w );
virtual void draw ( int X, int Y, int W, int H );
virtual void draw ( void ) { draw(x(),y(),w(),h()); }
virtual int handle(int event, int X, int Y, int W, int H);
virtual int handle(int event);
virtual void resize ( int X, int Y, int W, int H );
};

View File

@ -357,7 +357,10 @@ Module_Parameter_Editor::make_controls ( void )
o->maximum( p->hints.minimum );
o->minimum( p->hints.maximum );
}
if ( p->hints.type & Module::Port::Hints::LOGARITHMIC )
o->log(true);
o->precision( 2 );
/* a couple of plugins have ridiculously small units */
float r = fabs( p->hints.maximum - p->hints.minimum );