Boost logo

Boost-Commit :

From: pbristow_at_[hidden]
Date: 2008-05-24 12:22:39


Author: pbristow
Date: 2008-05-24 12:22:38 EDT (Sat, 24 May 2008)
New Revision: 45716
URL: http://svn.boost.org/trac/boost/changeset/45716

Log:
Revised for major update of 1D, 2D and Boxplot
Text files modified:
   sandbox/SOC/2007/visualization/boost/svg_plot/svg_2d_plot.hpp | 797 +++++++++++--
   sandbox/SOC/2007/visualization/boost/svg_plot/svg_boxplot.hpp | 2359 ++++++++++++++++++++++++++-------------
   sandbox/SOC/2007/visualization/boost/svg_plot/svg_color.hpp | 231 ++-
   sandbox/SOC/2007/visualization/boost/svg_plot/svg_style.hpp | 787 ++++++++----
   4 files changed, 2855 insertions(+), 1319 deletions(-)

Modified: sandbox/SOC/2007/visualization/boost/svg_plot/svg_2d_plot.hpp
==============================================================================
--- sandbox/SOC/2007/visualization/boost/svg_plot/svg_2d_plot.hpp (original)
+++ sandbox/SOC/2007/visualization/boost/svg_plot/svg_2d_plot.hpp 2008-05-24 12:22:38 EDT (Sat, 24 May 2008)
@@ -1,7 +1,7 @@
 // svg_2d_plot.hpp
 
 // Copyright Jacob Voytko 2007
-// Copyright Paul A. Bristow 2007
+// Copyright Paul A. Bristow 2007, 2008
 
 // Use, modification and distribution are subject to the
 // Boost Software License, Version 1.0.
@@ -18,21 +18,24 @@
 #endif
 
 #include <boost/iterator/transform_iterator.hpp>
+// using boost::make_transform_iterator;
 
 #include "svg_style.hpp"
 #include "detail/axis_plot_frame.hpp"
 #include "detail/numeric_limits_handling.hpp"
 #include "detail/functors.hpp"
+#include "detail/auto_axes.hpp"
+
 #include "svg.hpp"
 
-#include <map>
+#include <map> // for map & multimap
 #include <string>
 #include <sstream>
 #include <utility>
 #include <ostream>
 #include <iterator>
 #include <exception>
-#include <iostream> // for debug
+#include <iostream> // for debug.
 
 namespace boost
 {
@@ -40,7 +43,7 @@
   {
     // Forward declarations.
     const std::string strip_e0s(std::string s); // Strip unncessary zeros and e and sign.
- class svg_2d_plot; // Plot.
+ class svg_2d_plot; // Plot framework.
     class svg_2d_plot_series; // plot data series.
 
     // -----------------------------------------------------------------
@@ -51,7 +54,6 @@
     // -----------------------------------------------------------------
     class svg_2d_plot_series
     {
- //protected: // TODO this causes trouble.
 
     friend svg_2d_plot_series;
     friend void draw_straight_lines(const svg_2d_plot_series&);
@@ -65,13 +67,14 @@
       // are unaffected by the order in which data is presented.
       // (For 1-D a vector of doubles can be used).
 
- std::string title_; // Title of data series.
- plot_point_style point_style_;
- plot_point_style limit_point_style_;
+ std::string title_; // Title of data series (to show on legend).
+ plot_point_style point_style_; // circle, square...
+ plot_point_style limit_point_style_; // Default is cone pointing down.
       plot_line_style line_style_;
- public:
+ bar_style bar_style_;
+ histogram_style histogram_style_;
 
- template <class T>
+ template <class T> // T an STL container: for example: multimap.
       svg_2d_plot_series(T begin, T end, // of data series.
         std::string title) // Title of data series.
         :
@@ -80,9 +83,10 @@
         // int size = 10, point_shape shape = round, const std::string& symbols = "X")
         point_style_(black, blank, 10, round), // Default point style.
         limit_point_style_(grey, blank, 10, cone), // Default limit (inf or NaN) point style.
-
- // plot_line_style(const svg_color& col = black, const svg_color& acol = true, double width = 2, bool line_on = true, bool bezier_on = false)
- line_style_(black, true, 2, true, false) // Default line style, no fill.
+ line_style_(black, blank, 2, true, false), // Default line style, no fill, width 2, line_on, bezier off.
+ bar_style_(black, blank, 3, no_bar), // Default black, no fill, stick width 3, no bar.
+ // -2 block to Y-axis, -1 stick to Y-axis, no_bar, +1 stick to x_axis, -2 block to X-axis.
+ histogram_style_(no_histogram)
 
       { // Constructor.
         for(T i = begin; i != end; ++i)
@@ -126,34 +130,58 @@
 
       svg_2d_plot_series& line_color(const svg_color& col_)
       {
- line_style_.color_ = col_;
+ line_style_.stroke_color_ = col_;
         return *this;
       }
 
       svg_2d_plot_series& area_fill(const svg_color& col_)
       {
         line_style_.area_fill_ = col_;
- // Note that area_fill(true) will produce a *blank* color, and so NO FILL.
- // area_fill(false) will produce the default non-blank color (black?).
+ // Note that area_fill(false) will produce a *blank* color, and so NO FILL.
+ // area_fill(blank) will produce the default non-blank color (black?).
         return *this;
       }
 
       svg_2d_plot_series& line_width(double wid_)
- {
+ {
         line_style_.width_ = wid_; // Sets legend line width too.
- return *this;
+ return *this; // Make chainable.
       }
 
       svg_2d_plot_series& line_on(bool on_)
       {
         line_style_.line_on_ = on_;
- return *this;
+ return *this; // Make chainable.
       }
 
       svg_2d_plot_series& bezier_on(bool on_)
       {
         line_style_.bezier_on_ = on_;
- return *this;
+ return *this; // Make chainable.
+ }
+
+ svg_2d_plot_series& bar(bar_option opt_)
+ {
+ bar_style_.bar_option_ = opt_;
+ return *this; // Make chainable.
+ }
+
+ svg_2d_plot_series& bar_width(double wid_)
+ {
+ bar_style_.width_ = wid_;
+ return *this; // Make chainable.
+ }
+
+ svg_2d_plot_series& bar_color(const svg_color& col)
+ {
+ bar_style_.color_ = col;
+ return *this; // Make chainable.
+ }
+
+ svg_2d_plot_series& bar_area_fill(const svg_color& col)
+ {
+ bar_style_.area_fill_ = col;
+ return *this; // Make chainable.
       }
 
       // Get functions for the plot series.
@@ -179,7 +207,7 @@
 
       svg_color& line_color()
       {
- return line_style_.color_;
+ return line_style_.stroke_color_;
       }
 
       svg_color& area_fill()
@@ -197,21 +225,48 @@
         return point_style_.shape_;
       }
 
- }; // struct svg_2d_plot_series
+ bar_option bar_option()
+ {
+ return bar_style_.bar_option_;
+ }
+
+ double bar_width()
+ {
+ return bar_style_.width_;
+ }
+
+ svg_color& bar_color()
+ {
+ return bar_style_.color_;
+ }
+
+ svg_color& bar_area_fill()
+ {
+ return bar_style_.area_fill_;
+ }
+
+ svg_2d_plot_series& histogram(histogram_option opt_)
+ { // column = -1, // Bar or row line (stroke width) horizontal to Y-axis.
+ // no_histogram = 0,
+ // bar = +1 // Stick or column line (stroke width) vertical to X-axis.
+ histogram_style_.histogram_option_ = opt_;
+ return *this; // Make chainable.
+ }
+
+ }; // class svg_2d_plot_series
 
     class svg_2d_plot : public detail::axis_plot_frame<svg_2d_plot>
     { // See also svg_1d_plot.hpp for 1-D version.
      friend void show_plot_settings(svg_2d_plot&);
      friend svg_2d_plot_series;
+ friend class detail::axis_plot_frame<svg_2d_plot>;
+ // axis_plot_frame.hpp contains functions common to 1 and 2-D.
 
     private:
       // Member data names conventionally end with _,
- // for example: border_margin_
- // and set & get accessor functions are named without _ suffix:
- // border_margin() & border_margin(int).
-
- friend class detail::axis_plot_frame<svg_2d_plot>;
- // axis_plot_frame.hpp contains functions common to 1 and 2-D.
+ // for example: border_margin_,
+ // and set & get accessor functions are named without _ suffix,
+ // for example: border_margin() & border_margin(int).
 
       double x_scale_; // Use by function transform()
       double x_shift_; // to go from Cartesian to svg coordinates.
@@ -233,9 +288,6 @@
       text_style y_axis_label_style_;
       text_style y_value_label_style_;
       text_style point_symbols_style_; // Used for data point marking.
-
- //double x_label_width_;
-
       text_element title_info_; // Plot title.
       text_element legend_header_; // legend box header or title (if any).
       text_element x_label_info_; // For example: "length"
@@ -245,6 +297,17 @@
       text_element y_units_info_; // 2-D only.
       text_element y_label_value_; // For example: "1.2" or "1.2e+001"
 
+ text_style value_style_; // Used for data point value label.
+ value_style x_values_style_; // Data point X value marking.
+ value_style y_values_style_; // Data point Y value marking.
+ //bool x_plusminus_on_; // http://en.wikipedia.org/wiki/Plus-minus_sign
+ //// Unicode &#0xB1; HTML &plusmn;
+
+
+ //rotate_style y_value_label_rotation_; // Direction point Y value labels written.
+ //int y_value_precision_;
+ //std::ios_base::fmtflags y_value_ioflags_;
+
       box_style image_border_; // rectangular border of all image width, color...
       box_style plot_window_border_; // rectangular border of plot window width, color...
       box_style legend_box_; // rectangular box of legend width, color...
@@ -276,14 +339,42 @@
 
       bool title_on_; // Provide a title for the whole plot.
       bool legend_on_; // Provide a legend box.
- bool outside_legend_on_; // legend box outside the plot window.
+ bool outside_legend_on_; // Legend box outside the plot window.
       bool legend_lines_; // get/set by legend_lines(bool); data colored line type in legend box.
       bool plot_window_on_; // Use a separate plot window (not whole image).
       bool x_ticks_on_; // TODO check these are really useful.
       bool y_ticks_on_;
-
- int x_axis_position_; // TODO should these be in axis_style?
- int y_axis_position_; // But complications with difference between X and Y.
+ bool x_values_on_; // If values of X data are shown.
+ bool y_values_on_; // If values of Y data are shown.
+ bool xy_values_on_; // If values of X & Y pairs are shown.
+
+ int x_axis_position_;
+ int y_axis_position_;
+
+ bool autoscale_check_limits_; // Whether to check autoscale values for infinity, NaN, max, min.
+ bool x_autoscale_; // Whether to use any X-axis autoscale values.
+ bool x_include_zero_; // If autoscaled, include zero.
+ int x_min_ticks_; // If autoscaled, set a minimum number of X ticks.
+ double x_tight_;
+ int x_steps_; // If autoscaled, set any prescaling to decimal 1, 2, 5, 10 etc.
+
+ // Values calculated by scale_axis, and is used only if x_autoscale == true.
+ double x_auto_min_value_;
+ double x_auto_max_value_;
+ double x_auto_tick_interval_; // tick major interval.
+ int x_auto_ticks_; // Number of ticks.
+
+ bool y_autoscale_; // Whether to use any y_axis autoscale values.
+ bool y_include_zero_; // If autoscaled, include zero.
+ int y_min_ticks_; // If autoscaled, set a minimum number of Y ticks.
+ double y_tight_;
+ int y_steps_; // If autoscaled, set any prescaling to decimal 1, 2, 5, 10 etc.
+
+ // Values calculated by scale_axis, and is used only if y_autoscale == true.
+ double y_auto_min_value_;
+ double y_auto_max_value_;
+ double y_auto_tick_interval_; // tick major interval.
+ int y_auto_ticks_; // Number of ticks.
 
       // Where we will be storing the data points (series) for transformation.
       std::vector<svg_2d_plot_series> series; // Defined above.
@@ -300,23 +391,21 @@
       svg_2d_plot() // Constructor, including all the very many default plot options,
         // some of which use some or all of the class defaults.
         :
- // TODO check that *all* options are initialized here.
- // See documentation for default settings rationale.
- // text_styles:
+ // See documentation for default settings rationale.
+ // text_styles:
         title_style_(18, "Verdana", "", ""), // last "bold" ?
         legend_style_(14, "Verdana", "", ""), // 2nd "italic"?
         x_axis_label_style_(14, "Verdana", "", ""),
- x_value_label_style_(12, "Verdana", "", ""),
- // Separate x and y to allow axes to have a different style.
+ x_value_label_style_(14, "Verdana", "", ""),
+ // Separate x and y to allow axes to have different styles.
         y_axis_label_style_(14, "Verdana", "", ""),
         y_value_label_style_(12, "Verdana", "", ""),
         point_symbols_style_(12, "Lucida Sans Unicode"), // Used for data point marking.
+ //x_values_style_(10, "Verdana", "", ""), // Used for data point X values.
+ //y_values_style_(10, "Verdana", "", ""), // Used for data point Y values.
         title_info_(0, 0, "", title_style_, center_align, horizontal),
- // title_info_(0, 0, "Plot of data", title_style_, center_align, horizontal),
         x_label_info_(0, 0, "", x_axis_label_style_, center_align, horizontal),
         x_units_info_(0, 0, "", x_value_label_style_, center_align, horizontal),
- // x_label_info_(0, 0, "X Axis", x_axis_label_style_, center_align, horizontal),
- // x_units_info_(0, 0, " (units)", x_value_label_style_, center_align, horizontal),
         x_label_value_(0, 0, "", x_value_label_style_, center_align, horizontal),
         x_axis_(X, -10., +10., black, 1, 0, true, false, true),
         y_axis_(Y, -10., +10., black, 1, 0, true, false, true),
@@ -326,8 +415,6 @@
         y_ticks_(Y, y_value_label_style_),
         y_label_info_(0, 0, "", y_axis_label_style_, center_align, upward),
         y_units_info_(0, 0, "", y_axis_label_style_, center_align, upward),
- // y_label_info_(0, 0, "Y Axis", y_axis_label_style_, center_align, upward),
- // y_units_info_(0, 0, " (units)", y_axis_label_style_, center_align, upward),
         y_label_value_(0, 0, "", y_value_label_style_, center_align, upward),
         text_margin_(2.), // for axis label text, as a multiplier of the font size.
         image_border_(yellow, white, 2, 10, true, true), // margin should be about axis label font size.
@@ -344,16 +431,36 @@
         plot_window_clip_("plot_window"), // for <clipPath id="plot_window" ...
         title_on_(true),
         plot_window_on_(true),
+ // Can have both X and Y value shown.
+ x_values_on_(false), // If X values of data are shown.
+ y_values_on_(false), // If Y values of data are shown.
+ xy_values_on_(false), // If X & Y values of data are shown as a pair.
+ x_values_style_(horizontal, 3, std::ios::dec, true, value_style_, black, black, false, false),
+ y_values_style_(horizontal, 3, std::ios::dec, true, value_style_, black, black, false, false),
+
+ // Autoscaling defaults.
+ autoscale_check_limits_(true), // Do check all value for limits, infinity, max, min, NaN.
+ x_autoscale_(false),
+ x_include_zero_(false), // If autoscaled, include zero on X-axis.
+ x_min_ticks_(6), // If autoscaled, set a minimum number of ticks, default 6.
+ x_steps_(0), // If autoscaled, set any prescaling to decimal 1, 2, 5, 10 etc, default none.
+ x_tight_(1e-6), // margin that point can lie outside top and bottom tick.
+ y_autoscale_(false),
+ y_include_zero_(false), // If autoscaled, include zero on Y-axis.
+ y_min_ticks_(6), // If autoscaled, set a minimum number of ticks, default 6.
+ y_steps_(0), // If autoscaled, set any prescaling to decimal 1, 2, 5, 10 etc, default none.
+ y_tight_(1e-6), // margin that point can lie outside top and bottom tick.
+
         // Used to transform Cartesian to SVG.
         x_scale_(1.), x_shift_(0.),
         y_scale_(1.), y_shift_(0.),
- x_axis_position_(0), // TODO move into axis_style?
- y_axis_position_(0) // But some problems with this.
+ x_axis_position_(0),
+ y_axis_position_(0)
       {
         image_size(500, 400); // Default image size for 2-D.
         // 2-D usually needs to be squarer than 1-D.
 
- using namespace boost::svg::detail; // avoid detail::
+ using namespace boost::svg::detail; // Avoid need for prefix detail::
 
         // Build the document tree by adding all children of the root node.
         for(int i = 0; i < SVG_PLOT_DOC_CHILDREN; ++i)
@@ -372,11 +479,6 @@
         image.g(PLOT_LIMIT_POINTS).style().stroke_color(lightslategray).fill_color(antiquewhite);
         image.g(PLOT_X_AXIS).style().stroke_color(black).stroke_width(x_axis_.width());
         image.g(PLOT_Y_AXIS).style().stroke_color(black).stroke_width(y_axis_.width());
- image.g(PLOT_X_LABEL).style().fill_color(black);
- image.g(PLOT_Y_LABEL).style().fill_color(black);
- image.g(PLOT_VALUE_LABELS).style().fill_color(black);
- image.g(PLOT_LEGEND_TEXT).style().fill_color(black);
- image.g(PLOT_TITLE).style().fill_color(black).stroke_on(false);
 
         // Note that widths are stored in member data *and* copied here.
         // Not sure if this is wise but ...
@@ -394,8 +496,8 @@
         }
         // Grids.
         // Default color & width for grid, used or not.
+ // Might avoid empty grid stuff if this was only done if grid used? TODO
         image.g(PLOT_X_MAJOR_GRID).style().stroke_width(x_ticks_.major_grid_width_).stroke_color(svg_color(200, 220, 255));
- // BOOST_ASSERT(image.g(PLOT_X_MAJOR_GRID).style().stroke_color() == svg_color(200, 220, 255));
         image.g(PLOT_X_MINOR_GRID).style().stroke_width(x_ticks_.minor_grid_width_).stroke_color(svg_color(200, 220, 255));
         image.g(PLOT_Y_MAJOR_GRID).style().stroke_width(y_ticks_.major_grid_width_).stroke_color(svg_color(200, 220, 255));
         image.g(PLOT_Y_MINOR_GRID).style().stroke_width(y_ticks_.minor_grid_width_).stroke_color(svg_color(200, 220, 255));
@@ -437,9 +539,9 @@
 
         // Start by assuming we can use all the svg image,
         // but reduce by the width of any image border.
- plot_left_ = 0 + image_border_width();
+ plot_left_ = 0 + image_border_width(); // Top left of image.
         plot_top_ = 0 + image_border_width();
- plot_right_ = image.x_size() - image_border_width();
+ plot_right_ = image.x_size() - image_border_width(); // Bottom right of image.
         plot_bottom_ = image.y_size() - image_border_width();
 
         if(title_on_)
@@ -467,10 +569,37 @@
           plot_top_ += image_border_.margin_;
           plot_bottom_ -= image_border_.margin_;
         }
- size_legend_box(); // Size depends on its contents.
- place_legend_box(); // according to options chosen.
+ size_legend_box(); // Size depends on its contents.
+ place_legend_box(); // according to options chosen.
 
- // Check if the axes will intersect.
+ // Because there may be several datasets,
+ // and the scaling can be done by anyone or all of them.
+ // my_plot.x_autoscale(my_data1) // for 1 dataset.
+ // or my_plot.x_autoscale(my_datas) // for a vector of several datasets.
+ // calculates the min & max, increments & ticks.
+ if (x_autoscale_)
+ { // Use calculated autoscale values.
+ // Autoscale has been done in my_data.x_autoscale(my_data);
+ // and saved in x_auto_min_value_, x_auto_max_value_, & x_auto_tick_interval_,
+ // so copy these values to use them:
+ x_axis_.min_ = x_auto_min_value_;
+ x_axis_.max_ = x_auto_max_value_;
+ x_ticks_.major_interval_ = x_auto_tick_interval_;
+ // else ignore auto values, even if have been calculated.
+ }
+
+ if (y_autoscale_)
+ { // Use calculated autoscale values.
+ // Autoscale has been done in my_data.y_autoscale(my_data);
+ // and saved in y_auto_min_value_, y_auto_max_value_, & y_auto_tick_interval_,
+ // so copy these values to use them:
+ y_axis_.min_ = y_auto_min_value_;
+ y_axis_.max_ = y_auto_max_value_;
+ y_ticks_.major_interval_ = y_auto_tick_interval_;
+ // else ignore auto values, even if have been calculated.
+ }
+
+ // Check if the axes will intersect.
         // X axis position is determined by the range of Y min and max label values.
         x_axis_position_ = x_intersects_y; // Assume X-axis will intersect Y-axis (range of Y values includes zero).
         if (y_axis_.min_ > std::numeric_limits<double>::min()) // all Y values definitely > zero.
@@ -491,7 +620,7 @@
           y_ticks_.ticks_on_window_or_axis_ = -1; // left true; // because floating off end of X-axis.
           // so need to put the labels on the plot window instead of the X-axis.
         }
- else if (x_axis_.max_ < -std::numeric_limits<double>::min()) // Y all definitely < zero.
+ else if (x_axis_.max_ < -std::numeric_limits<double>::min()) // X all definitely < zero.
         { // Y-axis < 0, so will not intersect X-axis.
           y_axis_position_ = right;
           y_ticks_.ticks_on_window_or_axis_ = +1; // right = true;
@@ -647,7 +776,7 @@
 
         if (plot_top_ >= plot_bottom_)
         {
- std::cout << "plot window top " << plot_top_ << ", bottom " << plot_bottom_ << std::endl;
+ std::cout << "plot window top " << plot_top_ << ", bottom " << plot_bottom_ << std::endl;
           throw std::runtime_error("Plot window top >= bottom!");
         }
 
@@ -700,7 +829,7 @@
             if (x_axis_position_ == x_intersects_y)
             {
               if (x_ticks_.down_ticks_on_)
- {
+ {
                 if (x_ticks_.ticks_on_window_or_axis_ < 0) // at bottom
                 { // Extend the vertical line down in lieu of longest tick.
                   ybottom += (std::max)(x_ticks_.minor_tick_length_, x_ticks_.major_tick_length_);// Avoid macro max trap!
@@ -712,7 +841,7 @@
                 { // Extend the vertical line up in lieu of longest tick.
                   ytop += (std::max)(x_ticks_.minor_tick_length_, x_ticks_.major_tick_length_);// Avoid macro max trap!
                 }
- }
+ }
             }
             image.g(detail::PLOT_Y_AXIS).line(x, ytop, x, ybottom);
             // <g id="yAxis" stroke="rgb(0,0,0)"><line x1="70.5" y1="53" x2="70.5" y2="357"/>
@@ -759,7 +888,7 @@
           { // Draw minor tick.
             // This will output 'orphaned' minor ticks that are beyond the plot window,
             // if the last major tick does not coincide with the plot window.
- // These are just ignored in draw_x_minor_ticks.
+ // These are just ignored in draw_x_minor_tick.
             // There might be 9 of them,
             // if you have the common 9 minor tick between major ticks!
             // TODO this seems ugly - as does the negative ones below.
@@ -806,18 +935,18 @@
 
       void draw_y_label()
       { // Draw vertical y_axis_ label, and optional y units.
- // Y-label color is set in constructor thus:
+ // Y-label color is set in constructor thus:
         // image.g(detail::PLOT_Y_LABEL).style().stroke_color(black);
         // and changed using y_label_color(color);
 
         std::string label = y_label_info_.text();
         if (y_axis_.label_units_on_ && (y_units_info_.text() != ""))
- { // Append the units, user must provide any ()s, if wanted.
+ { // Append the units, user must provide any enclosing ()s, if wanted.
           label += y_units_info_.text() ;
         }
 
         double x = plot_left_;
- x -= y_axis_label_style_.font_size() * 0.7; // Shift left to suit.
+ x -= y_axis_label_style_.font_size() * 0.7; // Shift left to suit.
         if (y_ticks_.ticks_on_window_or_axis_ < 0)
         { // Ticks & value labels to left of Y-axis.
           if (y_ticks_.down_ticks_on_)
@@ -854,7 +983,7 @@
         if(y_ticks_.major_grid_on_ == true)
         { // Draw horizontal major Y grid line.
           if(!plot_window_on_ == true)
- {
+ {
             if(y_ticks_.major_value_labels_side_ < 0) // left
             { // Start further right to give space for y axis value label.
               y -= y_value_label_style_.font_size() * text_margin_;
@@ -1132,14 +1261,14 @@
 
         g_element& g_ptr = image.g(detail::PLOT_DATA_LINES).g();
         g_ptr.clip_id(plot_window_clip_);
- g_ptr.style().stroke_color(series.line_style_.color_);
- //g_ptr.style().fill_color(series.line_style_.area_fill_); // Now set in path below
+ g_ptr.style().stroke_color(series.line_style_.stroke_color_);
+ g_ptr.style().fill_color(series.line_style_.area_fill_); // Now set in path below too.
         g_ptr.style().stroke_width(series.line_style_.width_);
         path_element& path = g_ptr.path();
         path.style().fill_color(series.line_style_.area_fill_);
 
- bool is_fill = !series.line_style_.area_fill_.blank;
- path.fill = is_fill; // Ensure includes a fill="none" if no fill.
+ bool is_fill = !series.line_style_.area_fill_.is_blank;
+ path.style().fill_on(is_fill); // Ensure includes a fill="none" if no fill.
 
         if(series.series.size() > 1)
         { // Need at least two points for a line ;-)
@@ -1194,7 +1323,7 @@
       {
         g_element& g_ptr = image.g(detail::PLOT_DATA_LINES).g();
         g_ptr.clip_id(plot_window_clip_);
- g_ptr.style().stroke_color(series.line_style_.color_);
+ g_ptr.style().stroke_color(series.line_style_.stroke_color_);
         path_element& path = g_ptr.path();
 
         std::pair<double, double> n; // current point.
@@ -1203,13 +1332,13 @@
         std::pair<double, double> fwd_vtr;
         std::pair<double, double> back_vtr;
 
- bool is_fill = !series.line_style_.area_fill_.blank;
+ bool is_fill = !series.line_style_.area_fill_.is_blank;
         if(is_fill == false)
         {
- path.fill = false; // default path constructor is true - TODO why??
+ path.style().fill_on(false); // default path constructor is false
         }
         else
- { // fill
+ { // !is_blank so do want area fill.
           path.style().fill_color(series.line_style_.area_fill_);
         }
 
@@ -1267,64 +1396,25 @@
       } // draw_bezier_lines
 
       void draw_plot_lines()
- { // Draw line through data series, Bezier curved or straight.
+ { // Draw line through data series, Bezier curved or straight, or none.
         for(unsigned int i = 0; i < series.size(); ++i)
         {
           if(series[i].line_style_.bezier_on_)
           { // curved.
             draw_bezier_lines(series[i]);
           }
- else
+ else if(series[i].line_style_.line_on_)
           {
             draw_straight_lines(series[i]);
           }
+ else
+ { // No line joining points.
+ }
         }
       } // draw_plot_lines
 
- void update_image()
- {
- clear_all();
- // svg paint rules are that later 'painting' writes over previous
- // painting, so the order of drawing is important.
-
- // Draw image background (perhaps with border and/or fill color).
- image.g(detail::PLOT_BACKGROUND).push_back(
- new rect_element(0, 0, image.x_size(), image.y_size()));
-
- calculate_plot_window();
- draw_title(); // Moved to ensure plot_X and Y are valid.
-
- // Define the clip path for the plot window.
- // We don't want to allow overlap of the plot window border lines,
- // thus the minor adjustments.
- // TODO should this be border thickness?
-
- image.clip_path(rect_element(plot_left_ + 1, plot_top_ + 1,
- plot_right_ - plot_left_ - 2, plot_bottom_ - plot_top_ - 2),
- plot_window_clip_);
- // <clipPath id="plot_window"><rect x="35" y="38" width="309" height="322"/></clipPath>
-
- image.g(detail::PLOT_DATA_POINTS).clip_id(plot_window_clip_);
-
- // Draw axes, labels & legend, as required.
- draw_x_axis(); // Must do X-axis first.
- draw_y_axis(); // TODO is draw_axes used?
- if(legend_on_)
- {
- draw_legend();
- }
- if(x_axis_.label_on_)
- {
- draw_x_label();
- }
- if(y_axis_.label_on_)
- {
- draw_y_label();
- }
-
- draw_plot_lines(); // Draw lines between points.
-
- // Draw normal 'good' non-limit points.
+ void draw_plot_points()
+ { // Draw normal 'good' non-limit points.
         double x(0.);
         double y(0.);
         for(unsigned int i = 0; i < series.size(); ++i)
@@ -1339,11 +1429,31 @@
             j != series[i].series.end(); ++j)
           {
             x = j->first;
+ double vx = x;
             y = j->second;
+ double vy = y;
             transform_point(x, y);
             if((x > plot_left_) && (x < plot_right_) && (y > plot_top_) && (y < plot_bottom_))
             { // Is inside plot window, so draw a point.
               draw_plot_point(x, y, g_ptr, series[i].point_style_);
+ g_element& g_ptr_vx = image.g(detail::PLOT_X_POINT_VALUES).g();
+ g_element& g_ptr_vy = image.g(detail::PLOT_Y_POINT_VALUES).g();
+
+ if (x_values_on_)
+ { // Show the value of the X data point too.
+ draw_plot_point_value(x, y, g_ptr_vx, x_values_style_, vx);
+ }
+
+ if (y_values_on_)
+ { // show the value of the Y data point too.
+ draw_plot_point_value(x, y, g_ptr_vy, y_values_style_, vy);
+ }
+
+ if (xy_values_on_)
+ { // show the values of the X & Y data as a pair.
+ draw_plot_point_values(x, y, g_ptr_vx, g_ptr_vy, x_values_style_, y_values_style_, vx, vy);
+ }
+
             }
           } // for
         } // for normal points
@@ -1366,7 +1476,204 @@
             }
           }
         } // limits point
- } // void update_image()
+ } // void draw_plot_points()
+
+ void draw_bars()
+ { // Draw normal bar chart for 'good' non-limit points.
+ g_element& g_ptr = image.g(detail::PLOT_DATA_POINTS).g(); // Moved up out of loop.
+ double x(0.);
+ double y(0.); // Cartesian coord y = 0.
+ double x0(0.); // Y-axis line.
+ double y0(.0); // X-axis line.
+ transform_y(y0); // SVG coordinate of horizontal X-axis line.
+ transform_x(x0); // SVG coordinate of vertical Y-axis line.
+ for(unsigned int i = 0; i < series.size(); ++i)
+ {
+ if (series[i].bar_style_.bar_option_ == no_bar)
+ { // No bars wanted for this series.
+ continue;
+ }
+ g_ptr.style().stroke_color(series[i].bar_style_.color_); // stroke color of stick or rectangle block.
+ //.fill_color(series[i].bar_style_.area_fill_); // Only used for rectangle stroke.
+ //.stroke_width(series[i].bar_style_.width_) // Used for width of stick and rectangle block.
+ path_element& path = g_ptr.path();
+ //path.fill(series[i].bar_style_.area_fill_ != blank);
+ path.fill_on(false);
+
+ double h_w = series[i].bar_style_.width_; // For block bar chart.
+ //double h_h = 0.;
+ for(std::multimap<double, double>::const_iterator j = series[i].series.begin();
+ j != series[i].series.end(); ++j)
+ { // All the 'good' data points.
+ x = j->first;
+ y = j->second;
+ transform_point(x, y);
+ if((x > plot_left_) && (x < plot_right_) && (y > plot_top_) && (y < plot_bottom_))
+ { // Is inside plot window, so some bar to draw.
+ switch(series[i].bar_style_.bar_option_)
+ { // -2 block to Y-axis,-1 stick to Y-axis, none, +1 stick to X-axis, -2 block to X-axis.
+ case y_block: // Draw a rectangle centered on the data point horizontally to Y-axis.
+ {
+ g_ptr.style().stroke_width(series[i].line_style_.width_) // line_width used for rectangle line width.
+ .fill_color(series[i].bar_style_.area_fill_);
+ double h_left = x;
+ double h_top = y - h_w / 2; // Start a half-width above the data point center.
+ path.M(h_left, h_top).L(h_left, h_top + h_w).L(x0, h_top + h_w).L(x0, h_top).z();
+ }
+ break;
+ case y_stick:
+ path.style().stroke_width(series[i].bar_style_.width_); // bar_width used for stick line width.
+ path.M(x, y).L(x0, y); // Draw a line from point horizontally to Y-axis.
+ break;
+ case none:
+ break; // Already handled above, so should not get here.
+ case x_stick:
+ path.style().stroke_width(series[i].bar_style_.width_); // bar_width used for stick line width.
+ path.M(x, y).L(x, y0); // Draw a line from point vertically to X-axis.
+ break;
+ case x_block: // Draw a rectangle centered on the data point vertically to X-axis.
+ {
+ g_ptr.style().stroke_width(series[i].line_style_.width_) // line_width used for rectangle line width.
+ .fill_color(series[i].bar_style_.area_fill_);
+ double h_left = x - h_w / 2; // Start a half width left of the data point center.
+ double h_top = y;
+ path.M(h_left, h_top).L(h_left + h_w, h_top).L(h_left + h_w, y0).L(h_left, y0).z();
+ }
+ break;
+ } // switch
+ } // for
+ } // for normal points
+ }
+ // Ignore all the 'bad' at_limit points.
+ } // void draw_bars()
+
+ void draw_histogram()
+ { // Draw a histogram with variable width but contiguous bins.
+ // Histograms differ from bar charts in the the *area* denotes the value,
+ // whereas the bar *height* denotes the value for a bar chart.
+ // bin widths are provided from the X-axis data series values.
+ // The 1st data X-value provides the start of the 1st bin,
+ // the 2nd data X-value provides the end of the 1st bin,
+ // and the 1st Y-value the area of the 1st bin,
+ // and the start of the second bin, and so on, until the
+ // width of last bin is calculated from the last data point in series,
+ // that must have a zero area. ? NaN
+ // Bins can be the same (most common) or different widths.
+ // Intervals must not overlap and bins must be adjacent.
+ // http://en.wikipedia.org/wiki/Histogram
+
+ // Attempts to allow a row or horizontal were abandoned because of complications
+ // with the use of map which orders the x values providing the bins.
+ // Using the y values for the bins implies changing the Y axes labeling and scaling too.
+
+ g_element& g_ptr = image.g(detail::PLOT_DATA_POINTS).g(); // Moved up out of loop.
+ for(unsigned int i = 0; i < series.size(); ++i)
+ { // for each data series.
+ if (series[i].histogram_style_.histogram_option_ == no_histogram)
+ { // No histogram wanted for this series.
+ continue;
+ }
+ // Get the color scheme.
+ g_ptr.style().stroke_color(series[i].line_style_.stroke_color_); // stroke color around bin blocks.
+ g_ptr.style().fill_color(series[i].line_style_.area_fill_);
+ g_ptr.style().stroke_width(series[i].line_style_.width_); // line_width used for stick line width.
+
+ path_element& path = g_ptr.path();
+ path.fill_on(series[i].line_style_.area_fill_ != blank);
+ if (path.fill_on() == true)
+ {
+ path.style().fill_color(series[i].line_style_.area_fill_);
+ }
+ else
+ {
+ path.style().fill_color(blank);
+ }
+
+ std::multimap<double, double>::const_iterator last = series[i].series.end();
+ last--; // Final pair with first the last bin end, and value zero or NaN.
+ if (last->second != 0)
+ {
+ std::cout << "Last bin end " << last->first << " should have zero value! but is " << last->second << std::endl;
+ // Or Throw? or skip this series?
+ }
+ for(std::multimap<double, double>::const_iterator j = series[i].series.begin();
+ j != last; ++j)
+ { // All the 'good' 'real' data points.
+ double x = j->first;
+ double y = j->second;
+ std::multimap<double, double>::const_iterator j_next = j;
+ j_next++;
+ if (j != last)
+ { // Draw a column (perhaps filled) to show bin.
+ double x_next = j_next->first;
+ double w = x_next - x;
+ double h = y / w;
+ // std::cout << x << ' ' << y << ' ' << w << ' ' << h << std::endl;
+ double yy = h;
+ double y0(0.); // X-axis line.
+ transform_y(y0); // SVG y coordinate of horizontal X-axis line.
+ transform_x(x); // SVG x coordinate of start of bin,
+ transform_x(x_next); // SVG x coordinate of end of bin,
+ transform_y(yy); // SVG y coordinate of height of bin.
+ //if((x > plot_left_) && (x < plot_right_) && (y > plot_top_) && (y < plot_bottom_))
+ //{ // Is inside plot window, so some columns to draw. TODO checks?
+ path.M(x, y0).L(x, yy) // Draw a line from point vertically from X-axis.
+ .L(x_next, yy) // & horizonally to next bin end (next x value).
+ .L(x_next, y0) // back to X-axis.
+ .Z(); // So will fill.
+ } // if
+ } // for series
+ } // for normal points.
+ // Ignore all the 'bad' at_limit points.
+ } // void draw_histogram()
+
+ void update_image()
+ {
+ clear_all();
+ // svg paint rules are that later 'painting' writes over
+ // previous painting, so the order of drawing is important.
+
+ // Draw image background (perhaps with border and/or fill color).
+ image.g(detail::PLOT_BACKGROUND).push_back(
+ new rect_element(0, 0, image.x_size(), image.y_size()));
+
+ calculate_plot_window();
+ draw_title(); // Moved to ensure plot_X and Y are valid.
+
+ // Define the clip path for the plot window.
+ // We don't want to allow overlap of the plot window border lines,
+ // thus the minor adjustments.
+ // TODO should this be border thickness?
+ // Actually we DO want to allow a small amount of overlap
+ // so round point can lie on the axis line without being chopped in half or not show at all!!!
+ image.clip_path(rect_element(plot_left_ + 1, plot_top_ + 1,
+ plot_right_ - plot_left_ - 2, plot_bottom_ - plot_top_ - 2),
+ plot_window_clip_);
+ // <clipPath id="plot_window"><rect x="35" y="38" width="309" height="322"/></clipPath>
+
+ image.g(detail::PLOT_DATA_POINTS).clip_id(plot_window_clip_);
+
+ // Draw axes, labels & legend, as required.
+ draw_x_axis(); // Must do X-axis first.
+ draw_y_axis();
+ if(legend_on_)
+ {
+ draw_legend();
+ }
+ if(x_axis_.label_on_)
+ {
+ draw_x_label();
+ }
+ if(y_axis_.label_on_)
+ {
+ draw_y_label();
+ }
+
+ draw_plot_lines(); // Draw lines between points.
+ draw_plot_points();
+ draw_bars();
+ draw_histogram();
+ } // void update_image()
 
 
     public: // Member functions
@@ -1546,7 +1853,7 @@
       }
 
       bool y_label_units_on()
- { // But only return the stroke color.
+ {
         return y_axis_.label_units_on_;
       }
 
@@ -1633,6 +1940,15 @@
 
       svg_2d_plot& y_range(double min_y, double max_y)
       { // Set the range (max and min) for Y values.
+ if (!boost::math::isfinite(min_y))
+ {
+ throw std::runtime_error("Y range: min not finite!");
+ }
+ if (!boost::math::isfinite(max_y))
+ {
+ throw std::runtime_error("Y range: max not finite!");
+ }
+
         if(max_y <= min_y)
         { // max <= min.
           throw std::runtime_error("Y range: y max <= y min!");
@@ -1643,7 +1959,7 @@
         }
         y_axis_.min_ = min_y;
         y_axis_.max_ = max_y;
-
+ y_autoscale_ = false;
        return *this;
       }
 
@@ -1656,15 +1972,121 @@
       }
 
       double y_min()
- {
+ { // Can't check finite ness and relation to y_max.
         return y_axis_.min_;
       }
 
       double y_max()
- {
+ { // Can't check finite ness and relation to y_min.
         return y_axis_.max_;
       }
 
+ bool y_autoscale()
+ {
+ return y_autoscale_;
+ }
+
+ svg_2d_plot& y_autoscale(bool b)
+ {
+ y_autoscale_ = b;
+ return *this;
+ }
+
+ bool y_values_on()
+ {
+ return y_values_on_;
+ }
+
+ svg_2d_plot& y_values_on(bool b)
+ {
+ y_values_on_ = b;
+ return *this;
+ }
+
+ bool xy_values_on()
+ {
+ return xy_values_on_;
+ }
+
+ svg_2d_plot& xy_values_on(bool b)
+ {
+ xy_values_on_ = b;
+ return *this;
+ }
+
+ bool y_plusminus_on()
+ {
+ return y_values_style_.plusminus_on_;
+ }
+
+ svg_2d_plot& y_plusminus_on(bool b)
+ {
+ y_values_style_.plusminus_on_ = b;
+ return *this;
+ }
+
+ bool y_df_on()
+ {
+ return y_values_style_.df_on_;
+ }
+
+ svg_2d_plot& y_df_on(bool b)
+ {
+ y_values_style_.df_on_ = b;
+ return *this;
+ }
+
+ svg_2d_plot& y_autoscale(std::pair<double, double> p)
+ { // Use Y min & max pair values to autoscale.
+ scale_axis(p.first, p.second, // double min and max from pair.
+ &y_auto_min_value_, &y_auto_max_value_, &y_auto_tick_interval_, &y_auto_ticks_,
+ autoscale_check_limits_,
+ y_include_zero_, y_tight_, y_min_ticks_, y_steps_);
+ y_autoscale_ = true; // Default to use calculated values.
+ return *this;
+ } // autoscale(pair<double, double> p)
+
+ template <class T> // T an STL container: array, vector ...
+ svg_2d_plot& y_autoscale(const T& begin, const T& end) // Data series using iterators to
+ { // to use to calculate autoscaled values.
+ scale_axis(begin, end,
+ &y_auto_min_value_, &y_auto_max_value_, &y_auto_tick_interval_, &y_auto_ticks_,
+ autoscale_check_limits_,
+ y_include_zero_, y_tight_, y_min_ticks_, y_steps_);
+ y_autoscale_ = true; // Default to use calculated values.
+ return *this;
+ }
+
+ template <class T> // T an STL container: array, vector ...
+ svg_2d_plot& y_autoscale(const T& container) // Whole data series.
+ { // to use to calculate autoscaled values.
+ scale_axis(container.begin(), container.end(), // All the container.
+ &y_auto_min_value_, &y_auto_max_value_, &y_auto_tick_interval_, &y_auto_ticks_,
+ autoscale_check_limits_,
+ y_include_zero_, y_tight_, y_min_ticks_, y_steps_);
+ y_autoscale_ = true; // Default to use calculated values.
+ return *this;
+ }
+
+ template <class T> // T a 2D STL container: array, vector ...
+ svg_2d_plot& xy_autoscale(const T& container) // Whole data series,
+ { // to use to calculate autoscaled values for *both* X and Y axes.
+ scale_axis(container, // All the container.
+ &x_auto_min_value_, &x_auto_max_value_, &x_auto_tick_interval_, &x_auto_ticks_,
+ &y_auto_min_value_, &y_auto_max_value_, &y_auto_tick_interval_, &y_auto_ticks_,
+ autoscale_check_limits_,
+ x_include_zero_, x_tight_, x_min_ticks_, x_steps_,
+ y_include_zero_, y_tight_, y_min_ticks_, y_steps_);
+ x_autoscale_ = true; // Default to use calculated values.
+ y_autoscale_ = true; // Can be switch off with autoscale(false);
+ return *this;
+ } // xy_autoscale
+
+ bool xy_autoscale()
+ {
+ return y_autoscale_ && x_autoscale_;
+ }
+
       svg_2d_plot& y_major_interval(double inter)
       {
         y_ticks_.major_interval_ = inter;
@@ -1882,6 +2304,8 @@
       // return x_axis_label_style_.font_weight();
       //}
 
+
+
       svg_2d_plot& y_label_font_family(const std::string& family)
       {
         x_axis_label_style_.font_family(family);
@@ -1896,32 +2320,138 @@
 
       // Example of declaration but definition below.
       // TODO Probably better done this way,
- // but wait until parameter system removed.
 
 
- template <class T>
- svg_2d_plot_series& plot(const T& container, const std::string& title)
+ svg_2d_plot& y_values_font_size(unsigned int i)
+ {
+ y_values_style_.values_text_style_.font_size(i);
+ return *this;
+ }
+
+ unsigned int y_values_font_size()
+ {
+ return y_values_style_.values_text_style_.font_size();
+ }
+
+ svg_2d_plot& y_values_font_family(const std::string& family)
+ {
+ y_values_style_.values_text_style_.font_family(family);
+ return *this;
+ }
+
+ const std::string& y_values_font_family()
+ {
+ return y_values_style_.values_text_style_.font_family();
+ }
+
+ svg_2d_plot& y_values_color(const svg_color& col)
+ { // Function could set both fill (middle) and stroke (outside),
+ // but just setting fill if simplest,
+ // but does not allow separate inside & outside colors.
+ image.g(detail::PLOT_Y_POINT_VALUES).style().fill_color(col);
+ //svg_2d_plot().image.g(PLOT_Y_POINT_VALUES).style().stroke_color(col);
+ return *this;
+ }
+
+ svg_color y_values_color()
+ { // Function could get either fill and stroke,
+ // return svg_2d_plot().image.g(PLOT_Y_POINT_VALUES).style().stroke_color();
+ return image.g(detail::PLOT_Y_POINT_VALUES).style().fill_color();
+ }
+
+ svg_2d_plot& y_values_rotation(rotate_style rotate)
+ { // Degrees (0 to 360).
+ y_values_style_.value_label_rotation_ = rotate;
+ return *this;
+ }
+
+ int y_values_rotation()
       {
+ return y_values_style_.value_label_rotation_;
+ }
+
+ svg_2d_plot& y_values_precision(int p)
+ { // set iostream precision
+ y_values_style_.value_precision_ = p;
+ return *this;
+ }
+
+ int y_values_precision()
+ {
+ return y_values_style_.value_precision_;
+ }
+
+ svg_2d_plot& y_values_ioflags(std::ios_base::fmtflags f)
+ { // set iostream format flags
+ y_values_style_.value_ioflags_ = f;
+ return *this;
+ }
+
+ std::ios_base::fmtflags y_values_ioflags()
+ {
+ return y_values_style_.value_ioflags_;
+ }
+
+
+ // Versions of plot functions to add data series, all or part.
+
+ template <class T>
+ svg_2d_plot_series& plot(const T& container, const std::string& title = "")
+ { // Version converting to double.
+ // Note that this version assumes that *ALL* the data value in the container is used.
         series.push_back(
           svg_2d_plot_series(
           boost::make_transform_iterator(container.begin(), detail::boost_default_2d_convert()),
           boost::make_transform_iterator(container.end(), detail::boost_default_2d_convert()),
           title)
         );
- return series[series.size()-1];
+ return series[series.size()-1]; // Number of data series added so far.
       }
 
       template <class T, class U>
- svg_2d_plot_series& plot(const T& container, const std::string& title, U functor)
+ svg_2d_plot_series& plot(const T& container, const std::string& title = "", U functor = boost_default_2d_convert)
       { // Version with functor.
+ // Note that this version assumes that *ALL* the data value in the container is used.
         series.push_back(
           svg_2d_plot_series(
           boost::make_transform_iterator(container.begin(), functor),
           boost::make_transform_iterator(container.end(), functor),
           title)
         );
- return series[series.size()-1];
+ return series[series.size()-1]; // Number of data series added so far.
       }
+
+ template <class T>
+ svg_2d_plot_series& plot(const T& begin, const T& end, const std::string& title = "")
+ { // Add a data series to the plot (by default, converting to doubles).
+ // Note that this version permits a *partial range*,
+ // begin to end, of the container to be used.
+ series.push_back(
+ svg_2d_plot_series(
+ boost::make_transform_iterator(begin, detail::boost_default_convert()),
+ boost::make_transform_iterator(end, detail::boost_default_convert()),
+ title)
+ );
+ // For example: my_2d_plot.plot(my_data.begin(), my_data.end(), "My container");
+ // my_2d_plot.plot(&my_data[1], &my_data[4], "my_data 1 to 4"); // Add part of data series.
+ // Care: last == end which is one past the last, so this only does 1, 2 & 3 - *not* 4!
+ return series[series.size() - 1]; // Number of data series added so far.
+ }
+
+ template <class T, class U>
+ svg_2d_plot_series& plot(const T& begin, const T& end, const std::string& title = "", U functor = boost_default_2d_convert)
+ { // Add a data series to the plot. (Version with custom functor, rather than to double).
+ // Note that this version permits a partial range,
+ // begin to end, of the container to be used.
+ series.push_back(
+ svg_2d_plot_series(
+ boost::make_transform_iterator(container.begin(), functor),
+ boost::make_transform_iterator(container.end(), functor),
+ title)
+ );
+ return series[series.size() - 1]; // Number of data series added so far.
+ }
+
  }; // class svg_2d_plot : public detail::axis_plot_frame<svg_2d_plot>
 
     // sample of declared function, externally defined in another .cpp file.
@@ -1931,7 +2461,6 @@
       return y_axis_label_style_.font_family();
     }
 
-
 #if defined (BOOST_MSVC)
 # pragma warning(pop)
 #endif

Modified: sandbox/SOC/2007/visualization/boost/svg_plot/svg_boxplot.hpp
==============================================================================
--- sandbox/SOC/2007/visualization/boost/svg_plot/svg_boxplot.hpp (original)
+++ sandbox/SOC/2007/visualization/boost/svg_plot/svg_boxplot.hpp 2008-05-24 12:22:38 EDT (Sat, 24 May 2008)
@@ -1,38 +1,43 @@
 // svg_boxplot.hpp
 
 // Copyright Jacob Voytko 2007
+// Copyright Paul A. Bristow 2008
 
 // Use, modification and distribution are subject to the
 // Boost Software License, Version 1.0.
 // (See accompanying file LICENSE_1_0.txt
 // or copy at http://www.boost.org/LICENSE_1_0.txt)
 
+// Producing Boxplots as Scalable Vector Graphics.
+
+// A convenient way of graphically depicting groups of numerical data through their five-number summaries.
+// Show 1st quartile, median and 3rd quartile as a box.
+// http://en.wikipedia.org/wiki/Boxplot
+
+// Some Implementations of the Boxplot
+// Michael Frigge, David C. Hoaglin and Boris Iglewicz
+// The American Statistician, Vol. 43, No. 1 (Feb., 1989), pp. 50-54
+
+// The Bagplot: A Bivariate Boxplot
+// Peter J. Rousseeuw, Ida Ruts and John W. Tukey
+// The American Statistician, Vol. 53, No. 4 (Nov., 1999), pp. 382-387
+
 #ifndef BOOST_SVG_SVG_BOXPLOT_HPP
 #define BOOST_SVG_SVG_BOXPLOT_HPP
 
-#define BOOST_PARAMETER_MAX_ARITY 12
-
 #include <boost/iterator/transform_iterator.hpp>
 
 #if defined (BOOST_MSVC)
 # pragma warning(push)
 # pragma warning(disable: 4512) // "assignment operator could not be generated."
 # pragma warning(disable: 4127) // "conditional expression is constant."
-# pragma warning(disable: 4100) // "'boost_parameter_enabler_argument' : unreferenced formal parameter"
 #endif
 
-#include <boost/parameter/preprocessor.hpp>
-#include <boost/parameter/name.hpp>
-
+#include "svg.hpp"
 #include "svg_style.hpp"
 #include "detail/numeric_limits_handling.hpp"
 #include "detail/svg_boxplot_detail.hpp"
 #include "detail/functors.hpp"
-#include "svg.hpp"
-
-#if defined (BOOST_MSVC)
-# pragma warning(pop)
-#endif
 
 #include <vector>
 #include <string>
@@ -45,609 +50,1316 @@
 {
 namespace svg
 {
+ static const double sin45 = 0.707; // Use if axis value labels are sloping.
 
-//BOOST_PARAMETER_NAME((box_style, keyword) box);
-//BOOST_PARAMETER_NAME((median_style, keyword) median);
-//BOOST_PARAMETER_NAME((axis_style, keyword) axis);
-//BOOST_PARAMETER_NAME((min_whisker_style, keyword) min_whisker);
-//BOOST_PARAMETER_NAME((max_whisker_style, keyword) max_whisker);
-//BOOST_PARAMETER_NAME((mild_outlier_style, keyword) mild_outlier);
-//BOOST_PARAMETER_NAME((ext_outlier_style, keyword) ext_outlier);
-//BOOST_PARAMETER_NAME((whisker_length, keyword) whisker);
-//BOOST_PARAMETER_NAME((box_width, keyword) width);
-//BOOST_PARAMETER_NAME((functor, keyword) fnctr);
-//BOOST_PARAMETER_NAME((ctr, keyword) container);
-//BOOST_PARAMETER_NAME((name, keyword) title);
-//
-struct svg_boxplot_series
-{
- double whisker_min;
- double whisker_max;
- double q1; // 1st lower quartile.
- double q3;// 3rd upper quartile. TODO 4th?
- double median;
- std::vector<double> outliers;
- std::vector<double> extreme_outliers;
- std::string title;
-
- unsigned int whisker_length;
- unsigned int box_width;
-
- svg_style box_style;
- svg_style median_style;
- svg_style axis_style;
- svg_style min_whisker_style;
- svg_style max_whisker_style;
-
- plot_point_style mild_outlier;
- plot_point_style ext_outlier;
-
- template <class T>
- svg_boxplot_series(T begin, T end, const std::string& title,
- unsigned int whisk_length,
- unsigned int box_width,
- svg_style box_style,
- svg_style median_style,
- svg_style axis_style,
- svg_style min_whisker,
- svg_style max_whisker,
- plot_point_style mild_outlier,
- plot_point_style ext_outlier) :
- title(title),
- whisker_length(whisk_length),
- box_width(box_width), box_style(box_style),
- median_style(median_style), axis_style(axis_style),
- min_whisker_style(min_whisker),
- max_whisker_style(max_whisker),
- mild_outlier(mild_outlier), ext_outlier(ext_outlier)
- {
- //std::vector used for fast lookups of quartile values.
- std::vector<double> data(begin, end);
-
- if(data.empty())
+ // x_axis_position_ and y_axis_position_ use these.
+ enum x_axis_intersect {bottom = -1, x_intersects_y = 0, top = +1};
+ // bottom = X-axis free below bottom of end of Y-axis (case of all Y definitely < 0).
+ // top = X-axis free above top of X-axis (case of all Y definitely > 0).
+ // x_intersects_y when Y values include zero, so X intersects the Y axis.
+
+ enum y_axis_intersect {left = -1, y_intersects_x = 0, right = +1};
+ // left = Y-axis free to left of end of X-axis (case of all X definitely < 0).
+ // right = Y-axis free to left of end of X-axis (case of all X definitely > 0).
+ // y_intersects_x when X values include zero, so intersects the X axis.
+
+ enum legend_places
+ { // Placing of legend box, if requested by legend_on(true).
+ nowhere = 0,
+ inside = -1, // Default place for inside is top left of plot window,
+ // exact location controlled by legend_top_left().
+ outside_left = 1, // Outside
+ outside_right = +2, // Default for outside.
+ outside_top = +3,
+ outside_bottom = +4,
+ somewhere = +5 // legend_top_left(x, y)
+ };
+
+ class svg_boxplot; // Box Plot.
+ class svg_boxplot_series; // Box Plot data series.
+
+ // By convention, class data is suffixed by _ to permit function names to be used.
+ // For example, string title_, but set with title("my title") and get with title().
+
+ class svg_boxplot_series
+ { // Class holding information about a data series to be boxplotted.
+ public: // TODO private?
+ double whisker_min_;
+ double whisker_max_;
+ double q1_; // 1st lower quartile.
+ double q3_; // 3rd upper quartile.
+ double median_;
+ std::vector<double> outliers_;
+ std::vector<double> extreme_outliers_;
+
+ //std::string title_;
+ text_style series_style_;
+ text_element series_info_;
+
+ double text_margin_;
+ unsigned int whisker_length_;
+ unsigned int box_width_;
+
+ //svg_style(stroke, fill, width)
+ svg_style box_style_;
+ svg_style median_style_;
+ svg_style axis_style_;
+ svg_style min_whisker_style_;
+ svg_style max_whisker_style_;
+
+ plot_point_style mild_outlier_;
+ plot_point_style ext_outlier_;
+
+ template <class T> // T is data container type. ? default = std::vector<double>
+ svg_boxplot_series( // Constructor.
+ T begin, T end, // Data container.
+ const std::string& title) // Data series title.
+ // All other parameters can now be added using chainable functions.
+ : //
+ series_style_(12, "Verdana", "", "", "", ""),
+ //series_style_(x_axis_label_style_), // want to use the x_label style here
+ series_info_(0, 0, title, series_style_, center_align, horizontal),
+ text_margin_(2.), // for axis label text, as a multiplier of the font size.
+ whisker_length_(30),
+ box_width_(40),
+ box_style_(green, azure, 1),
+ median_style_(blue, blank, 1),
+ axis_style_(black, blank, 1),
+ min_whisker_style_(magenta, black, 1),
+ max_whisker_style_(cyan, black, 1),
+ mild_outlier_(brown, blank, 5, round),
+ ext_outlier_(red, blank, 5, cone)
+ { // Constructor.
+ //std::vector copy is sorted and used for fast lookup of quartile values.
+ std::vector<double> series(begin, end);
+ if(series.empty())
+ {
+ throw std::runtime_error("Empty data series!");
+ }
+ std::sort(series.begin(), series.end());
+ size_t data_size = series.size();
+
+ // Pth quartile in n values is P(n+1)/100,
+ // rounded to the nearest integer.
+ q1_ = series[(unsigned int)((data_size + 1) / 4. + 1)];
+ median_ = series[(unsigned int)((data_size + 1) / 2. + 1)];
+ q3_ = series[(unsigned int)(3*(data_size + 1) / 4. + 1)];
+
+ double iqr(q3_ - q1_);
+ double min_ext_cutoff = q1_ - 3. * iqr;
+ double min_cutoff = q1_ - text_margin_ * iqr;
+ double max_ext_cutoff = q3_ + 3. * iqr;
+ double max_cutoff = q3_ + text_margin_ * iqr;
+
+ std::vector<double>::const_iterator i;
+ for(i = series.begin(); *i < min_cutoff; ++i)
+ { // Separate any minimum outliers and extreme outliers.
+ if(*i < min_ext_cutoff)
         {
- throw std::runtime_error("Empty dataset!");
+ extreme_outliers_.push_back(*i);
         }
-
- std::sort(data.begin(), data.end());
-
- size_t data_size = data.size();
-
- // Pth quartile in n numbers is P(n+1)/100, rounded
- // to the nearest integer.
- q1 = data[(unsigned int)((data_size + 1) / 4. + 1)];
- median = data[(unsigned int)((data_size + 1) / 2. + 1)];
- q3 = data[(unsigned int)(3*(data_size + 1) / 4. + 1)];
-
- double iqr(q3-q1);
-
- double min_ext_cutoff = q1 - 3. * iqr;
- double min_cutoff = q1 - text_margin_ * iqr;
- double max_ext_cutoff = q3 + 3. * iqr;
- double max_cutoff = q3 + text_margin_ * iqr;
-
- std::vector<double>::const_iterator i;
-
- for(i = data.begin(); *i < min_cutoff; ++i)
+ else if(*i < min_cutoff)
         {
- if(*i < min_ext_cutoff)
- {
- extreme_outliers.push_back(*i);
- }
- else if(*i < min_cutoff)
- {
- outliers.push_back(*i);
- }
+ outliers_.push_back(*i);
         }
+ } // for
 
- whisker_min = *i;
- std::vector<double>::const_reverse_iterator j;
- for(j = data.rbegin(); *j > max_cutoff; ++j)
+ whisker_min_ = *i;
+ std::vector<double>::const_reverse_iterator j;
+ for(j = series.rbegin(); *j > max_cutoff; ++j)
+ { // Separate any maximum outliers and extreme outliers.
+ if(*j > max_ext_cutoff)
+ {
+ extreme_outliers_.push_back(*j);
+ }
+ else if(*j > max_cutoff)
         {
- if(*j > max_ext_cutoff)
- {
- extreme_outliers.push_back(*j);
- }
- else if(*j > max_cutoff)
- {
- outliers.push_back(*j);
- }
- }
- whisker_max = *j;
- } //
-}; // struct svg_boxplot_series
+ outliers_.push_back(*j);
+ }
+ } // for
+ whisker_max_ = *j;
+ } // svg_boxplot_series constructor.
+
+ // Set functions for the boxplot series.
+ svg_boxplot_series& whisker_length(int l);
+ int whisker_length();
+ svg_boxplot_series& box_width(int l);
+ int box_width();
+ svg_boxplot_series& title(const std::string& t);
+ const std::string title();
+
+ // Still need chainable functions to set and get these.
+ //BOOST_PARAMETER_NAME((box_style, keyword) box);
+ //BOOST_PARAMETER_NAME((median_style, keyword) median);
+ //BOOST_PARAMETER_NAME((axis_style, keyword) axis);
+ //BOOST_PARAMETER_NAME((min_whisker_style, keyword) min_whisker);
+ //BOOST_PARAMETER_NAME((max_whisker_style, keyword) max_whisker);
+ //BOOST_PARAMETER_NAME((mild_outlier_style, keyword) mild_outlier);
+ //BOOST_PARAMETER_NAME((ext_outlier_style, keyword) ext_outlier);
+ //BOOST_PARAMETER_NAME((functor, keyword) fnctr); ??
+
+ }; // struct svg_boxplot_series
+
+ // svg_boxplot_series Definitions to go into separate file.
+
+ const std::string svg_boxplot_series::title()
+ { // Title of data series.
+ return series_info_.text();
+ }
+
+ svg_boxplot_series& svg_boxplot_series::title(const std::string& t)
+ { // Title of data series.
+ series_info_.text(t);
+ return *this; // Chainable.
+ }
+
+ int svg_boxplot_series::whisker_length()
+ {
+ return whisker_length_;
+ }
+
+ svg_boxplot_series& svg_boxplot_series::whisker_length(int l)
+ {
+ whisker_length_ = l;
+ return *this; // Chainable.
+ }
+ int svg_boxplot_series::box_width()
+ {
+ return box_width_;
+ }
+
+ svg_boxplot_series& svg_boxplot_series::box_width(int l)
+ {
+ box_width_ = l;
+ return *this; // Chainable.
+ }
+
+// End of svg_boxplot_series Definitions.
 
 class svg_boxplot
 {
 private:
- // Stored so as to avoid rewriting style information constantly.
- svg image;
-
- double y_scale_;
- double y_shift_;
-
- text_element title_info_;
- text_element x_label_info_;
- text_element y_label_info_;
+ svg image; // Stored so as to avoid rewriting style information constantly.
+ //image.image_size(500, 500);
 
- // Border information for the plot window.
- int plot_left_;
- int plot_right_;
- int plot_top_;
- int plot_bottom_;
+ double x_scale_; // Use by function transform()
+ double x_shift_; // to go from Cartesian to svg coordinates.
+ double y_scale_;
+ double y_shift_;
+
+ double x_axis_position_;
+ double y_axis_position_;
+
+ // Plot window.
+ double plot_left_; // calculate_plot_window() sets these values.
+ double plot_top_;
+ double plot_right_;
+ double plot_bottom_;
+
+ // Order of these declarations (not definitions) seems to matter - styles 1st.
+ text_style a_style_; // Defaults.
+ text_style title_style_;
+ text_style x_axis_label_style_;
+ text_style x_value_label_style_;
+ text_style y_axis_label_style_;
+ text_style y_value_label_style_;
+ text_style point_symbols_style_; // Used for data point marking.
+ text_style value_style_; // Used for data point value label.
+
+ axis_line_style x_axis_;
+ axis_line_style y_axis_;
+
+ // text_style contains font size & type etc.
+ value_style x_values_style_; // Data point X value marking.
+ value_style y_values_style_; // Data point Y value marking.
+
+ text_element title_info_;
+ text_element x_label_info_;
+ text_element y_label_info_;
+ text_element x_units_info_; // For example: "mm"
+ text_element y_units_info_; // 2-D only.
+
+ // Should use axis_style?
+ // x_axis_(X, -10., +10., black, 1, 0, true, false, true),
+ box_style image_border_; // rectangular border of all image width, color...
+ box_style plot_window_border_; // rectangular border of plot window width, color...
+
+ ticks_labels_style x_ticks_;
+ ticks_labels_style y_ticks_; // Added to permit shared code!
+
+ bool autoscale_check_limits_; // Whether to check autoscale values for infinity, NaN, max, min.
+ bool x_autoscale_; // Whether to use any X-axis autoscale values.
+ bool x_include_zero_; // If autoscaled, include zero.
+ int x_min_ticks_; // If autoscaled, set a minimum number of X ticks.
+ double x_tight_;
+ int x_steps_; // If autoscaled, set any prescaling to decimal 1, 2, 5, 10 etc.
+
+ // Values calculated by scale_axis, and is used only if x_autoscale == true.
+ double x_auto_min_value_;
+ double x_auto_max_value_;
+ double x_auto_tick_interval_; // tick major interval.
+ int x_auto_ticks_; // Number of ticks.
+
+ bool y_autoscale_; // Whether to use any y_axis autoscale values.
+ bool y_include_zero_; // If autoscaled, include zero.
+ int y_min_ticks_; // If autoscaled, set a minimum number of Y ticks.
+ double y_tight_;
+ int y_steps_; // If autoscaled, set any prescaling to decimal 1, 2, 5, 10 etc.
+
+ // Values calculated by scale_axis, and is used only if y_autoscale == true.
+ double y_auto_min_value_;
+ double y_auto_max_value_;
+ double y_auto_tick_interval_; // tick major interval.
+ int y_auto_ticks_; // Number of ticks.
+
+ double text_margin_; // Marginal space around text items like title,
+ // text_margin_ * font_size to get distance in svg units.
+ bool title_on_;
 
- // Yes/no questions.
- bool use_x_label; // // Show X-axis label text.
- bool use_x_major_labels;
- bool use_y_major_labels;
- bool use_y_label;
- bool title_on_;
+ std::string plot_window_clip_;
+ bool plot_window_on_;
 
- // Where we will be storing the data points for transformation.
- std::vector<svg_boxplot_series> series;
- std::string plot_window_clip_;
-
- // Axes information.
- double y_min_;
- double y_max_;
- double y_major_interval_;
- double y_axis_;
- double x_axis_;
- unsigned int x_major_tick_length_;
- unsigned int x_major_tick_width_; // pixels.
-
- unsigned int y_major_tick_length_;
- unsigned int y_major_tick_width_;
- unsigned int y_minor_tick_length_;
- unsigned int y_minor_tick_width_;
- unsigned int y_num_minor_ticks_;
-
- void clear_all()
- {
- // TODO, fill
- }
-
- void transform_y(double& y)
- {
- y = y * y_scale_ + y_shift_;
- }
+ // For storing the data points for sorting.
+ std::vector<svg_boxplot_series> series;
 
- void draw_y_minor_tick(double j, path_element& tick_path)
- {
- double y1(j), x1(plot_left_);
- double y_tick_length = y_minor_tick_length_ / 2.;
- double x2(plot_left_ - y_tick_length / 2.);
-
- transform_y(y1);
+public:
+ // text_element(double x = 0., double y = 0., const std::string text = "", text_style ts = no_style, align_style align = left_align, rotate_style rotate = horizontal)
 
- tick_path.M(x1, y1).L(x2, y1);
+ svg_boxplot()
+ :
+ title_style_(18, "Verdana", "", "", "", ""),
+ title_info_(0, 0, "", title_style_, center_align, horizontal),
+ title_on_(true),
+ x_label_info_(0, 0, "", x_axis_label_style_ ,center_align, horizontal),
+ x_units_info_(0, 0, "", x_value_label_style_, center_align, horizontal),
+ y_label_info_(0, 0, "", y_axis_label_style_, center_align, upward),
+ y_units_info_(0, 0, "", y_axis_label_style_, center_align, upward),
+ x_axis_(X, -10., +10., black, 1, 0, true, false, true),
+ y_axis_(Y, 0., +1., black, 1, 0, true, false, true),
+ x_axis_label_style_(14, "Verdana", "", ""),
+ x_value_label_style_(12, "Verdana", "", ""),
+ y_axis_label_style_(14, "Verdana", "", ""),
+ y_value_label_style_(12, "Verdana", "", ""),
+ point_symbols_style_(12, "Lucida Sans Unicode"), // Used for data point marking.
+ x_values_style_(horizontal, 3, std::ios::dec, true, value_style_, black, black, false, false),
+ y_values_style_(horizontal, 3, std::ios::dec, true, value_style_, black, black, false, false),
+
+ x_ticks_(X, x_value_label_style_),// so for other defaults see ticks_labels_style.
+ y_ticks_(Y, y_value_label_style_),
+
+ autoscale_check_limits_(true), // Do check all value for limits, infinity, max, min, NaN.
+ x_autoscale_(false),
+ x_include_zero_(false), // If autoscaled, include zero.
+ x_min_ticks_(6), // If autoscaled, set a minimum number of ticks, default 6.
+ x_steps_(0), // If autoscaled, set any prescaling to decimal 1, 2, 5, 10 etc, default none.
+ x_tight_(1e-6), // margin that point can lie outside top and bottom tick.
+ y_autoscale_(false), // Not used for 1-D.
+
+ text_margin_(2.), // for axis label text, as a multiplier of the font size.
+ image_border_(yellow, white, 2, 10, true, true), // margin should be about axis label font size.
+ plot_window_border_(yellow, svg_color(255, 255, 255), 1, 3, true, false),
+
+ plot_window_clip_("plot_window"),
+ plot_window_on_(true),
+ plot_left_(0.), plot_top_(0.), plot_right_(0.), plot_bottom_(0.),
+
+ // Used to transform Cartesian to SVG.
+ x_scale_(1.), x_shift_(0.),
+ y_scale_(1.), y_shift_(0.),
+ x_axis_position_(x_intersects_y),
+ y_axis_position_(0)
+ {
+ using std::cout;
+ using std::endl;
+ std::cout << image.x_size() << std::endl;
+ image_size(500, 350);
+
+ // Build the document tree. add children of the root node.
+ for(int i = 0; i < boxplot::BOXPLOT_DOC_CHILDREN; ++i)
+ {
+ image.g();
+ }
+ //set_ids();
+ // void set_ids() // This only used once in constructor and IS now inlined.
+ // document ids for use in <g id = "PLOT_TITLE".../>
+ for(int i = 0; i < boxplot::BOXPLOT_DOC_CHILDREN; ++i)
+ { // Order determines the painting order.
+ image.g(i).id(boxplot::document_ids[i]);
     }
 
-
- void draw_x_major_ticks(double i, path_element& tick_path,
- const std::string& str)
- {
- double x1(i), y1(0.), y2(image.x_size());
-
- // Draw major tick.
- x1=i;
-
- y1 = plot_bottom_;
- y2 = plot_bottom_ + x_major_tick_length_;
-
- tick_path.M(x1, y1).L(x1, y2);
-
- if(use_x_major_labels)
- {
- y1 += x_major_tick_length_;
- image.get_g_element(boxplot::PLOT_LABELS).text(x1, y1 + 12, str);
+ // Set boxplot color defaults.
+ image.g(boxplot::PLOT_BACKGROUND).style().fill_color(image_border_.fill_);
+ image.g(boxplot::PLOT_BACKGROUND).style().stroke_color(image_border_.stroke_);
+ image.g(boxplot::PLOT_BACKGROUND).style().stroke_width(image_border_.width_); //
+
+ image.g(boxplot::PLOT_WINDOW_BACKGROUND).style().fill_color(plot_window_border_.fill_);
+ image.g(boxplot::PLOT_WINDOW_BACKGROUND).style().stroke_width(plot_window_border_.width_).stroke_color(plot_window_border_.stroke_);
+ image.g(boxplot::X_AXIS).style().stroke_color(black).stroke_width(x_axis_.width());
+ image.g(boxplot::Y_AXIS).style().stroke_color(black).stroke_width(y_axis_.width());
+
+ // Ticks & grids.
+ if(x_ticks_.use_up_ticks() || x_ticks_.use_down_ticks())
+ {
+ image.g(boxplot::X_TICKS).style().stroke_width(x_ticks_.minor_tick_width_).stroke_color(black);
+ }
+ if(y_ticks_.left_ticks_on_ || y_ticks_.right_ticks_on_)
+ {
+ image.g(boxplot::Y_MAJOR_TICKS).style().stroke_width(y_ticks_.major_tick_width_).stroke_color(black);
+ image.g(boxplot::Y_MINOR_TICKS).style().stroke_width(y_ticks_.minor_tick_width_).stroke_color(black);
+ image.g(boxplot::Y_MAJOR_GRID).style().stroke_width(y_ticks_.major_grid_width_).stroke_color(svg_color(200, 220, 255));
+ image.g(boxplot::Y_MINOR_GRID).style().stroke_width(y_ticks_.minor_grid_width_).stroke_color(svg_color(200, 220, 255));
+ }
+
+ image.g(boxplot::X_LABEL).style().fill_color(black); // for text only specify fill_color
+ image.g(boxplot::Y_LABEL).style().fill_color(black);
+ image.g(boxplot::VALUE_LABELS).style().fill_color(black);
+ image.g(boxplot::PLOT_TITLE).style().fill_color(black);
+
+ image.g(boxplot::WHISKER).style().stroke_color(black);
+ image.g(boxplot::BOX_AXIS).style().stroke_color(black);
+ image.g(boxplot::BOX).style().stroke_color(black).fill_color(ghostwhite);
+ image.g(boxplot::MEDIAN).style().stroke_color(red).stroke_width(2);
+ image.g(boxplot::EXTREME_OUTLIERS).style().stroke_color(purple).fill_color(white);
+ image.g(boxplot::MILD_OUTLIERS).style().stroke_color(pink).fill_color(black);
+ } // boxplot constructor.
+
+ // Clear Functions.
+ // When writing to multiple documents, the contents of the plot
+ // may change significantly between. Rather than figuring out what
+ // has and has not changed, just erase the contents of the
+ // legend, title... in the document and start over.
+ void clear_all()
+ { // TODO?
+ //clear_legend();
+ //clear_background();
+ //clear_x_axis();
+ //clear_y_axis();
+ //clear_title();
+ //clear_points();
+ //clear_plot_background();
+ //clear_grids();
+ }
+
+ void transform_x(double& x)
+ {
+ x = x * x_scale_ + x_shift_;
+ }
+
+ void transform_y(double& y)
+ {
+ y = y * y_scale_ + y_shift_;
+ }
+ void calculate_plot_window()
+ {
+ // Start by assuming we can use all the svg image,
+ // but reduce by the width of any image border.
+ plot_left_ = 0. + image_border_.width_; // Top left of image.
+ plot_top_ = 0. + image_border_.width_;
+ plot_right_ = image.x_size() - image_border_.width_; // Bottom right of image.
+ plot_bottom_ = image.y_size() - image_border_.width_;
+
+ // Assume that axis labels are always at bottom and left.
+ if(title_on_)
+ { // Allow space for plot title.
+ plot_top_ += title_info_.style().font_size() * (text_margin_ + 0.5);
+ }
+ if(x_axis_.label_on_)
+ { // Move bottom up to leave space at bottom for X axis label.
+ plot_bottom_ -= x_axis_label_style_.font_size() * (text_margin_ + 0.5);
+ }
+
+ if (x_ticks_.down_ticks_on_)
+ {
+ plot_bottom_ -= x_ticks_.major_tick_length_;
+ }
+
+ if(y_axis_.label_on_)
+ { // Leave space at left for Y axis label.
+ plot_left_ += y_axis_label_style_.font_size() * text_margin_;
+ }
+ if(plot_window_on_)
+ { // Needed to allow any plot window border rectangle to show OK.
+ // A small margin is to prevent it overlapping the image border.
+ // Also allows for axis value labels that mark the min and max
+ // that must extend half a font width beyond the plot window border.
+ plot_left_ += image_border_.margin_;
+ plot_right_ -= image_border_.margin_;
+ plot_top_ += image_border_.margin_;
+ plot_bottom_ -= image_border_.margin_;
+ }
+ if (y_autoscale_) // TODO elsewhere - implement Y autoscale (no x autoscale of course).
+ { // Use calculated autoscale values.
+ // Autoscale has been done in my_data.y_autoscale(my_data);
+ // and saved in y_auto_min_value_, y_auto_max_value_, & y_auto_tick_interval_,
+ // so copy these values to use them:
+ y_axis_.min_ = y_auto_min_value_;
+ y_axis_.max_ = y_auto_max_value_;
+ y_ticks_.major_interval_ = y_auto_tick_interval_;
+ // else ignore auto values, even if have been calculated.
+ }
+
+ // Check if the axes will intersect.
+ // X axis position is determined by the range of Y min and max label values.
+ x_axis_position_ = x_intersects_y; // Assume X-axis will intersect Y-axis (range of Y values includes zero).
+ if (y_axis_.min_ > std::numeric_limits<double>::min()) // all Y values definitely > zero.
+ { // y_min_ > 0, so X-axis will not intersect Y-axis, so use bottom plot window.
+ x_axis_position_ = bottom; // X-axis to bottom of plot window.
+ x_ticks_.ticks_on_window_or_axis_ = -1; // bottom = true;
+ }
+ else if(y_axis_.max_ < -std::numeric_limits<double>::min()) // all Y values definitely < zero.
+ { // // y_max_ < 0, so X-axis will not intersect Y-axis, so use top plot window.
+ x_axis_position_ = top; // X-axis to top of plot window.
+ x_ticks_.ticks_on_window_or_axis_ = +1; // top = true;
+ }
+ // Y axis position is NOT determined by the range of X values.
+ y_axis_position_ = y_intersects_x; // Assume Y-axis will intersect X-axis (X range includes zero).
+ //x_axis_position_ = x_intersects_y; // Assume X-axis will intersect Y-axis (X range includes zero).
+ x_axis_position_ = bottom; // Assume X-axis will intersect Y-axis (X range includes zero).
+
+ // Ensure both axis and ticks have the *same* range.
+ // (To use the separation, made to give the potential for different ranges,
+ // one would have to *not* do this,
+ // but to make sure they are both assigned correctly).
+ x_ticks_.max_ = x_axis_.max_;
+ x_ticks_.min_ = x_axis_.min_;
+ y_ticks_.max_ = y_axis_.max_;
+ y_ticks_.min_ = y_axis_.min_;
+
+ // Calculate the number of chars of the longest value labels.
+ //x_ticks_.longest_label(); // Can't use this for boxplot.
+ // - it is the length of the value labels
+ y_ticks_.longest_label(); // Updates y_label_max_length_
+ double longest = 0;
+ for (size_t i = 0; i < series.size(); i++)
+ {
+
+ double l = series[i].series_info_.text().size(); // Length of an X label.
+ if(l > longest)
+ {
+ longest = l;
+ }
+ }
+ if (longest == 0)
+ {
+ std::cout << "no x labels!" << std::endl;
+ // No labels provided so switch off labelling.
+ x_ticks_.major_value_labels_side_ = 0;
+ }
+ x_ticks_.label_max_length_ = longest;
+
+ // Check that labels won't collide and advise if they will.
+ // Change rotation to avoid collision?
+
+ y_ticks_.label_max_space_ = 0.; // Space for Y axis labels, depending on orientation.
+ if (y_ticks_.label_rotation_ == horizontal)
+ { // Move plot left edge right to give space for y_value_precision_ digits.
+ y_ticks_.label_max_space_ += y_ticks_.label_max_length_; // SVG units.
+ }
+ else if((y_ticks_.label_rotation_ == upward) || (y_ticks_.label_rotation_ == downward))
+ { // Only need one char & 1 space width from Y-axis label.
+ y_ticks_.label_max_space_ += 2 * y_value_label_style_.font_size() * wh;
+ }
+ else
+ { // Assume some slope 45, so diagonally down from tick,
+ // and takes a bit less room.
+ y_ticks_.label_max_space_ = y_ticks_.label_max_length_ * sin45;
+ }
+
+ if (y_ticks_.major_value_labels_side_ != 0) // != none
+ { // Major tick value labels wanted left or right .
+ if ((y_ticks_.ticks_on_window_or_axis_ < 0) // On left of plot window.
+ && (y_ticks_.major_value_labels_side_ < 0) ) // & labels on left.
+ { // Contract plot window left edge to right to make space for value labels on left.
+ plot_left_ += y_ticks_.label_max_space_;
+ }
+ else if ((y_ticks_.ticks_on_window_or_axis_ > 0) // On right of plot window.
+ && (y_ticks_.major_value_labels_side_ > 0) ) // & labels to right.
+ { // Contract plot window right to left to make space for value labels on right.
+ plot_right_ -= y_ticks_.label_max_space_;
+ }
+ else
+ { // y_ticks_.ticks_on_window_or_axis_ == 0
+ // no value labels on plot window (may be on mid-plot Y-axis line).
+ // Ignore the unusual case of Y-axis line too close to the axis label.
+ // In this case the value labels may overflow the plot window
+ // and collide with the axis label!
+ // User must change to put value label downward, or on other side of the axis line.
+ // using major_value_labels_side(int d)
+ // to set tick value labels to left (<0), none (==0) or right (>0).
+ }
+ } // y_ticks_. major_value_labels_side
+
+ // Make space for any Y ticks.
+ if(y_ticks_.left_ticks_on_)
+ { // Start left of plot to right to give space for biggest of any external left ticks.
+ plot_left_ += (std::max)(y_ticks_.major_tick_length_, y_ticks_.minor_tick_length_); // Avoid macro max trap!
+ }
+
+ x_ticks_.label_max_space_ = 0; // Work out the longest value label for X-Axis.
+ if (x_ticks_.label_rotation_ == horizontal)
+ { // Only 1 char height & 1 space needed if labels are horizontal, regardless of length of string.
+ x_ticks_.label_max_space_ = 2 * x_value_label_style_.font_size() * wh; // 2 SVG chars
+ }
+ else if ((x_ticks_.label_rotation_ == upward) || (x_ticks_.label_rotation_ == downward))
+ { // ! horizontal so will need more than 2 chars worth.
+ x_ticks_.label_max_space_ += x_ticks_.label_max_length_ ; // Number of SVG chars.
+ }
+ else
+ { // Assume label is sloping, say 45, so * sin(45) = 0.707.
+ x_ticks_.label_max_space_ += x_ticks_.label_max_length_ * sin45; // SVG 'chars'.
+ }
+
+ if (x_ticks_.major_value_labels_side_ != 0)
+ { // Some value labels.
+ if ((x_ticks_.ticks_on_window_or_axis_ < 0) // on bottom of plot window.
+ && (x_ticks_.major_value_labels_side_ < 0) ) // & labels on bottom.
+ { // Contract plot window bottom edge up to make space for x value labels on bottom.
+ plot_bottom_ -= x_ticks_.label_max_space_; // Move up.
+ }
+ else if ((x_ticks_.ticks_on_window_or_axis_ > 0) //
+ && (x_ticks_.major_value_labels_side_ > 0) ) // & x labels to top.
+ { // Move top of plot window down to give space for x value labels.
+ plot_top_ += x_ticks_.label_max_space_; // Move down.
+ }
+ else
+ { // no labels on plot window (may be on mid-plot X-axis).
+ // See also notes above on case where labels can overwrite axis.
+ }
+ } // x_ticks_. major_value_labels_side
+
+ // Make space for any X ticks.
+ if(x_ticks_.down_ticks_on_)
+ { // Start bottom of plot higher to give space for any external down ticks.
+ plot_bottom_ -= (std::max)(x_ticks_.major_tick_length_, x_ticks_.minor_tick_length_);// Avoid macro max trap!
+ }
+
+ if (x_axis_.axis_line_on_)
+ { // Want an X-axis line, so check if range includes zero, so axes intersect,
+ // and x_axis_ is svg coordinate of Y-axis (usually y = 0).
+ // If not fix axis to bottom (or top) of the plot window.
+ if ((x_axis_position_ == bottom) // All Y values definitely > zero.
+ )// && !(x_ticks_.ticks_on_window_or_axis_ < 0) ) // & not already at bottom.
+ { // y_min_ > 0 so X-axis will not intersect Y-axis, so use plot window.
+ plot_bottom_ -= x_ticks_.label_max_space_; // Move up for the value labels.
+ x_axis_.axis_ = plot_bottom_; // Put X-axis on bottom.
+ }
+ else if ((x_axis_position_ == top) // All Y values definitely < zero.
+ && !(x_ticks_.ticks_on_window_or_axis_ > 0) ) // & not already at top.
+ { // // y_max_ < 0 so X-axis will not intersect Y-axis, so use plot window.
+ plot_top_ += x_ticks_.label_max_space_; // Move down for value labels.
+ x_axis_.axis_ = plot_top_; // Put X-axis on top.
+ }
+ else
+ { // y_axis_position_ == y_intersects_x
+ // Calculate below after transform is calculated.
+ }
+ } // if (use_x_axis_line_)
+
+ if (y_axis_.axis_line_on_)
+ { // Want a Y-axis line, so check if range includes zero, so axes intersect,
+ // and y_axis_ is svg coordinate of X-axis (usually x = 0).
+ // If not fix axis to left (or right) of the plot window.
+ if ((y_axis_position_ == left) // All X values definitely > zero.
+ //&& !(y_ticks_.ticks_on_window_or_axis_ < 0) // & not already at left.
+ )
+ { // Y-axis will not intersect X -axis, so put Y-axis line on plot window.
+ y_axis_.axis_ = plot_left_; // Y-axis to left,
+ //plot_left_ += 2 * y_label_info_.font_size(); // with a space.
+ }
+ else if ((y_axis_position_ == right) // All X values definitely < zero.
+ //&& !(y_ticks_.ticks_on_window_or_axis_ > 0) // & not already at right.
+ )
+ {
+ y_axis_.axis_ = plot_right_; // Y-axis to right of plot window,
+ }
+ else
+ {
+ x_axis_position_ = x_intersects_y;
+ // Calculate below after transform is calculated.
+ }
+ } // if (use_y_axis_line_)
+
+ // plot window now calculated, so make some checks (might also check it fits into image?).
+ if (plot_right_ <= plot_left_)
+ {
+ std::cout << "plot window left " << plot_left_ << ", right " << plot_right_ << std::endl;
+ throw std::runtime_error("Plot window right <= left!");
+ }
+ if (plot_top_ >= plot_bottom_)
+ {
+ std::cout << "plot window top " << plot_top_ << ", bottom " << plot_bottom_ << std::endl;
+ throw std::runtime_error("Plot window top >= bottom!");
+ }
+
+ // Calculate scale and shift factors for transform from Cartesian to plot.
+ // SVG image is 0, 0 at top left,y increase *downwards*
+ // Cartesian 0, 0 at bottom left, y increasing upwards.
+ x_scale_ = (plot_right_ - plot_left_) / (x_axis_.max_ - x_axis_.min_);
+ x_shift_ = plot_left_ - x_axis_.min_ * (plot_right_ - plot_left_) / (x_axis_.max_ - x_axis_.min_);
+ y_scale_ = -(plot_bottom_ - plot_top_) / (y_axis_.max_ - y_axis_.min_);
+ y_shift_ = plot_top_ - (y_axis_.max_ * (plot_top_ - plot_bottom_) / (y_axis_.max_ - y_axis_.min_));
+
+ if (x_axis_.axis_line_on_)
+ {
+ if(x_axis_position_ == x_intersects_y)
+ { // Y Range *does* include zero, so x_axis_ not yet calculated.
+ double y(0.); // Use y = 0
+ transform_y(y);
+ x_axis_.axis_ = y; // svg Y coordinate of horizontal X-axis line.
+ }
+ }
+ if (y_axis_.axis_line_on_)
+ { // May need to calculate axes, if will intersect.
+ if(y_axis_position_ == y_intersects_x)
+ { // X Range *does* include zero, so y_axis_ not yet calculated.
+ double x(0.);
+ transform_x(x);
+ y_axis_.axis_ = x; // SVG x coordinate of vertical Y-axis.
+ }
+ }
+ if (plot_window_on_)
+ { // Draw plot window rectangle with border and/or background.
+ image.g(detail::PLOT_WINDOW_BACKGROUND).push_back(
+ new rect_element(plot_left_, plot_top_, (plot_right_ - plot_left_), plot_bottom_ - plot_top_));
+ }
+ } // void calculate_plot_window()
+
+ void draw_title()
+ {
+ // Update title_info_ with position.
+ title_info_.x(image.x_size() / 2.); // Center of image.
+ // Assumes align = center_align.
+ // And center_align will ensure that will center correctly
+ // even if original string is long because contains Unicode like &#x3A9;
+ // because the render engine does the centering.
+ double y = title_info_.style().font_size() * text_margin_; // Leave a linespace above.
+ title_info_.y(y); // Vertical position.
+ image.g(boxplot::PLOT_TITLE).push_back(new text_element(title_info_));
+ } // void draw_title()
+
+ void draw_x_axis()
+ { // Draw the horizontal X axis line.
+ double y(0.);
+ transform_y(y);
+ //x_axis_. = y;
+
+ if (x_axis_.axis_line_on_)
+ { // Want a Y-axis line.
+
+ if (x_axis_position_ == top)
+ { // horizontal line at top of plot window.
+ image.g(boxplot::X_TICKS).line(plot_left_, plot_top_, plot_right_, plot_top_);
+ }
+ else if (x_axis_position_ == bottom)
+ {// horizontal line at bottom of plot window.
+ image.g(boxplot::X_TICKS).line(plot_left_, plot_bottom_, plot_right_, plot_bottom_);
+ }
+ }
+ path_element& major_tick_path = image.g(boxplot::X_TICKS).path();
+ for(size_t i = 0; i < series.size(); ++i)
+ { // Draw the ticks for each series, evenly spaced along X axis.
+ draw_x_major_tick(
+ plot_left_ + (plot_right_- plot_left_) *
+ ((double)(i + 1)) / (double)(series.size() + 1),
+ major_tick_path, series[i].series_info_);
+ }
+ } // void draw_x_axis()
+
+ void draw_y_axis()
+ {
+ path_element& minor_tick_path = image.g(boxplot::Y_MINOR_TICKS).path();
+ path_element& major_tick_path = image.g(boxplot::Y_MAJOR_TICKS).path();
+ path_element& major_grid_path = image.g(boxplot::Y_MAJOR_GRID).path();
+ path_element& minor_grid_path = image.g(boxplot::Y_MINOR_GRID).path();
+
+ // y_minor_jump is the interval between minor ticks.
+ double y_minor_jump = y_ticks_.major_interval_ / ((double)(y_ticks_.num_minor_ticks_ + 1.) );
+
+ // Draw the ticks on the positive side.
+ for(double i = 0; i < y_axis_.max_; i += y_ticks_.major_interval_)
+ {
+ for(double j = i + y_minor_jump;
+ j < i + y_ticks_.major_interval_;
+ j += y_minor_jump)
+ {
+ draw_y_minor_tick(j, minor_tick_path, minor_grid_path);
+ }
+
+ draw_y_major_tick(i, major_tick_path, major_grid_path);
+ }
+
+ // Draw the ticks on the negative side.
+ for(double i = 0; i > y_axis_.min_; i -= y_ticks_.major_interval_)
+ {
+ // draw minor ticks
+ for(double j = i;
+ j > i - y_ticks_.major_interval_;
+ j -= y_ticks_.major_interval_ / (y_ticks_.num_minor_ticks_ + 1.))
+ {
+ draw_y_minor_tick(j, minor_tick_path, minor_grid_path);
+ }
+
+ draw_y_major_tick(i, major_tick_path, major_grid_path);
+ }
+ } // void draw_y_axis()
+
+ // void draw_x_major_tick(double i, path_element& tick_path, const std::string& str)
+ void draw_x_major_tick(double i, path_element& tick_path, text_element& series_info)
+ {
+ double x_left(i);
+ double y_top(0.);
+ double y_bottom(image.x_size());
+
+ // Draw major tick.
+ x_left = i;
+ y_top = plot_bottom_;
+ y_bottom = plot_bottom_ + x_ticks_.major_tick_length_;
+ tick_path.M(x_left, y_top).L(x_left, y_bottom); // leaving current position at bottom or tick.
+ if(x_axis_.label_on_) // use_x_major_labels)
+ {
+ series_info.x(x_left); // horizontal position.
+ double y = y_bottom + x_ticks_.major_tick_length_;
+ y += series_info.style().font_size() * text_margin_; // Leave a linespace above.
+ series_info.y(y); // Vertical position.
+ image.g(boxplot::VALUE_LABELS).push_back(new text_element(series_info));
+ }
+ } // void draw_x_major_tick
+
+ void draw_y_major_tick(double value, path_element& tick_path, path_element& grid_path)
+ { // Draw a Y axis major tick, tick value labels & horizontal grid.
+ double y(value); // for tick and/or grid.
+ transform_y(y); // Cartesian to SVG coordinates.
+ if((y < plot_top_ - 0.01) || (y > plot_bottom_ + 0.01))
+ // Allow a bit extra to allow for round-off errors.
+ { // tick value is way outside plot window, so nothing to do.
+ return;
+ }
+ double x_left(0.); // Left end of tick.
+ double x_right(image.y_size()); // Right end of tick.
+ if(y_ticks_.major_grid_on_ == true)
+ { // Draw horizontal major Y grid line.
+ if(!plot_window_on_ == true)
+ {
+ if(y_ticks_.major_value_labels_side_ < 0) // left
+ { // Start further right to give space for y axis value label.
+ y -= y_value_label_style_.font_size() * text_margin_;
         }
- }
-
- void draw_y_major_tick(double i, path_element& tick_path)
- {
- double y1(i), x1(0.), x2(image.y_size());
-
- // Draw ith major tick.
- y1 = i;
-
- transform_y(y1);
-
- double y_tick_length = y_minor_tick_length_ / 2.;
 
- x1 = plot_left_;
- x2 = plot_left_ - y_tick_length / 2.;
-
- tick_path.M(x1, y1).L(x2, y1);
-
- if(use_y_major_labels)
- {
- std::stringstream fmt;
- fmt << i; // So fmt.str() has the value to label the tick.
-
- x1 -= y_minor_tick_length_;
-
- image.get_g_element(boxplot::PLOT_LABELS).text(x1 + 12,
- y1, fmt.str(), 12, "", "", "", "", "", center_align, upward);
+ if(y_ticks_.left_ticks_on_ == true)
+ { // And similarly space for left ticks.
+ y -= y_ticks_.major_tick_length_;
         }
- }
-
- void draw_y_axis()
- {
- path_element& minor_tick_path =
- image.get_g_element(boxplot::Y_MINOR_TICKS).path();
-
- path_element& major_tick_path =
- image.get_g_element(boxplot::Y_MAJOR_TICKS).path();
-
- // y_minor_jump is the interval between minor ticks.
- double y_minor_jump = y_major_interval_/((double)(y_num_minor_ticks_ + 1.) );
-
- // Draw the ticks on the positive side.
- for(double i = 0; i < y_max_; i += y_major_interval_)
- {
- for(double j = i + y_minor_jump;
- j < i + y_major_interval_;
- j += y_minor_jump)
- {
- draw_y_minor_tick(j, minor_tick_path);
- }
-
- draw_y_major_tick(i, major_tick_path);
+ }
+ else
+ { // plot_window_on_ to use full width of plot window.
+ x_left = plot_left_ + plot_window_border_.width_; // Don't write over either border.
+ x_right = plot_right_ - plot_window_border_.width_;
+ }
+ grid_path.M(x_left, y).L(x_right, y); // Horizontal grid line.
+ } // y_major_grid_on
+
+ // Draw major ticks & value label, if necessary.
+ double y_tick_length = y_ticks_.major_tick_length_;
+ if (y_ticks_.ticks_on_window_or_axis_ < 0)
+ { // Start ticks on the plot window border left.
+ x_left = plot_left_; // x_left = left,
+ x_right = plot_left_; // x_right = right.
+ }
+ else if (y_ticks_.ticks_on_window_or_axis_ > 0)
+ { // Start ticks on the plot window border right.
+ x_left = plot_right_;
+ x_right = plot_right_;
+ }
+ else // y_ticks_.ticks_on_window_or_axis_== 0
+ { // Internal style ticks on vertical Y-axis.
+ x_left = y_axis_.axis_; // Y-axis line.
+ x_right = y_axis_.axis_;
+ }
+ if(y_ticks_.left_ticks_on_)
+ {
+ x_left -= y_tick_length; // left
+ }
+ if (y_ticks_.right_ticks_on_)
+ {
+ x_right += y_tick_length; // right.
+ }
+ tick_path.M(x_left, y).L(x_right, y); // Draw the major tick.
+ // leaving x_left at the left most end of any tick,
+ // and x_right at the rightmost end of any tick.
+ // These may be on the axis line: y is the vertical tick position.
+
+ if(y_ticks_.major_value_labels_side_ != 0)
+ { // Label the tick with a value, for example "1.2"
+ std::stringstream label;
+ label.precision(y_ticks_.value_precision_);
+ label.flags(y_ticks_.value_ioflags_); // set ALL IOflags.
+ label << value; // Example: label.str() == "20" or "0.25" or "1.2e+015"
+ if (y_ticks_.strip_e0s_)
+ { // Remove unecessary e, +, leadings 0s.
+ std::string v = strip_e0s(label.str());
+ label.str(v);
+ }
+
+ double x = 0; // Where to start writing from, at end of left or right tick, if any.
+ // = 0 is only to avoid unitialised warning.
+ align_style alignment = center_align;
+ // Adjustments to provide space from end of tick before or after writing label.
+ if (y_ticks_.label_rotation_ == horizontal)
+ { // Just shift up to center value digits on tick.
+ if (y_ticks_.major_value_labels_side_ < 0)
+ { // labels to left, so start a little to left of x_left.
+ y += y_value_label_style_.font_size() * 0.2;
+ x = x_left - y_value_label_style_.font_size() * 0.5;
+ alignment = right_align;
         }
-
- // Draw the ticks on the negative side.
- for(double i = 0; i > y_min_; i -= y_major_interval_)
- {
- // draw minor ticks
- for(double j=i; j>i-y_major_interval_; j-=y_major_interval_ / (y_num_minor_ticks_+1))
- {
- draw_y_minor_tick(j, minor_tick_path);
- }
-
- draw_y_major_tick(i, major_tick_path);
+ else if(y_ticks_.major_value_labels_side_ > 0)
+ { // labels to right, so start a little to right of x_right.
+ y += y_value_label_style_.font_size() * 0.2;
+ x = x_right + y_value_label_style_.font_size() * 0.5;
+ alignment = left_align;
         }
- }
-
- void draw_x_axis()
- {
- double y1(0.);
-
- // Draw the axis line.
- transform_y(y1);
-
- x_axis_ = y1;
-
- path_element& major_tick_path =
- image.get_g_element(boxplot::X_TICKS).path();
-
- // Draw the ticks on the positive side.
- for(size_t i = 0; i < series.size(); ++i)
- {
- draw_x_major_ticks(
- plot_left_ + (plot_right_-plot_left_)*((double)(i + 1)) /
- (double)(series.size() + 1),
- major_tick_path, series[i].title);
+ }
+ else if (y_ticks_.label_rotation_ == upsidedown)
+ { // Just shift up to center value digits on tick.
+ if (y_ticks_.major_value_labels_side_ < 0)
+ { // labels to left, so start a little to left of x_left.
+ y -= y_value_label_style_.font_size() * 0.1;
+ x = x_left - y_value_label_style_.font_size() * 0.5;
+ alignment = left_align;
         }
- }
-
- void draw_axes()
- {
- draw_y_axis();
- draw_x_axis();
- }
-
- void draw_title()
- {
- text_element title(image.x_size()/2.,
- title_info_.font_size(),
- title_info_.text());
-
- title.font_alignment(center_align);
- title.font_size(title_info_.font_size());
- image.get_g_element(boxplot::TITLE).push_back(new text_element(title));
- }
-
- void draw_x_label()
- {
- text_element to_use((plot_right_ + plot_left_) / 2.,
- image.y_size() - 8, x_label_info_.text());
-
- to_use.font_size(12);
- to_use.font_alignment(center_align);
-
- image.get_g_element(boxplot::X_LABEL).push_back(new text_element(to_use));
- }
-
- void draw_y_label()
- {
- image.get_g_element(boxplot::Y_LABEL).style().stroke_color(black);
- image.get_g_element(boxplot::Y_LABEL).push_back(new
- text_element(12, (plot_bottom_ + plot_top_) /2.,
- y_label_info_.text(), 12, "", "", "", "", "",center_align, upward));
- }
-
-
- void calculate_transform()
- {
- y_scale_ = -(plot_bottom_-plot_top_)/(y_max_-y_min_);
- y_shift_ = plot_top_ - (y_max_ *(plot_top_-plot_bottom_)/(y_max_-y_min_));
- }
-
- void calculate_plot_window()
- {
- plot_left_ = plot_top_ = 0;
-
- plot_right_ = image.x_size();
- plot_bottom_ = image.y_size();
-
- if(use_x_label)
- {
- plot_bottom_ -= (int)(x_label_info_.font_size() * 2);
+ else if(y_ticks_.major_value_labels_side_ > 0)
+ { // labels to right, so start a little to right of x_right.
+ y -= y_value_label_style_.font_size() * 0.1;
+ x = x_right + y_value_label_style_.font_size() * 0.5;
+ alignment = right_align;
         }
-
- if(use_y_label)
- {
- plot_left_ += (int)(y_label_info_.font_size() * text_margin_);
+ }
+ else if (y_ticks_.label_rotation_ == uphill)
+ { // Assume some 45 slope, so need about sqrt(2) less space.
+ if (y_ticks_.major_value_labels_side_ < 0)
+ { // labels to left, so start a little to left of x_left.
+ y -= y_value_label_style_.font_size() * 0.2;
+ x = x_left - y_value_label_style_.font_size() * 0.2;
+ // Seems to need a bit more space for right than left if rotated.
+ alignment = right_align;
         }
-
- if(title_on_)
- {
- plot_top_ += (int)(title_info_.font_size() * text_margin_);
+ else if(y_ticks_.major_value_labels_side_ > 0)
+ { // labels to right, so start a little to right of x_right.
+ y += y_value_label_style_.font_size() * 0.2;
+ x = x_right + y_value_label_style_.font_size() * 0.7;
+ alignment = left_align;
         }
-
- // Give the plot window a natural bit of padding.
- plot_left_+=5;
- plot_right_-=5;
- plot_top_+=5;
- plot_bottom_-=5;
-
- plot_left_ +=
- y_minor_tick_length_ > y_minor_tick_length_ ?
- y_minor_tick_length_ : y_minor_tick_length_ ;
-
- plot_bottom_ -= x_major_tick_length_ + 10;
-
- image.get_g_element(boxplot::PLOT_BACKGROUND).push_back(
- new rect_element(plot_left_, plot_top_,
- (plot_right_-plot_left_), plot_bottom_-plot_top_));
-
- }
-
- void draw_whiskers(double min, double max, double length, double x,
- const svg_style& min_whisker, const svg_style& max_whisker,
- const svg_style& axis_whisker)
- {
- // Set up document structure for whiskers.
- g_element& g_whisk_ptr = image.get_g_element(boxplot::WHISKER).add_g_element();
-
- // Set colors for whiskers.
- g_whisk_ptr.add_g_element().style()
- .stroke_color(min_whisker.stroke_color())
- .fill_color(min_whisker.fill_color())
- .stroke_width(min_whisker.stroke_width());
-
- g_whisk_ptr.add_g_element().style()
- .stroke_color(max_whisker.stroke_color())
- .fill_color(max_whisker.fill_color())
- .stroke_width(max_whisker.stroke_width());
-
- // Set axis structure and colors.
- g_element& g_axis_ptr = image.get_g_element(boxplot::BOX_AXIS)
- .add_g_element();
-
- g_axis_ptr.style()
- .stroke_color(axis_whisker.stroke_color())
- .fill_color(axis_whisker.fill_color())
- .stroke_width(axis_whisker.stroke_width());
-
- transform_y(min);
- transform_y(max);
-
- double half_length = length / 2.;
-
- g_whisk_ptr.get_g_element(0)
- .line(x-half_length, min, x+half_length, min);
-
- g_whisk_ptr.get_g_element(1)
- .line(x-half_length, max, x+half_length, max);
-
- g_axis_ptr.line(x, min, x, max);
-
- // Clip elements.
- g_axis_ptr.clip_id(plot_window_clip_);
- g_whisk_ptr.clip_id(plot_window_clip_);
- }
-
- void draw_box(double q1, double q3, double x, double width,
- const svg_style& box_style)
- {
- g_element& g_ptr = image.get_g_element(boxplot::MEDIAN)
- .add_g_element();
-
- g_ptr.style().stroke_color(box_style.stroke_color())
- .stroke_width(box_style.stroke_width())
- .fill_color(box_style.fill_color());
-
- transform_y(q1); transform_y(q3);
-
- double half_width = width/2;
-
- g_ptr.rect(x - half_width, q3, width, q1 - q3);
-
- // Clip elements.
- g_ptr.clip_id(plot_window_clip_);
- }
-
- void draw_median(double median, double x_offset, double box_width,
- const svg_style& median_style)
- {
- transform_y(median);
-
- g_element& g_ptr = image.get_g_element(boxplot::MEDIAN)
- .add_g_element();
-
- g_ptr.style().stroke_color(median_style.stroke_color())
- .stroke_width(median_style.stroke_width());
-
- double half_width = box_width / 2.;
-
- g_ptr.line(x_offset - half_width, median,
- x_offset + half_width, median);
-
- // Clip elements.
- g_ptr.clip_id(plot_window_clip_);
- }
-
- void draw_outliers(double x, const std::vector<double>& outliers,
- const std::vector<double>& extreme_outliers,
- const plot_point_style& mild_style, const plot_point_style& extreme_style)
- {
- std::vector<double>::const_iterator i;
- double temp;
-
- g_element& g_mild_ptr = image.get_g_element(boxplot::MILD_OUTLIERS)
- .add_g_element();
- g_element& g_ext_ptr = image.get_g_element(boxplot::EXTREME_OUTLIERS)
- .add_g_element();
-
- g_mild_ptr.style().fill_color(mild_style.fill_color)
- .stroke_color(mild_style.stroke_color);
-
- g_ext_ptr.style().fill_color(extreme_style.fill_color)
- .stroke_color(extreme_style.stroke_color);
-
- for(i = outliers.begin(); i != outliers.end(); ++i)
- {
- transform_y(temp = *i);
- g_mild_ptr.circle(x, temp, 2);
+ }
+ else if (y_ticks_.label_rotation_ == downhill)
+ { // Assume some 45 slope, so need about sqrt(2) less space.
+ if (y_ticks_.major_value_labels_side_ < 0)
+ { // labels to left, so start a little to left of x_left.
+ y += y_value_label_style_.font_size() * 0.3;
+ x = x_left - y_value_label_style_.font_size() * 0.7;
+ // Seems to need a bit more space for right than left if rotated.
+ alignment = right_align;
         }
-
- for(i = extreme_outliers.begin(); i != extreme_outliers.end(); ++i)
- {
- transform_y(temp = *i);
- g_ext_ptr.circle(x, temp, 2);
+ else if(y_ticks_.major_value_labels_side_ > 0)
+ { // labels to right, so start a little to right of x_right.
+ y -= y_value_label_style_.font_size() * 0.3;
+ x = x_right + y_value_label_style_.font_size() * 0.1;
+ alignment = left_align;
         }
-
- // Clip elements.
- g_mild_ptr.clip_id(plot_window_clip_);
- g_ext_ptr.clip_id(plot_window_clip_);
- }
-
- void draw_boxplot(const svg_boxplot_series& series, double x_offset)
- {
- draw_whiskers(series.whisker_min, series.whisker_max,
- series.whisker_length, x_offset,
- series.min_whisker_style, series.max_whisker_style,
- series.axis_style);
-
- draw_box(series.q1, series.q3, x_offset, series.box_width,
- series.box_style);
-
- draw_median(series.median, x_offset,
- series.box_width - series.box_style.stroke_width(),
- series.median_style);
-
- draw_outliers(x_offset, series.outliers, series.extreme_outliers,
- series.mild_outlier, series.ext_outlier);
- }
-
- void update_image()
- {
- clear_all();
-
- // Draw background.
- image.get_g_element(boxplot::BACKGROUND).push_back(
- new rect_element(0, 0, image.x_size(), image.y_size()) );
-
- draw_title();
- calculate_plot_window();
- calculate_transform();
-
- // Define the clip path for the plot window.
- // We don't want to allow overlap of the plot window lines,
- // thus the minor adjustments.
-
- image.clip_path(rect_element(plot_left_ + 1, plot_top_ + 1,
- plot_right_ - plot_left_ - 2, plot_bottom_ - plot_top_ - 2),
- plot_window_clip_);
-
- draw_y_axis();
- draw_x_axis();
-
- if(use_x_label)
- {
- draw_x_label();
+ }
+ else if (y_ticks_.label_rotation_ == upward)
+ { // Tick value label straight up vertically on Y-axis.
+ y -= y_value_label_style_.font_size() * 0.1;
+ if (y_ticks_.major_value_labels_side_ < 0)
+ { // labels to left, so start a little to left of x_left.
+ x = x_left - y_value_label_style_.font_size() * 0.7;
+ // Seems to need a bit more space for right than left if rotated.
+ alignment = center_align;
         }
-
- if(use_y_label)
- {
- draw_y_label();
+ else if(y_ticks_.major_value_labels_side_ > 0)
+ { // labels to right, so start a little to right of x_right.
+ x = x_right + y_value_label_style_.font_size() * 1.5;
+ alignment = center_align;
         }
-
- for(unsigned int i=0; i<series.size(); ++i)
+ }
+ else if (y_ticks_.label_rotation_ == downward)
+ { // Tick value label straight down vertically on Y-axis.
+ y -= y_value_label_style_.font_size() * 0.1;
+ if (y_ticks_.major_value_labels_side_ < 0)
+ { // labels to left, so start a little to left of x_left.
+ x = x_left - y_value_label_style_.font_size() * 1.2;
+ // Seems to need a bit more space for right than left if rotated.
+ alignment = center_align;
+ }
+ else if(y_ticks_.major_value_labels_side_ > 0)
+ { // labels to right, so start a little to right of x_right.
+ x = x_right + y_value_label_style_.font_size() * 0.7;
+ alignment = center_align;
+ }
+ }
+ else
+ { // Others not yet implemented.
+ return; // Without any value label.
+ } // All rotations.
+ if (x <= 0)
+ { // Sanity checks on svg coordinates.
+ throw std::runtime_error("Y-tick X value wrong!");
+ }
+ if (y <= 0)
+ {
+ throw std::runtime_error("Y-tick Y value wrong!");
+ }
+ if(y_ticks_.ticks_on_window_or_axis_ != 0)
+ { // External to plot window style left or right.
+ // Always want all values including "0", if labeling external to plot window.
+ // y_ticks_.ticks_on_window_or_axis_ == true != 0
+ image.g(boxplot::VALUE_LABELS).text(
+ x,
+ y,
+ label.str(), y_value_label_style_, alignment, y_ticks_.label_rotation_);
+ }
+ else
+ { // ! y_ticks_.y_ticks_on_plot_window_ == 0 'Internal' - value labels either side of vertical Y-axis.
+ if ((value != 0) && y_axis_.axis_line_on_)
+ { // Avoid a zero ON the Y-axis if it would be cut through by any horizontal X-axis line.
+ image.g(boxplot::VALUE_LABELS).text(
+ x,
+ y,
+ label.str(),
+ y_value_label_style_,
+ alignment,
+ y_ticks_.label_rotation_);
+ }
+ } // either on plot window or 'on axis'.
+ } // want value label on tick
+ } // draw_y_major_tick
+
+ void draw_y_minor_tick(double value, path_element& tick_path, path_element& grid_path)
+ { // Draw a Y-axis minor tick and optional grid.
+ double x_left(0.); // Start on vertical Y axis line.
+ double x_right(image.y_size()); // right edge of image.
+ double y(value); // Tick position and value label,
+ transform_y(y); // convert to svg.
+
+ if(y_ticks_.minor_grid_on_)
+ { // Draw the minor grid, if wanted.
+ if(!plot_window_on_)
+ {
+ if(x_axis_.label_on_)
         {
- draw_boxplot(series[i],
- plot_left_ + (plot_right_-plot_left_)*((double)(i + 1)) / (double)(series.size() + 1));
+ x_left += y_value_label_style_.font_size() * text_margin_;
+ x_right -= y_value_label_style_.font_size() * text_margin_;
         }
- }
-
-public:
+ }
+ else
+ { // plot_window_on_
+ x_left = plot_left_ + plot_window_border_.width_;
+ x_right = plot_right_ - plot_window_border_.width_; // Ensure just *inside* window?
+ }
+ if((y >= plot_top_) && (y <= plot_bottom_) && (x_left >= plot_left_) && (x_right <= plot_right_) )
+ { // Make sure that we are drawing inside the allowed plot window.
+ // Note comparisons are 'upside-down' - y is increasing downwards!
+ grid_path.M(x_left, y).L(x_right, y); // Draw grid line.
+ }
+ else
+ {
+ // Just ignore outside plot window
+ }
+ } // y_minor_grid
+
+ // Draw y minor ticks.
+ if(y_ticks_.ticks_on_window_or_axis_ < 0)
+ { // Put y minor ticks on the plot window border left.
+ x_left = plot_left_;
+ x_right = plot_left_;
+ }
+ else if (y_ticks_.ticks_on_window_or_axis_ > 0)
+ { // Put y minor ticks on the plot window border left.
+ x_left = plot_right_;
+ x_right = plot_right_;
+ }
+ else
+ { // Internal style, y_ticks_.ticks_on_window_or_axis_ == 0
+ x_left = y_axis_.axis_; // On the Y-axis line itself.
+ x_right = y_axis_.axis_;
+ }
+ if(y_ticks_.left_ticks_on_)
+ {
+ x_left -= y_ticks_.minor_tick_length_;
+ }
+ if(y_ticks_.right_ticks_on_)
+ {
+ x_right += y_ticks_.minor_tick_length_;
+ }
+ //if((x_left >= plot_left_) && (x_right <= plot_right_) && (y <= plot_bottom_) && (y >= plot_top_))
+ // but can never be inside if left tick!
+ if((y <= plot_bottom_) && (y >= plot_top_))
+ { // Make sure that we are drawing inside of the allowed plot window.
+ tick_path.M(x_left, y).L(x_right, y); // Draw the tick.
+ }
+ else
+ {// Do nothing? warn?
+ // std::cout << "y minor tick OUTside " << x_left << ' ' << y << ' ' << x_right << std::endl;
+ }
+ } // void draw_y_minor_tick
+
+ void draw_x_label()
+ { // Draw a label (and optional Units) for example "length (km)".
+ // X-label color is set in constructor thus:
+ // image.g(detail::PLOT_X_LABEL).style().stroke_color(black);
+ // and changed using x_label_color(color);
+
+ std::string label = x_label_info_.text(); // x_axis_ label, and optional units.
+ if (x_axis_.label_units_on_ && (x_units_info_.text() != ""))
+ { // Append the units, if any, user providing brackets () if required.
+ label += x_units_info_.text();
+ }
+
+ double y = plot_bottom_;
+ y += x_ticks_.value_label_style_.font_size() * 2.; // Shift down to suit.
+ y += x_label_info_.style().font_size() * 2.; // Shift down to suit.
+ if (x_ticks_.ticks_on_window_or_axis_ < 0) // bottom == -1
+ { // Ticks & value labels below X-axis.
+ if (x_ticks_.major_value_labels_side_ < 0) // - is bottom.
+ { // Shift down to allow for any value labels.
+ y += x_ticks_.label_max_space_;
+ }
+ if (x_ticks_.down_ticks_on_)
+ { // Shift down for biggest of any ticks.
+ y += (std::max)(x_ticks_.minor_tick_length_, x_ticks_.major_tick_length_);
+ }
+ }
+
+ image.g(boxplot::X_LABEL).push_back(new text_element(
+ ( // x position relative to the x-axis which is middle of plot window.
+ plot_right_ + plot_left_) / 2, // x coordinate - middle.
+ y, // Down from plot window.
+ label,
+ x_label_info_.style(),
+ center_align, horizontal)
+ );
+ } // void draw_x_label()
 
-svg_boxplot()
-:
- title_info_(0, 0, "Plot of data", 30),
- x_label_info_(0, 0, "X Axis", 12,"Lucida Sans Console", "", "", "", "", center_align, horizontal),
- y_label_info_(0, 0, "Y Axis", 12, "Lucida Sans Console", "", "", "", "", center_align, upward),
- // x_units_info_(0, 0, "(units)", 12, "Lucida Sans Console", "", "", "", "", center_align, horizontal),
- // y_units_info_(0, 0, "(units)", 12, "Lucida Sans Console", "", "", "", "", center_align, upward),
-
- y_min_(0), y_max_(100),
- y_major_interval_(10),
- use_y_label(true),
- use_x_label(true),
- x_major_tick_length_(10),
- y_num_minor_ticks_(1),
- y_minor_tick_length_(20),
- plot_window_clip_("__clip_window"), // TODO Why the __???
- use_x_major_labels(true),
- title_on_(true),
- use_y_major_labels(true)
 
-{
- image.image_size(500, 350);
-
- // Build the document tree.. add children of the root node.
- for(int i=0; i<boxplot::BOXPLOT_DOC_CHILDREN; ++i)
- {
- image.add_g_element();
- }
-
- // Set boxplot color defaults.
- image.get_g_element(boxplot::BACKGROUND).style().fill_color(white);
- image.get_g_element(boxplot::PLOT_BACKGROUND).style().fill_color(white).stroke_color(black);
- image.get_g_element(boxplot::X_TICKS).style().stroke_color(black).stroke_width(2);
- image.get_g_element(boxplot::Y_MINOR_TICKS).style().stroke_color(black).stroke_width(1);
- image.get_g_element(boxplot::Y_MAJOR_TICKS).style().stroke_color(black).stroke_width(2);
- image.get_g_element(boxplot::X_LABEL).style().stroke_color(black);
- image.get_g_element(boxplot::Y_LABEL).style().stroke_color(black);
- image.get_g_element(boxplot::TITLE).style().stroke_color(black);
- image.get_g_element(boxplot::WHISKER).style().stroke_color(black);
- image.get_g_element(boxplot::BOX_AXIS).style().stroke_color(black);
- image.get_g_element(boxplot::BOX).style().stroke_color(black).fill_color(white);
- image.get_g_element(boxplot::MEDIAN).style().stroke_color(black).stroke_width(2);
- image.get_g_element(boxplot::EXTREME_OUTLIERS).style().stroke_color(black).fill_color(white);
- image.get_g_element(boxplot::MILD_OUTLIERS).style().stroke_color(black).fill_color(black);
-}
-
-svg_boxplot& load_stylesheet(const std::string& file)
-{
- image.load_stylesheet(file);
- return *this;
-}
+ void draw_y_label()
+ { // Draw vertical y_axis label, and optional y units.
+ // Y-label color is set in constructor thus:
+ // image.g(boxplot::Y_LABEL).style().fill_color(black);
+ // and changed using y_label_color(color);
+
+ std::string label = y_label_info_.text();
+ if (y_axis_.label_units_on_ && (y_units_info_.text() != ""))
+ { // Append the units, user must provide any enclosing ()s, if wanted.
+ label += y_units_info_.text() ;
+ }
+
+ double x = plot_left_;
+ x -= y_axis_label_style_.font_size() * 0.7; // Shift left to suit.
+ if (y_ticks_.ticks_on_window_or_axis_ < 0)
+ { // Ticks & value labels to left of Y-axis.
+ if (y_ticks_.down_ticks_on_)
+ { // Shift left for biggest of any ticks.
+ x -= (std::max)(y_ticks_.minor_tick_length_, y_ticks_.major_tick_length_);
+ }
+ if (y_ticks_.major_value_labels_side_ < 0)
+ { // Shift left to allow for any value labels.
+ x -= y_ticks_.label_max_space_;
+ }
+ }
+
+ image.g(boxplot::Y_LABEL).push_back(new
+ text_element(x,
+ (plot_bottom_ + plot_top_) / 2., // center on the plot window.
+ label, // "Y-Axis" for example.
+ y_axis_label_style_,
+ center_align, // One might want it to left or right_align?
+ upward)); // Y label must be drawn vertically.
+
+ } // draw_y_label
+
+
+ void draw_box(double q1, double q3, // Quartiles
+ double x, double width,
+ const svg_style& box_styl)
+ {
+ g_element& g_ptr = image.g(boxplot::MEDIAN).g();
+
+ g_ptr.style().stroke_color(box_styl.stroke_color())
+ .stroke_width(box_styl.stroke_width())
+ .fill_color(box_styl.fill_color());
+ transform_y(q1);
+ transform_y(q3);
+ double half_width = width/2;
+ g_ptr.rect(x - half_width, q3, width, q1 - q3);
+ g_ptr.clip_id(plot_window_clip_); // Clip elements.
+ } // void draw_box
+
+ void draw_median(double median, double x_offset, double box_width,
+ const svg_style& median_style)
+ { // Draw the median of the data series line within the box.
+ transform_y(median);
+ g_element& g_ptr = image.g(boxplot::MEDIAN)
+ .g();
+
+ g_ptr.style().stroke_color(median_style.stroke_color())
+ .stroke_width(median_style.stroke_width());
+
+ double half_width = box_width / 2.;
+
+ g_ptr.line(x_offset - half_width, median,
+ x_offset + half_width, median);
+
+ // Clip elements.
+ g_ptr.clip_id(plot_window_clip_);
+ } // void draw_median
+
+ void draw_whiskers(double min, double max, double length, double x,
+ const svg_style& min_whisker, const svg_style& max_whisker,
+ const svg_style& axis_whisker)
+ {
+ // Set up document structure for whiskers.
+ // g_element& g_whisk_ptr = image.g(boxplot::WHISKER).g();
+ g_element& g_whisk_ptr = image.g(boxplot::WHISKER).g();
+
+ // Set colors for whiskers.
+ g_whisk_ptr.g().style()
+ .stroke_color(min_whisker.stroke_color())
+ .fill_color(min_whisker.fill_color())
+ .stroke_width(min_whisker.stroke_width());
+
+ g_whisk_ptr.g().style()
+ .stroke_color(max_whisker.stroke_color())
+ .fill_color(max_whisker.fill_color())
+ .stroke_width(max_whisker.stroke_width());
+
+ // Set axis structure and colors.
+ g_element& g_axis_ptr = image.g(boxplot::BOX_AXIS)
+ .g();
+
+ g_axis_ptr.style()
+ .stroke_color(axis_whisker.stroke_color())
+ .fill_color(axis_whisker.fill_color())
+ .stroke_width(axis_whisker.stroke_width());
+
+ transform_y(min);
+ transform_y(max);
+
+ double half_length = length / 2.;
+ g_whisk_ptr.g(0)
+ .line(x-half_length, min, x + half_length, min);
+ g_whisk_ptr.g(1)
+ .line(x-half_length, max, x + half_length, max);
+
+ g_axis_ptr.line(x, min, x, max);
+
+ // Clip elements.
+ g_axis_ptr.clip_id(plot_window_clip_);
+ g_whisk_ptr.clip_id(plot_window_clip_);
+ } // void draw_whiskers
+
+ void draw_outliers(double x, const std::vector<double>& outliers,
+ const std::vector<double>& extreme_outliers,
+ const plot_point_style& mild_style, const plot_point_style& extreme_style)
+ { // Draw marker points for any mild and/or extreme outliers.
+ g_element& g_mild_ptr = image.g(boxplot::MILD_OUTLIERS).g();
+ g_element& g_ext_ptr = image.g(boxplot::EXTREME_OUTLIERS).g();
+
+ const svg_color c = mild_style.fill_color_;
+ g_mild_ptr.style().fill_color(mild_style.fill_color_)
+ .stroke_color(mild_style.stroke_color_);
+
+ g_ext_ptr.style().fill_color(extreme_style.fill_color_)
+ .stroke_color(extreme_style.stroke_color_);
+
+ std::vector<double>::const_iterator i;
+ double temp;
+ for(i = outliers.begin(); i != outliers.end(); ++i)
+ { // Mild outliers.
+ transform_y(temp = *i);
+ g_mild_ptr.circle(x, temp, 2);
+ }
+
+ for(i = extreme_outliers.begin(); i != extreme_outliers.end(); ++i)
+ { // Extreme outliers.
+ transform_y(temp = *i);
+ g_ext_ptr.circle(x, temp, 2);
+ }
+
+ // Clip elements.
+ g_mild_ptr.clip_id(plot_window_clip_);
+ g_ext_ptr.clip_id(plot_window_clip_);
+ } // void draw_outliers
+
+ void draw_boxplot(const svg_boxplot_series& series, double x_offset)
+ {
+ draw_whiskers(series.whisker_min_, series.whisker_max_,
+ series.whisker_length_, x_offset,
+ series.min_whisker_style_, series.max_whisker_style_,
+ series.axis_style_);
+
+ draw_box(series.q1_, series.q3_, x_offset, series.box_width_,
+ series.box_style_);
+
+ draw_median(series.median_, x_offset,
+ series.box_width_ - series.box_style_.stroke_width(),
+ series.median_style_);
+
+ draw_outliers(x_offset, series.outliers_, series.extreme_outliers_,
+ series.mild_outlier_, series.ext_outlier_);
+ } // void draw_boxplot
+
+ void update_image()
+ {
+ clear_all();
+
+ // Draw background.
+ image.g(boxplot::PLOT_BACKGROUND).push_back(
+ new rect_element(0, 0, image.x_size(), image.y_size()) );
+
+ draw_title();
+ calculate_plot_window();
+
+ // Define the clip path for the plot window.
+ // We don't want to allow too much overlap of the plot window lines,
+ // so allow the border
+
+ double margin = plot_window_border_.width_ * 1.; // Or more?
+ image.clip_path(rect_element(plot_left_ - margin, // margin left
+ plot_top_ - margin, // margin above top
+ plot_right_ - plot_left_ + margin, // incrased lengths for rect (not x, y)
+ plot_bottom_ - plot_top_ + margin),
+ plot_window_clip_);
+
+ draw_x_axis(); // Must do X-axis first.
+ draw_y_axis();
+
+ if(x_axis_.label_on_)
+ {
+ draw_x_label();
+ }
+
+ if(y_axis_.label_on_)
+ {
+ draw_y_label();
+ }
+
+ for(unsigned int i=0; i < series.size(); ++i)
+ { // Draw box'n'whiskers for each data series.
+ draw_boxplot(series[i],
+ plot_left_ + (plot_right_ - plot_left_) * ((double)(i + 1)) / (double)(series.size() + 1));
+ // Spaced evenly across the plot window width.
+ }
+ } // void update_image()
+
+
+ // Removed pending reimplementation of stylesheets.
+ //svg_boxplot& load_stylesheet(const std::string& file)
+ //{
+ // image.load_stylesheet(file);
+ // return *this;
+ //}
 
-svg_boxplot& write(std::string& file)
-{
+ svg_boxplot& write(const std::string& file)
+ {
     std::string filename(file); // Copy to avoid problems with const if need to append.
     if (filename.find(".svg") == std::string::npos)
     { // No file type suffix, so provide the default .svg.
@@ -656,327 +1368,352 @@
     std::ofstream fout(filename.c_str());
     if(fout.fail())
     {
- throw std::runtime_error("Unable to open "+ filename);
+ throw std::runtime_error("Unable to open "+ filename);
     }
+
+ image.image_filename(filename);
+ // Note filename for optional output as comment in the .svg file.
+ svg_boxplot::write(fout); // Use the ostream version.
     return *this;
-}
+ } // svg_boxplot& write(const std::string& file)
 
-svg_boxplot& write(std::ostream& s_out)
-{
+ svg_boxplot& write(std::ostream& s_out)
+ {
     update_image();
     image.write(s_out);
     return *this;
-}
+ }
 
-svg_boxplot& title_on(bool cmd)
-{
+ svg_boxplot& title_on(bool cmd)
+ {
     title_on_ = cmd;
     return *this;
-}
+ }
 
-svg_boxplot& y_label_on(bool cmd)
-{
- use_y_label = cmd;
+ svg_boxplot& x_label_on(bool cmd)
+ { // If Y axis name or label, for example: "length of thing".
+ x_axis_.label_on_ = cmd;
     return *this;
-}
+ }
 
-svg_boxplot& x_label_on(bool cmd)
-{
- use_x_label = cmd;
+ svg_boxplot& y_label_on(bool cmd)
+ {
+ y_axis_.label_on_ = cmd;
     return *this;
-}
+ }
 
-svg_boxplot& y_major_value_labels_side(int cmd)
-{
- use_y_major_labels = cmd;
+ svg_boxplot& y_major_labels_on(int cmd)
+ { //< 0 means to left or down (default), 0 (false) means none, > 0 means to right (or top).
+ y_ticks_.major_value_labels_side_ = cmd;
     return *this;
-}
+ }
 
-svg_boxplot& x_labels_on(bool cmd)
-{
- use_x_major_labels = cmd;
+ svg_boxplot& x_major_labels_on(int cmd)
+ { //< 0 means to left or down (default), 0 (false) means none, > 0 means to right (or top).
+ x_ticks_.major_value_labels_side_ = cmd;
     return *this;
-}
+ }
 
-svg_boxplot& y_major_tick_color(const svg_color& col)
-{
- image.get_g_element(boxplot::Y_MAJOR_TICKS).style().stroke_color(col);
+ svg_boxplot& y_major_tick_color(const svg_color& col)
+ {
+ image.g(boxplot::Y_MAJOR_TICKS).style().stroke_color(col);
     return *this;
-}
+ }
 
-svg_boxplot& x_tick_color(const svg_color& col)
-{
- image.get_g_element(boxplot::X_TICKS).style().stroke_color(col);
+ svg_boxplot& x_tick_color(const svg_color& col)
+ {
+ image.g(boxplot::X_TICKS).style().stroke_color(col);
     return *this;
-}
+ }
 
-svg_boxplot& y_minor_tick_color(const svg_color& col)
-{
- image.get_g_element(detail::PLOT_Y_MINOR_TICKS).style().stroke_color(col);
+ svg_boxplot& y_minor_tick_color(const svg_color& col)
+ {
+ image.g(detail::PLOT_Y_MINOR_TICKS).style().stroke_color(col);
     return *this;
-}
+ }
 
-svg_boxplot& title_color(const svg_color& col)
-{
- image.get_g_element(boxplot::TITLE).style().stroke_color(col);
+ svg_boxplot& title_color(const svg_color& col)
+ {
+ image.g(boxplot::PLOT_TITLE).style().stroke_color(col);
     return *this;
-}
+ }
 
-svg_boxplot& background_color(const svg_color& col)
-{
- image.get_g_element(boxplot::BACKGROUND).style().fill_color(col);
+ svg_boxplot& background_color(const svg_color& col)
+ { // "imageBackground"
+ image.g(boxplot::PLOT_BACKGROUND).style().fill_color(col);
     return *this;
-}
+ }
 
-svg_boxplot& background_border_color(const svg_color& col)
-{
- image.get_g_element(boxplot::BACKGROUND).style().stroke_color(col);
+ svg_boxplot& background_border_color(const svg_color& col)
+ { // "imageBackground"
+ image.g(boxplot::PLOT_BACKGROUND).style().stroke_color(col);
     return *this;
-}
+ }
 
-svg_boxplot& plot_background_color(const svg_color& col)
-{
- image.get_g_element(boxplot::PLOT_BACKGROUND).style().fill_color(col);
+ svg_boxplot& plot_background_color(const svg_color& col)
+ { // Plot Window "plotBackground"
+ image.g(boxplot::PLOT_WINDOW_BACKGROUND).style().fill_color(col);
     return *this;
-}
+ }
 
-svg_boxplot& plot_background_border_color(const svg_color& col)
-{
- image.get_g_element(boxplot::PLOT_BACKGROUND).style().stroke_color(col);
+ svg_boxplot& plot_border_color(const svg_color& col)
+ { // Plot window "plotBackground"
+ image.g(boxplot::PLOT_WINDOW_BACKGROUND).style().stroke_color(col);
     return *this;
-}
-
-svg_boxplot& y_range(double y1, double y2)
-{
- y_min_ = y1;
- y_max_ = y2;
+ }
 
- if(y2 <= y1)
+ svg_boxplot& y_range(double min_y, double max_y)
+ {
+ if (!boost::math::isfinite(min_y))
+ {
+ throw std::runtime_error("Y range: min not finite!");
+ }
+ if (!boost::math::isfinite(max_y))
     {
- throw std::runtime_error("Illegal Argument: X scale: x2 < x1");
+ throw std::runtime_error("Y range: max not finite!");
     }
 
+ if(max_y <= min_y)
+ { // max <= min.
+ throw std::runtime_error("Y range: y max <= y min!");
+ }
+ if((max_y - min_y) < std::numeric_limits<double>::epsilon() * 1000)
+ { // Range too small to display.
+ throw std::runtime_error("Y range too small!" );
+ }
+ y_axis_.min_ = min_y;
+ y_axis_.max_ = max_y;
+ y_autoscale_ = false;
     return *this;
-}
+ } // svg_boxplot& y_range(double y1, double y2)
 
-svg_boxplot& y_label(const std::string& str)
-{
+ svg_boxplot& y_label(const std::string& str)
+ {
     y_label_info_.text(str);
     return *this;
-}
+ }
 
-svg_boxplot& image_size(unsigned int x, unsigned int y)
-{
+ svg_boxplot& image_size(unsigned int x, unsigned int y)
+ {
     image.image_size(x, y);
     return *this;
-}
+ }
 
-svg_boxplot& y_label_size(unsigned int size)
-{
- y_label_info_.font_size(size);
+ svg_boxplot& y_label_font_size(unsigned int size)
+ {
+ y_axis_label_style_.font_size(size);
     return *this;
-}
+ }
 
-svg_boxplot& y_label_color(const svg_color& col)
-{
- image.get_g_element(boxplot::Y_LABEL).style().stroke_color(col);
+ svg_boxplot& y_label_color(const svg_color& col)
+ {
+ image.g(boxplot::Y_LABEL).style().fill_color(col);
     return *this;
-}
+ }
 
-svg_boxplot& title(const std::string& str)
-{
- y_label_info_.text(str);
+ svg_boxplot& title(const std::string& str)
+ {
+ title_info_.text(str);
     return *this;
-}
+ }
 
-svg_boxplot& title_size(unsigned int size)
-{
- y_label_info_.font_size(size);
+ svg_boxplot& title_size(unsigned int size)
+ {
+ y_label_info_.style().font_size(size);
     return *this;
-}
+ }
 
-svg_boxplot& x_label(const std::string& str)
-{
+ svg_boxplot& x_label(const std::string& str)
+ {
     x_label_info_.text(str);
     return *this;
-}
+ }
 
-svg_boxplot& x_label_size(unsigned int size)
-{
- x_label_info_.font_size(size);
+ svg_boxplot& x_label_size(unsigned int size)
+ {
+ x_label_info_.style().font_size(size);
     return *this;
-}
+ }
 
-svg_boxplot& x_label_color(const svg_color& col)
-{
- image.get_g_element(boxplot::X_LABEL).style().stroke_color(col);
+ svg_boxplot& x_label_color(const svg_color& col)
+ {
+ image.g(boxplot::X_LABEL).style().fill_color(col);
     return *this;
-}
+ }
 
-svg_boxplot& y_major_interval(double inter)
-{
- y_major_interval_ = inter;
-
- return *this;
-}
-
-svg_boxplot& x_tick_length(unsigned int length)
-{
- x_major_tick_length_ = length;
- return *this;
-}
+ svg_boxplot& y_major_interval(double inter)
+ {
+ y_ticks_.major_interval_ = inter;
 
-svg_boxplot& y_major_tick_length(unsigned int length)
-{
- y_major_tick_length_ = length;
     return *this;
-}
+ }
 
-svg_boxplot& y_minor_tick_length(unsigned int length)
-{
- y_minor_tick_length_ = length;
+ svg_boxplot& x_tick_length(unsigned int length)
+ {
+ x_ticks_.major_tick_length_ = length;
     return *this;
-}
+ }
 
-svg_boxplot& y_num_minor_ticks(unsigned int num)
-{
- y_num_minor_ticks_ = num;
+ svg_boxplot& y_major_tick_length(unsigned int length)
+ {
+ y_ticks_.major_tick_length_ = length;
     return *this;
-}
+ }
 
-svg_boxplot& x_tick_wide(unsigned int width)
-{
- image.get_g_element(boxplot::X_TICKS).style().stroke_width(width);
+ svg_boxplot& y_minor_tick_length(unsigned int length)
+ {
+ y_ticks_.minor_tick_length_ = length;
     return *this;
-}
+ }
 
-svg_boxplot& y_major_tick_width(unsigned int width)
-{
- image.get_g_element(boxplot::Y_MAJOR_TICKS).style().stroke_width(width);
+ svg_boxplot& y_num_minor_ticks(unsigned int num)
+ {
+ y_ticks_.num_minor_ticks_ = num;
     return *this;
-}
+ }
 
-svg_boxplot& y_minor_tick_width(unsigned int width)
-{
- image.get_g_element(boxplot::Y_MINOR_TICKS).style().stroke_width(width);
+ svg_boxplot& x_tick_wide(unsigned int width)
+ {
+ image.g(boxplot::X_TICKS).style().stroke_width(width);
     return *this;
-}
-
-unsigned int image_x_size()
-{ // TODO is this needed?
- return image.x_size();
-}
-
-unsigned int image_y_size()
-{
- return image.x_size();
-}
+ }
 
-std::string title()
-{
- return title_info_.text();
-}
+ svg_boxplot& y_major_tick_width(unsigned int width);
+ svg_boxplot& y_minor_tick_width(unsigned int width);
+ unsigned int image_x_size();
+ unsigned int image_y_size();
+ std::string title();
+ bool x_label_on();
+ int x_major_labels();
+ // get color information
+ svg_color title_color();
+ svg_color background_color();
+ svg_color background_border_color();
+ svg_color plot_background_color();
+ svg_color plot_border_color();
+ svg_color x_label_color();
+ svg_color x_tick_color();
+ double x_tick_length();
+ double x_major_tick_width();
+ std::string x_label_text();
+ std::string y_label_text();
 
-bool x_label()
-{
- return use_x_label;
-}
+ svg& get_svg()
+ {
+ update_image();
+ return image;
+ }
 
-bool x_major_labels()
-{
- return use_x_major_labels;
-}
+ template <class T>
+ svg_boxplot_series& plot(const T& container, const std::string& title = "")
+ {
+ series.push_back(
+ svg_boxplot_series(
+ boost::make_transform_iterator(container.begin(), detail::boost_default_convert()),
+ boost::make_transform_iterator(container.end(), detail::boost_default_convert()),
+ title)
+ // whisker, width, box, median, axis, min_whisker, max_whisker, mild_outlier, ext_outlier
+ );
+ return series[series.size() - 1]; // Number of data series added so far.
+ } // svg_boxplot_series& plot(const T& container, const std::string& title )
 
-// color information
-svg_color title_color()
-{
- return image.get_g_element(boxplot::TITLE).style().stroke_color();
-}
+}; // class svg_boxplot
 
-svg_color background_color()
-{
- return image.get_g_element(boxplot::BACKGROUND).style().fill_color();
-}
+// Definitions to match declarations above for separate file.
 
-svg_color background_border_color()
-{
- return image.get_g_element(boxplot::BACKGROUND).style().stroke_color();
-}
+ svg_boxplot& svg_boxplot::y_major_tick_width(unsigned int width)
+ {
+ image.g(boxplot::Y_MAJOR_TICKS).style().stroke_width(width);
+ return *this;
+ }
 
-svg_color plot_background_color()
-{
- return image.get_g_element(boxplot::PLOT_BACKGROUND).style().fill_color();
-}
+ svg_boxplot& svg_boxplot::y_minor_tick_width(unsigned int width)
+ {
+ image.g(boxplot::Y_MINOR_TICKS).style().stroke_width(width);
+ return *this;
+ }
 
-svg_color x_label_color()
-{
- return image.get_g_element(boxplot::X_LABEL).style().stroke_color();
-}
+ unsigned int svg_boxplot::image_x_size()
+ {
+ return image.x_size();
+ }
 
-svg_color x_tick_color()
-{
- return image.get_g_element(boxplot::X_TICKS).style().stroke_color();
-}
+ unsigned int svg_boxplot::image_y_size()
+ {
+ return image.x_size();
+ }
 
-unsigned int x_tick_length()
-{
- return x_major_tick_length_;
-}
+ std::string svg_boxplot::title()
+ {
+ return title_info_.text();
+ }
 
-unsigned int x_major_tick_width()
-{
- return image.get_g_element(boxplot::X_TICKS).style().stroke_width();
-}
+ bool svg_boxplot::x_label_on()
+ {
+ return x_axis_.label_on_;
+ }
+
+ int svg_boxplot::x_major_labels()
+ {
+ return y_ticks_.major_value_labels_side_;
+ }
+
+ svg_color svg_boxplot::title_color()
+ {
+ return image.g(boxplot::PLOT_TITLE).style().fill_color();
+ }
+
+ svg_color svg_boxplot::background_color()
+ {
+ return image.g(boxplot::PLOT_BACKGROUND).style().fill_color();
+ }
+
+ svg_color svg_boxplot::background_border_color()
+ {
+ return image.g(boxplot::PLOT_BACKGROUND).style().stroke_color();
+ }
+
+ svg_color svg_boxplot::plot_background_color()
+ {
+ return image.g(boxplot::PLOT_WINDOW_BACKGROUND).style().fill_color();
+ }
+
+ svg_color svg_boxplot::plot_border_color()
+ {
+ return image.g(boxplot::PLOT_WINDOW_BACKGROUND).style().stroke_color();
+ }
+
+ svg_color svg_boxplot::x_label_color()
+ {
+ return image.g(boxplot::X_LABEL).style().fill_color();
+ }
+
+ svg_color svg_boxplot::x_tick_color()
+ {
+ return image.g(boxplot::X_TICKS).style().stroke_color();
+ }
+
+ double svg_boxplot::x_tick_length()
+ {
+ return x_ticks_.major_tick_length_;
+ }
+
+ double svg_boxplot::x_major_tick_width()
+ {
+ return image.g(boxplot::X_TICKS).style().stroke_width();
+ }
 
-std::string x_label_text()
-{
+ std::string svg_boxplot::x_label_text()
+ {
     return x_label_info_.text();
-}
-
-svg& get_svg()
-{
- update_image();
+ }
 
- return image;
-}
+ std::string svg_boxplot::y_label_text()
+ {
+ return y_label_info_.text();
+ }
 
-#if defined (BOOST_MSVC)
-# pragma warning(push)
-# pragma warning(disable: 4100) // "'boost_parameter_enabler_argument' : unreferenced formal parameter"
-#endif
 
-BOOST_PARAMETER_MEMBER_FUNCTION
-(
- (void),
- plot,
- keyword,
- (required
- (container, *)
- (title, (const std::string&))
- )
- (optional
- (box, (const svg_style&), svg_style(white, black, 1))
- (median, (const svg_style&), svg_style(white, black, 1))
- (axis, (const svg_style&), svg_style(white, black, 1))
- (min_whisker, (const svg_style&), svg_style(white, black, 1))
- (max_whisker, (const svg_style&), svg_style(white, black, 1))
- (mild_outlier, (const plot_point_style&), plot_point_style(black, black, 0))
- (ext_outlier, (const plot_point_style&), plot_point_style(white, black, 1))
- (whisker, (unsigned int), 30)
- (width, (unsigned int), 60)
- (fnctr, *, detail::boost_default_convert())
- )
-)
-{
- series.push_back(
- svg_boxplot_series(
- boost::make_transform_iterator(container.begin(), fnctr),
- boost::make_transform_iterator(container.end(), fnctr), title,
- whisker, width, box, median, axis, min_whisker, max_whisker, mild_outlier,
- ext_outlier
- ));
-}
-}; // class svg_boxplot
 
 #if defined (BOOST_MSVC)
 # pragma warning(pop)

Modified: sandbox/SOC/2007/visualization/boost/svg_plot/svg_color.hpp
==============================================================================
--- sandbox/SOC/2007/visualization/boost/svg_plot/svg_color.hpp (original)
+++ sandbox/SOC/2007/visualization/boost/svg_plot/svg_color.hpp 2008-05-24 12:22:38 EDT (Sat, 24 May 2008)
@@ -19,15 +19,15 @@
 namespace svg
 {
 
-// -----------------------------------------------------------------
-// Deals with colors that have special names. The reason that the
-// underscore separator convention does not match the normal Boost format
-// is that these names that are specified by the SVG standard.
-// http://www.w3.org/TR/SVG/types.html#ColorKeywords
-// tan is also renamed to tanned to avoid clash with function tan in math.h
-// -----------------------------------------------------------------
-enum svg_color_constant
-{
+ // -----------------------------------------------------------------
+ // Deals with colors that have special names. The reason that the
+ // underscore separator convention does not match the normal Boost format
+ // is that these names that are specified by the SVG standard.
+ // http://www.w3.org/TR/SVG/types.html#ColorKeywords
+ // tan is also renamed to tanned to avoid clash with function tan in math.h
+ // -----------------------------------------------------------------
+ enum svg_color_constant
+ {
     aliceblue, antiquewhite, aqua, aquamarine, azure, beige,
     bisque, black, blanchedalmond, blue, blueviolet, brown,
     burlywood, cadetblue, chartreuse, chocolate, coral,
@@ -55,81 +55,97 @@
     salmon, sandybrown, seagreen, seashell, sienna, silver,
     skyblue, slateblue, slategray, slategrey, snow, springgreen,
     steelblue, tanned,
- // tan, // Note that tan would clash with tan in math.h
+ // tan, // Note that tan would clash with geometric tan in math.h!
     teal, thistle, tomato, turquoise, violet,
     wheat, white, whitesmoke, yellow, yellowgreen,
     blank // 'NotAColor' == 147
-}; // enum svg_color_constant
+ }; // enum svg_color_constant
 
-// Forward declarations in this module (see svg_fwd):
-struct svg_color;
-void constant_to_rgb(svg_color_constant c,
- unsigned char& r, unsigned char& g, unsigned char& b);
-std::ostream& operator<< (std::ostream&, const svg_color&);
-
-// --------------------------------------------------------------------
-// svg_color is the struct that contains information about RGB colors.
-//
-// For the constructor, the SVG standard specifies that numbers
-// outside the normal rgb range are to be accepted,
-// but are constrained to acceptable range of integer values [0, 255].
-// --------------------------------------------------------------------
+ // Forward declarations in this module (see svg_fwd):
+ struct svg_color;
+ void constant_to_rgb(svg_color_constant c,
+ unsigned char& r, unsigned char& g, unsigned char& b);
+ std::ostream& operator<< (std::ostream&, const svg_color&);
+
+ // --------------------------------------------------------------------
+ // svg_color is the struct that contains information about RGB colors.
+ //
+ // For the constructor, the SVG standard specifies that numbers
+ // outside the normal rgb range are to be accepted,
+ // but are constrained to acceptable range of integer values [0, 255].
+ // --------------------------------------------------------------------
 
-struct svg_color
-{
+ struct svg_color
+ {
     friend std::ostream& operator<< (std::ostream& os, const svg_color& rhs);
 
     unsigned char r; // unsigned char provides range [0 to 255].
     unsigned char g;
     unsigned char b;
- bool blank; // true means "Not to be displayed" a 'pseudo-color'.
- // TODO seems to display as black? - Need a check if is_blank == true?
+ bool is_blank; // true means "Not to be displayed" a 'pseudo-color'.
+ // If true should display & write as "none".
 
- svg_color(int red, int green, int blue) : blank(false)
+ svg_color(int red, int green, int blue) : is_blank(false)
     { // Constrain rgb to [0 .. 255]
- red = ( red < 0 ) ? 0 : red;
- green = ( green < 0 ) ? 0 : green;
- blue = ( blue < 0 ) ? 0 : blue;
- r = (unsigned char)(( red > 255 ) ? 255 : red);
- g = (unsigned char)(( green > 255 ) ? 255 : green);
- b = (unsigned char)(( blue > 255 ) ? 255 : blue);
+ red = ( red < 0 ) ? 0 : red;
+ green = ( green < 0 ) ? 0 : green;
+ blue = ( blue < 0 ) ? 0 : blue;
+ r = (unsigned char)(( red > 255 ) ? 255 : red);
+ g = (unsigned char)(( green > 255 ) ? 255 : green);
+ b = (unsigned char)(( blue > 255 ) ? 255 : blue);
     }
 
- svg_color(bool is_blank) : blank(is_blank)
- { // Permits blank (=true) as a (non-)color.
- // svg_color(true) returns blank (and color value as below).
- // svg_color(false) returns color values below and NOT blank.
- // So plot.area_fill(true) will be a blank == no fill!
- // So plot.area_fill(false) will be a default(black) fill!
- // This is somewhat counter-intuitive!
- r = 0; // Safer to assign *some* value to rgb? zero, or 255?
+ svg_color(bool is) : is_blank(!is)
+ { // Constructor from bool permits svg_color my_blank(false) as a (non-)color.
+ // with same effect as svg_color my_blank(blank);
+ // color is set to zeros (black) rather than undefined.
+ // my_blank(true) also set color to default (black), but is_blank is false,
+ // so effect is same as svg_color my_blank(black).
+
+ //// So plot.area_fill(true) will be a blank == no fill!
+ //// So plot.area_fill(false) will be a default(black) fill!
+ //// This is somewhat counter-intuitive!
+ //// NOw changed - svg_color(true) means default (black?)
+ //// svg_color(false) means blank
+ r = 0; // Safer to assign *some* value to rgb: zero, or 255 or something
       g = 0; // rather than leaving them random?
       b = 0; // Default 'blank' color 0,0,0 is black.
- }
+ } // svg_color(bool is)
 
- svg_color(svg_color_constant col) : blank(false)
- {
+ svg_color(svg_color_constant col) // : is_blank(false)
+ { // Set a color, including blank.
+ if (col == blank)
+ { // NotAColor.
+ is_blank = true;
+ r = 255; // Safer to assign *some* value to rgb: zero, or 255 or something
+ g = 255; // rather than leaving them random.
+ b = 255; // Default 'blank' color here is white.
+ }
+ else
+ { // Proper color.
+ is_blank = false;
         constant_to_rgb(col, r, g, b);
+ }
     }
 
- void write(std::ostream& rhs)
+ void write(std::ostream& os)
     { // Write color in svg format, for example, rgb(127,255,212).
- if(!blank)
- {
- rhs << "rgb(" << (unsigned int)r << ","
- << (unsigned int) g << ","
- << (unsigned int)b << ")" ;
- }
- else
- {
- rhs << "blank";
- }
- // Usage: my_color.write(cout); cout << endl; outputs: rgb(127,255,212)
- } // void write(std::ostream& rhs)
+ if(!is_blank)
+ {
+ os << "rgb(" << (unsigned int)r << ","
+ << (unsigned int) g << ","
+ << (unsigned int)b << ")" ;
+ }
+ else
+ {
+ os << "none";
+ }
+ // Usage: my_color.write(cout); cout << endl; outputs: rgb(127,255,212)
+ } // void write(std::ostream& os)
 
     bool operator== (const svg_color& rhs)
     {
- if ((blank) || (rhs.blank == true))
+ if ((is_blank) || (rhs.is_blank == true))
       { // Make blank a sort of NaN, that never compares true?
         // not even if both rhs and lhs are blank.
         return false;
@@ -139,59 +155,59 @@
 
     bool operator!= (const svg_color& rhs)
     {
- if ((blank) || (rhs.blank == true))
+ if ((is_blank) || (rhs.is_blank == true))
       { // Make blank a sort of NaN, that never compares true?
         // not even if both rhs and lhs are blank.
         return true;
       }
       return (r != rhs.r) || (g != rhs.g) || (b != rhs.b);
     }
-}; // struct svg_color
+ }; // struct svg_color
 
-// Note operator== and operator<< are both needed to use Boost.Test.
+ // Note operator== and operator<< are both needed to use Boost.Test.
 
-bool operator== (const svg_color& lhs, const svg_color& rhs)
-{ // Note operator== and operator << both needed to use Boost.Test.
- if ((rhs.blank == true) || (rhs.blank == true))
- { // Make blank a sort of NaN, that never compares true?
- // not even if both rhs and lhs are blank.
- return false;
+ bool operator== (const svg_color& lhs, const svg_color& rhs)
+ { // Note operator== and operator << both needed to use Boost.Test.
+ if ((rhs.is_blank == true) || (rhs.is_blank == true))
+ { // Make blank a sort of NaN, that never compares true?
+ // not even if both rhs and lhs are blank.
+ return false;
+ }
+ return (lhs.r == rhs.r) && (lhs.g == rhs.g) && (lhs.b == rhs.b);
   }
- return (lhs.r == rhs.r) && (lhs.g == rhs.g) && (lhs.b == rhs.b);
-}
 
-bool operator!= (const svg_color& lhs, const svg_color& rhs)
-{ // Note operator== and operator << both needed to use Boost.Test.
- if ((rhs.blank == true) || (rhs.blank == true))
- { // Make blank a sort of NaN, that never compares true?
- // not even if both rhs and lhs are blank.
- return true;
+ bool operator!= (const svg_color& lhs, const svg_color& rhs)
+ { // Note operator== and operator << both needed to use Boost.Test.
+ if ((rhs.is_blank == true) || (rhs.is_blank == true))
+ { // Make blank a sort of NaN, that never compares true?
+ // not even if both rhs and lhs are blank.
+ return true;
+ }
+ return (lhs.r == rhs.r) || (lhs.g == rhs.g) || (lhs.b == rhs.b);
   }
- return (lhs.r == rhs.r) || (lhs.g == rhs.g) || (lhs.b == rhs.b);
-}
 
-std::ostream& operator<< (std::ostream& os, const svg_color& color)
-{ //
- if(!color.blank)
+ std::ostream& operator<< (std::ostream& os, const svg_color& color)
+ { //
+ if(!color.is_blank)
     {
- os << "RGB("
- << (unsigned int)color.r << ","
- << (unsigned int)color.g << ","
- << (unsigned int)color.b << ")" ;
+ os << "RGB(" // Note deliberate uppercase to show difference between write and operator<<
+ << (unsigned int)color.r << ","
+ << (unsigned int)color.g << ","
+ << (unsigned int)color.b << ")" ;
     }
     else
     {
- os << "blank";
+ os << "blank";
     }
     // Usage: svg_color my_color(127, 255, 212); cout << "my_color " << my_color << endl;
     // Outputs: my_color RGB(127,255,212)
     // cout << "magenta " << svg_color(magenta) << endl;
     // but caution! cout << magenta << endl; outputs 85 because magenta is an enum!
     return os;
-} // std::ostream& operator<<
+ } // std::ostream& operator<<
 
-svg_color color_array[] =
-{
+ svg_color color_array[] =
+ {
     svg_color(240, 248, 255), // aliceblue
     svg_color(250, 235, 215), // antiquewhite
     svg_color(0 , 255, 255), // aqua
@@ -339,31 +355,24 @@
     svg_color(245, 245, 245), // whitesmoke
     svg_color(255, 255, 0 ), // yellow
     svg_color(154, 205, 50 ), // yellowgreen
- svg_color(false) // blank - "Not to be displayed" pseudo-color.
-}; // svg_color color_array[]
+ svg_color(true) // blank - "Not to be displayed" pseudo-color.
+ }; // svg_color color_array[]
 
-void constant_to_rgb(svg_color_constant c,
- unsigned char& r, unsigned char& g, unsigned char& b)
-{ // Convert a named SVG standard color to update three rgb variables.
+ void constant_to_rgb(svg_color_constant c,
+ unsigned char& r, unsigned char& g, unsigned char& b)
+ { // Convert a named SVG standard color to update three rgb variables.
+ // Assume is c NOT the blank color.
+ BOOST_ASSERT(c != blank);
     svg_color color(color_array[c]);
- if(!color.blank)
- {
- r = color.r;
- g = color.g;
- b = color.b;
- }
- else
- { // Unwise to leave r, g, & b unchanged?
- r = 255; // Set all to white (255) or 0?
- g = 255;
- b = 255;
- }
-} // void constant_to_rgb
+ r = color.r;
+ g = color.g;
+ b = color.b;
+ } // void constant_to_rgb
 
-svg_color constant_to_rgb(svg_color_constant c)
-{
+ svg_color constant_to_rgb(svg_color_constant c)
+ {
     return color_array[c];
-}
+ }
 
 } // svg
 } // boost

Modified: sandbox/SOC/2007/visualization/boost/svg_plot/svg_style.hpp
==============================================================================
--- sandbox/SOC/2007/visualization/boost/svg_plot/svg_style.hpp (original)
+++ sandbox/SOC/2007/visualization/boost/svg_plot/svg_style.hpp 2008-05-24 12:22:38 EDT (Sat, 24 May 2008)
@@ -2,6 +2,7 @@
 
 // Copyright Jacob Voytko 2007
 // Copyright Paul A. Bristow 2008
+
 // Use, modification and distribution are subject to the
 // Boost Software License, Version 1.0.
 // (See accompanying file LICENSE_1_0.txt
@@ -33,21 +34,25 @@
 // Forward declarations of classes in svg_style.hpp
 class svg_style;
 class text_style;
+class value_style;
 class plot_point_style;
 class plot_line_style;
 class axis_line_style;
 class ticks_labels_style;
+class box_style;
 
 enum rotate_style
 { // Rotation in degrees from horizontal.
- horizontal = 0, // normal left to right.
+ horizontal = 0, // normal left to right, centered.
   uphill = -45, // slope up.
   upward = -90, // vertical writing up.
- backup = -135, // slope up backwards.
+ backup = -135, // // slope up backwards - upside down!
+ leftward= -180, // horizontal to left.
+ rightward = 360, // horizontal to right.
   downhill = 45, // slope down.
   downward = 90, // vertical writing down.
   backdown = 135, // slope down backwards.
- upsidedown = 180 // == -180
+ upsidedown = 180 // == -180 - upside down!
 };
 
 const std::string strip_e0s(std::string s);
@@ -59,134 +64,158 @@
 // This is the style information for any <g> tag.
 // This may be expanded to include more data from the SVG standard.
 // -----------------------------------------------------------------
+
+// There are some strange effects for text on some browsers
+// (Firefox especially) when only stroke is specified.
+// fill is interpreted as black, and the font outline is fuzzy and bolder.
+// <g id="title" stroke="rgb(255,0,0)"> .. is red border and black fill.
+// (because created as a graphic not a builtin font?)
+// <g id="title" fill="rgb(255,0,0)"> .. is red sharp font.
+// <g id="title" stroke="rgb(255,0,0)" fill="rgb(255,0,0)"> red and red fill also fuzzy.
+// So for text, only specific the fill unless a different outline is really wanted.
+// Defaults for text provide a built-in glyph, for example for title:
+// <g id="title">
+// <text x="250" y="36" text-anchor="middle" font-size="18" font-family="Verdana">
+// Plot of data
+// </text>
+// </g>
+// and this is not a graphic.
+
 class svg_style
-{
+{ // Holds the basic stroke, fill colors and width, and their switches.
   friend std::ostream& operator<< (std::ostream&, svg_style&);
 
 private: // Accesses only by set and get member functions below.
- // Names changed so private data member variables end with _,
+ // Private data member variables names end with _,
   // to permit use of names for set & get member functions.
- svg_color fill_;
- svg_color stroke_;
- double width_; // Only valid if > 0 & width_on_ == true
- bool fill_on_; // true means there is fill info.
- bool stroke_on_;
- bool width_on_;
+ svg_color stroke_;
+ svg_color fill_;
+ double width_; // Only valid if > 0 & width_on_ == true
+ bool stroke_on_;
+ bool fill_on_; // true means there is fill info.
+ bool width_on_;
     
 public:
- svg_style() :
- fill_(blank), // Should avoid any fill =
- //fill_(svg_color(0, 0, 0)), // == black
- stroke_(svg_color(0, 0, 0)), // == black
- width_(0), // No width
- fill_on_(false), stroke_on_(false), width_on_(false)
- { // Default constructor initialises all private data.
- }
+ // Constructors:
+ svg_style();
+ svg_style(const svg_color& stroke, const svg_color& fill, unsigned int width);
+
+ // Set svg_style member functions
+ // to set fill color and stroke color & width.
+ svg_style& stroke_color(const svg_color& col) ;
+ svg_style& fill_color(const svg_color& col);
+ svg_style& stroke_width(double width);
+ svg_style& fill_on(bool is);
+ svg_style& stroke_on(bool is);
+ svg_style& width_on(bool is);
+
+ // Get svg_style member functions
+ // to return fill color and stroke color & width.
+ svg_color fill_color() const;
+ svg_color stroke_color() const;
+ double stroke_width() const;
+ bool fill_on() const;
+ bool stroke_on() const;
+ bool width_on() const;
+
+ void write(std::ostream& os); // Output to file or stream.
+
+ // operators useful for testing at least.
+ bool operator==(svg_style& s);
+ bool operator!=(svg_style& s);
 
- svg_style(const svg_color& fill, const svg_color& stroke, unsigned int width = 0) :
- fill_(fill), stroke_(stroke), width_(width),
- fill_on_(false), stroke_on_(false), width_on_(false)
- { // Construct with specified fill and stroke colors, and width.
- }
+}; // class svg_style
 
- // Set & get svg_style member functions
- // to return fill color and stroke color & width.
- svg_color fill_color() const
- {
- return svg_color(fill_);
- }
 
- svg_color stroke_color() const
- {
- return svg_color(stroke_);
- }
+// class svg Definitions.
 
- double stroke_width() const
- {
- return width_;
- }
+ svg_style::svg_style(const svg_color& stroke, const svg_color& fill, unsigned int width)
+ :
+ stroke_(stroke), fill_(fill), width_(width),
+ stroke_on_(false), fill_on_(false), width_on_(false)
+ { // Construct with specified fill and stroke colors, and width.
+ }
 
- bool fill_on() const
- {
- return fill_on_;
- }
+ svg_style::svg_style()
+ :
+ stroke_(svg_color(0, 0, 0)), // == black.
+ fill_(blank),
+ width_(0), // No width specified.
+ fill_on_(false), stroke_on_(false), width_on_(false)
+ { // Default constructor initialises all private data.
+ }
 
- svg_style& fill_on(bool is)
- {
- fill_on_ = is;
- return *this; // Make chainable.
- }
+ svg_color svg_style::fill_color() const
+ {
+ return svg_color(fill_);
+ }
 
- bool stroke_on() const
- {
- return stroke_on_;
- }
+ svg_color svg_style::stroke_color() const
+ {
+ return svg_color(stroke_);
+ }
 
- svg_style& stroke_on(bool is)
- {
- stroke_on_ = is;
- return *this; // Make chainable.
- }
+ double svg_style::stroke_width() const
+ {
+ return width_;
+ }
 
- bool width_on() const
- {
- return width_on_;
- }
+ bool svg_style::fill_on() const
+ {
+ return fill_on_;
+ }
 
- svg_style& width_on(bool is)
- {
- width_on_ = is;
- return *this; // Make chainable.
- }
-
- // Set svg_style member functions to set fill, stroke & width.
- svg_style& fill_color(const svg_color& col)
- {
- fill_ = col;
- fill_on_ = ! col.blank; // If blank fill is off or "none".
- return *this; // Make chainable.
- }
+ svg_style& svg_style::fill_on(bool is)
+ { // fill is wanted.
+ fill_on_ = is;
+ return *this; // Make chainable.
+ }
 
- svg_style& stroke_color(const svg_color& col)
- {
- stroke_ = col;
- stroke_on_ = true; // Assume want a stroke if color is set.
- return *this; // Make chainable.
- }
+ bool svg_style::stroke_on() const
+ {
+ return stroke_on_;
+ }
 
- svg_style& stroke_width(double width)
- {
- width_ = width;
- width_on_ = ((width > 0) ? true : false);
- return *this; // Make chainable.
- }
-
- void write(std::ostream& rhs)
- { // Write any stroke, fill colors and/or width info (start with space).
- if(stroke_on_)
- {
- rhs << " stroke=\"";
- stroke_.write(rhs);
- rhs << "\"";
- }
- if(fill_on_ && (fill_ != blank))
- { // Don't add fill info if color is blank.
- rhs << " fill=\"";
- fill_.write(rhs);
- rhs << "\"";
- }
- if(width_on_ && (width_ > 0))
- { // We never want a 0 (or <0) width output?
- rhs << " stroke-width=\""
- << width_
- << "\"";
- }
- // Examples: <g id="yMinorTicks" stroke="rgb(0,0,0)" stroke-width="1">
- } // void write
+ svg_style& svg_style::stroke_on(bool is)
+ {
+ stroke_on_ = is;
+ return *this; // Make chainable.
+ }
 
- // operators needed for testing at least.
+ bool svg_style::width_on() const
+ {
+ return width_on_;
+ }
 
- bool operator==(svg_style& s)
+ svg_style& svg_style::width_on(bool is)
+ {
+ width_on_ = is;
+ return *this; // Make chainable.
+ }
+
+ // Set svg_style member functions to set fill, stroke & width.
+ svg_style& svg_style::stroke_color(const svg_color& col)
+ {
+ stroke_ = col;
+ stroke_on_ = true; // Assume want a stroke if color is set.
+ return *this; // Make chainable.
+ }
+
+ svg_style& svg_style::fill_color(const svg_color& col)
+ {
+ fill_ = col;
+ fill_on_ = ! col.is_blank; // If blank fill is off or "none".
+ return *this; // Make chainable.
+ }
+
+ svg_style& svg_style::stroke_width(double width)
+ {
+ width_ = width;
+ width_on_ = ((width > 0) ? true : false);
+ return *this; // Make chainable.
+ }
+
+ bool svg_style::operator==(svg_style& s)
   {
      return (s.fill_color() == fill_)
        && (s.stroke_color() == stroke_)
@@ -196,7 +225,7 @@
        && (s.width_on() == width_on_);
    }
 
- bool operator!=(svg_style& s)
+ bool svg_style::operator!=(svg_style& s)
    {
      return (s.fill_color() != fill_)
        || (s.stroke_color() != stroke_)
@@ -205,22 +234,45 @@
        || (s.stroke_on() != stroke_on_)
        || (s.width_on() != width_on_);
    }
-}; // class svg_style
 
-std::ostream& operator<< (std::ostream& os, svg_style& s)
-{ //
- os << "svg_style("
- << s.fill_ << ", "
- << s.stroke_ << ", "
- << s.width_ << ", " // italic
- << ((s.fill_on_) ? "fill, " : "no fill, ")
- << ((s.stroke_on_) ? "stroke, " : "no stroke, ")
- << ((s.fill_on_) ? "width)" : "no width)");
- // Usage: svg_style my_svg_style; cout << my_svg_style << endl;
- // Outputs: svg_style(RGB(0,0,0), RGB(0,0,0), 0, no fill, no stroke, no width)
+ std::ostream& operator<< (std::ostream& os, svg_style& s)
+ { //
+ os << "svg_style("
+ << s.fill_ << ", "
+ << s.stroke_ << ", "
+ << s.width_ << ", " // italic
+ << ((s.fill_on_) ? "fill, " : "no fill, ")
+ << ((s.stroke_on_) ? "stroke, " : "no stroke, ")
+ << ((s.fill_on_) ? "width)" : "no width)");
+ // Usage: svg_style my_svg_style; cout << my_svg_style << endl;
+ // Outputs: svg_style(RGB(0,0,0), RGB(0,0,0), 0, no fill, no stroke, no width)
+ return os;
+ } // std::ostream& operator<<
+
+ void svg_style::write(std::ostream& os)
+ { // Write any stroke, fill colors and/or width info (start with space).
+ if(stroke_on_)
+ {
+ os << " stroke=\"";
+ stroke_.write(os);
+ os << "\"";
+ }
+ if(fill_on_) // && (fill_ != blank))
+ { // Don't add fill info if color is blank.
+ os << " fill=\"";
+ fill_.write(os);
+ os << "\"";
+ }
+
+ if(width_on_ && (width_ > 0))
+ { // We never want a 0 (or <0) width output?
+ os << " stroke-width=\""
+ << width_
+ << "\"";
+ }
+ // Examples: <g id="yMinorTicks" stroke="rgb(0,0,0)" stroke-width="1">
+ } // void write
 
- return os;
-} // std::ostream& operator<<
 
 class text_style
 {
@@ -238,91 +290,144 @@
 
 public:
   text_style(int size = 20,
- const std::string& font = "", // Default is sans with Firefox & IE but serif with Opera.
- const std::string& style = "",
- const std::string& weight = "",
- const std::string& stretch = "",
- const std::string& decoration = "")
+ const std::string& font = "", // Examples: "Arial", "Times New Roman", "Verdana", "Lucida Sans Unicode"
+ const std::string& style = "", // font-style: normal | bold | italic | oblique
+ const std::string& weight = "", // Examples: "bold", "normal"
+ const std::string& stretch = "", // font-stretch: normal | wider | narrower ...
+ const std::string& decoration = ""); // Examples: "underline" | "overline" | "line-through"
+
+ text_style& font_size(unsigned int i);
+ text_style& font_family(const std::string& s);
+ text_style& font_style(const std::string& s);
+ text_style& font_weight(const std::string& s);
+ text_style& font_stretch(const std::string& s);
+ text_style& font_decoration(const std::string& s);
+
+ int font_size() const;
+ const std::string& font_family() const;
+ const std::string& font_style() const;
+ const std::string& font_weight() const;
+ const std::string& font_stretch() const;
+ const std::string& font_decoration() const;
+
+ // Comparison operators useful for testing at least.
+ bool operator==(const text_style& ts);
+ bool operator!=(const text_style& ts);
+ // bool operator==(const text_style& lhs, const text_style& rhs);
+
+}; // class text_style
+
+// class text_style function *Definitions* separated.
+
+ text_style::text_style(
+ int size,
+ const std::string& font , // Default for browser is sans with Firefox & IE but serif with Opera.
+ const std::string& style, // font-style: normal | bold | italic | oblique
+ const std::string& weight, // Examples: "bold", "normal"
+ const std::string& stretch, // font-stretch: normal | wider | narrower ...
+ const std::string& decoration) // Examples: "underline" | "overline" | "line-through"
   : // Constructor.
   font_size_(size),
- font_family_(font),
- style_(style),
- weight_(weight),
- stretch_(stretch),
- decoration_(decoration)
+ font_family_(font),
+ style_(style),
+ weight_(weight),
+ stretch_(stretch),
+ decoration_(decoration)
   { // text_style default constructor, defines defaults for all private members.
   }
 
- int font_size() const
+ int text_style::font_size() const
   {
     return font_size_;
   }
 
- const text_style& font_size(unsigned int i)
+ text_style& text_style::font_size(unsigned int i)
   { // pixels, default 10.
     font_size_ = i;
- return *this;
+ return *this; // Should be chainable (but isn't?).
+ // error C2663: 'boost::svg::text_style::font_size' : 2 overloads have no legal conversion for 'this' pointer
+ // label_style.font_size(20).font_family("sans");
+ // MS docs say:
+ // This error can be caused by invoking a non-const member function on a const object.
+ // So removed const from text_style& font_size(unsigned i) and all others below.
   }
 
- const std::string& font_family() const
+ const std::string& text_style::font_family() const
   {
     return font_family_;
   }
 
- const text_style& font_family(const std::string& s)
- { // Examples: "Arial", "Times New Roman", "Verdana", "Lucida Sans Unicode"
+ text_style& text_style::font_family(const std::string& s)
+ { // Default for browser is sans with Firefox & IE but serif with Opera.
+ // Examples: "Arial", "Times New Roman", "Verdana", "Lucida Sans Unicode"
+ // http://www.croczilla.com/~alex/conformance_suite/svg/text-fonts-01-t.svg
+ // tests three styles of font, serfi, sans-serif and mono-spaced.
+ // <text font-family="Georgia, 'Minion Web', 'Times New Roman', Times, 'MS PMincho', Heisei-Mincho, serif " x="20" y="80">A serifed face</text>
+ // <text font-family="Arial, 'Arial Unicode', 'Myriad Web', Geneva, 'Lucida Sans Unicode', 'MS PGothic', Osaka, sans-serif " x="20" y="160">A sans-serif face</text>
+ // <text font-family="'Lucida Console', 'Courier New', Courier, Monaco, 'MS Gothic', Osaka-Mono, monospace" x="20" y="240">A mono (iW) face</text>
+ // Helvetica,sans-serif,
     font_family_ = s;
     return *this;
   }
 
- const std::string& font_style() const
+ const std::string& text_style::font_style() const
   { // font-style: normal | bold | italic | oblique
     return style_; // example "normal"
   }
 
- const text_style& font_style(const std::string& s)
+ text_style& text_style::font_style(const std::string& s)
   { // Examples: "italic"
+ // http://www.croczilla.com/~alex/conformance_suite/svg/text-fonts-02-t.svg
     style_ = s;
     return *this;
   }
 
- const std::string& font_weight() const
+ const std::string& text_style::font_weight() const
   {
     return weight_;
   }
 
- const text_style& font_weight(const std::string& s)
+ text_style& text_style::font_weight(const std::string& s)
   { // svg font-weight: normal | bold | bolder | lighter | 100 | 200 .. 900
     // Examples: "bold", "normal"
+ // http://www.croczilla.com/~alex/conformance_suite/svg/text-fonts-02-t.svg
+ // tests conformance. Only two weights are supported by Firefox, Opera, Inkscape
     weight_ = s;
     return *this;
   }
 
- const std::string& font_stretch() const
+ const std::string& text_style::font_stretch() const
   {
     return stretch_;
   }
 
- const text_style& font_stretch(const std::string& s)
+ text_style& text_style::font_stretch(const std::string& s)
   { // Examples: "wider" but implementation?
     // font-stretch: normal | wider | narrower ...
     stretch_ = s;
     return *this;
   }
 
- const std::string& font_decoration() const
+ const std::string& text_style::font_decoration() const
   {
     return decoration_;
   }
 
- const text_style& font_decoration(const std::string& s)
+ text_style& text_style::font_decoration(const std::string& s)
   { // Examples: "underline" | "overline" | "line-through"
- decoration_ = s; // But implementation doubtful.
+ decoration_ = s; // But implementation varies.
+ // http://www.croczilla.com/~alex/conformance_suite/svg/text-deco-01-b.svg
+ // tests line-through and underline. (Firefox 3 Beta 4 fails)
     return *this;
   }
 
+ // http://www.croczilla.com/~alex/conformance_suite/svg/text-align-02-b.svg
+ // tests for baseline shifted text. This is needed for subscript and superscript,
+ // vital for nice display of units like m^2 and chemical formulae like H2O
+ // IE (Adobe SVG viewer) and Opera conforms but not Firefox.
+
   // operators needed for testing at least.
- bool operator==(const text_style& ts)
+ bool text_style::operator==(const text_style& ts)
   {
    return (ts.font_size_ == font_size_)
      && (ts.font_family_ == font_family_)
@@ -332,7 +437,7 @@
      && (ts.decoration_ == decoration_);
   } // operator==
 
- bool operator!=(const text_style& ts)
+ bool text_style::operator!=(const text_style& ts)
   {
    return (ts.font_size_ != font_size_)
      || (ts.font_family_ != font_family_)
@@ -342,48 +447,91 @@
      || (ts.decoration_ != decoration_);
   } // operator!=
 
-}; // class text_style
-
-bool operator== (const text_style& lhs, const text_style& rhs)
-{ // Note operator== and operator << both needed to use Boost.text.
- return (lhs.font_size_ == rhs.font_size_)
- && (lhs.font_family() == rhs.font_family())
- && (lhs.stretch_ == rhs.stretch_)
- && (lhs.style_ == rhs.style_)
- && (lhs.weight_ == rhs.weight_)
- && (lhs.decoration_ == rhs.decoration_);
-} // operator==
+ //bool text_style::operator==(const text_style& lhs, const text_style& rhs)
+ //{ // Note operator== and operator << both needed to use Boost.text.
+ // return (lhs.font_size_ == rhs.font_size_)
+ // && (lhs.font_family() == rhs.font_family())
+ // && (lhs.stretch_ == rhs.stretch_)
+ // && (lhs.style_ == rhs.style_)
+ // && (lhs.weight_ == rhs.weight_)
+ // && (lhs.decoration_ == rhs.decoration_);
+ //} // operator==
 
 //bool operator!= (const text_style& lhs, const text_style& rhs)
-//{ // Note operator== and operator << both needed to use Boost.text.
+//{ // Note operator== and operator << both needed to use Boost.Test.
+// But can be avoided with a macro whose name I forget.
 // return false;
 //}
 
 std::ostream& operator<< (std::ostream& os, const text_style& ts)
 { //
     os << "text_style("
- << ts.font_size_ << ", "
- << ts.font_family_ << ", "
- << ts.style_ << ", " // italic
+ << ts.font_size_ << ", \""
+ << ts.font_family_ << "\", \""
+ << ts.style_ << "\", \"" // italic
        << ts.weight_ // bold
        // enable if implemented by rendering programs.
- // << ", "
- // << ts.stretch_ << ", "
- //<< ts.decoration_
- << ")" ;
+ << "\", \""
+ << ts.stretch_ << "\", \""
+ << ts.decoration_
+ << "\")" ;
   // Usage: text_style ts(12, "Arial", "italic", "bold", "", ""); cout << t << endl;
- // Outputs: text_style(12, Arial, italic, bold)
+ // Outputs: text_style(18, "Arial", "italic", "bold", "", "")
 
   return os;
 } // std::ostream& operator<<
 
 text_style no_style; // Uses all constructor defaults.
 
+class value_style
+{ // Data series point value information, text, color, uncertainty, orientation.
+ // for example to output: 5.123 +- 0.01 (19)
+public:
+ //private: // ??
+ rotate_style value_label_rotation_; // Direction point value labels written.
+ int value_precision_; // Precision of value.
+ std::ios_base::fmtflags value_ioflags_; // Control of scientific, fixed etc.
+ bool strip_e0s_;
+ text_style values_text_style_; // Font etc used for data point value marking.
+ // svg_style
+ svg_color fill_color_;
+ svg_color stroke_color_;
+ bool plusminus_on_; // If an uncertainty is to be appended.
+ // http://en.wikipedia.org/wiki/Plus-minus_sign
+ bool df_on_; // If an degrees of freedom estimate is to be appended.
+
+ public:
+ value_style()
+ :
+ value_label_rotation_(horizontal),
+ value_precision_(3), // Reduced from default of 6 which is usually too long.
+ value_ioflags_(std::ios::dec),
+ strip_e0s_(true), // See also similar x_ticks value
+ values_text_style_(no_style), // All defaults, black etc.
+ stroke_color_(black), // == black.
+ fill_color_(svg_color(0, 0, 0)), // == black.
+ plusminus_on_(false),
+ df_on_(false)
+ { // Default constructor initialises all private data.
+ }
+
+ value_style(rotate_style r, int p, std::ios_base::fmtflags f, bool s,
+ text_style ts, const svg_color& scol = black, svg_color fcol = black, bool pm = false, bool df = false)
+ :
+ value_label_rotation_(r), value_precision_(p), value_ioflags_(f), strip_e0s_(s),
+ values_text_style_(ts), stroke_color_(scol), fill_color_(fcol), plusminus_on_(pm), df_on_(df)
+ { // Constructor.
+ }
+
+}; // class value_style
+
 enum point_shape
 { // Marking a data point.
   // Used in draw_plot_point in axis_plot_frame.hpp
   none = 0,
- round, square, point, egg, // ellipse
+ round,
+ // name circle changed to round to avoid clash with function named circle.
+ square, point, egg, // ellipse
   vertical_line, // line up & down from axis.
   horizontal_line, // line left & right from axis.
   vertical_tick, // tick up from axis.
@@ -400,10 +548,13 @@
   asterisk, // *
   x, // x
   cross,
- symbol // Unicode symbol including letters, digits, greek & 'squiggles'.
- // &#x3A9; = greek omega
- // TODO add other shapes?
-// name circle changed to round to avoid clash with function named circle.
+ symbol //Unicode symbol including letters, digits, greek & 'squiggles'.
+ // Default "X", "&#x3A9;"= greek omega, "&#x2721;" = Star of David hexagram
+ // &#2720 Maltese cross & other dingbats
+ // http://en.wikipedia.org/wiki/List_of_Unicode_characters#Basic_Latin geometric shapes
+ // may be a better way to make these symbols.
+ // &#25A0 black square ...to &#25FF
+ // But unclear how many browsers implement these properly.
 };
 
 class plot_point_style
@@ -411,33 +562,36 @@
   friend std::ostream& operator<< (std::ostream&, plot_point_style);
 
 public:
- svg_color fill_color_; // Color of the centre of the shape.
- svg_color stroke_color_; // Color of circumference of shape.
- int size_; // diameter of circle, height of square, font_size ...
- point_shape shape_; // round, square, point...
- std::string symbols_; // Unicode symbol (letters, digits, squiggles etc)
- // Caution: not all Unicode symbols are output by all browsers!
- text_style symbols_style_;
- //bool show_x_value_; // Show the X value near the point.
- //bool show_y_value_; // Show the Y value near the point.
- //// if both true, then shows both as a pair "1.2, 3.4"
- //int dist; // from the point to the value.
- //rotate_style orient_; // Orientation of the value from the point.
- //// Note that this needs to alter the text alignment, center, left or right,
- //// to avoid writing over the point marker.
- //rotate_style rotation_; // Rotation of the value text string itself.
- //// Note that this also needs to alter the text alignment, center, left or right,
- //// to avoid writing over the point marker.
- //text_style value_style_; // Size, font, color etc of the value.
-
- plot_point_style(const svg_color& fill = blank, const svg_color& stroke = black,
- int size = 10, point_shape shape = round, const std::string& symbols = "X")
- :
- fill_color_(fill), stroke_color_(stroke), size_(size), shape_(shape), symbols_(symbols)
- { // Best to have a fixed-width font for symbols?
- symbols_style_.font_family("Lucida Sans Unicode");
- symbols_style_.font_size(size);
- }
+ svg_color fill_color_; // Color of the centre of the shape.
+ svg_color stroke_color_; // Color of circumference of shape.
+ int size_; // diameter of circle, height of square, font_size ...
+ point_shape shape_; // round, square, point...
+ std::string symbols_; // Unicode symbol (letters, digits, squiggles etc)
+ // Caution: not all Unicode symbols are output by all browsers! Example:
+ // U2721 is Star of David or hexagram http://en.wikipedia.org/wiki/Hexagram
+ // symbols("&#x2721;")
+ text_style symbols_style_;
+ bool show_x_value_; // Show the X value near the point.
+ bool show_y_value_; // Show the Y value near the point.
+ // If both true, then shows both as a pair "1.2, 3.4"
+ //int dist; // from the point to the value.
+ //rotate_style orient_; // Orientation of the value from the point.
+ //// Note that this needs to alter the text alignment, center, left or right,
+ //// to avoid writing over the point marker.
+ //rotate_style rotation_; // Rotation of the value text string itself.
+ //// Note that this also needs to alter the text alignment, center, left or right,
+ //// to avoid writing over the point marker.
+ //text_style value_style_; // Size, font, color etc of the value.
+
+ plot_point_style(const svg_color& stroke = black, const svg_color& fill = blank,
+ int size = 10, point_shape shape = round, const std::string& symbols = "X")
+ :
+ fill_color_(fill), stroke_color_(stroke), size_(size), shape_(shape), symbols_(symbols),
+ show_x_value_(false), show_y_value_(false)
+ { // Best to have a fixed-width font for symbols?
+ symbols_style_.font_family("Lucida Sans Unicode");
+ symbols_style_.font_size(size);
+ }
 
   plot_point_style& size(int i)
   {
@@ -485,7 +639,7 @@
   }
 
   plot_point_style& symbols(const std::string s)
- {
+ { // Override default symbol "X" - only effective if .shape(symbol) used.
     symbols_ = s;
     return *this;
   }
@@ -502,9 +656,8 @@
   }
 
   text_style& style() const
- {
+ { // To allow control of symbol font, size, decoration etc.
     return const_cast<text_style&>(symbols_style_);
- // error C2440: 'return' : cannot convert from 'const boost::svg::text_style' to 'boost::svg::text_style &'
   }
 }; // struct plot_point_style
 
@@ -527,15 +680,15 @@
 { // Style of line joining data series values.
   // TODO dotted and dashed line style? Useful for B&W?
 public:
- svg_color color_; // line stroke color. (no fill color for lines)
- svg_color area_fill_; // Fill color from line to axis. == true means color.blank = true.
+ svg_color stroke_color_; // line stroke color. (no fill color for lines)
+ svg_color area_fill_; // Fill color from line to axis. == false means color.is_blank = true, or = blank.
     double width_;
     bool line_on_;
     bool bezier_on_;
 
- plot_line_style(const svg_color& col = black, const svg_color& acol = true, double width = 2, bool line_on = true, bool bezier_on = false)
- :
- color_(col), area_fill_(acol), width_(width), line_on_(line_on), bezier_on_(bezier_on)
+ plot_line_style(const svg_color& col = black, const svg_color& fill_col = blank, double width = 2, bool line_on = true, bool bezier_on = false)
+ :
+ stroke_color_(col), area_fill_(fill_col), width_(width), line_on_(line_on), bezier_on_(bezier_on)
     { // Defaults for all private data.
     }
 
@@ -552,13 +705,13 @@
 
   plot_line_style& color(const svg_color& f)
   {
- color_ = f;
+ stroke_color_ = f;
     return *this; // Make chainable.
   }
 
   svg_color& color()
   {
- return color_;
+ return stroke_color_;
   }
 
   plot_line_style& area_fill(const svg_color& f)
@@ -599,7 +752,7 @@
 std::ostream& operator<< (std::ostream& os, plot_line_style p)
 { //
   os << "point_line_style("
- << p.color_ << ", "
+ << p.stroke_color_ << ", "
      << p.area_fill_ << " area fill, "
      << ((p.line_on_) ? "line, " : "no line, ")
      << ((p.bezier_on_) ? "bezier)" : "no bezier)");
@@ -608,7 +761,7 @@
   return os;
 } // std::ostream& operator<<
 
-enum dim{X = 1, Y = 2, Z = 3}; // Used so that an axis knows what type it is.
+enum dim{N = 0, X = 1, Y = 2, Z = 3}; // Used so that an axis knows what type it is, or none = N.
 
 class axis_line_style
 { // Style of the x and/or y axes lines.
@@ -616,50 +769,51 @@
   // (Different styles for x and y are possible).
 public:
   dim dim_; // x, y or z
- double min_; // minimum x value (Cartesian units).
- double max_; // maximum x value (Cartesian units).
- // Note that these duplicate the same named in ticks_labels_style,
- // but they might have different uses, so are left pro tem.
- // TODO reconsider the implications of this (largely accidental) decision.
- double axis_; //
- // X-axis (y = 0) transformed into SVG Y coordinates. -1 if not calculated yet.
- // or Y-axis (x = 0) transformed into SVG X coordinates. -1 if not calculated yet.
-
- svg_color color_; // line stroke color.
- double axis_width_; // line width.
- int axis_position_; // How the axes intersect with values as below:
- // enum x_axis_intersect {bottom = -1, x_intersects_y = 0, top = +1};
- // enum y_axis_intersect {left = -1, y_intersects_x = 0, right = +1};
- // If axes look like an L, then is bottom left.
- // If a T then y intersects and x is at bottom.
- bool label_on_; // Label X-axis with text - example: "length".
- bool label_units_on_; // Label X-axis units, example: "cm".
- bool axis_line_on_; // Draw a X horizontal or Y vertical axis line.
-
- axis_line_style(dim d = X,
- double min = -10., double max = +10., // Defaults.
- // See also default in ticks_labels_style.
- const svg_color col = black, double width = 1,
- int axis_position = 0, bool label_on = true,
- bool label_units_on = false,
- bool axis_lines_on = true)
- :
- dim_(d), min_(min), max_(max), color_(col), axis_width_(width),
- axis_position_(axis_position),
- label_on_(label_on),
- label_units_on_(label_units_on), // default is include units.
- axis_line_on_(axis_lines_on),
- axis_(-1) // Not calculated yet.
- { // Initialize all private data.
- if(max_ <= min_)
- { // max_ <= min_.
- throw std::runtime_error("Axis range: max <= min!");
- }
- if((max_ - min_) < std::numeric_limits<double>::epsilon() * 1000)
- { // Range too small to display.
- throw std::runtime_error("Axis range too small!" );
- }
- } // axis_line_style constructor
+ double min_; // minimum x value (Cartesian units).
+ double max_; // maximum x value (Cartesian units).
+ // Note that these duplicate the same named in ticks_labels_style,
+ // but they might have different uses, so are left pro tem.
+ // TODO reconsider the implications of this (largely accidental) decision.
+ // double interval_; does NOT duplicate major_interval_ in ticks_label_style.
+ double axis_; //
+ // X-axis (y = 0) transformed into SVG Y coordinates. -1 if not calculated yet.
+ // or Y-axis (x = 0) transformed into SVG X coordinates. -1 if not calculated yet.
+
+ svg_color color_; // line stroke color.
+ double axis_width_; // line width.
+ int axis_position_; // How the axes intersect with values as below:
+ // enum x_axis_intersect {bottom = -1, x_intersects_y = 0, top = +1};
+ // enum y_axis_intersect {left = -1, y_intersects_x = 0, right = +1};
+ // If axes look like an L, then is bottom left.
+ // If a T then y intersects and x is at bottom.
+ bool label_on_; // Label X-axis with text - example: "length".
+ bool label_units_on_; // Label X-axis units, example: "cm".
+ bool axis_line_on_; // Draw a X horizontal or Y vertical axis line.
+
+ axis_line_style(dim d = X,
+ double min = -10., double max = +10., // Defaults.
+ // See also default in ticks_labels_style.
+ const svg_color col = black, double width = 1,
+ int axis_position = 0, bool label_on = true,
+ bool label_units_on = false,
+ bool axis_lines_on = true)
+ :
+ dim_(d), min_(min), max_(max), color_(col), axis_width_(width),
+ axis_position_(axis_position),
+ label_on_(label_on),
+ label_units_on_(label_units_on), // default is include units.
+ axis_line_on_(axis_lines_on),
+ axis_(-1) // Not calculated yet.
+ { // Initialize all private data.
+ if(max_ <= min_)
+ { // max_ <= min_.
+ throw std::runtime_error("Axis range: max <= min!");
+ }
+ if((max_ - min_) < std::numeric_limits<double>::epsilon() * 1000)
+ { // Range too small to display.
+ throw std::runtime_error("Axis range too small!" );
+ }
+ } // axis_line_style constructor
 
   // Set and get functions.
   axis_line_style& color(const svg_color& color)
@@ -684,7 +838,6 @@
     return axis_width_;
   }
 
-
   bool label_on() const
   {
     return label_on_;
@@ -742,7 +895,7 @@
     bool down_ticks_on_; // Draw ticks down from horizontal X-axis line.
     bool left_ticks_on_; // Draw ticks left from vertical Y-axis line.
     bool right_ticks_on_; // Draw ticks right from vertical Y-axis line.
- // Simplest to have all of these although only one pair like up or down is used.
+ // Simplest to have all of these although only one pair (up or down) or (left or right) is used.
     // Unused are always false.
     int major_value_labels_side_; // Label values for major ticks, and direction.
     // < 0 means to left or down (default), 0 (false) means none, > 0 means to right (or top)/
@@ -940,7 +1093,7 @@
     bool fill_on_; // Color fill the box.
 
     box_style(const svg_color& scolor = black,
- const svg_color& fcolor = white,
+ const svg_color& fcolor = white, // No fill.
       double width = 1, // of border
       double margin = 4., //
       bool border_on = true, // Draw a border of width.
@@ -1021,6 +1174,111 @@
 
 }; // class box_style
 
+enum bar_option
+{
+ y_block = -2, // Rectangular (optionally filled) block style horizontal to Y-axis,
+ y_stick = -1, // Bar or row line (stroke width) horizontal to Y-axis.
+ no_bar = 0, // No bar.
+ x_stick = +1, // Stick or column line (stroke width) vertical to X-axis.
+ x_block = +2 // Rectangular (optionally filled) block style vertical to X-axis,
+ // Other options like cylinders and cones might be added here?
+ // x_cyl = +3, x_cone = +4 ...
+};
+
+enum histogram_option
+{
+ //row = -1, // Row line (stroke width) horizontal to Y-axis. Not implemented.
+ // See svg_2d_plot for details of why not.
+ no_histogram = 0,
+ column = +1 // Stick or column line (stroke width) vertically to/from X-axis.
+ // Column is the most common histogram style.
+};
+
+class histogram_style
+{
+public:
+ histogram_option histogram_option_; // bar, no_histogram or column.
+
+ histogram_style(histogram_option opt = no_histogram)
+ :
+ histogram_option_(opt)
+ { // Default for all private data.
+ // Line width and area-fill are taken from the plot_line_style style.
+ }
+
+ histogram_style& histogram(histogram_option opt)
+ { // stick or bar.
+ histogram_option_ = opt;
+ return *this; // Make chainable.
+ }
+
+ double histogram()
+ { //
+ return histogram_option_;
+ }
+
+}; // class histogram_style
+
+class bar_style
+{
+public:
+ svg_color color_; // line stroke color. (no fill color for lines).
+ svg_color area_fill_; // Fill color from line to axis. == true means color.blank = true.
+ double width_; // of bar, not enclosing line width.
+ bar_option bar_option_; // stick or bar.
+
+ bar_style(const svg_color& col = black, const svg_color& acol = true, double width = 2, bar_option opt = no_bar)
+ :
+ color_(col), area_fill_(acol), width_(width), bar_option_(opt)
+ { // Defaults for all private data.
+ }
+
+ bar_style& width(double w)
+ { // of bar, not the enclosing line (stroke) width.
+ width_ = w;
+ return *this; // Make chainable.
+ }
+
+ double width()
+ { // of bar, not enclosing line width.
+ return width_;
+ }
+
+ bar_style& color(const svg_color& f)
+ { // of line or enclosing line.
+ color_ = f;
+ return *this; // Make chainable.
+ }
+
+ svg_color& color()
+ { // of line or enclosing line.
+ return color_;
+ }
+
+ bar_style& area_fill(const svg_color& f)
+ { // rectangle fill color.
+ area_fill_ = f;
+ return *this; // Make chainable.
+ }
+
+ svg_color& area_fill()
+ { // rectangle fill color.
+ return area_fill_;
+ }
+
+ bar_style& bar(bar_option option)
+ { // stick or bar.
+ bar_option_ = option;
+ return *this; // Make chainable.
+ }
+
+ double bar_option()
+ { // stick or bar.
+ return bar_option_;
+ }
+
+}; // class bar_style
+
 const std::string strip_e0s(std::string s);
 
 const std::string strip_e0s(std::string s)
@@ -1029,6 +1287,9 @@
   // Should also be useful for values that spill over into exponent format
   // 'by accident' - when leading zeros are likely.
   // For example, "1.2e+000" becomes "1.2"
+ // (Could also do the same for uppercase E cases).
+ // (Considered doing a repeated strip but complicated).
+
   using std::string;
   size_t j = s.find("e+000");
   if (j != string::npos)


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk