// This file is part of fityk program. Copyright (C) Marcin Wojdyr
// Licence: GNU General Public License version 2
// $Id: aplot.cpp 320 2007-07-22 01:44:23Z wojdyr $
/// In this file:
/// Auxiliary Plot, for displaying residuals, peak positions, etc. (AuxPlot)
#include <wx/wxprec.h>
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/numdlg.h>
#include "aplot.h"
#include "gui.h"
#include "../sum.h"
#include "../data.h"
#include "../logic.h"
using namespace std;
enum {
ID_aux_plot0 = 25310,
ID_aux_plot_ctr = 25340,
ID_aux_revd ,
ID_aux_c_background ,
ID_aux_c_active_data ,
ID_aux_c_inactive_data ,
ID_aux_c_axis ,
ID_aux_color ,
ID_aux_m_tfont ,
ID_aux_yz_fit ,
ID_aux_yz_change ,
ID_aux_yz_auto
};
//===============================================================
// AuxPlot (auxiliary plot)
//===============================================================
BEGIN_EVENT_TABLE (AuxPlot, FPlot)
EVT_PAINT ( AuxPlot::OnPaint)
EVT_MOTION ( AuxPlot::OnMouseMove)
EVT_LEAVE_WINDOW ( AuxPlot::OnLeaveWindow)
EVT_LEFT_DOWN ( AuxPlot::OnLeftDown)
EVT_LEFT_UP ( AuxPlot::OnLeftUp)
EVT_RIGHT_DOWN ( AuxPlot::OnRightDown)
EVT_MIDDLE_DOWN ( AuxPlot::OnMiddleDown)
EVT_KEY_DOWN ( AuxPlot::OnKeyDown)
EVT_MENU_RANGE (ID_aux_plot0, ID_aux_plot0+10, AuxPlot::OnPopupPlot)
EVT_MENU (ID_aux_plot_ctr, AuxPlot::OnPopupPlotCtr)
EVT_MENU (ID_aux_revd, AuxPlot::OnPopupReversedDiff)
EVT_MENU_RANGE (ID_aux_c_background, ID_aux_color-1, AuxPlot::OnPopupColor)
EVT_MENU (ID_aux_m_tfont, AuxPlot::OnTicsFont)
EVT_MENU (ID_aux_yz_change, AuxPlot::OnPopupYZoom)
EVT_MENU (ID_aux_yz_fit, AuxPlot::OnPopupYZoomFit)
EVT_MENU (ID_aux_yz_auto, AuxPlot::OnPopupYZoomAuto)
END_EVENT_TABLE()
void AuxPlot::OnPaint(wxPaintEvent&)
{
frame->draw_crosshair(-1, -1);
buffered_draw();
vert_line_following_cursor(mat_redraw);//draw, if necessary, vertical lines
}
inline double sum_value(vector<Point>::const_iterator pt, Sum const* sum)
{
return sum->value(pt->x);
}
double diff_of_data_for_draw_data (vector<Point>::const_iterator i,
Sum const* sum)
{
return i->y - sum_value(i, sum);
}
double rdiff_of_data_for_draw_data (vector<Point>::const_iterator i,
Sum const* sum)
{
return sum_value(i, sum) - i->y;
}
double diff_stddev_of_data_for_draw_data (vector<Point>::const_iterator i,
Sum const* sum)
{
return (i->y - sum_value(i, sum)) / i->sigma;
}
double rdiff_stddev_of_data_for_draw_data (vector<Point>::const_iterator i,
Sum const* sum)
{
return (sum_value(i, sum) - i->y) / i->sigma;
}
double diff_chi2_of_data_for_draw_data (vector<Point>::const_iterator i,
Sum const* sum)
{
double t = (i->y - sum_value(i, sum)) / i->sigma;
return t*t;
}
double diff_y_perc_of_data_for_draw_data (vector<Point>::const_iterator i,
Sum const* sum)
{
return i->y ? (i->y - sum_value(i, sum)) / i->y * 100 : 0;
}
double rdiff_y_perc_of_data_for_draw_data (vector<Point>::const_iterator i,
Sum const* sum)
{
return i->y ? (sum_value(i, sum) - i->y) / i->y * 100 : 0;
}
void AuxPlot::draw(wxDC &dc, bool monochrome)
{
int pos = ftk->get_active_ds_position();
Data const* data = ftk->get_data(pos);
Sum const* sum = ftk->get_sum(pos);
if (auto_zoom_y || fit_y_once) {
fit_y_zoom(data, sum);
fit_y_once = false;
}
set_scale();
if (monochrome) {
dc.SetPen(*wxBLACK_PEN);
dc.SetBrush(*wxBLACK_BRUSH);
}
else
dc.SetPen(wxPen(xAxisCol));
if (mark_peak_ctrs) {
int ymax = GetClientSize().GetHeight();
std::vector<wxPoint> const& t = master->get_special_points();
for (vector<wxPoint>::const_iterator i = t.begin(); i != t.end(); i++)
dc.DrawLine(i->x, 0, i->x, ymax);
}
if (kind == apk_empty || data->is_empty())
return;
if (x_axis_visible) {
int Y0 = ys.px(0.);
dc.DrawLine (0, Y0, GetClientSize().GetWidth(), Y0);
if (kind == apk_diff)
draw_zoom_text(dc, !monochrome);
}
if (y_axis_visible) {
int X0 = xs.px(0.);
dc.DrawLine (X0, 0, X0, GetClientSize().GetHeight());
}
if (ytics_visible) {
View v(0, 0, ys.val(GetClientSize().GetHeight()), ys.val(0));
draw_ytics(dc, v, !monochrome);
}
fp (*f)(vector<Point>::const_iterator, Sum const*) = 0;
bool cummulative = false;
if (kind == apk_diff)
f = reversed_diff ? rdiff_of_data_for_draw_data
: diff_of_data_for_draw_data;
else if (kind == apk_diff_stddev)
f = reversed_diff ? rdiff_stddev_of_data_for_draw_data
: diff_stddev_of_data_for_draw_data;
else if (kind == apk_diff_y_perc)
f = reversed_diff ? rdiff_y_perc_of_data_for_draw_data
: diff_y_perc_of_data_for_draw_data;
else if (kind == apk_cum_chi2) {
f = diff_chi2_of_data_for_draw_data;
cummulative = true;
}
wxColour col = monochrome ? dc.GetPen().GetColour() : wxNullColour;
draw_data (dc, f, data, sum, col, col, 0, cummulative);
}
/// print zoom info - how it compares to zoom of the master plot (e.g. "x3"),
/// it makes sense only for apk_diff plot, when master plot is not logarithmic
void AuxPlot::draw_zoom_text(wxDC& dc, bool set_pen)
{
if (master->get_y_scale().logarithm)
return;
if (set_pen)
dc.SetTextForeground(xAxisCol);
dc.SetFont(*wxNORMAL_FONT);
string s = "x" + S(y_zoom);
wxCoord w, h;
dc.GetTextExtent (s2wx(s), &w, &h);
dc.DrawText (s2wx(s), GetClientSize().GetWidth() - w - 2, 2);
}
void AuxPlot::OnMouseMove(wxMouseEvent &event)
{
int X = event.GetX();
vert_line_following_cursor(mat_move, X);
frame->set_status_coord_info(xs.val(X), ys.val(event.GetY()), true);
int new_cursor;
if (X < move_plot_margin_width)
new_cursor = wxCURSOR_POINT_LEFT;
else if (X > GetClientSize().GetWidth() - move_plot_margin_width)
new_cursor = wxCURSOR_POINT_RIGHT;
else {
frame->draw_crosshair(X, -1);
new_cursor = wxCURSOR_CROSS;
}
if (new_cursor != cursor_id) {
cursor_id = new_cursor;
SetCursor(wxCursor(new_cursor));
}
}
void AuxPlot::OnLeaveWindow (wxMouseEvent&)
{
frame->set_status_text("", sbf_coord);
frame->draw_crosshair(-1, -1);
}
bool AuxPlot::is_zoomable()
{
return kind == apk_diff || kind == apk_diff_stddev
|| kind == apk_diff_y_perc || kind == apk_cum_chi2;
}
void AuxPlot::set_scale()
{
master->set_scale();
xs = master->get_x_scale();
int h = GetClientSize().GetHeight();
if (kind == apk_cum_chi2) {
ys.scale = -1. * y_zoom_base * y_zoom;
ys.origin = - h / ys.scale;
return;
}
switch (kind) {
case apk_empty:
ys.scale = 1.; //y scale doesn't matter
break;
case apk_diff:
if (master->get_y_scale().logarithm)
ys.scale = y_zoom;
else
ys.scale = master->get_y_scale().scale * y_zoom;
break;
case apk_diff_stddev:
case apk_diff_y_perc:
ys.scale = -1. * y_zoom_base * y_zoom;
break;
default:
assert(0);
}
ys.origin = - h / 2. / ys.scale;
}
void AuxPlot::read_settings(wxConfigBase *cf)
{
wxString path = wxT("/AuxPlot_") + name;
cf->SetPath(path);
kind = static_cast <Aux_plot_kind_enum> (cf->Read (wxT("kind"), apk_diff));
mark_peak_ctrs = cfg_read_bool (cf, wxT("markCtr"), false);
reversed_diff = cfg_read_bool (cf, wxT("reversedDiff"), false);
auto_zoom_y = false;
line_between_points = cfg_read_bool(cf, wxT("line_between_points"), true);
point_radius = cf->Read (wxT("point_radius"), 1);
y_max_tics = cf->Read(wxT("yMaxTics"), 5);
y_tic_size = cf->Read(wxT("yTicSize"), 4);
cf->SetPath(wxT("Visible"));
// nothing here now
cf->SetPath(wxT("../Colors"));
backgroundCol = cfg_read_color (cf, wxT("bg"), wxColour(50, 50, 50));
activeDataCol = cfg_read_color (cf, wxT("active_data"),
wxColour (wxT("GREEN")));
inactiveDataCol = cfg_read_color(cf,wxT("inactive_data"),
wxColour (128, 128, 128));
cf->SetPath(wxT(".."));
FPlot::read_settings(cf);
refresh();
}
void AuxPlot::save_settings(wxConfigBase *cf) const
{
cf->SetPath(wxT("/AuxPlot_") + name);
cf->Write (wxT("kind"), (int) kind);
cf->Write (wxT("markCtr"), mark_peak_ctrs);
cf->Write (wxT("reversedDiff"), reversed_diff);
cf->Write (wxT("line_between_points"), line_between_points);
cf->Write (wxT("point_radius"), point_radius);
cf->Write(wxT("yMaxTics"), y_max_tics);
cf->Write(wxT("yTicSize"), y_tic_size);
cf->SetPath(wxT("Visible"));
// nothing here now
cf->SetPath(wxT("../Colors"));
cfg_write_color(cf, wxT("bg"), backgroundCol);
cfg_write_color(cf, wxT("active_data"), activeDataCol);
cfg_write_color(cf, wxT("inactive_data"),inactiveDataCol);
cf->SetPath(wxT(".."));
FPlot::save_settings(cf);
}
void AuxPlot::OnLeftDown (wxMouseEvent &event)
{
cancel_mouse_left_press();
if (event.ShiftDown()) { // the same as OnMiddleDown()
frame->GViewAll();
return;
}
int X = event.GetPosition().x;
// if mouse pointer is near to left or right border, move view
if (X < move_plot_margin_width)
frame->scroll_view_horizontally(-0.33); // <--
else if (X > GetClientSize().GetWidth() - move_plot_margin_width)
frame->scroll_view_horizontally(+0.33); // -->
else {
mouse_press_X = X;
vert_line_following_cursor(mat_start, mouse_press_X+1, mouse_press_X);
SetCursor(wxCursor(wxCURSOR_SIZEWE));
frame->set_status_text("Select x range and release button to zoom...");
CaptureMouse();
}
}
bool AuxPlot::cancel_mouse_left_press()
{
if (mouse_press_X != INT_MIN) {
vert_line_following_cursor(mat_stop);
ReleaseMouse();
mouse_press_X = INT_MIN;
cursor_id = wxCURSOR_CROSS;
SetCursor(wxCursor(wxCURSOR_CROSS));
frame->set_status_text("");
return true;
}
else
return false;
}
void AuxPlot::OnLeftUp (wxMouseEvent &event)
{
if (mouse_press_X == INT_MIN)
return;
if (abs(event.GetX() - mouse_press_X) < 5) { //cancel
cancel_mouse_left_press();
return;
}
fp x1 = xs.val(event.GetX());
fp x2 = xs.val(mouse_press_X);
cancel_mouse_left_press();
frame->change_zoom("[" + S(min(x1,x2)) + " : " + S(max(x1,x2)) + "]");
}
//popup-menu
void AuxPlot::OnRightDown (wxMouseEvent &event)
{
if (cancel_mouse_left_press())
return;
wxMenu popup_menu (wxT("aux. plot menu"));
//wxMenu *kind_menu = new wxMenu;
popup_menu.AppendRadioItem(ID_aux_plot0+0, wxT("&empty"), wxT("nothing"));
popup_menu.AppendRadioItem(ID_aux_plot0+1, wxT("&diff"), wxT("y_d - y_s"));
popup_menu.AppendRadioItem(ID_aux_plot0+2, wxT("&weighted diff"),
wxT("(y_d - y_s) / sigma"));
popup_menu.AppendRadioItem(ID_aux_plot0+3, wxT("&proc diff"),
wxT("(y_d - y_s) / y_d [%]"));
popup_menu.AppendRadioItem(ID_aux_plot0+4, wxT("cumul. &chi2"),
wxT("cumulative chi square"));
popup_menu.Check(ID_aux_plot0+kind, true);
popup_menu.AppendSeparator();
popup_menu.AppendCheckItem(ID_aux_revd, wxT("reversed diff"),
wxT(""));
popup_menu.Check(ID_aux_revd, reversed_diff);
popup_menu.AppendSeparator();
popup_menu.AppendCheckItem(ID_aux_plot_ctr, wxT("show peak po&sitions"),
wxT("mark centers of peaks"));
popup_menu.Check(ID_aux_plot_ctr, mark_peak_ctrs);
popup_menu.AppendSeparator();
popup_menu.Append (ID_aux_yz_fit, wxT("&Fit to window"));
popup_menu.Enable(ID_aux_yz_fit, is_zoomable());
popup_menu.Append (ID_aux_yz_change, wxT("Change &y scale"));
popup_menu.Enable(ID_aux_yz_change, is_zoomable());
popup_menu.AppendCheckItem (ID_aux_yz_auto, wxT("&Auto-fit"));
popup_menu.Check (ID_aux_yz_auto, auto_zoom_y);
popup_menu.Enable(ID_aux_yz_auto, is_zoomable());
popup_menu.AppendSeparator();
wxMenu *color_menu = new wxMenu;
color_menu->Append (ID_aux_c_background, wxT("&Background"));
color_menu->Append (ID_aux_c_active_data, wxT("&Active Data"));
color_menu->Append (ID_aux_c_inactive_data, wxT("&Inactive Data"));
color_menu->Append (ID_aux_c_axis, wxT("&X Axis"));
popup_menu.Append (ID_aux_color, wxT("&Color"), color_menu);
wxMenu *misc_menu = new wxMenu;
misc_menu->Append (ID_aux_m_tfont, wxT("&Tics font"));
popup_menu.Append (wxNewId(), wxT("&Miscellaneous"), misc_menu);
PopupMenu (&popup_menu, event.GetX(), event.GetY());
}
void AuxPlot::OnMiddleDown (wxMouseEvent&)
{
if (cancel_mouse_left_press())
return;
frame->GViewAll();
}
void AuxPlot::OnKeyDown (wxKeyEvent& event)
{
if (event.GetKeyCode() == WXK_ESCAPE) {
cancel_mouse_left_press();
}
else if (should_focus_input(event)) {
cancel_mouse_left_press();
frame->focus_input(event);
}
else
event.Skip();
}
void AuxPlot::OnPopupPlot (wxCommandEvent& event)
{
kind = static_cast<Aux_plot_kind_enum>(event.GetId()-ID_aux_plot0);
//fit_y_zoom();
fit_y_once = true;
refresh(false);
}
void AuxPlot::OnPopupPlotCtr (wxCommandEvent& event)
{
mark_peak_ctrs = event.IsChecked();
refresh(false);
}
void AuxPlot::OnPopupReversedDiff (wxCommandEvent& event)
{
reversed_diff = event.IsChecked();
refresh(false);
}
void AuxPlot::OnPopupColor (wxCommandEvent& event)
{
wxColour *color = 0;
int n = event.GetId();
if (n == ID_aux_c_background)
color = &backgroundCol;
else if (n == ID_aux_c_active_data) {
color = &activeDataCol;
}
else if (n == ID_aux_c_inactive_data) {
color = &inactiveDataCol;
}
else if (n == ID_aux_c_axis)
color = &xAxisCol;
else
return;
if (change_color_dlg(*color)) {
refresh();
}
}
void AuxPlot::OnPopupYZoom (wxCommandEvent&)
{
int r = wxGetNumberFromUser(wxT("Set zoom in y direction [%]"),
wxT(""), wxT(""),
static_cast<int>(y_zoom * 100 + 0.5),
1, 10000000);
if (r > 0)
y_zoom = r / 100.;
refresh(false);
}
void AuxPlot::OnPopupYZoomFit (wxCommandEvent&)
{
//fit_y_zoom();
fit_y_once = true;
refresh(false);
}
void AuxPlot::fit_y_zoom(Data const* data, Sum const* sum)
{
if (!is_zoomable())
return;
fp y = 0.;
vector<Point>::const_iterator first = data->get_point_at(ftk->view.left),
last = data->get_point_at(ftk->view.right);
if (data->is_empty() || last==first)
return;
switch (kind) { // setting y_zoom
case apk_diff:
{
y = get_max_abs_y(diff_of_data_for_draw_data, first, last, sum);
Scale const& mys = master->get_y_scale();
y_zoom = fabs (GetClientSize().GetHeight() / (2 * y
* (mys.logarithm ? 1 : mys.scale)));
fp order = pow (10, floor (log10(y_zoom)));
y_zoom = floor(y_zoom / order) * order;
}
break;
case apk_diff_stddev:
y = get_max_abs_y(diff_stddev_of_data_for_draw_data,
first, last, sum);
y_zoom_base = GetClientSize().GetHeight() / (2. * y);
y_zoom = 0.9;
break;
case apk_diff_y_perc:
y = get_max_abs_y(diff_y_perc_of_data_for_draw_data,
first, last, sum);
y_zoom_base = GetClientSize().GetHeight() / (2. * y);
y_zoom = 0.9;
break;
case apk_cum_chi2:
y = 0.;
for (vector<Point>::const_iterator i = first; i < last; i++)
y += diff_chi2_of_data_for_draw_data(i, sum);
y_zoom_base = GetClientSize().GetHeight() / y;
y_zoom = 0.9;
break;
default:
assert(0);
}
}
void AuxPlot::OnPopupYZoomAuto (wxCommandEvent&)
{
auto_zoom_y = !auto_zoom_y;
if (auto_zoom_y) {
//fit_y_zoom() is called from draw
refresh(false);
}
}
syntax highlighted by Code2HTML, v. 0.9.1