1 /*
2 * ===========================
3 * VDK Component Library
4 * Version 0.2
5 * ===========================
6 * Copyright (C) 1998, Mario Motta
7 * Developed by Mario Motta <mmotta@guest.net>
8 * ===========================================
9 * This library is a component of:
10 * VDK Visual Development Kit
11 * Version 0.4.1
12 * Copyright (C) 1998, Mario Motta
13 * Developed by Mario Motta <mmotta@guest.net>
14 * ===========================================
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Library General Public
17 * License as published by the Free Software Foundation; either
18 * version 2 of the License, or (at your option) any later version.
19 *
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Library General Public License for more details.
24 *
25 * You should have received a copy of the GNU Library General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28 * 02111-130
29 */
30
31 #include <vdk/chart.h>
32 #include <cstdlib>
33 #include <cstring>
34 /////////////////////////////////////
35 DEFINE_EVENT_LIST(VDKChart,VDKCanvas);
36 /*
37 captures configure event:
38 recompute chart allocated size
39 and redraw chart
40 */
OnConfigure(VDKObject * sender,GdkEvent * event)41 bool VDKChart::OnConfigure(VDKObject* sender, GdkEvent* event)
42 {
43 if(!gc)
44 gc = gdk_gc_new(pixmap);
45 size = Usize;
46 printf("\nsize:%d,%d",size.x,size.y);
47 fflush(stdout);
48 // patch Bug#262091
49 // axis = ChartAxis(this,size.X(),size.Y());
50 ChartAxis axis_tmp(this, size.X(), size.Y());
51 axis = axis_tmp;
52 axis.Draw();
53 DrawTitle();
54 DrawChart();
55 Redraw();
56 return true;
57 }
58 /*
59 */
OnClick(VDKObject * sender,GdkEvent * event)60 bool VDKChart::OnClick(VDKObject* sender, GdkEvent* event)
61 {
62 if(series.size() <= 0)
63 return true;
64 if(!tip_window)
65 {
66 char buff[64];
67 GdkEventButton *ev = (GdkEventButton*) event;
68 double x = ev->x;
69 double y = ev->y;
70 double sx = (x - xn1 + kx*xv1)/kx;
71 double sy = (y - yn1 + ky*yv1)/ky;
72 sprintf(buff,"%.3f,%.3f",sx,sy);
73 tip_window = gtk_window_new (GTK_WINDOW_POPUP);
74 gtk_window_position(GTK_WINDOW(tip_window),GTK_WIN_POS_MOUSE);
75 GtkWidget* label = gtk_label_new(buff);
76 gtk_container_add(GTK_CONTAINER(tip_window),label);
77 gtk_widget_show(label);
78 gtk_widget_show(tip_window);
79 }
80 return false;
81 }
82
83 /*
84 */
OnClickRelease(VDKObject * sender,GdkEvent * event)85 bool VDKChart::OnClickRelease(VDKObject* sender, GdkEvent* event)
86 {
87 if(tip_window)
88 {
89 gtk_widget_destroy(tip_window);
90 tip_window = NULL;
91 }
92 return true;
93 }
94 /////////////////////////////////////////////////////////////
95 /*
96 constructor
97 */
VDKChart(VDKForm * owner,int w,int h)98 VDKChart::VDKChart(VDKForm* owner, int w, int h):
99 VDKCanvas(owner,w,h),
100 ChartBorder("ChartBorder",this,20,&VDKChart::SetChartBorder),
101 Title("Title",this,"Untitled"),
102 LabelX("LabelX",this,""),
103 LabelY("LabelX",this,""),
104 LabelXDigits("LabelXDigits",this,2),
105 LabelYDigits("LabelYDigits",this,2)
106 {
107 gc = NULL;
108 tip_window = NULL;
109 EventConnect("configure_event",&VDKChart::OnConfigure);
110 EventConnect("button_press_event",&VDKChart::OnClick);
111 EventConnect("button_release_event",&VDKChart::OnClickRelease);
112 Font(new VDKFont(owner,"helvetica Medium 10"));
113 }
114
115 /*
116 set chart border
117 */
SetChartBorder(int b)118 void VDKChart::SetChartBorder(int b)
119 {
120 size = Usize;
121 // patch Bug#262091
122 // axis = ChartAxis(this,size.X(),size.Y());
123 ChartAxis axis_tmp(this,size.X(),size.Y());
124 axis = axis_tmp;
125 DrawChart();
126 }
127 /*
128 draw title
129 */
DrawTitle()130 void VDKChart::DrawTitle()
131 {
132 int half_w;
133 VDKUString s = Title;
134 VDKPoint size = Usize;
135 VDKPoint center(size.X()/2,ChartBorder/2);
136 VDKFont* font = Font;
137 half_w = font ? (gdk_string_width (font->AsGdkFont(), (char*) s))/2 : 5;
138 VDKRgb fg = Foreground;
139 if(fg.red >= 0) SetColor(fg);
140 DrawString(center.X()-half_w,center.Y(), (char*) s);
141 }
142 /*
143 destructor:
144 frees series
145 and graphic context
146 */
~VDKChart()147 VDKChart::~VDKChart()
148 {
149 SeriesListIterator li(series);
150 for(;li;li++)
151 delete li.current();
152 }
153 /*
154 add serie to chart, unicity is checked,
155 <s> will substitute actual series
156 if a match is found. Old series will be destroyed
157 */
AddSeries(Series * s)158 void VDKChart::AddSeries(Series* s)
159 {
160 if(series.size() > 0)
161 {
162 SeriesListIterator li(series);
163 for(;li;li++)
164 if((*li.current() == *s))
165 break;
166 if(li)
167 {
168 Series* p = li.current();
169 // removes series li.current()
170 series.remove(p);
171 delete p;
172 }
173 }
174 series.add(s);
175 ComputeDomainLimits(s);
176 DrawChart();
177 }
178 /*
179 update domain limits:
180 lowest and highest x and y
181 */
ComputeDomainLimits(Series * s)182 void VDKChart::ComputeDomainLimits(Series* s)
183 {
184 if(series.size() == 1)
185 {
186 domainmax.x = s->Max().x;
187 domainmax.y = s->Max().y;
188 domainmin.x = s->Min().x;
189 domainmin.y = s->Min().y;
190 }
191 else
192 {
193 domainmax.x = domainmax.x < s->Max().x ? s->Max().x : domainmax.x;
194 domainmax.y = domainmax.y < s->Max().y ? s->Max().y : domainmax.y;
195 domainmin.x = domainmin.x > s->Min().x ? s->Min().x : domainmin.x;
196 domainmin.y = domainmin.y > s->Min().y ? s->Min().y : domainmin.y;
197 }
198 domainmin.x = domainmin.x == domainmax.x ? 0 : domainmin.x;
199 domainmin.y = domainmin.y == domainmax.y ? 0 : domainmin.y;
200
201 }
202 /*
203 clear chart, destroy series
204 */
Clear()205 void VDKChart::Clear()
206 {
207 SeriesListIterator li(series);
208 for(;li;li++)
209 delete li.current();
210 series.flush();
211 VDKCanvas::Clear();
212 axis.Draw();
213 DrawTitle();
214 Redraw();
215 }
216 /*
217 actually draw chart:
218 1. Clear chart
219 2. Compute scaling factors
220 3. Calls a virtual drawing function:
221 Plot(Series* s, int n, Coord c) with scaled coordinates.
222 VDKChart::Plot() does nothing.
223 Subclasses should override Plot() in order
224 to draw different chart shapes (lines, bars, etc.)
225 */
226 const int tickL = 8;
DrawChart()227 void VDKChart::DrawChart()
228 {
229 if(series.size() <= 0)
230 return;
231 else
232 {
233 VDKCanvas::Clear();
234 axis.Draw();
235 DrawTitle();
236 }
237 // recompute domain with chart allocated size
238 xn1 = axis.Domain().Origin().X();
239 xn2 = xn1 + axis.Domain().W();
240 xv1 = domainmin.x; xv2 = domainmax.x;
241 //xv1 = xv1 == xv2 ? 0 : xv1;
242 yn1 = axis.Domain().Origin().Y();
243 yn2 = yn1 - axis.Domain().H();
244 yv1 = domainmin.y; yv2 = domainmax.y;
245 //yv1 = yv1 == yv2 ? 0 : yv1;
246 kx = (xn2-xn1)/(xv2-xv1);
247 ky = (yn2-yn1)/(yv2-yv1);
248 // iterates over series
249 // n-square algorithm :-(
250 SeriesListIterator li(series);
251 int z;
252 for(z=0;li;li++,z++)
253 {
254 int t;
255 CoordListIterator lc(*li.current());
256 // scales each point
257 for(t = 0;lc;lc++,t++)
258 {
259 VDKPoint p(
260 int(xn1+kx*(lc.current().x-xv1)),
261 int((yn1+ky*(lc.current().y-yv1)))
262 );
263 Plot(p,t,li.current());
264 }
265 }
266 // draw x,y axis ticks
267 DrawTicks();
268 //
269 DrawLabels();
270 // redraw with no expose event
271 Redraw();
272 }
273 /*
274 Draws x,y ticks (major and minor)
275 */
DrawTicks()276 void VDKChart::DrawTicks()
277 {
278 char buff[32];
279 // compute and draws x,y axis min,max value
280 double lminx = xn1+kx*(domainmin.x-xv1);
281 double lminy = yn1+ky*(domainmin.y-yv1);
282 double lmaxx = xn1+kx*(domainmax.x-xv1);
283 double lmaxy = yn1+ky*(domainmax.y-yv1);
284 VDKFont* vdkfont = Font;
285 GdkFont* font = vdkfont->AsGdkFont();
286 int h = font ? font->ascent + font->descent + 1 : 10;
287 int w;
288 // draws x ticks
289 int t;
290 VDKRgb fg = Foreground;
291 if(fg.red >= 0)
292 SetColor(fg);
293 double j,lx,k = (domainmax.x-domainmin.x)/10.0;
294 int x_digits = LabelXDigits;
295 int y_digits = LabelYDigits;
296 for(j = domainmin.x, t=0; j <= domainmax.x; j+=k,t++)
297 {
298 lx = xn1+kx*(j-xv1);
299 if ( (t%2) == 0)
300 {
301 sprintf(buff,"%.*f",x_digits,j);
302 w = gdk_string_width (font, buff);
303 DrawString(int(lx-w/2),int(lminy+tickL+h),buff);
304 // major tick
305 DrawLine(int(lx),int(lminy),int(lx),int(lminy+tickL));
306 }
307 else
308 // minor tick
309 DrawLine(int(lx),int(lminy),int(lx),int(lminy+tickL/2));
310 }
311 if(t <= 10)
312 {
313 sprintf(buff,"%.*f",x_digits,domainmax.x);
314 w = gdk_string_width (font, buff);
315 DrawString(int(lmaxx-w/2),int(lminy+tickL+h),buff);
316 }
317 // draws y ticks
318 double ly;
319 k = (domainmax.y-domainmin.y)/10;
320 for(j = domainmin.y, t=0; j <= domainmax.y ; j+=k,t++)
321 {
322 ly = yn1+ky*(j-yv1);
323 if ( (t%2) == 0)
324 {
325 sprintf(buff,"%.*f",y_digits,j);
326 w = gdk_string_width (font, buff);
327 DrawString(int(lminx-w-tickL),int(ly+h/3),buff);
328 // major tick
329 DrawLine(int(lminx),int(ly),int(lminx-tickL),int(ly));
330 }
331 else
332 // minor tick
333 DrawLine(int(lminx),int(ly),int(lminx-tickL/2),int(ly));
334 }
335 if(t <= 10)
336 {
337 sprintf(buff,"%.*f",y_digits,domainmax.y);
338 w = gdk_string_width (font, buff);
339 DrawString(int(lminx-w-tickL),int(lmaxy),buff);
340 }
341 }
342 /*
343 draw x and y labels
344 */
DrawLabels()345 void VDKChart::DrawLabels()
346 {
347 // x labels
348 VDKUString s = LabelX;
349 VDKPoint size = Usize;
350 VDKFont* vdkfont = Font;
351 GdkFont* font = vdkfont->AsGdkFont();
352 VDKRgb fg = Foreground;
353 if(fg.red >= 0)
354 SetColor(fg);
355 if(! s.isNull())
356 {
357 VDKPoint center(size.X()/2,
358 axis.Domain().Origin().Y() +
359 ChartBorder - 5);
360 int half_w = font ? (gdk_string_width (font, (char*) s))/2 : 5;
361 DrawString(center.X()-half_w,center.Y(), (char*) s);
362 }
363 s = LabelY;
364 if(! s.isNull())
365 {
366 int h = font ? font->ascent+font->descent : 10;
367 size_t z = std::strlen((char*) s);
368 int sh = (int)(h*z);
369 char *p = (char*) s;
370 VDKPoint center(axis.Domain().Origin().X() -
371 ChartBorder + 5, size.Y()/2-sh/2);
372 size_t t = 0;
373 for(; t < z; t++,p++) DrawText(center.X(),center.Y()+t*h, p,1);
374 }
375 }
376 /*
377 set drawing color.
378 Use this to set color for Plot()
379 function. (see VDKLineChart::Plot())
380 */
SetColor(VDKRgb rgb)381 void VDKChart::SetColor(VDKRgb rgb)
382 {
383 GdkColor *color = (GdkColor*) malloc (sizeof (GdkColor));
384 GdkColormap *colormap = gdk_window_get_colormap (Widget()->window);
385 color->red = (guint16)(rgb.red << 8);
386 color->green = (guint16)(rgb.green << 8);
387 color->blue = (guint16)(rgb.blue << 8);
388 if (!gdk_color_alloc (colormap, color))
389 gdk_color_black (colormap, color);
390 gdk_gc_set_foreground(GC(), color);
391 free(color);
392 }
393 /*
394 set line style
395 Use this to set line style for Plot()
396 function. (see VDKLineChart::Plot())
397 */
SetLineAttributes(gint lineWidth,GdkLineStyle lineStyle,GdkCapStyle capStyle,GdkJoinStyle joinStyle)398 void VDKChart::SetLineAttributes(
399 gint lineWidth,
400 GdkLineStyle lineStyle,
401 GdkCapStyle capStyle,
402 GdkJoinStyle joinStyle)
403 {
404 if(gc)
405 gdk_gc_set_line_attributes(gc,
406 lineWidth,
407 lineStyle,
408 capStyle,
409 joinStyle);
410 }
411 //////////////////// CHART AXIS /////////////////////////////
412 /*
413 constructor:
414 compute axis origin and chart domain
415 based on chart size and chart border.
416 This will be surely changed by
417 configure event. (see OnConfigure())
418 */
ChartAxis(VDKChart * owner,int w,int h)419 ChartAxis::ChartAxis(VDKChart* owner,int w, int h):
420 owner(owner)
421 {
422 // patch Bug#262091
423 /*
424 domain = VDKRect(owner->ChartBorder,
425 h-owner->ChartBorder,
426 w-owner->ChartBorder*2,
427 h-owner->ChartBorder*2);
428 */
429 VDKRect r(owner->ChartBorder,
430 h-owner->ChartBorder,
431 w-owner->ChartBorder*2,
432 h-owner->ChartBorder*2);
433 domain = r;
434 }
435 /*
436 copy-initializer
437 */
ChartAxis(ChartAxis & a)438 ChartAxis::ChartAxis(ChartAxis& a)
439 {
440 owner = a.owner;
441 domain = a.domain;
442 }
443 /*
444 draws axis on owner canvas
445 */
Draw()446 void ChartAxis::Draw()
447 {
448 if(!owner)
449 return;
450 VDKRgb fg = owner->Foreground;
451 if(fg.red >= 0)
452 owner-> SetColor(fg);
453 owner->DrawLine(domain.Origin().X(),
454 domain.Origin().Y(),
455 domain.Origin().X(),
456 owner->ChartBorder);
457 owner->DrawLine(domain.Origin().X(),
458 domain.Origin().Y(),
459 domain.Origin().X()+ domain.W(),
460 domain.Origin().Y());
461 }
462
463 /////////////////////// SERIES ///////////////////////
464 /*
465 add a point ot series,
466 computes on the fly
467 max and min x,y
468 */
Add(double x,double y)469 void Series::Add(double x, double y)
470 {
471 if(size() < 1)
472 {
473 max.x = x; max.y = y;
474 min.x = x; min.y = y;
475 }
476 else
477 {
478 max.x = max.x < x ? x : max.x;
479 max.y = max.y < y ? y : max.y;
480 min.x = min.x > x ? x : min.x;
481 min.y = min.y > y ? y : min.y;
482 }
483 Coord c(x,y);
484 CoordList::add(c);
485 }
486 /*
487 add a point array
488 */
Add(double * x,double * y,int n)489 void Series::Add(double* x, double* y, int n)
490 {
491 int i;
492 for(i=0;i<n;i++)
493 Add(x[i],y[i]);
494 }
495
496 ///////////////////////// LINE CHART //////////////
497 /*
498 Plot a line chart
499 */
Plot(VDKPoint & p,int t,Series * series)500 void VDKLineChart::Plot(VDKPoint& p, int t, Series* series)
501 {
502 static int fx,fy;
503 // each time a new series should be plotted
504 // set canvas color and line attributes.
505 // This happens whenever t == 0
506 if(t == 0)
507 {
508 VDKRgb color = series->Color;
509 SetColor(color);
510 SetLineAttributes(
511 series->LineWidth,
512 series->LineStyle,
513 series->LineCapStyle,
514 series->LineJoinStyle);
515 fx = p.X();
516 fy = p.Y();
517 }
518 else
519 {
520 // pixmap is a protected member of VDKCanvas class
521 if(pixmap)
522 gdk_draw_line(pixmap, GC(), fx,fy,p.X(),p.Y());
523 fx = p.X(); fy = p.Y();
524 }
525 }
526 ///////////////////////// SCATTERED CHART //////////////
527 /*
528 Plot a scattered chart
529 */
Plot(VDKPoint & p,int t,Series * series)530 void VDKScatteredChart::Plot(VDKPoint& p, int t, Series* series)
531 {
532 if(t == 0)
533 {
534 VDKRgb color = series->Color;
535 SetColor(color);
536 SetLineAttributes(
537 series->LineWidth,
538 series->LineStyle,
539 series->LineCapStyle,
540 series->LineJoinStyle);
541 }
542 gdk_draw_rectangle(pixmap,GC(),TRUE,p.X()-2,p.Y()-2,4,4);
543 }
544 ///////////////////////// BAR CHART //////////////
545 /*
546 Plot a bar chart
547 */
Plot(VDKPoint & p,int t,Series * series)548 void VDKBarChart::Plot(VDKPoint& p, int t, Series* series)
549 {
550 if(t == 0)
551 {
552 VDKRgb color = series->Color;
553 SetColor(color);
554 SetLineAttributes(
555 series->LineWidth,
556 series->LineStyle,
557 series->LineCapStyle,
558 series->LineJoinStyle);
559 }
560
561 if(Labels)
562 {
563 char buff[64];
564 VDKFont* vdkfont = Font;
565 GdkFont* font = vdkfont->AsGdkFont();
566 sprintf(buff,"%.1f",(p.Y() - yn1 + ky*yv1)/ky);
567 int w = font ? gdk_string_width (font, buff): 10;
568 gdk_draw_string(pixmap,font,GC(),p.X()- w/2,p.Y()-2,buff);
569 }
570
571 gdk_draw_rectangle(pixmap,GC(),TRUE,
572 p.X()-BarWidth/2,
573 p.Y(),
574 BarWidth,
575 axis.Domain().Origin().Y()-p.Y()
576 );
577 }
578
579
580
581
582
583
584
585