// This file is part of fityk program. Copyright (C) Marcin Wojdyr
// Licence: GNU General Public License version 2
// $Id: mplot.cpp 326 2007-08-01 05:54:03Z wojdyr $
#include <wx/wxprec.h>
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/fontdlg.h>
#include <algorithm>
#include "mplot.h"
#include "gui.h"
#include "sidebar.h"
#include "../data.h"
#include "../logic.h"
#include "../sum.h"
#include "../var.h"
#include "../func.h"
#include "../ui.h"
#include "../settings.h"
using namespace std;
enum {
ID_plot_popup_za = 25001,
ID_plot_popup_sum = 25011,
ID_plot_popup_groups ,
ID_plot_popup_peak ,
ID_plot_popup_c_background ,
ID_plot_popup_c_inactive_data ,
ID_plot_popup_c_sum ,
ID_plot_popup_c_inv ,
ID_plot_popup_axes ,
ID_plot_popup_plabels ,
ID_plot_popup_pt_size = 25210,// and next 10 ,
ID_peak_popup_info = 25250,
ID_peak_popup_del ,
ID_peak_popup_guess ,
ID_CAD_COLOR,
ID_CAD_FONT,
ID_CPL_FONT,
ID_CPL_SHOW,
ID_CPL_RADIO,
ID_CPL_TEXT,
};
//---------------------- FunctionMouseDrag --------------------------------
void FunctionMouseDrag::Drag::change_value(fp x, fp dx, int dX)
{
if (how == no_drag || dx == 0. || dX == 0)
return;
if (how == relative_value) {
if (ini_x == 0.) {
ini_x = x - dx;
if (is_zero(ini_x))
ini_x += dx;
}
//value += dx * fabs(value / x) * multiplier;
value = x / ini_x * ini_value;
}
else if (how == absolute_value)
value += dx * multiplier;
else if (how == absolute_pixels)
value += dX * multiplier;
else
assert(0);
}
void FunctionMouseDrag::Drag::set(Function const* p, int idx,
drag_type how_, fp multiplier_)
{
Variable const* var = ftk->get_variable(p->get_var_idx(idx));
if (!var->is_simple()) {
how = no_drag;
return;
}
how = how_;
parameter_idx = idx;
parameter_name = p->type_var_names[idx];
variable_name = p->get_var_name(idx);
value = ini_value = p->get_var_value(idx);
multiplier = multiplier_;
ini_x = 0.;
}
void FunctionMouseDrag::start(Function const* p, int X, int Y, fp x, fp y)
{
drag_x.parameter_name = drag_y.parameter_name
= drag_shift_x.parameter_name = drag_shift_y.parameter_name = "-";
set_defined_drags();
if (drag_x.how == no_drag)
bind_parameter_to_drag(drag_x, "center", p, absolute_value);
if (drag_y.how == no_drag)
bind_parameter_to_drag(drag_y, "height", p, absolute_value)
|| bind_parameter_to_drag(drag_y, "area", p, relative_value)
|| bind_parameter_to_drag(drag_y, "avgy", p, absolute_value)
|| bind_parameter_to_drag(drag_y, "intercept", p, relative_value);
if (drag_shift_x.how == no_drag)
bind_parameter_to_drag(drag_shift_x, "hwhm", p, absolute_value, 0.5)
|| bind_parameter_to_drag(drag_shift_x, "fwhm", p, absolute_value, 0.5);
values = p->get_var_values();
status = "Move to change: " + drag_x.parameter_name + "/"
+ drag_y.parameter_name + ", with [Shift]: "
+ drag_shift_x.parameter_name + "/" + drag_shift_y.parameter_name;
pX = X;
pY = Y;
px = x;
py = y;
}
void FunctionMouseDrag::set_defined_drags()
{
drag_x.how = no_drag;
drag_y.how = no_drag;
drag_shift_x.how = no_drag;
drag_shift_y.how = no_drag;
}
bool FunctionMouseDrag::bind_parameter_to_drag(Drag &drag, string const& name,
Function const* p, drag_type how,
fp multiplier)
{
// search for Function(..., height, ...)
int idx = index_of_element(p->type_var_names, name);
if (idx != -1) {
drag.set(p, idx, how, multiplier);
return true;
}
vector<string> defvalues
= Function::get_defvalues_from_formula(p->type_formula);
// search for Function(..., foo=height, ...)
idx = index_of_element(defvalues, name);
if (idx != -1) {
drag.set(p, idx, how, multiplier);
return true;
}
// search for Function(..., foo=height*..., ...)
for (vector<string>::const_iterator i = defvalues.begin();
i != defvalues.end(); ++i)
if (startswith(*i, name+"*")) {
drag.set(p, i - defvalues.begin(), how, multiplier);
return true;
}
return false;
}
void FunctionMouseDrag::move(bool shift, int X, int Y, fp x, fp y)
{
SideBar *sib = frame->get_sidebar();
Drag &hor = shift ? drag_shift_x : drag_x;
hor.change_value(x, x - px, X - pX);
if (hor.how != no_drag) {
values[hor.parameter_idx] = hor.value;
sib->change_bp_parameter_value(hor.parameter_idx, hor.value);
sidebar_dirty = true;
}
pX = X;
px = x;
Drag &vert = shift ? drag_shift_y : drag_y;
vert.change_value(y, y - py, Y - pY);
if (vert.how != no_drag) {
values[vert.parameter_idx] = vert.value;
sib->change_bp_parameter_value(vert.parameter_idx, vert.value);
sidebar_dirty = true;
}
pY = Y;
py = y;
}
void FunctionMouseDrag::stop()
{
if (sidebar_dirty) {
frame->get_sidebar()->update_bottom_panel();
sidebar_dirty = false;
}
}
string FunctionMouseDrag::get_cmd() const
{
return drag_x.get_cmd() + drag_y.get_cmd() + drag_shift_x.get_cmd()
+ drag_shift_y.get_cmd();
}
//===============================================================
// MainPlot (plot with data and fitted curves)
//===============================================================
BEGIN_EVENT_TABLE(MainPlot, FPlot)
EVT_PAINT ( MainPlot::OnPaint)
EVT_MOTION ( MainPlot::OnMouseMove)
EVT_LEAVE_WINDOW ( MainPlot::OnLeaveWindow)
EVT_LEFT_DOWN ( MainPlot::OnButtonDown)
EVT_RIGHT_DOWN ( MainPlot::OnButtonDown)
EVT_MIDDLE_DOWN ( MainPlot::OnButtonDown)
EVT_LEFT_DCLICK ( MainPlot::OnLeftDClick)
EVT_LEFT_UP ( MainPlot::OnButtonUp)
EVT_RIGHT_UP ( MainPlot::OnButtonUp)
EVT_MIDDLE_UP ( MainPlot::OnButtonUp)
EVT_KEY_DOWN ( MainPlot::OnKeyDown)
EVT_MENU (ID_plot_popup_za, MainPlot::OnZoomAll)
EVT_MENU_RANGE (ID_plot_popup_sum, ID_plot_popup_peak,
MainPlot::OnPopupShowXX)
EVT_MENU_RANGE (ID_plot_popup_c_background, ID_plot_popup_c_sum,
MainPlot::OnPopupColor)
EVT_MENU_RANGE (ID_plot_popup_pt_size, ID_plot_popup_pt_size + max_radius,
MainPlot::OnPopupRadius)
EVT_MENU (ID_plot_popup_c_inv, MainPlot::OnInvertColors)
EVT_MENU (ID_plot_popup_axes, MainPlot::OnConfigureAxes)
EVT_MENU (ID_plot_popup_plabels,MainPlot::OnConfigurePLabels)
EVT_MENU (ID_peak_popup_info, MainPlot::OnPeakInfo)
EVT_MENU (ID_peak_popup_del, MainPlot::OnPeakDelete)
EVT_MENU (ID_peak_popup_guess, MainPlot::OnPeakGuess)
END_EVENT_TABLE()
MainPlot::MainPlot (wxWindow *parent)
: FPlot(parent), BgManager(xs),
basic_mode(mmd_zoom), mode(mmd_zoom),
pressed_mouse_button(0), ctrl_on_down(false), shift_on_down(false),
over_peak(-1), limit1(INT_MIN), limit2(INT_MIN)
{ }
void MainPlot::OnPaint(wxPaintEvent&)
{
frame->draw_crosshair(-1, -1);
limit1 = limit2 = INT_MIN;
buffered_draw();
vert_line_following_cursor(mat_redraw);//draw, if necessary, vertical lines
peak_draft(mat_redraw);
draw_moving_func(mat_redraw);
frame->update_app_title();
}
fp y_of_data_for_draw_data(vector<Point>::const_iterator i, Sum const* /*sum*/)
{
return i->y;
}
void MainPlot::draw_dataset(wxDC& dc, int n, bool set_pen)
{
bool shadowed;
int offset;
bool r = frame->get_sidebar()->howto_plot_dataset(n, shadowed, offset);
if (!r)
return;
wxColour col = get_data_color(n);
if (shadowed) {
wxColour const& bg_col = get_bg_color();
col.Set((col.Red() + bg_col.Red())/2,
(col.Green() + bg_col.Green())/2,
(col.Blue() + bg_col.Blue())/2);
}
if (set_pen)
draw_data(dc, y_of_data_for_draw_data, ftk->get_data(n), 0,
col, wxNullColour, offset);
else
draw_data(dc, y_of_data_for_draw_data, ftk->get_data(n), 0,
dc.GetPen().GetColour(), dc.GetPen().GetColour(), offset);
}
void MainPlot::draw(wxDC &dc, bool monochrome)
{
int focused_data = ftk->get_active_ds_position();
Sum const* sum = ftk->get_sum(focused_data);
set_scale();
frame->draw_crosshair(-1, -1); //erase crosshair before redrawing plot
prepare_peaktops(sum);
if (monochrome) {
dc.SetPen(*wxBLACK_PEN);
dc.SetBrush(*wxBLACK_BRUSH);
}
//draw datasets
for (int i = 0; i < ftk->get_ds_count(); i++) {
if (i != focused_data) {
draw_dataset(dc, i, !monochrome);
}
}
// focused dataset is drawed at the end (to be at the top)
draw_dataset(dc, focused_data, !monochrome);
if (xtics_visible)
draw_xtics(dc, ftk->view, !monochrome);
if (ytics_visible)
draw_ytics(dc, ftk->view, !monochrome);
if (peaks_visible)
draw_peaks(dc, sum, !monochrome);
if (groups_visible)
draw_groups(dc, sum, !monochrome);
if (sum_visible)
draw_sum(dc, sum, !monochrome);
if (x_axis_visible)
draw_x_axis(dc, !monochrome);
if (y_axis_visible)
draw_y_axis(dc, !monochrome);
if (visible_peaktops(mode) && !monochrome)
draw_peaktops(dc, sum);
if (mode == mmd_bg) {
draw_background(dc);
}
else {
if (plabels_visible)
draw_plabels(dc, sum, !monochrome);
}
}
bool MainPlot::visible_peaktops(MouseModeEnum mode)
{
return (mode == mmd_zoom || mode == mmd_add || mode == mmd_peak
|| mode == mmd_range);
}
void MainPlot::draw_x_axis (wxDC& dc, bool set_pen)
{
if (set_pen)
dc.SetPen(wxPen(xAxisCol));
int Y0 = ys.px(0.);
dc.DrawLine (0, Y0, GetClientSize().GetWidth(), Y0);
}
void MainPlot::draw_y_axis (wxDC& dc, bool set_pen)
{
if (set_pen)
dc.SetPen(wxPen(xAxisCol));
int X0 = xs.px(0.);
dc.DrawLine (X0, 0, X0, GetClientSize().GetHeight());
}
void MainPlot::draw_sum(wxDC& dc, Sum const* sum, bool set_pen)
{
if (set_pen)
dc.SetPen(wxPen(sumCol));
int n = GetClientSize().GetWidth();
vector<fp> xx(n), yy(n);
vector<int> YY(n);
for (int i = 0; i < n; ++i)
xx[i] = xs.val(i);
sum->calculate_sum_value(xx, yy);
for (int i = 0; i < n; ++i)
YY[i] = ys.px(yy[i]);
for (int i = 1; i < n; i++)
dc.DrawLine (i-1, YY[i-1], i, YY[i]);
}
//TODO draw groups
void MainPlot::draw_groups (wxDC& /*dc*/, Sum const*, bool)
{
}
void MainPlot::draw_peaks(wxDC& dc, Sum const* sum, bool set_pen)
{
fp level = 0;
vector<int> const& idx = sum->get_ff_idx();
int n = GetClientSize().GetWidth();
vector<fp> xx(n), yy(n);
vector<int> YY(n);
for (int i = 0; i < n; ++i)
xx[i] = xs.val(i);
for (int k = 0; k < size(idx); k++) {
fill(yy.begin(), yy.end(), 0.);
Function const* f = ftk->get_function(idx[k]);
int from=0, to=n-1;
fp left, right;
if (f->get_nonzero_range(level, left, right)) {
from = max(from, xs.px(left));
to = min(to, xs.px(right));
}
if (set_pen)
dc.SetPen(wxPen(peakCol[k % max_peak_cols]));
f->calculate_value(xx, yy);
for (int i = from; i <= to; ++i)
YY[i] = ys.px(yy[i]);
for (int i = from+1; i <= to; i++)
dc.DrawLine (i-1, YY[i-1], i, YY[i]);
}
}
void MainPlot::draw_peaktops (wxDC& dc, Sum const* sum)
{
dc.SetPen(wxPen(xAxisCol));
dc.SetBrush (*wxTRANSPARENT_BRUSH);
for (vector<wxPoint>::const_iterator i = special_points.begin();
i != special_points.end(); i++) {
dc.DrawRectangle (i->x - 1, i->y - 1, 3, 3);
}
draw_peaktop_selection(dc, sum);
}
void MainPlot::draw_peaktop_selection (wxDC& dc, Sum const* sum)
{
int n = frame->get_sidebar()->get_active_function();
if (n == -1)
return;
vector<int> const& idx = sum->get_ff_idx();
vector<int>::const_iterator t = find(idx.begin(), idx.end(), n);
if (t != idx.end()) {
wxPoint const&p = special_points[t-idx.begin()];
dc.SetLogicalFunction (wxINVERT);
dc.SetPen(*wxBLACK_PEN);
dc.DrawCircle(p.x, p.y, 4);
}
}
void MainPlot::draw_plabels (wxDC& dc, Sum const* sum, bool set_pen)
{
prepare_peak_labels(sum); //TODO re-prepare only when peaks where changed
dc.SetFont(plabelFont);
vector<wxRect> previous;
vector<int> const& idx = sum->get_ff_idx();
for (int k = 0; k < size(idx); k++) {
const wxPoint &peaktop = special_points[k];
if (set_pen)
dc.SetTextForeground(peakCol[k % max_peak_cols]);
wxString label = s2wx(plabels[k]);
wxCoord w, h;
if (vertical_plabels) {
dc.GetMultiLineTextExtent(label, &h, &w); // w and h swapped
h = 0; // Y correction is not needed
}
else
dc.GetMultiLineTextExtent(label, &w, &h);
int X = peaktop.x - w/2;
int Y = peaktop.y - h - 2;
wxRect rect(X, Y, w, h);
// eliminate labels overlap
// perhaps more sophisticated algorithm for automatic label placement
// should be used
const int mrg = 0; //margin around label, can be negative
int counter = 0; // the number of different placements checked
vector<wxRect>::const_iterator i = previous.begin();
while (i != previous.end() && counter < 10) {
if (i->x > rect.GetRight()+mrg || rect.x > i->GetRight()+mrg
|| i->y > rect.GetBottom()+mrg || rect.y > i->GetBottom()+mrg)
//there is no intersection
++i;
else { // intersection -- try upper rectangle
rect.SetY(i->y - h - 2);
i = previous.begin(); //and check for intersections with all...
++counter;
}
}
previous.push_back(rect);
if (vertical_plabels)
dc.DrawRotatedText(label, rect.x, rect.y, 90);
else
dc.DrawLabel(label, rect, wxALIGN_CENTER|wxALIGN_BOTTOM);
}
}
/*
static bool operator< (const wxPoint& a, const wxPoint& b)
{
return a.x != b.x ? a.x < b.x : a.y < b.y;
}
*/
void MainPlot::prepare_peaktops(Sum const* sum)
{
int H = GetClientSize().GetHeight();
int Y0 = ys.px(0);
vector<int> const& idx = sum->get_ff_idx();
int n = idx.size();
special_points.resize(n);
for (int k = 0; k < n; k++) {
Function const *f = ftk->get_function(idx[k]);
fp x;
int X, Y;
if (f->has_center()) {
x = f->center();
X = xs.px (x - sum->zero_shift(x));
}
else {
X = k * 10;
x = xs.val(X);
x += sum->zero_shift(x);
}
//FIXME: check if these zero_shift()'s above are needed
Y = ys.px(f->calculate_value(x));
if (Y < 0 || Y > H)
Y = Y0;
special_points[k] = wxPoint(X, Y);
}
}
void MainPlot::prepare_peak_labels(Sum const* sum)
{
vector<int> const& idx = sum->get_ff_idx();
plabels.resize(idx.size());
for (int k = 0; k < size(idx); k++) {
Function const *f = ftk->get_function(idx[k]);
string label = plabel_format;
string::size_type pos = 0;
while ((pos = label.find("<", pos)) != string::npos) {
string::size_type right = label.find(">", pos+1);
if (right == string::npos)
break;
string tag(label, pos+1, right-pos-1);
if (tag == "area")
label.replace(pos, right-pos+1, S(f->area()));
else if (tag == "height")
label.replace(pos, right-pos+1, S(f->height()));
else if (tag == "center")
label.replace(pos, right-pos+1, S(f->center()));
else if (tag == "fwhm")
label.replace(pos, right-pos+1, S(f->fwhm()));
else if (tag == "ib")
label.replace(pos, right-pos+1, S(f->area()/f->height()));
else if (tag == "name")
label.replace(pos, right-pos+1, f->name);
else if (tag == "br")
label.replace(pos, right-pos+1, "\n");
else
++pos;
}
plabels[k] = label;
}
}
void MainPlot::draw_background(wxDC& dc, bool set_pen)
{
if (set_pen)
dc.SetPen(wxPen(bg_pointsCol));
dc.SetBrush (*wxTRANSPARENT_BRUSH);
// bg line
int X = -1, Y = -1;
for (vector<t_xy>::const_iterator i=bgline.begin(); i != bgline.end(); i++){
int X_ = X, Y_ = Y;
X = xs.px(i->x);
Y = ys.px(i->y);
if (X_ >= 0 && (X != X_ || Y != Y_))
dc.DrawLine (X_, Y_, X, Y);
}
// bg points (circles)
for (bg_const_iterator i = bg.begin(); i != bg.end(); i++) {
dc.DrawCircle(xs.px(i->x), ys.px(i->y), 3);
dc.DrawCircle(xs.px(i->x), ys.px(i->y), 4);
}
}
void MainPlot::read_settings(wxConfigBase *cf)
{
cf->SetPath(wxT("/MainPlot/Colors"));
backgroundCol = cfg_read_color(cf, wxT("bg"), wxColour(wxT("BLACK")));
for (int i = 0; i < max_data_cols; i++)
dataColour[i] = cfg_read_color(cf,
wxString::Format(wxT("data/%i"), i),
wxColour(0, 255, 0));
inactiveDataCol = cfg_read_color(cf,wxT("inactive_data"),
wxColour (128, 128, 128));
sumCol = cfg_read_color (cf, wxT("sum"), wxColour(wxT("YELLOW")));
bg_pointsCol = cfg_read_color(cf, wxT("BgPoints"), wxColour(wxT("RED")));
for (int i = 0; i < max_group_cols; i++)
groupCol[i] = cfg_read_color(cf, wxString::Format(wxT("group/%i"), i),
wxColour(173, 216, 230));
for (int i = 0; i < max_peak_cols; i++)
peakCol[i] = cfg_read_color(cf, wxString::Format(wxT("peak/%i"), i),
wxColour(255, 0, 0));
cf->SetPath(wxT("/MainPlot/Visible"));
peaks_visible = cfg_read_bool(cf, wxT("peaks"), true);
plabels_visible = cfg_read_bool(cf, wxT("plabels"), false);
groups_visible = cfg_read_bool(cf, wxT("groups"), false);
sum_visible = cfg_read_bool(cf, wxT("sum"), true);
cf->SetPath(wxT("/MainPlot"));
point_radius = cf->Read (wxT("point_radius"), 1);
line_between_points = cfg_read_bool(cf,wxT("line_between_points"), false);
draw_sigma = cfg_read_bool(cf,wxT("draw_sigma"), false);
plabelFont = cfg_read_font(cf, wxT("plabelFont"), *wxNORMAL_FONT);
plabel_format = wx2s(cf->Read(wxT("plabel_format"), wxT("<area>")));
vertical_plabels = cfg_read_bool(cf, wxT("vertical_plabels"), false);
x_max_tics = cf->Read(wxT("xMaxTics"), 7);
y_max_tics = cf->Read(wxT("yMaxTics"), 7);
x_tic_size = cf->Read(wxT("xTicSize"), 4);
y_tic_size = cf->Read(wxT("yTicSize"), 4);
xs.reversed = cfg_read_bool (cf, wxT("xReversed"), false);
ys.reversed = cfg_read_bool (cf, wxT("yReversed"), false);
xs.logarithm = cfg_read_bool (cf, wxT("xLogarithm"), false);
ys.logarithm = cfg_read_bool (cf, wxT("yLogarithm"), false);
FPlot::read_settings(cf);
refresh();
}
void MainPlot::save_settings(wxConfigBase *cf) const
{
cf->SetPath(wxT("/MainPlot"));
cf->Write (wxT("point_radius"), point_radius);
cf->Write (wxT("line_between_points"), line_between_points);
cf->Write (wxT("draw_sigma"), draw_sigma);
cfg_write_font (cf, wxT("plabelFont"), plabelFont);
cf->Write(wxT("plabel_format"), s2wx(plabel_format));
cf->Write (wxT("vertical_plabels"), vertical_plabels);
cf->Write(wxT("xMaxTics"), x_max_tics);
cf->Write(wxT("yMaxTics"), y_max_tics);
cf->Write(wxT("xTicSize"), x_tic_size);
cf->Write(wxT("yTicSize"), y_tic_size);
cf->Write(wxT("xReversed"), xs.reversed);
cf->Write(wxT("yReversed"), ys.reversed);
cf->Write(wxT("xLogarithm"), xs.logarithm);
cf->Write(wxT("yLogarithm"), ys.logarithm);
cf->SetPath(wxT("/MainPlot/Colors"));
cfg_write_color(cf, wxT("bg"), backgroundCol);
for (int i = 0; i < max_data_cols; i++)
cfg_write_color(cf, wxString::Format(wxT("data/%i"), i), dataColour[i]);
cfg_write_color (cf, wxT("inactive_data"), inactiveDataCol);
cfg_write_color (cf, wxT("sum"), sumCol);
cfg_write_color (cf, wxT("BgPoints"), bg_pointsCol);
for (int i = 0; i < max_group_cols; i++)
cfg_write_color(cf, wxString::Format(wxT("group/%i"), i), groupCol[i]);
for (int i = 0; i < max_peak_cols; i++)
cfg_write_color(cf, wxString::Format(wxT("peak/%i"), i), peakCol[i]);
cf->SetPath(wxT("/MainPlot/Visible"));
cf->Write (wxT("peaks"), peaks_visible);
cf->Write (wxT("plabels"), plabels_visible);
cf->Write (wxT("groups"), groups_visible);
cf->Write (wxT("sum"), sum_visible);
cf->SetPath(wxT("/MainPlot"));
FPlot::save_settings(cf);
}
void MainPlot::OnLeaveWindow (wxMouseEvent&)
{
frame->set_status_text("", sbf_coord);
frame->draw_crosshair(-1, -1);
}
void MainPlot::show_popup_menu (wxMouseEvent &event)
{
wxMenu popup_menu; //("main plot menu");
popup_menu.Append(ID_plot_popup_za, wxT("Zoom &All"));
popup_menu.AppendSeparator();
wxMenu *show_menu = new wxMenu;
show_menu->AppendCheckItem (ID_plot_popup_sum, wxT("S&um"), wxT(""));
show_menu->Check (ID_plot_popup_sum, sum_visible);
//show_menu->AppendCheckItem (ID_plot_popup_groups,
// wxT("Grouped peaks"), wxT(""));
//show_menu->Check (ID_plot_popup_groups, groups_visible);
show_menu->AppendCheckItem (ID_plot_popup_peak, wxT("&Peaks"), wxT(""));
show_menu->Check (ID_plot_popup_peak, peaks_visible);
popup_menu.Append (wxNewId(), wxT("&Show"), show_menu);
wxMenu *color_menu = new wxMenu;
color_menu->Append (ID_plot_popup_c_background, wxT("&Background"));
color_menu->Append (ID_plot_popup_c_inactive_data, wxT("&Inactive Data"));
color_menu->Append (ID_plot_popup_c_sum, wxT("&Sum"));
color_menu->AppendSeparator();
color_menu->Append (ID_plot_popup_c_inv, wxT("&Invert colors"));
popup_menu.Append (wxNewId(), wxT("&Color"), color_menu);
wxMenu *size_menu = new wxMenu;
size_menu->AppendCheckItem (ID_plot_popup_pt_size, wxT("&Line"), wxT(""));
size_menu->Check (ID_plot_popup_pt_size, line_between_points);
size_menu->AppendSeparator();
for (int i = 1; i <= max_radius; i++)
size_menu->AppendRadioItem (ID_plot_popup_pt_size + i,
wxString::Format (wxT("&%d"), i), wxT(""));
size_menu->Check (ID_plot_popup_pt_size + point_radius, true);
popup_menu.Append (wxNewId(), wxT("Data point si&ze"), size_menu);
popup_menu.Append (ID_plot_popup_axes, wxT("Configure &Axes..."));
popup_menu.Append (ID_plot_popup_plabels, wxT("Configure Peak &Labels..."));
PopupMenu (&popup_menu, event.GetX(), event.GetY());
}
void MainPlot::show_peak_menu (wxMouseEvent &event)
{
if (over_peak == -1) return;
wxMenu peak_menu;
peak_menu.Append(ID_peak_popup_info, wxT("Show &Info"));
peak_menu.Append(ID_peak_popup_del, wxT("&Delete"));
peak_menu.Append(ID_peak_popup_guess, wxT("&Guess parameters"));
peak_menu.Enable(ID_peak_popup_guess,
ftk->get_function(over_peak)->has_center());
PopupMenu (&peak_menu, event.GetX(), event.GetY());
}
void MainPlot::PeakInfo()
{
if (over_peak >= 0)
ftk->exec("info+ " + ftk->get_function(over_peak)->xname);
}
void MainPlot::OnPeakDelete(wxCommandEvent&)
{
if (over_peak >= 0)
ftk->exec("delete " + ftk->get_function(over_peak)->xname);
}
void MainPlot::OnPeakGuess(wxCommandEvent&)
{
if (over_peak >= 0) {
Function const* p = ftk->get_function(over_peak);
if (p->has_center()) {
fp ctr = p->center();
fp plusmin = max(fabs(p->fwhm()), p->iwidth());
ftk->exec(p->xname + " = guess " + p->type_name + " ["
+ S(ctr-plusmin) + ":" + S(ctr+plusmin) + "]"
+ frame->get_in_dataset());
}
}
}
// mouse usage
//
// Simple Rules:
// 1. When one button is down, pressing other button cancels action
// 2. Releasing / keeping down / pressing Ctrl/Alt keys, when you keep
// mouse button down makes no difference.
// 3. Ctrl and Alt buttons are equivalent.
// ----------------------------------
// Usage:
// Left/Right Button (no Ctrl) -- mode dependent
// Ctrl + Left/Right Button -- the same as Left/Right in normal mode
// Middle Button -- rectangle zoom
// Shift sometimes makes a difference
void MainPlot::set_mouse_mode(MouseModeEnum m)
{
if (pressed_mouse_button) cancel_mouse_press();
MouseModeEnum old = mode;
if (m != mmd_peak)
basic_mode = m;
mode = m;
update_mouse_hints();
if (old != mode && (old == mmd_bg || mode == mmd_bg
|| visible_peaktops(old) != visible_peaktops(mode)))
refresh(false);
}
void MainPlot::update_mouse_hints()
// update mouse hint on status bar and cursor
{
string left="", right="";
switch (pressed_mouse_button) {
case 1:
left = ""; right = "cancel";
break;
case 2:
left = "cancel"; right = "cancel";
break;
case 3:
left = "cancel"; right = "";
break;
default:
//button not pressed
switch (mode) {
case mmd_peak:
left = "move peak"; right = "peak menu";
SetCursor (wxCURSOR_CROSS);
break;
case mmd_zoom:
left = "rect zoom"; right = "plot menu";
SetCursor (wxCURSOR_ARROW);
break;
case mmd_bg:
left = "add point"; right = "del point";
SetCursor (wxCURSOR_ARROW);
break;
case mmd_add:
left = "draw-add"; right = "add-in-range";
SetCursor (wxCURSOR_ARROW);
break;
case mmd_range:
left = "activate"; right = "disactivate";
SetCursor (wxCURSOR_ARROW);
break;
default:
assert(0);
}
}
frame->set_status_hint(left, right);
}
void MainPlot::OnMouseMove(wxMouseEvent &event)
{
//display coords in status bar
int X = event.GetX();
int Y = event.GetY();
frame->set_status_coord_info(xs.val(X), ys.val(Y));
if (pressed_mouse_button == 0) {
if (mode == mmd_range) {
if (!ctrl_on_down)
vert_line_following_cursor(mat_move, event.GetX());
else
vert_line_following_cursor(mat_stop);
}
if (visible_peaktops(mode))
look_for_peaktop (event);
frame->draw_crosshair(X, Y);
}
else {
vert_line_following_cursor(mat_move, event.GetX());
draw_moving_func(mat_move, event.GetX(), event.GetY(),
event.ShiftDown());
peak_draft(mat_move, event.GetX(), event.GetY());
draw_temporary_rect(mat_move, event.GetX(), event.GetY());
}
}
void MainPlot::look_for_peaktop (wxMouseEvent& event)
{
int focused_data = ftk->get_active_ds_position();
Sum const* sum = ftk->get_sum(focused_data);
vector<int> const& idx = sum->get_ff_idx();
if (special_points.size() != idx.size())
refresh(false);
int n = get_special_point_at_pointer(event);
int nearest = n == -1 ? -1 : idx[n];
if (over_peak == nearest)
return;
//if we are here, over_peak != nearest; changing cursor and statusbar text
// and show limits
over_peak = nearest;
if (nearest != -1) {
Function const* f = ftk->get_function(over_peak);
string s = f->xname + " " + f->type_name + " ";
vector<string> const& vn = f->type_var_names;
for (int i = 0; i < size(vn); ++i)
s += " " + vn[i] + "=" + S(f->get_var_value(i));
frame->set_status_text(s);
set_mouse_mode(mmd_peak);
fp x1=0., x2=0.;
bool r = f->get_nonzero_range(ftk->get_settings()->get_cut_level(),
x1, x2);
if (r) {
limit1 = xs.px(x1);
limit2 = xs.px(x2);
draw_dashed_vert_line(limit1, wxDOT_DASH);
draw_dashed_vert_line(limit2, wxDOT_DASH);
}
else
limit1 = limit2 = INT_MIN;
}
else { //was over peak, but now is not
frame->set_status_text("");
set_mouse_mode(basic_mode);
draw_dashed_vert_line(limit1, wxDOT_DASH);
draw_dashed_vert_line(limit2, wxDOT_DASH);
limit1 = limit2 = INT_MIN;
}
}
void MainPlot::cancel_mouse_press()
{
if (pressed_mouse_button) {
draw_temporary_rect(mat_stop);
draw_moving_func(mat_stop);
peak_draft(mat_stop);
vert_line_following_cursor(mat_stop);
mouse_press_X = mouse_press_Y = INT_MIN;
pressed_mouse_button = 0;
frame->set_status_text("");
update_mouse_hints();
}
}
void MainPlot::OnButtonDown (wxMouseEvent &event)
{
if (pressed_mouse_button) {
cancel_mouse_press();
return;
}
frame->draw_crosshair(-1, -1);
int button = event.GetButton();
pressed_mouse_button = button;
ctrl_on_down = (event.AltDown() || event.CmdDown());
shift_on_down = event.ShiftDown();
mouse_press_X = event.GetX();
mouse_press_Y = event.GetY();
fp x = xs.val (event.GetX());
fp y = ys.val (event.GetY());
if (button == 1 && (ctrl_on_down || mode == mmd_zoom) || button == 2) {
draw_temporary_rect(mat_start, event.GetX(), event.GetY());
SetCursor(wxCURSOR_MAGNIFIER);
frame->set_status_text("Select second corner to zoom...");
}
else if (button == 3 && (ctrl_on_down || mode == mmd_zoom)) {
show_popup_menu (event);
cancel_mouse_press();
}
else if (button == 1 && mode == mmd_peak) {
frame->activate_function(over_peak);
draw_moving_func(mat_start, event.GetX(), event.GetY());
frame->set_status_text("Moving " + ftk->get_function(over_peak)->xname
+ "...");
}
else if (button == 3 && mode == mmd_peak) {
show_peak_menu(event);
cancel_mouse_press();
}
else if (button == 1 && mode == mmd_bg) {
add_background_point(x, y);
refresh(false);
}
else if (button == 3 && mode == mmd_bg) {
rm_background_point(x);
refresh(false);
}
else if (button == 1 && mode == mmd_add) {
func_draft_kind
= get_function_kind(Function::get_formula(frame->get_peak_type()));
peak_draft (mat_start, event.GetX(), event.GetY());
SetCursor(wxCURSOR_SIZING);
frame->set_status_text("Add drawed peak...");
}
else if (button == 3 && mode == mmd_add) {
vert_line_following_cursor(mat_start, mouse_press_X+1, mouse_press_X);
SetCursor(wxCURSOR_SIZEWE);
frame->set_status_text("Select range to add a peak in it...");
}
else if (button != 2 && mode == mmd_range) {
if (button == 1) {
Data const* data = ftk->get_data(ftk->get_active_ds_position());
if (!data->is_empty() && data->get_n() == size(data->points())) {
cancel_mouse_press();
wxMessageBox(
wxT("You pressed the left mouse button in data-range mode,")
wxT("\nbut all data points are already active.")
wxT("\n\nYou can see mouse hints at the status bar: the left")
wxT("\nbutton activates, and the right disactivates points.")
wxT("\n\nExtra hint: to activate/disactivate points in")
wxT(" a rectangle,\npress Shift"),
wxT("How to use mouse..."),
wxOK|wxICON_INFORMATION);
return;
}
}
string status_info;
if (shift_on_down) {
SetCursor(wxCURSOR_SIZENWSE);
draw_temporary_rect(mat_start, event.GetX(), event.GetY());
status_info = "Select data in rectangle to ";
}
else {
SetCursor(wxCURSOR_SIZEWE);
vert_line_following_cursor(mat_start, mouse_press_X+1,
mouse_press_X);
status_info = "Select data range to ";
}
frame->set_status_text(status_info + (button==1 ? "activate..."
: "disactivate..."));
}
update_mouse_hints();
}
void MainPlot::OnButtonUp (wxMouseEvent &event)
{
int button = event.GetButton();
if (button != pressed_mouse_button) {
pressed_mouse_button = 0;
return;
}
int dist_X = abs(event.GetX() - mouse_press_X);
int dist_Y = abs(event.GetY() - mouse_press_Y);
// if Down and Up events are at the same position -> cancel
if (button == 1 && (ctrl_on_down || mode == mmd_zoom) || button == 2) {
draw_temporary_rect(mat_stop);
if (dist_X + dist_Y >= 10) {
fp x1 = xs.val(mouse_press_X);
fp x2 = xs.val(event.GetX());
fp y1 = ys.val(mouse_press_Y);
fp y2 = ys.val(event.GetY());
frame->change_zoom("[ " + S(min(x1,x2)) + " : "
+ S(max(x1,x2)) + " ]"
"[ " + S(min(y1,y2), 12) + " : "
+ S(max(y1,y2), 12) + " ]");
}
frame->set_status_text("");
}
else if (mode == mmd_peak && button == 1) {
if (dist_X + dist_Y >= 2) {
string cmd = fmd.get_cmd();
if (!cmd.empty())
ftk->exec(cmd);
}
draw_moving_func(mat_stop);
frame->set_status_text("");
}
else if (mode == mmd_range && button != 2) {
vert_line_following_cursor(mat_stop);
draw_temporary_rect(mat_stop);
string c = (button == 1 ? "A = a or " : "A = a and not ");
if (!shift_on_down && dist_X >= 5) {
fp xmin = xs.val (min (event.GetX(), mouse_press_X));
fp xmax = xs.val (max (event.GetX(), mouse_press_X));
string cond = "(" + S(xmin) + "< x <" + S(xmax) + ")";
ftk->exec(c + cond + frame->get_in_one_or_all_datasets());
}
else if (shift_on_down && dist_X + dist_Y >= 10) {
fp x1 = xs.val(mouse_press_X);
fp x2 = xs.val(event.GetX());
fp y1 = ys.val(mouse_press_Y);
fp y2 = ys.val(event.GetY());
string cond = "(" + S(min(x1,x2)) + " < x < " + S(max(x1,x2))
+ " and " + S(min(y1,y2)) + " < y < " + S(max(y1,y2)) + ")";
ftk->exec(c + cond + frame->get_in_one_or_all_datasets());
}
frame->set_status_text("");
}
else if (mode == mmd_add && button == 1) {
frame->set_status_text("");
peak_draft(mat_stop, event.GetX(), event.GetY());
string F = ftk->get_ds_count() > 1 ? frame->get_active_data_str()+".F"
: "F";
if (func_draft_kind == fk_linear) {
fp y = ys.val(event.GetY());
ftk->exec(F + " += " + frame->get_peak_type()
+ "(slope=~" + S(0) + ", intercept=~" + S(y)
+ ", avgy=~" + S(y) + ")");
}
else {
if (dist_X + dist_Y >= 5) {
fp height = ys.val(event.GetY());
fp center = xs.val(mouse_press_X);
fp fwhm = fabs(center - xs.val(event.GetX()));
fp area = height * fwhm;
ftk->exec(F + " += " + frame->get_peak_type()
+ "(height=~" + S(height) + ", center=~" + S(center)
+ ", fwhm=~" + S(fwhm) + ", area=~" + S(area) + ")");
}
}
}
else if (mode == mmd_add && button == 3) {
frame->set_status_text("");
if (dist_X >= 5) {
fp x1 = xs.val(mouse_press_X);
fp x2 = xs.val(event.GetX());
ftk->exec("guess " + frame->get_peak_type()
+ " [" + S(min(x1,x2)) + " : " + S(max(x1,x2)) + "]"
+ frame->get_in_dataset());
}
vert_line_following_cursor(mat_stop);
}
else {
;// nothing - action done in OnButtonDown()
}
pressed_mouse_button = 0;
update_mouse_hints();
}
void MainPlot::OnKeyDown (wxKeyEvent& event)
{
if (event.GetKeyCode() == WXK_ESCAPE) {
cancel_mouse_press();
}
else if (should_focus_input(event)) {
cancel_mouse_press();
frame->focus_input(event);
}
else
event.Skip();
}
bool MainPlot::draw_moving_func(MouseActEnum ma, int X, int Y, bool shift)
{
static int prevX=INT_MIN, prevY=INT_MIN;
static wxCursor old_cursor = wxNullCursor;
static int func_nr = -1;
if (over_peak == -1)
return false;
Function const* p = ftk->get_function(over_peak);
if (ma != mat_start) {
if (func_nr != over_peak)
return false;
draw_xor_peak(p, fmd.get_values()); //clear old or redraw
}
if (ma == mat_redraw)
; //do nothing, already redrawn
else if (ma == mat_start) {
func_nr = over_peak;
fmd.start(p, X, Y, xs.val(X), ys.val(Y));
draw_xor_peak(p, fmd.get_values());
prevX = X;
prevY = Y;
old_cursor = GetCursor();
SetCursor(wxCURSOR_SIZENWSE);
}
else if (ma == mat_move) {
fmd.move(shift, X, Y, xs.val(X), ys.val(Y));
frame->set_status_text(fmd.get_status());
draw_xor_peak(p, fmd.get_values());
prevX = X;
prevY = Y;
}
else if (ma == mat_stop) {
func_nr = -1;
if (old_cursor.Ok()) {
SetCursor(old_cursor);
old_cursor = wxNullCursor;
}
fmd.stop();
}
return true;
}
void MainPlot::draw_xor_peak(Function const* func, vector<fp> const& p_values)
{
wxClientDC dc(this);
dc.SetLogicalFunction (wxINVERT);
dc.SetPen(*wxBLACK_DASHED_PEN);
int n = GetClientSize().GetWidth();
if (n <= 0)
return;
vector<fp> xx(n), yy(n, 0);
for (int i = 0; i < n; ++i)
xx[i] = xs.val(i);
func->calculate_values_with_params(xx, yy, p_values);
vector<int> YY(n);
for (int i = 0; i < n; ++i)
YY[i] = ys.px(yy[i]);
for (int i = 1; i < n; i++)
dc.DrawLine (i-1, YY[i-1], i, YY[i]);
}
void MainPlot::peak_draft(MouseActEnum ma, int X_, int Y_)
{
static wxPoint prev(INT_MIN, INT_MIN);
if (ma != mat_start) {
if (prev.x == INT_MIN)
return;
//clear/redraw old peak-draft
draw_peak_draft(mouse_press_X, abs(mouse_press_X - prev.x), prev.y);
}
switch (ma) {
case mat_start:
case mat_move:
prev.x = X_, prev.y = Y_;
draw_peak_draft(mouse_press_X, abs(mouse_press_X-prev.x), prev.y);
break;
case mat_stop:
prev.x = prev.y = INT_MIN;
break;
case mat_redraw: //already redrawn
break;
default: assert(0);
}
}
void MainPlot::draw_peak_draft(int Ctr, int Hwhm, int Y)
{
if (Ctr == INT_MIN || Hwhm == INT_MIN || Y == INT_MIN)
return;
wxClientDC dc(this);
dc.SetLogicalFunction (wxINVERT);
dc.SetPen(*wxBLACK_DASHED_PEN);
int Y0 = ys.px(0);
if (func_draft_kind == fk_linear) {
dc.DrawLine (0, Y, GetClientSize().GetWidth(), Y);
}
else {
dc.DrawLine (Ctr, Y0, Ctr, Y); //vertical line
dc.DrawLine (Ctr - Hwhm, (Y+Y0)/2, Ctr + Hwhm, (Y+Y0)/2); //horizontal
dc.DrawLine (Ctr, Y, Ctr - 2 * Hwhm, Y0); //left slope
dc.DrawLine (Ctr, Y, Ctr + 2 * Hwhm, Y0); //right slope
}
}
void MainPlot::draw_temporary_rect(MouseActEnum ma, int X_, int Y_)
{
static int X1 = INT_MIN, Y1 = INT_MIN, X2 = INT_MIN, Y2 = INT_MIN;
if (ma != mat_start && X1 == INT_MIN)
return;
switch (ma) {
case mat_start:
X1 = X2 = X_;
Y1 = Y2 = Y_;
draw_rect (X1, Y1, X2, Y2);
CaptureMouse();
break;
case mat_move:
draw_rect (X1, Y1, X2, Y2); //clear old rectangle
X2 = X_;
Y2 = Y_;
draw_rect (X1, Y1, X2, Y2);
break;
case mat_stop:
draw_rect (X1, Y1, X2, Y2); //clear old rectangle
X1 = INT_MIN;
ReleaseMouse();
break;
case mat_redraw: //unused
break;
}
}
void MainPlot::draw_rect (int X1, int Y1, int X2, int Y2)
{
if (X1 == INT_MIN || Y1 == INT_MIN || X2 == INT_MIN || Y2 == INT_MIN)
return;
wxClientDC dc(this);
dc.SetLogicalFunction (wxINVERT);
dc.SetPen(*wxBLACK_DASHED_PEN);
dc.SetBrush (*wxTRANSPARENT_BRUSH);
int xmin = min (X1, X2);
int width = max (X1, X2) - xmin;
int ymin = min (Y1, Y2);
int height = max (Y1, Y2) - ymin;
dc.DrawRectangle (xmin, ymin, width, height);
}
void MainPlot::OnPopupShowXX (wxCommandEvent& event)
{
switch (event.GetId()) {
case ID_plot_popup_sum : sum_visible = !sum_visible; break;
case ID_plot_popup_groups: groups_visible = !groups_visible; break;
case ID_plot_popup_peak : peaks_visible = !peaks_visible; break;
default: assert(0);
}
refresh(false);
}
void MainPlot::OnPopupColor(wxCommandEvent& event)
{
int n = event.GetId();
wxColour *color = 0;
if (n == ID_plot_popup_c_background)
color = &backgroundCol;
else if (n == ID_plot_popup_c_inactive_data) {
color = &inactiveDataCol;
}
else if (n == ID_plot_popup_c_sum)
color = &sumCol;
else
return;
if (change_color_dlg(*color)) {
if (n == ID_plot_popup_c_background)
frame->update_data_pane();
refresh();
}
}
void MainPlot::OnInvertColors (wxCommandEvent&)
{
backgroundCol = invert_colour(backgroundCol);
for (int i = 0; i < max_data_cols; i++)
dataColour[i] = invert_colour(dataColour[i]);
inactiveDataCol = invert_colour(inactiveDataCol);
sumCol = invert_colour(sumCol);
xAxisCol = invert_colour(xAxisCol);
for (int i = 0; i < max_group_cols; i++)
groupCol[i] = invert_colour(groupCol[i]);
for (int i = 0; i < max_peak_cols; i++)
peakCol[i] = invert_colour(peakCol[i]);
frame->update_data_pane();
refresh();
}
void MainPlot::OnPopupRadius (wxCommandEvent& event)
{
int nr = event.GetId() - ID_plot_popup_pt_size;
if (nr == 0)
line_between_points = !line_between_points;
else
point_radius = nr;
refresh(false);
}
void MainPlot::OnConfigureAxes (wxCommandEvent&)
{
ConfigureAxesDlg dialog(frame, -1, this);
dialog.ShowModal();
}
void MainPlot::OnConfigurePLabels (wxCommandEvent&)
{
ConfigurePLabelsDlg dialog(frame, -1, this);
dialog.ShowModal();
}
void MainPlot::OnZoomAll (wxCommandEvent&)
{
frame->OnGViewAll(dummy_cmd_event);
}
//===============================================================
// ConfigureAxesDlg
//===============================================================
BEGIN_EVENT_TABLE(ConfigureAxesDlg, wxDialog)
EVT_BUTTON(wxID_APPLY, ConfigureAxesDlg::OnApply)
EVT_BUTTON(wxID_CLOSE, ConfigureAxesDlg::OnClose)
EVT_BUTTON(ID_CAD_COLOR, ConfigureAxesDlg::OnChangeColor)
EVT_BUTTON(ID_CAD_FONT, ConfigureAxesDlg::OnChangeFont)
END_EVENT_TABLE()
ConfigureAxesDlg::ConfigureAxesDlg(wxWindow* parent, wxWindowID id,
MainPlot* plot_)
//explicit conversion of title to wxString() is neccessary
: wxDialog(parent, id, wxString(wxT("Configure Axes"))), plot(plot_),
axis_color(plot_->xAxisCol)
{
wxBoxSizer *top_sizer = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *sizer1 = new wxBoxSizer(wxHORIZONTAL);
wxStaticBoxSizer *xsizer = new wxStaticBoxSizer(wxVERTICAL, this,
wxT("X axis"));
x_show_axis = new wxCheckBox(this, -1, wxT("show axis"));
x_show_axis->SetValue(plot->x_axis_visible);
xsizer->Add(x_show_axis, 0, wxALL, 5);
x_show_tics = new wxCheckBox(this, -1, wxT("show tics"));
x_show_tics->SetValue(plot->xtics_visible);
xsizer->Add(x_show_tics, 0, wxALL, 5);
wxBoxSizer* xsizer_t = new wxBoxSizer(wxVERTICAL);
x_show_minor_tics = new wxCheckBox(this, -1, wxT("show minor tics"));
x_show_minor_tics->SetValue(plot->xminor_tics_visible);
xsizer_t->Add(x_show_minor_tics, 0, wxALL, 5);
x_show_grid = new wxCheckBox(this, -1, wxT("show grid"));
x_show_grid->SetValue(plot->x_grid);
xsizer_t->Add(x_show_grid, 0, wxALL, 5);
wxBoxSizer *xmt_sizer = new wxBoxSizer(wxHORIZONTAL);
xmt_sizer->Add(new wxStaticText(this, -1, wxT("max. number of tics")),
0, wxALL|wxALIGN_CENTRE_VERTICAL, 5);
x_max_tics = new wxSpinCtrl (this, -1, wxT("7"),
wxDefaultPosition, wxSize(50, -1),
wxSP_ARROW_KEYS, 1, 30, 7);
x_max_tics->SetValue(plot->x_max_tics);
xmt_sizer->Add(x_max_tics, 0, wxALL, 5);
xsizer_t->Add(xmt_sizer);
wxBoxSizer *xts_sizer = new wxBoxSizer(wxHORIZONTAL);
xts_sizer->Add(new wxStaticText(this, -1, wxT("length of tics")),
0, wxALL|wxALIGN_CENTRE_VERTICAL, 5);
x_tics_size = new wxSpinCtrl (this, -1, wxT("4"),
wxDefaultPosition, wxSize(50, -1),
wxSP_ARROW_KEYS, -10, 20, 4);
x_tics_size->SetValue(plot->x_tic_size);
xts_sizer->Add(x_tics_size, 0, wxALL, 5);
xsizer_t->Add(xts_sizer);
xsizer->Add(xsizer_t, 0, wxLEFT, 15);
x_reversed_cb = new wxCheckBox(this, -1, wxT("reversed axis"));
x_reversed_cb->SetValue(plot->xs.reversed);
xsizer->Add(x_reversed_cb, 0, wxALL, 5);
x_logarithm_cb = new wxCheckBox(this, -1, wxT("logarithmic scale"));
x_logarithm_cb->SetValue(plot->xs.logarithm);
xsizer->Add(x_logarithm_cb, 0, wxALL, 5);
sizer1->Add(xsizer, 0, wxALL, 5);
wxStaticBoxSizer *ysizer = new wxStaticBoxSizer(wxVERTICAL, this,
wxT("Y axis"));
y_show_axis = new wxCheckBox(this, -1, wxT("show axis"));
y_show_axis->SetValue(plot->y_axis_visible);
ysizer->Add(y_show_axis, 0, wxALL, 5);
y_show_tics = new wxCheckBox(this, -1, wxT("show tics"));
y_show_tics->SetValue(plot->ytics_visible);
ysizer->Add(y_show_tics, 0, wxALL, 5);
wxBoxSizer* ysizer_t = new wxBoxSizer(wxVERTICAL);
y_show_minor_tics = new wxCheckBox(this, -1, wxT("show minor tics"));
y_show_minor_tics->SetValue(plot->yminor_tics_visible);
ysizer_t->Add(y_show_minor_tics, 0, wxALL, 5);
y_show_grid = new wxCheckBox(this, -1, wxT("show grid"));
y_show_grid->SetValue(plot->y_grid);
ysizer_t->Add(y_show_grid, 0, wxALL, 5);
wxBoxSizer *ymt_sizer = new wxBoxSizer(wxHORIZONTAL);
ymt_sizer->Add(new wxStaticText(this, -1, wxT("max. number of tics")),
0, wxALL|wxALIGN_CENTRE_VERTICAL, 5);
y_max_tics = new wxSpinCtrl (this, -1, wxT("7"),
wxDefaultPosition, wxSize(50, -1),
wxSP_ARROW_KEYS, 1, 30, 7);
y_max_tics->SetValue(plot->y_max_tics);
ymt_sizer->Add(y_max_tics, 0, wxALL, 5);
ysizer_t->Add(ymt_sizer);
wxBoxSizer *yts_sizer = new wxBoxSizer(wxHORIZONTAL);
yts_sizer->Add(new wxStaticText(this, -1, wxT("length of tics")),
0, wxALL|wxALIGN_CENTRE_VERTICAL, 5);
y_tics_size = new wxSpinCtrl (this, -1, wxT("4"),
wxDefaultPosition, wxSize(50, -1),
wxSP_ARROW_KEYS, 1, 20, 4);
y_tics_size->SetValue(plot->y_tic_size);
yts_sizer->Add(y_tics_size, 0, wxALL, 5);
ysizer_t->Add(yts_sizer);
ysizer->Add(ysizer_t, 0, wxLEFT, 15);
y_reversed_cb = new wxCheckBox(this, -1, wxT("reversed axis"));
y_reversed_cb->SetValue(plot->ys.reversed);
ysizer->Add(y_reversed_cb, 0, wxALL, 5);
y_logarithm_cb = new wxCheckBox(this, -1, wxT("logarithmic scale"));
y_logarithm_cb->SetValue(plot->ys.logarithm);
ysizer->Add(y_logarithm_cb, 0, wxALL, 5);
sizer1->Add(ysizer, 0, wxALL, 5);
top_sizer->Add(sizer1, 0);
wxBoxSizer *common_sizer = new wxBoxSizer(wxHORIZONTAL);
common_sizer->Add(new wxButton(this, ID_CAD_COLOR,
wxT("Change axes color...")),
0, wxALL, 5);
common_sizer->Add(new wxButton(this, ID_CAD_FONT,
wxT("Change tics font...")),
0, wxALL, 5);
top_sizer->Add(common_sizer, 0, wxALIGN_CENTER);
add_apply_close_buttons(this, top_sizer);
SetSizerAndFit(top_sizer);
}
void ConfigureAxesDlg::OnApply (wxCommandEvent&)
{
bool scale_changed = false;
plot->xAxisCol = axis_color;
plot->x_axis_visible = x_show_axis->GetValue();
plot->xtics_visible = x_show_tics->GetValue();
plot->xminor_tics_visible = x_show_minor_tics->GetValue();
plot->x_grid = x_show_grid->GetValue();
plot->x_max_tics = x_max_tics->GetValue();
plot->x_tic_size = x_tics_size->GetValue();
if (plot->xs.reversed != x_reversed_cb->GetValue()) {
plot->xs.reversed = x_reversed_cb->GetValue();
scale_changed = true;
}
if (plot->xs.logarithm != x_logarithm_cb->GetValue()) {
plot->xs.logarithm = x_logarithm_cb->GetValue();
scale_changed = true;
}
plot->y_axis_visible = y_show_axis->GetValue();
plot->ytics_visible = y_show_tics->GetValue();
plot->yminor_tics_visible = y_show_minor_tics->GetValue();
plot->y_grid = y_show_grid->GetValue();
plot->y_max_tics = y_max_tics->GetValue();
plot->y_tic_size = y_tics_size->GetValue();
plot->ys.reversed = y_reversed_cb->GetValue();
plot->ys.logarithm = y_logarithm_cb->GetValue();
frame->refresh_plots(false, !scale_changed);
}
void ConfigureAxesDlg::OnChangeFont (wxCommandEvent&)
{
plot->change_tics_font();
}
//===============================================================
// ConfigurePLabelsDlg
//===============================================================
BEGIN_EVENT_TABLE(ConfigurePLabelsDlg, wxDialog)
EVT_BUTTON(wxID_APPLY, ConfigurePLabelsDlg::OnApply)
EVT_BUTTON(wxID_CLOSE, ConfigurePLabelsDlg::OnClose)
EVT_BUTTON(ID_CPL_FONT, ConfigurePLabelsDlg::OnChangeLabelFont)
EVT_CHECKBOX(ID_CPL_SHOW, ConfigurePLabelsDlg::OnCheckShowLabel)
EVT_TEXT(ID_CPL_TEXT, ConfigurePLabelsDlg::OnChangeLabelText)
EVT_RADIOBOX(ID_CPL_RADIO, ConfigurePLabelsDlg::OnRadioLabel)
END_EVENT_TABLE()
ConfigurePLabelsDlg::ConfigurePLabelsDlg(wxWindow* parent, wxWindowID id,
MainPlot* plot_)
//explicit conversion of title to wxString() is neccessary
: wxDialog(parent, id, wxString(wxT("Configure Peak Labels"))), plot(plot_),
in_onradiolabel(false)
{
wxBoxSizer *top_sizer = new wxBoxSizer(wxVERTICAL);
show_plabels = new wxCheckBox(this, ID_CPL_SHOW, wxT("show peak labels"));
top_sizer->Add(show_plabels, 0, wxALL, 5);
wxBoxSizer *sizer1 = new wxBoxSizer(wxHORIZONTAL);
vector<string> label_radio_choice;
label_radio_choice.push_back("area");
label_radio_choice.push_back("height");
label_radio_choice.push_back("center");
label_radio_choice.push_back("fwhm");
label_radio_choice.push_back("name");
label_radio_choice.push_back("custom");
label_radio = new wxRadioBox(this, ID_CPL_RADIO, wxT("labels with:"),
wxDefaultPosition, wxDefaultSize,
stl2wxArrayString(label_radio_choice),
1, wxRA_SPECIFY_COLS);
sizer1->Add(label_radio, 0, wxALL|wxEXPAND, 5);
wxStaticBoxSizer *xsizer = new wxStaticBoxSizer(wxVERTICAL, this,
wxT("list of replaceable tokens"));
xsizer->Add(new wxStaticText(this, -1, wxT("<area> area of peak\n")
wxT("<height> height of peak\n")
wxT("<center> center of peak\n")
wxT("<fwhm> FWHM of peak\n")
wxT("<ib> integral breadth of peak\n")
wxT("<name> name of function\n")
wxT("<br> line break\n")),
0, wxALL|wxEXPAND, 5);
sizer1->Add(xsizer, 0, wxALL, 5);
top_sizer->Add(sizer1, 0);
label_text = new wxTextCtrl(this, ID_CPL_TEXT, wxT(""));
top_sizer->Add(label_text, 0, wxALL|wxEXPAND, 5);
vertical_rb = new wxRadioBox(this, ID_CPL_RADIO, wxT("label direction"),
wxDefaultPosition, wxDefaultSize,
stl2wxArrayString(vector2(string("horizontal"),
string("vertical"))),
2);
top_sizer->Add(vertical_rb, 0, wxALL|wxEXPAND, 5);
top_sizer->Add(new wxButton(this, ID_CPL_FONT, wxT("Change label font...")),
0, wxALL|wxALIGN_CENTER, 5);
top_sizer->Add(new wxStaticText(this, -1,
wxT("Labels have the same colors as peaks")),
0, wxALL, 5);
add_apply_close_buttons(this, top_sizer);
SetSizerAndFit(top_sizer);
//set initial values
show_plabels->SetValue(plot->plabels_visible);
label_text->SetValue(s2wx(plot->plabel_format));
vertical_rb->SetSelection(plot->vertical_plabels ? 1 : 0);
label_radio->SetStringSelection(wxT("custom"));
label_text->Enable(plot->plabels_visible);
label_radio->Enable(plot->plabels_visible);
vertical_rb->Enable(plot->plabels_visible);
}
void ConfigurePLabelsDlg::OnChangeLabelText (wxCommandEvent&)
{
// don't change radio if this event is triggered by label_text->SetValue()
// from radiobox event handler
if (!in_onradiolabel)
label_radio->SetStringSelection(wxT("custom"));
}
void ConfigurePLabelsDlg::OnCheckShowLabel (wxCommandEvent& event)
{
bool checked = event.IsChecked();
label_radio->Enable(checked);
label_text->Enable(checked);
vertical_rb->Enable(checked);
}
void ConfigurePLabelsDlg::OnRadioLabel (wxCommandEvent&)
{
in_onradiolabel = true;
wxString s = label_radio->GetStringSelection();
if (s != wxT("custom"))
label_text->SetValue(wxT("<") + s + wxT(">"));
in_onradiolabel = false;
}
void ConfigurePLabelsDlg::OnApply (wxCommandEvent&)
{
plot->plabels_visible = show_plabels->GetValue();
plot->plabel_format = wx2s(label_text->GetValue());
plot->vertical_plabels = vertical_rb->GetSelection() != 0;
frame->refresh_plots(false, true);
}
void ConfigurePLabelsDlg::OnChangeLabelFont (wxCommandEvent&)
{
wxFontData data;
data.SetInitialFont(plot->plabelFont);
wxFontDialog dialog(frame, data);
if (dialog.ShowModal() == wxID_OK)
{
wxFontData retData = dialog.GetFontData();
plot->plabelFont = retData.GetChosenFont();
plot->refresh(false);
}
}
//===============================================================
// BgManager (for interactive background setting)
//===============================================================
/*
// outdated
void auto_background (int n, fp p1, bool is_perc1, fp p2, bool is_perc2)
{
//FIXME: Do you know any good algorithm, that can extract background
// from data?
if (n <= 0 || n >= size(p) / 2)
return;
int ps = p.size();
for (int i = 0; i < n; i++) {
int l = ps * i / n;
int u = ps * (i + 1) / n;
vector<fp> v (u - l);
for (int k = l; k < u; k++)
v[k - l] = p[k].orig_y;
sort (v.begin(), v.end());
int y_avg_beg = 0, y_avg_end = v.size();
if (is_perc1) {
p1 = min (max (p1, 0.), 100.);
y_avg_beg = static_cast<int>(v.size() * p1 / 100);
}
else {
y_avg_beg = upper_bound (v.begin(), v.end(), v[0] + p1) - v.begin();
if (y_avg_beg == size(v))
y_avg_beg--;
}
if (is_perc2) {
p2 = min (max (p2, 0.), 100.);
y_avg_end = y_avg_beg + static_cast<int>(v.size() * p2 / 100);
}
else {
fp end_val = v[y_avg_beg] + p2;
y_avg_end = upper_bound (v.begin(), v.end(), end_val) - v.begin();
}
if (y_avg_beg < 0)
y_avg_beg = 0;
if (y_avg_end > size(v))
y_avg_end = v.size();
if (y_avg_beg >= y_avg_end) {
if (y_avg_beg >= size(v))
y_avg_beg = v.size() - 1;
y_avg_end = y_avg_beg + 1;
}
int counter = 0;
fp y = 0;
for (int j = y_avg_beg; j < y_avg_end; j++){
counter++;
y += v[j];
}
y /= counter;
add_background_point ((p[l].x + p[u - 1].x) / 2, y, bgc_bg);
}
}
*/
void BgManager::add_background_point(fp x, fp y)
{
if (!bg_backup.empty()) {
int r = wxMessageBox(wxT("The old background has been removed\n")
wxT("and now you start to create a new one.\n")
wxT("It will make impossible to undo\n")
wxT("removing the old background.\n\n")
wxT("Use GUI->Mode->Baseline Handling->Clear...\n")
wxT("to forget the old background explicitly.\n\n")
wxT("Continue?"),
wxT("Do you want to start a new background?"),
wxICON_QUESTION|wxYES_NO);
if (r == wxYES) {
bg_backup.clear();
frame->update_toolbar();
}
else
return;
}
rm_background_point(x);
PointQ t(x, y);
bg_iterator l = lower_bound(bg.begin(), bg.end(), t);
bg.insert (l, t);
recompute_bgline();
}
void BgManager::rm_background_point (fp x)
{
int X = x_scale.px(x);
fp lower = x_scale.val(X - min_dist);
fp upper = x_scale.val(X + min_dist);
if (lower > upper)
swap(lower, upper);
bg_iterator l = lower_bound(bg.begin(), bg.end(), PointQ(lower, 0));
bg_iterator u = upper_bound (bg.begin(), bg.end(), PointQ(upper, 0));
if (u > l) {
bg.erase(l, u);
ftk->vmsg (S(u - l) + " background points removed.");
recompute_bgline();
}
}
void BgManager::clear_background()
{
int n = bg.size();
bg.clear();
recompute_bgline();
if (n != 0)
ftk->vmsg (S(n) + " background points deleted.");
}
void BgManager::strip_background()
{
if (bg.empty())
return;
vector<fp> pars;
for (bg_const_iterator i = bg.begin(); i != bg.end(); i++) {
pars.push_back(i->x);
pars.push_back(i->y);
}
bg_backup = bg;
cmd_tail = (spline_bg ? "spline[" : "interpolate[")
+ join_vector(pars, ", ") + "](x)"
+ frame->get_in_one_or_all_datasets();
clear_background();
ftk->exec("Y = y - " + cmd_tail);
ftk->vmsg("Background stripped.");
}
void BgManager::undo_strip_background()
{
if (bg_backup.empty())
return;
bg = bg_backup;
bg_backup.clear();
recompute_bgline();
ftk->exec("Y = y + " + cmd_tail);
}
void BgManager::recompute_bgline()
{
int focused_data = ftk->get_active_ds_position();
const std::vector<Point>& p = ftk->get_data(focused_data)->points();
bgline.resize(p.size());
if (spline_bg)
prepare_spline_interpolation(bg);
for (int i = 0; i < size(p); i++) {
bgline[i].x = p[i].x;
bgline[i].y = spline_bg ? get_spline_interpolation(bg, p[i].x)
: get_linear_interpolation(bg, p[i].x);
}
}
void BgManager::set_as_convex_hull()
{
SimplePolylineConvex convex;
int focused_data = ftk->get_active_ds_position();
const Data* data = ftk->get_data(focused_data);
for (int i = 0; i < data->get_n(); ++i)
convex.push_point(PointQ(data->get_x(i), data->get_y(i)));
bg = convex.get_vertices();
bg_backup.clear();
recompute_bgline();
}
syntax highlighted by Code2HTML, v. 0.9.1