]> git.saurik.com Git - wxWidgets.git/blob - src/generic/plot.cpp
'Details' button is now disabled if no details
[wxWidgets.git] / src / generic / plot.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: plot.cpp
3 // Purpose: wxPlotWindow
4 // Author: Robert Roebling
5 // Modified by:
6 // Created: 12/01/2000
7 // RCS-ID: $Id$
8 // Copyright: (c) Robert Roebling
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "plot.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #ifndef WX_PRECOMP
24 #include "wx/object.h"
25 #include "wx/font.h"
26 #include "wx/colour.h"
27 #include "wx/settings.h"
28 #include "wx/sizer.h"
29 #include "wx/log.h"
30 #include "wx/intl.h"
31 #include "wx/dcclient.h"
32 #endif
33
34 #include "wx/generic/plot.h"
35
36 #include <math.h>
37
38 //-----------------------------------------------------------------------------
39 // wxPlotCurve
40 //-----------------------------------------------------------------------------
41
42 IMPLEMENT_ABSTRACT_CLASS(wxPlotCurve, wxObject)
43
44 wxPlotCurve::wxPlotCurve( int offsetY, double startY, double endY )
45 {
46 m_offsetY = offsetY;
47 m_startY = startY;
48 m_endY = endY;
49 }
50
51 //-----------------------------------------------------------------------------
52 // wxPlotArea
53 //-----------------------------------------------------------------------------
54
55 IMPLEMENT_DYNAMIC_CLASS(wxPlotArea, wxWindow)
56
57 BEGIN_EVENT_TABLE(wxPlotArea, wxWindow)
58 EVT_PAINT( wxPlotArea::OnPaint)
59 EVT_LEFT_DOWN( wxPlotArea::OnMouse)
60 END_EVENT_TABLE()
61
62 wxPlotArea::wxPlotArea( wxPlotWindow *parent )
63 : wxWindow( parent, -1, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER, "plotarea" )
64 {
65 m_owner = parent;
66
67 SetBackgroundColour( *wxWHITE );
68 }
69
70 void wxPlotArea::OnMouse( wxMouseEvent &event )
71 {
72 int client_width;
73 int client_height;
74 GetClientSize( &client_width, &client_height);
75 int view_x;
76 int view_y;
77 m_owner->GetViewStart( &view_x, &view_y );
78 view_x *= 10;
79 view_y *= 10;
80
81 int x = event.GetX();
82 int y = event.GetY();
83 x += view_x;
84 y += view_y;
85
86 wxNode *node = m_owner->m_curves.First();
87 while (node)
88 {
89 wxPlotCurve *curve = (wxPlotCurve*)node->Data();
90
91 double double_client_height = (double)client_height;
92 double range = curve->GetEndY() - curve->GetStartY();
93 double end = curve->GetEndY();
94 wxCoord offset_y = curve->GetOffsetY();
95
96 double dy = (end - curve->GetY( x )) / range;
97 wxCoord curve_y = (wxCoord)(dy * double_client_height) - offset_y - 1;
98
99 if ((y-curve_y < 4) && (y-curve_y > -4))
100 {
101 m_owner->SetCurrent( curve );
102 return;
103 }
104
105 node = node->Next();
106 }
107 }
108
109 void wxPlotArea::DeleteCurve( wxPlotCurve *curve, int from, int to )
110 {
111 wxClientDC dc(this);
112 m_owner->PrepareDC( dc );
113 dc.SetPen( *wxWHITE_PEN );
114 DrawCurve( &dc, curve, from, to );
115 }
116
117 void wxPlotArea::DrawCurve( wxDC *dc, wxPlotCurve *curve, int from, int to )
118 {
119 int view_x;
120 int view_y;
121 m_owner->GetViewStart( &view_x, &view_y );
122 view_x *= 10;
123
124 if (from == -1)
125 from = view_x;
126
127 int client_width;
128 int client_height;
129 GetClientSize( &client_width, &client_height);
130
131 if (to == -1)
132 to = view_x + client_width;
133
134 int start_x = wxMax( from, curve->GetStartX() );
135 int end_x = wxMin( to, curve->GetEndX() );
136
137 start_x = wxMax( view_x, start_x );
138 end_x = wxMin( view_x + client_width, end_x );
139
140 double double_client_height = (double)client_height;
141 double range = curve->GetEndY() - curve->GetStartY();
142 double end = curve->GetEndY();
143 wxCoord offset_y = curve->GetOffsetY();
144
145 wxCoord y=0,last_y=0;
146 for (int x = start_x; x < end_x; x++)
147 {
148 double dy = (end - curve->GetY( x )) / range;
149 y = (wxCoord)(dy * double_client_height) - offset_y - 1;
150
151 if (x != start_x)
152 dc->DrawLine( x-1, last_y, x, y );
153
154 last_y = y;
155 }
156 }
157
158 void wxPlotArea::OnPaint( wxPaintEvent &WXUNUSED(event) )
159 {
160 int view_x;
161 int view_y;
162 m_owner->GetViewStart( &view_x, &view_y );
163 view_x *= 10;
164 view_y *= 10;
165
166 wxPaintDC dc( this );
167 m_owner->PrepareDC( dc );
168
169 wxRegionIterator upd( GetUpdateRegion() );
170
171 while (upd)
172 {
173 int update_x = upd.GetX();
174 int update_y = upd.GetY();
175 int update_width = upd.GetWidth();
176
177 update_x += view_x;
178 update_y += view_y;
179
180 /*
181 if (m_owner->m_current)
182 {
183 dc.SetPen( *wxLIGHT_GREY_PEN );
184 int base_line = client_height - m_owner->m_current->GetOffsetY();
185 dc.DrawLine( update_x-1, base_line-1, update_x+update_width+2, base_line-1 );
186 }
187 */
188
189 wxNode *node = m_owner->m_curves.First();
190 while (node)
191 {
192 wxPlotCurve *curve = (wxPlotCurve*)node->Data();
193
194 if (curve == m_owner->GetCurrent())
195 dc.SetPen( *wxBLACK_PEN );
196 else
197 dc.SetPen( *wxLIGHT_GREY_PEN );
198
199 DrawCurve( &dc, curve, update_x-1, update_x+update_width+2 );
200
201 node = node->Next();
202 }
203 upd ++;
204 }
205 }
206
207 //-----------------------------------------------------------------------------
208 // wxPlotWindow
209 //-----------------------------------------------------------------------------
210
211 #define ID_ENLARGE_100 1000
212 #define ID_ENLARGE_50 1001
213 #define ID_SHRINK_33 1002
214 #define ID_SHRINK_50 1003
215
216 #define ID_MOVE_UP 1006
217 #define ID_MOVE_DOWN 1007
218
219
220 IMPLEMENT_DYNAMIC_CLASS(wxPlotWindow, wxScrolledWindow)
221
222 BEGIN_EVENT_TABLE(wxPlotWindow, wxScrolledWindow)
223 EVT_PAINT( wxPlotWindow::OnPaint)
224 EVT_BUTTON( ID_MOVE_UP, wxPlotWindow::OnMoveUp)
225 EVT_BUTTON( ID_MOVE_DOWN, wxPlotWindow::OnMoveDown)
226
227 EVT_BUTTON( ID_ENLARGE_100, wxPlotWindow::OnEnlarge100)
228 EVT_BUTTON( ID_ENLARGE_50, wxPlotWindow::OnEnlarge50)
229 EVT_BUTTON( ID_SHRINK_50, wxPlotWindow::OnShrink50)
230 EVT_BUTTON( ID_SHRINK_33, wxPlotWindow::OnShrink33)
231 END_EVENT_TABLE()
232
233 wxPlotWindow::wxPlotWindow( wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, int flag )
234 : wxScrolledWindow( parent, id, pos, size, flag, "plotcanvas" )
235 {
236 m_area = new wxPlotArea( this );
237
238 wxBoxSizer *mainsizer = new wxBoxSizer( wxHORIZONTAL );
239
240 wxBoxSizer *buttonlist = new wxBoxSizer( wxVERTICAL );
241 buttonlist->Add( new wxButton( this, ID_ENLARGE_100, _("+ 100%") ), 0, wxEXPAND|wxALL, 5 );
242 buttonlist->Add( new wxButton( this, ID_ENLARGE_50, _("+ 50%") ), 0, wxEXPAND|wxALL, 5 );
243 buttonlist->Add( new wxButton( this, ID_SHRINK_33, _("- 33%") ), 0, wxEXPAND|wxALL, 5 );
244 buttonlist->Add( new wxButton( this, ID_SHRINK_50, _("- 50%") ), 0, wxEXPAND|wxALL, 5 );
245 buttonlist->Add( 20,20, 0 );
246 buttonlist->Add( new wxButton( this, ID_MOVE_UP, _("Up") ), 0, wxEXPAND|wxALL, 5 );
247 buttonlist->Add( new wxButton( this, ID_MOVE_DOWN, _("Down") ), 0, wxEXPAND|wxALL, 5 );
248 buttonlist->Add( 20,20, 1 );
249
250 mainsizer->Add( buttonlist, 0, wxEXPAND );
251
252 mainsizer->Add( m_area, 1, wxEXPAND|wxLEFT, 50 );
253
254 SetAutoLayout( TRUE );
255 SetSizer( mainsizer );
256
257 SetTargetWindow( m_area );
258
259 SetBackgroundColour( *wxWHITE );
260
261 m_current = (wxPlotCurve*) NULL;
262 }
263
264 wxPlotWindow::~wxPlotWindow()
265 {
266 }
267
268 void wxPlotWindow::Add( wxPlotCurve *curve )
269 {
270 m_curves.Append( curve );
271 if (!m_current) m_current = curve;
272 }
273
274 size_t wxPlotWindow::GetCount()
275 {
276 return m_curves.GetCount();
277 }
278
279 wxPlotCurve *wxPlotWindow::GetAt( size_t n )
280 {
281 wxNode *node = m_curves.Nth( n );
282 if (!node)
283 return (wxPlotCurve*) NULL;
284
285 return (wxPlotCurve*) node->Data();
286 }
287
288 void wxPlotWindow::SetCurrent( wxPlotCurve* current )
289 {
290 m_current = current;
291 m_area->Refresh( FALSE );
292
293 RedrawYAxis();
294 }
295
296 wxPlotCurve *wxPlotWindow::GetCurrent()
297 {
298 return m_current;
299 }
300
301 void wxPlotWindow::Move( wxPlotCurve* curve, int pixels_up )
302 {
303 m_area->DeleteCurve( curve );
304
305 curve->SetOffsetY( curve->GetOffsetY() + pixels_up );
306
307 m_area->Refresh( FALSE );
308
309 RedrawYAxis();
310 }
311
312 void wxPlotWindow::OnMoveUp( wxCommandEvent& WXUNUSED(event) )
313 {
314 if (!m_current) return;
315
316 Move( m_current, 25 );
317 }
318
319 void wxPlotWindow::OnMoveDown( wxCommandEvent& WXUNUSED(event) )
320 {
321 if (!m_current) return;
322
323 Move( m_current, -25 );
324 }
325
326 void wxPlotWindow::Enlarge( wxPlotCurve *curve, double factor )
327 {
328 m_area->DeleteCurve( curve );
329
330 double range = curve->GetEndY() - curve->GetStartY();
331 double new_range = range * factor;
332 double middle = curve->GetEndY() - range/2;
333 curve->SetStartY( middle - new_range / 2 );
334 curve->SetEndY( middle + new_range / 2 );
335
336 m_area->Refresh( FALSE );
337
338 RedrawYAxis();
339 }
340
341 void wxPlotWindow::OnEnlarge100( wxCommandEvent& WXUNUSED(event) )
342 {
343 if (!m_current) return;
344
345 Enlarge( m_current, 2.0 );
346 }
347
348 void wxPlotWindow::OnEnlarge50( wxCommandEvent& WXUNUSED(event) )
349 {
350 if (!m_current) return;
351
352 Enlarge( m_current, 1.5 );
353 }
354
355 void wxPlotWindow::OnShrink50( wxCommandEvent& WXUNUSED(event) )
356 {
357 if (!m_current) return;
358
359 Enlarge( m_current, 0.5 );
360 }
361
362 void wxPlotWindow::OnShrink33( wxCommandEvent& WXUNUSED(event) )
363 {
364 if (!m_current) return;
365
366 Enlarge( m_current, 0.6666666 );
367 }
368
369 void wxPlotWindow::RedrawYAxis()
370 {
371 int client_width;
372 int client_height;
373 GetClientSize( &client_width, &client_height);
374
375 wxPoint pos( m_area->GetPosition() );
376
377 wxRect rect(pos.x-45,0,45,client_height);
378 Refresh(TRUE,&rect);
379 }
380
381 void wxPlotWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
382 {
383 wxPaintDC dc( this );
384
385 if (!m_current) return;
386
387 int client_width;
388 int client_height;
389 GetClientSize( &client_width, &client_height);
390
391 dc.SetPen( *wxBLACK_PEN );
392
393 wxPoint pos( m_area->GetPosition() );
394
395 double range = m_current->GetEndY() - m_current->GetStartY();
396 double offset = ((double) m_current->GetOffsetY() / (double)client_height ) * range;
397 double start = m_current->GetStartY() - offset;
398 double end = m_current->GetEndY() - offset;
399 int int_log_range = (int)floor( log10( range ) );
400 double step = 1.0;
401 if (int_log_range > 0)
402 {
403 for (int i = 0; i < int_log_range; i++)
404 step *= 10;
405 }
406 if (int_log_range < 0)
407 {
408 for (int i = 0; i < -int_log_range; i++)
409 step /= 10;
410 }
411 double lower = ceil(start / step) * step;
412 double upper = floor(end / step) * step;
413
414 // if too few values, shrink size
415 int steps = (int)ceil((upper-lower)/step);
416 if (steps < 4)
417 {
418 step /= 2;
419 if (lower-step > start) lower -= step;
420 if (upper+step < end) upper += step;
421 }
422
423 // if still too few, again
424 steps = (int)ceil((upper-lower)/step);
425 if (steps < 4)
426 {
427 step /= 2;
428 if (lower-step > start) lower -= step;
429 if (upper+step < end) upper += step;
430 }
431
432 double current = lower;
433 while (current < upper+(step/2))
434 {
435 int y = (int)((m_current->GetEndY()-current) / range * (double)client_height) - 1;
436 y -= m_current->GetOffsetY();
437 if ((y > 10) && (y < client_height-7))
438 {
439 dc.DrawLine( pos.x-15, y, pos.x-7, y );
440 wxString label;
441 label.Printf( "%.1f", current );
442 dc.DrawText( label, pos.x-45, y-7 );
443 }
444
445 current += step;
446 }
447
448 dc.DrawLine( pos.x-15, 6, pos.x-15, client_height-5 );
449 dc.DrawLine( pos.x-19, 8, pos.x-15, 2 );
450 dc.DrawLine( pos.x-10, 9, pos.x-15, 2 );
451
452 }
453