1 // This may look like C code, but it's really -*- C++ -*-
2 /*
3  * Copyright (C) 2008 Emweb bv, Herent, Belgium.
4  *
5  * See the LICENSE file for terms of use.
6  */
7 #ifndef CHART_WDATA_SERIES_H_
8 #define CHART_WDATA_SERIES_H_
9 
10 #include <Wt/Chart/WAxis.h>
11 #include <Wt/WBrush.h>
12 #include <Wt/WObject.h>
13 #include <Wt/WPen.h>
14 #include <Wt/WShadow.h>
15 #include <Wt/Chart/WChartGlobal.h>
16 #include <Wt/WPainterPath.h>
17 
18 namespace Wt {
19 
20   class WPointF;
21 
22   namespace Chart {
23 
24     class WAbstractChartModel;
25 
26 /*! \brief Enumeration that indicates an aspect of the look.
27  *
28  * These flags are used to keep track of which aspects of the look
29  * that are overridden from the values provided by the chart palette,
30  * using one of the methods in this class.
31  *
32  * \sa setPen(), setBrush(), setMarkerPen(), setMarkerBrush(), setLabelColor()
33  */
34 enum class CustomFlag {
35   Pen = 0x1,         //!< A custom pen is set.
36   Brush = 0x2,       //!< A custom brush is set.
37   MarkerPen = 0x4,   //!< A custom marker pen is set.
38   MarkerBrush = 0x8, //!< A custom marker brush is set.
39   LabelColor = 0x10  //!< A custom label color is set.
40 };
41 
42 /*! \class WDataSeries Wt/Chart/WDataSeries.h Wt/Chart/WDataSeries.h
43  *  \brief A single data series in a cartesian chart.
44  *
45  * This class configures all aspects for rendering a single data series
46  * in a cartesian chart. A data series renders Y data from a single
47  * model column against the X series configured for the chart.
48  *
49  * \if cpp
50  * The data column should contain data that can be converted to
51  * a number, but should not necessarily be of a number type, see
52  * also asNumber(const boost::any&).
53  * \elseif java
54  * The data column should contain data that can be converted to
55  * a number, but should not necessarily be of a number type, see
56  * also {javadoclink eu.webtoolkit.jwt.StringUtils#asNumber(Object)}.
57  * \endif
58  *
59  * Multiple series of different types may be combined on a single chart.
60  *
61  * \image html ChartWDataSeries-1.png "Different styles of data series"
62  *
63  * For a category chart, series may be stacked on top of each other.
64  * This is controlled by setStacked() for a series, which if enabled,
65  * will stack that series on top of the preceding data series. This
66  * works regardless of whether they are of the same type, but
67  * obviously works visually best if these series are of the same
68  * type. When not stacked, bar series are rendered next to each other.
69  * The margin between bars of different data series is controlled
70  * using WCartesianChart::setBarMargin().
71  *
72  * The line and color type are by default based on the \link
73  * WCartesianChart::palette() chart palette\endlink, but may be
74  * overridden for a series using setPen(), setBrush(), etc...
75  *
76  * \sa WCartesianChart::addSeries()
77  *
78  * \ingroup charts
79  */
80 class WT_API WDataSeries
81 #ifdef WT_TARGET_JAVA
82   : public Wt::WObject
83 #endif // WT_TARGET_JAVA
84 {
85 public:
86   /*! \brief Constructs a new data series.
87    *
88    * Creates a new data series which plots the Y values from the
89    * model column <i>modelColumn</i>, with the indicated
90    * <i>seriesType</i>. The Y values are mapped to the indicated
91    * <i>axis</i>, which should correspond to one of the two Y axes.
92    *
93    * \sa WCartesianChart::addSeries()
94    */
95   WDataSeries(int modelColumn, SeriesType seriesType = SeriesType::Point,
96 	      Axis axis = Axis::Y1);
97 
98   /*! \brief Constructs a new data series.
99    *
100    * Creates a new data series which plots the Y values from the
101    * model column <i>modelColumn</i>, with the indicated
102    * <i>seriesType</i>. The Y values are mapped to the indicated
103    * <i>yAxis</i>, which should correspond to one of the two Y axes.
104    *
105    * \sa WCartesianChart::addSeries()
106    */
107   WDataSeries(int modelColumn, SeriesType seriesType, int yAxis);
108 
109   /*! \brief Destructor.
110    */
111   ~WDataSeries();
112 
113   /*! \brief Sets the bar width.
114    *
115    * The bar width specifies the bar width (in axis dimensions).  For
116    * category plots, which may have several bars for different series
117    * next to each other, you will want to specify the same bar width
118    * for each series.
119    *
120    * For scatter plots, you may want to set the bar width to a natural
121    * size. E.g. if you are plotting weekly measurements, you could set
122    * the width to correspond to a week (=7).
123    *
124    * The default value is 0.8 (which leaves a 20% margin between bars
125    * for different categories in a category chart.
126    *
127    * \sa WCartesianChart::setBarMargin()
128    */
129   void setBarWidth(const double width);
130 
131   /*! \brief Returns the bar width.
132    *
133    * \sa setBarWidth()
134    */
135   double barWidth() const;
136 
137   /*! \brief Sets the series type.
138    *
139    * The series type specifies how the data is plotted, i.e. using
140    * mere point markers, lines, curves, or bars.
141    */
142   void setType(SeriesType t);
143 
144   /*! \brief Returns the series type.
145    *
146    * \sa setType()
147    */
type()148   SeriesType type() const { return type_; }
149 
150   /*! \brief Sets the model column.
151    *
152    * This specifies the model column from which the Y data is retrieved
153    * that is plotted by this series.
154    *
155    * The data column should contain data that can be converted to
156    * a number (but should not necessarily be of a number type).
157    * \if java
158    * See also {javadoclink eu.webtoolkit.jwt.StringUtils#asNumber(Object)}.
159    * \endif
160    *
161    * \if cpp
162    * \sa Wt::asNumber()
163    * \endif
164    */
165   void setModelColumn(int modelColumn);
166 
167   /*! \brief Returns the model column.
168    *
169    * \sa setModelColumn()
170    */
modelColumn()171   int modelColumn() const { return modelColumn_; }
172 
173   /*! \brief Sets the X series column.
174    *
175    * By default, the data series uses the X series column configured
176    * for the chart. For a scatter plot, each series can have its own
177    * matching X data, which is configured here. For other plots, this
178    * setting is ignored.
179    *
180    * The default value is -1, which indicates that
181    * WCartesianChart::XSeriesColumn() is to be used.
182    *
183    * \sa WCartesianChart::setXSeriesColumn()
184    */
185   void setXSeriesColumn(int modelColumn);
186 
187   /*! \brief Returns the X series column.
188    *
189    * \sa setXSeriesColumn()
190    */
XSeriesColumn()191   int XSeriesColumn() const { return XSeriesColumn_; }
192 
193   /*! \brief Sets whether this series is stacked on top of the preceding series.
194    *
195    * For category charts, data from different series may be rendered
196    * stacked on top of each other. The rendered value is the sum of the
197    * value of this series plus the rendered value of the preceding
198    * series. For line series, you probably will want to add filling
199    * under the curve. A stacked bar series is rendered by a bar on top
200    * of the preceding bar series.
201    *
202    * The default value is false.
203    */
204   void setStacked(bool stacked);
205 
206   /*! \brief Returns whether this series is stacked on top of the preceding
207    *         series.
208    *
209    * \sa setStacked()
210    */
isStacked()211   bool isStacked() const { return stacked_; }
212 
213   /*! \brief Binds this series to a chart axis.
214    *
215    * A data series may be bound to either the first or second Y axis.
216    * Note that the second Y axis is by default not displayed.
217    *
218    * The default value is the first Y axis.
219    *
220    * \sa WAxis::setVisible()
221    */
222   void bindToAxis(Axis axis);
223 
224   /*! \brief Binds this series to a chart's X axis.
225    *
226    * Note that the second Y axis will not be displayed by default.
227    *
228    * The default value is the first X axis.
229    *
230    * \sa WAxis::setVisible()
231    */
232   void bindToXAxis(int xAxis);
233 
234   /*! \brief Binds this series to a chart's Y axis.
235    *
236    * Note that the second Y axis will not be displayed by default.
237    *
238    * The default value is the first Y axis.
239    *
240    * \sa WAxis::setVisible()
241    */
242   void bindToYAxis(int yAxis);
243 
244   /*! \brief Returns the Y axis used for this series.
245    *
246    * \sa bindToAxis()
247    */
axis()248   Axis axis() const { return yAxis_ == 1 ? Axis::Y2 : Axis::Y1; }
249 
250   /*! \brief Returns the Y axis used for this series.
251    *
252    * \sa bindToXAxis()
253    */
xAxis()254   int xAxis() const { return xAxis_; }
255 
256   /*! \brief Returns the Y axis used for this series.
257    *
258    * \sa bindToYAxis()
259    */
yAxis()260   int yAxis() const { return yAxis_; }
261 
262   /*! \brief Sets which aspects of the look are overriden.
263    *
264    * Set which aspects of the look, that are by default based on the
265    * chart palette, are overridden by custom settings.
266    *
267    * The default value is 0 (nothing overridden).
268    */
269   void setCustomFlags(WFlags<CustomFlag> customFlags);
270 
271   /*! \brief Returns which aspects of the look are overriden.
272    *
273    * \sa setCustomFlags()
274    */
customFlags()275   WFlags<CustomFlag> customFlags() const { return customFlags_; }
276 
277   /*! \brief Overrides the pen used for drawing lines for this series.
278    *
279    * Overrides the pen that is used to draw this series. Calling this
280    * method automatically adds CustomPen to the custom flags.
281    *
282    * The default value is a default WPen().
283    *
284    * \sa WChartPalette::strokePen(), WChartPalette::borderPen()
285    */
286   void setPen(const WPen& pen);
287 
288   /*! \brief Returns the pen used for drawing lines for this series.
289    *
290    * \sa setPen()
291    */
292   WPen pen() const;
293 
294   /*! \brief Overrides the brush used for filling areas for this series.
295    *
296    * Overrides the brush that is used to draw this series which is
297    * otherwise provided by the chart palette. For a bar plot, this is the
298    * brush used to fill the bars. For a line chart, this is the brush
299    * used to fill the area under (or above) the line. Calling this
300    * method automatically adds CustomBrush to the custom flags.
301    *
302    * \sa WChartPalette::brush()
303    */
304   void setBrush(const WBrush& brush);
305 
306   /*! \brief Returns the brush used for filling areas for this series.
307    *
308    * \sa setBrush()
309    */
310   WBrush brush() const;
311 
312   /*! \brief Sets a shadow used for stroking lines for this series.
313    */
314   void setShadow(const WShadow& shadow);
315 
316   /*! \brief Returns the shadow used for stroking lines for this series.
317    *
318    * \sa setShadow()
319    */
320   const WShadow& shadow() const;
321 
322   /*! \brief Sets the fill range for line or curve series.
323    *
324    * Line or curve series may be filled under or above the curve,
325    * using the brush(). This setting specifies the range that is
326    * filled. The default value for all but SeriesType::Bar is FillRangeType::None.
327    *
328    * Bar series may use FillRangeType::MinimumValue to configure the chart to
329    * render its bars from the data point to the bottom of the chart or
330    * FillRangeType::MaximumValue to render the bars from the data point to the
331    * top of the chart. The default value for SeriesType::Bar is
332    * FillRangeType::ZeroValue, which render bars from zero to the data value.
333    */
334   void setFillRange(FillRangeType fillRange);
335 
336   /*! \brief Returns the fill range (for line, curve and bar series).
337    *
338    * \sa setFillRange()
339    */
340   FillRangeType fillRange() const;
341 
342   /*! \brief Sets the data point marker.
343    *
344    * Specifies a marker that is displayed at the (X,Y) coordinate for each
345    * series data point.
346    *
347    * The default value is a MarkerType::Circle for a SeriesType::Point, or MarkerType::None
348    * otherwise.
349    *
350    * \sa setMarkerPen(), setMarkerBrush(), setCustomMarker()
351    */
352   void setMarker(MarkerType marker);
353 
354   /*! \brief Sets the custom marker.
355    *
356    * This will also changes the marker type to MarkerType::Custom.
357    *
358    * \sa setMarker()
359    */
360   void setCustomMarker(const WPainterPath& path);
361 
362   /*! \brief Returns the custom marker.
363    *
364    * \sa setCustomMarker()
365    */
customMarker()366   WPainterPath customMarker() const { return customMarker_; };
367 
368   /*! \brief Returns the data point marker.
369    *
370    * \sa setMarker()
371    */
marker()372   MarkerType marker() const { return marker_; }
373 
374   /*! \brief Sets the marker size.
375    *
376    * The default marker size is 6 pixels.
377    */
378   void setMarkerSize(double size);
379 
380   /*! \brief Returns the marker size.
381    *
382    * \sa setMarkerSize()
383    */
markerSize()384   double markerSize() const { return markerSize_; }
385 
386   /*! \brief Sets the marker pen.
387    *
388    * Overrides the pen used for stroking the marker. By default the
389    * marker pen is the same as pen(). Calling this method automatically adds
390    * CustomMarkerPen to the custom flags.
391    *
392    * \sa setPen(), setMarkerBrush()
393    */
394   void setMarkerPen(const WPen& pen);
395 
396   /*! \brief Returns the marker pen.
397    *
398    * \sa setMarkerPen()
399    */
400   WPen markerPen() const;
401 
402   /*! \brief Sets the marker brush.
403    *
404    * Overrides the brush used for filling the marker. By default the
405    * marker brush is the same as brush(). Calling this method
406    * automatically adds CustomMarkerBrush to the custom flags.
407    *
408    * \sa setBrush(), setMarkerPen()
409    */
410   void setMarkerBrush(const WBrush& brush);
411 
412   /*! \brief Returns the marker brush.
413    *
414    * \sa setMarkerBrush()
415    */
416   WBrush markerBrush() const;
417 
418   /*! \brief Enables the entry for this series in the legend.
419    *
420    * When <i>enabled</i>, this series is added to the chart
421    * legend.
422    *
423    * The default value is true.
424    *
425    * \sa WCartesianChart::setLegendEnabled().
426    */
427   void setLegendEnabled(bool enabled);
428 
429   /*! \brief Returns whether this series has an entry in the legend.
430    *
431    * \sa setLegendEnabled()
432    */
433   bool isLegendEnabled() const;
434 
435   /*! \brief Enables a label that is shown at the series data points.
436    *
437    * You may enable labels for the Axis::X, Axis::Y or both axes. The
438    * label that is displayed is the corresponding value on that
439    * axis. If both labels are enabled then they are combined in a
440    * single text using the format: "<x-value>: <y-value>".
441    *
442    * The default values are false for both axes (no labels).
443    *
444    * \sa isLabelsEnabled()
445    */
446   void setLabelsEnabled(Axis axis, bool enabled = true);
447 
448   /*! \brief Returns whether labels are enabled for the given axis.
449    *
450    * \sa setLabelsEnabled()
451    */
452   bool isLabelsEnabled(Axis axis) const;
453 
454   /*! \brief Sets the label color.
455    *
456    * Specify the color used for the rendering labels at the data
457    * points.
458    *
459    * \sa setLabelsEnabled()
460    */
461   void setLabelColor(const WColor& color);
462 
463   /*! \brief Returns the label color.
464    *
465    * \sa setLabelColor()
466    */
467   WColor labelColor() const;
468 
469   /*! \brief Hide/unhide this series.
470    *
471    * A hidden series will not be show in the chart and legend.
472    */
473   void setHidden(bool hidden);
474 
475   /*! \brief Return whether the series is hidden.
476    *
477    * \sa setHidden()
478    */
479   bool isHidden() const;
480 
481   /*! \brief Maps from device coordinates to model coordinates.
482    *
483    * Maps a position in the chart back to model coordinates, for data
484    * in this data series.
485    *
486    * This uses WChart::mapFromDevice() passing the axis() to which this
487    * series is bound.
488    *
489    * This method uses the axis dimensions that are based on the latest
490    * chart rendering. If you have not yet rendered the chart, or wish
491    * to already the mapping reflect model changes since the last
492    * rendering, you should call WCartesianChart::initLayout() first.
493    *
494    * \sa mapToDevice()
495    */
496   WPointF mapFromDevice(const WPointF& deviceCoordinates) const;
497 
498   /*! \brief Maps from model values to device coordinates.
499    *
500    * Maps model values to device coordinates, for data in this data series.
501    *
502    * This uses WChart::mapToDevice() passing the axis() to which this
503    * series is bound.
504    *
505    * This method uses the axis dimensions that are based on the latest
506    * chart rendering. If you have not yet rendered the chart, or wish
507    * to already the mapping reflect model changes since the last
508    * rendering, you should call WCartesianChart::initLayout() first.
509    *
510    * \sa mapFromDevice()
511    */
512   WPointF mapToDevice(const cpp17::any& xValue, const cpp17::any& yValue,
513 		      int segment = 0) const;
514 
515   /*! \brief Set an offset to draw the data series at.
516    *
517    * The Y position of the data series will be drawn at an offset,
518    * expressed in model coordinates. The axis labels won't follow
519    * the same offset.
520    *
521    * The offset can be manipulated client side using a mouse or touch
522    * drag if WCartesianChart::curveManipulationEnabled() is enabled.
523    *
524    * \note This is only supported for axes with linear scale.
525    *
526    * \sa setScale()
527    * \sa WCartesianChart::setCurveManipulationEnabled()
528    */
529   void setOffset(double offset);
530 
531   /*! \brief Get the offset for this data series.
532    *
533    * \sa setOffset()
534    */
offset()535   double offset() const { return offset_; }
536 
537 
538   /*! \brief Set the scale to draw the data series at.
539    *
540    * The Y position of the data series will be scaled around the zero
541    * position, and offset by offset().
542    *
543    * The scale can be manipulated client side using the scroll wheel
544    * or a pinch motion if WCartesianChart::curveManipulationEnabled()
545    * is enabled.
546    *
547    * \note This is only supported for axes with linear scale.
548    *
549    * \sa setOffset()
550    * \sa WCartesianChart::setCurveManipulationEnabled()
551    */
552   void setScale(double scale);
553 
554   /*! \brief Get the scale for this data series.
555    *
556    * \sa setScale()
557    */
scale()558   double scale() const { return scale_; }
559 
560   /*! \brief Set a model for this data series.
561    *
562    * If no model is set for this data series, the model of
563    * the chart will be used.
564    *
565    * \note Individual models per data series are only supported for
566    * ChartType::Scatter type charts.
567    *
568    * \sa WCartesianChart::setModel()
569    */
570   void setModel(const std::shared_ptr<WAbstractChartModel>& model);
571 
572   /*! \brief Get the model for this data series.
573    *
574    * This will return the model set for this data series,
575    * if it is set.
576    *
577    * If no model is set for this data series, and the series
578    * is associated with  a chart, the model of the chart is returned.
579    *
580    * If no model is set for this data series, and the series is
581    * not associated with any data series, this will return null.
582    *
583    * \sa setModel()
584    * \sa WCartesianChart::setModel()
585    */
586   std::shared_ptr<WAbstractChartModel> model() const;
587 
chart()588   WCartesianChart *chart() { return chart_; }
589 
590 private:
591   WCartesianChart   *chart_;
592   std::shared_ptr<WAbstractChartModel> model_;
593   int                modelColumn_;
594   int                XSeriesColumn_;
595   bool               stacked_;
596   SeriesType         type_;
597   int                xAxis_;
598   int                yAxis_;
599   WFlags<CustomFlag> customFlags_;
600   WPen               pen_, markerPen_;
601   WBrush             brush_, markerBrush_;
602   WColor             labelColor_;
603   WShadow            shadow_;
604   FillRangeType      fillRange_;
605   MarkerType         marker_;
606   double             markerSize_;
607   bool               legend_;
608   bool               xLabel_;
609   bool               yLabel_;
610   double             barWidth_;
611   bool               hidden_;
612   WPainterPath       customMarker_;
613   double             offset_;
614   double             scale_;
615   mutable bool       offsetDirty_;
616   mutable bool       scaleDirty_;
617 
618   // connections with the current model, used to disconnect from a model
619   // when the model changes.
620   std::vector<Wt::Signals::connection> modelConnections_;
621 
622   void modelReset();
623 
624   template <typename T>
625   bool set(T& m, const T& v);
626 
627   void setChart(WCartesianChart *chart);
628   void update();
629 
630   friend class WCartesianChart;
631   friend class LineSeriesRenderer;
632 };
633 
634 template <typename T>
set(T & m,const T & v)635 bool WDataSeries::set(T& m, const T& v)
636 {
637   if (m != v) {
638     m = v;
639     update();
640     return true;
641   } else
642     return false;
643 }
644 
645   }
646 }
647 
648 #endif // CHART_WDATA_SERIES_H_
649