• 自制绘图之坐标轴


    写代码之前得先了解坐标轴的一些属性,坐标轴有范围,每隔多少显示一条数值信息。然而间隔信息有时并不确定,一旦设置不准确,图形会乱掉。最好的方法是使用另一个参数:分隔符总数。这样可以利用坐标范围计算出间隔。

    首先需要定义范围:

     1 template <class T>
     2 class DataRange
     3 {
     4 public:
     5     DataRange(const T& minValue, const T& maxValue): itsMinValue(minValue), itsMaxValue(maxValue){}
     6     DataRange(){}
     7     T itsMinValue;
     8     T itsMaxValue;
     9     T Length() const { return itsMaxValue - itsMinValue ;}
    10 };

    先写测试代码:

    1 void TestPicture::TestCoordSimple()
    2 {
    3     CoordinateAttribute<double> ca;
    4     ca.itsNumSeparate = 10;
    5     ca.itsGivenDataRange = DataRange<double>(0.0,2.4);
    6     ca.CalcCoordinate();
    7     assert(0.24==ca.itsSeparateLength);
    8 }

    通过这一测试很简单只需一个除法就搞定了。代码如下:

     1 template <class T>
     2 class CoordinateAttribute
     3 {
     4 public:
     5     CoordinateAttribute(){}
     6     //attribute
     7     int itsNumSeparate;
     8     DataRange<T> itsGivenDataRange;
     9     T itsSeparateLength;
    10     //operators
    11     void CalcCoordinate()
    12     {
    13         //calculate separate length
    14         itsSeparateLength = itsGivenDataRange.Length() / itsNumSeparate;
    15     }
    16 };

    有个问题就是如果坐标轴的范围不能被分隔符整除,显示出来的坐标将带有很多小数位,往往不是我们所想要的。于是还需要另一个参数给坐标轴,那就是最小刻度。比如有些情况下需要显示的都是整数,有时需要保留一位小数。

    1 void TestPicture::TestCoordSimple2()
    2 {
    3     CoordinateAttribute<double> ca;
    4     ca.itsNumSeparate = 10;
    5     ca.itsGivenDataRange = DataRange<double>(0.0,2.4);
    6     ca.itsMinScale = 0.1;
    7     ca.CalcCoordinate();
    8     assert(0.3==ca.itsSeparateLength);
    9 }

    修改代码为:

     1 template <class T>
     2 class CoordinateAttribute
     3 {
     4 public:
     5     CoordinateAttribute(){}
     6     //attribute
     7     int itsNumSeparate;
     8     DataRange<T> itsGivenDataRange;
     9     T itsMinScale;
    10     T itsSeparateLength;
    11     //operators
    12     void CalcCoordinate()
    13     {
    14         //calculate separate length
    15         T temp = itsGivenDataRange.Length() / itsNumSeparate;
    16         itsSeparateLength = itsMinScale * ceil((double)temp / itsMinScale);
    17     }
    18 };

    这个坐标轴的计算方法先做到这,下面来测试其效果。坐标轴的信息需要给图形,最简洁的方法是告诉图形在坐标轴的什么位置显示多少坐标值,由于坐标轴对图形是未知的,其位置可以使用百分比来表示,在C++中可以使用std::map<double,std::string>来传递坐标轴的信息。

    在CoordinateAttribute加入一个FillToAxisMap函数

     1 void FillToAxisMap(std::map<double,std::string>& sm)
     2 {
     3     sm.clear();
     4     T iT = itsGivenDataRange.itsMinValue;
     5     for (int i = 0; i <= itsNumSeparate; i++,iT += itsSeparateLength)
     6     {
     7         double dIndex = iT - itsGivenDataRange.itsMinValue ;
     8         dIndex /= itsCalculateDataRange.Length();
     9             sm.insert(std::make_pair(dIndex,ConvertToString<T>(iT)));
    10     }
    11 }

    其中ConvertToString函数如下:

    1 template <typename T>
    2 std::string ConvectToString(const T& v)
    3 {
    4     std::strstream str;
    5     str<<v<<'\0';
    6     return str.str();
    7 }

    写个输出函数来看我们的结果。

    测试函数:

    1     map<double,string> CoordInfo;
    2     ca.FillToAxisMap(CoordInfo);
    3     for (map<double,string>::iterator it = CoordInfo.begin();it != CoordInfo.end(); ++it)
    4     {
    5         cout << endl << it->first << " ---- " << it->second ;
    6     }

    运行结果:

    0 ---- 0
    0.1 ---- 0.3
    0.2 ---- 0.6
    0.3 ---- 0.9
    0.4 ---- 1.2
    0.5 ---- 1.5
    0.6 ---- 1.8
    0.7 ---- 2.1
    0.8 ---- 2.4
    0.9 ---- 2.7
    1 ---- 3
    

      

    可以看到,其结果还是在我们的意料之中的,有一点不好,我们给的范围是0~2.4,而现在的范围变成了0~3了。先不做改动,再做下一次测试。

    1     CoordinateAttribute<double> ca;
    2     ca.itsNumSeparate = 10;
    3     ca.itsGivenDataRange = DataRange<double>(0.0,2.4);
    4     ca.itsMinScale = 0.5;
    5     ca.CalcCoordinate();

    其输出结果为:

    0 ---- 0
    0.1 ---- 0.5
    0.2 ---- 1
    0.3 ---- 1.5
    0.4 ---- 2
    0.5 ---- 2.5
    0.6 ---- 3
    0.7 ---- 3.5
    0.8 ---- 4
    0.9 ---- 4.5
    1 ---- 5

    问题更严重了,计算出的范围比之前我们给的大很多。其实这是由于所给参数不合理导致的。既然itsMinScale设了0.5,那么itsNumSeparate设为10显然不正确,这时最好由程序自己来调整。

    理想的输出结果是:

    0 ---- 0
    0.2 ---- 0.5
    0.4 ---- 1
    0.6 ---- 1.5
    0.8 ---- 2
    1 ---- 2.5

    修改代码如下:

     1 template <class T>
     2 class CoordinateAttribute
     3 {
     4 public:
     5     CoordinateAttribute(){}
     6     //the param user give
     7     int itsNumSeparate;
     8     DataRange<T> itsGivenDataRange;
     9     T itsMinScale;
    10     //the param calculate
    11     T itsSeparateLength;
    12     DataRange<T> itsCalculateDataRange;
    13 
    14     //operators
    15     void CalcCoordinate()
    16     {
    17         //adjust its min value
    18         itsCalculateDataRange.itsMinValue = itsMinScale * floor((double)itsGivenDataRange.itsMinValue / itsMinScale);
    19         //calculate separate length
    20         itsCalculateDataRange.itsMaxValue = itsGivenDataRange.itsMaxValue;
    21         T temp = itsCalculateDataRange.Length() / itsNumSeparate;
    22         itsSeparateLength =  itsMinScale * ceil((double)temp / itsMinScale);
    23         //calculate its max value
    24         itsCalculateDataRange.itsMaxValue = itsCalculateDataRange.itsMinValue + itsSeparateLength * itsNumSeparate;
    25         //delete unused value
    26         while( itsCalculateDataRange.itsMaxValue - itsSeparateLength > itsGivenDataRange.itsMaxValue)
    27         {
    28             itsCalculateDataRange.itsMaxValue -= itsSeparateLength;
    29             itsNumSeparate--;
    30         }
    31     }
    32 };

    这里加了itsCalculateDataRange,用来记录计算后的数据范围,对max和min的范围进行了调整。

    测试如下:

     1     ca.itsNumSeparate = 7;
     2     ca.itsMinScale = 0.1;
     3     ca.itsGivenDataRange = DataRange<double>(0.0,2.34);
     4     ca.CalcCoordinate();
     5     CPPUNIT_ASSERT_DOUBLES_EQUAL(0.4,ca.itsSeparateLength,0.01);
     6     CPPUNIT_ASSERT_DOUBLES_EQUAL(2.4,ca.itsCalculateDataRange.itsMaxValue,0.01);
     7     ca.itsNumSeparate = 5;
     8     ca.CalcCoordinate();
     9     CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5,ca.itsSeparateLength,0.01);
    10     CPPUNIT_ASSERT_DOUBLES_EQUAL(2.5,ca.itsCalculateDataRange.itsMaxValue,0.01);
    11     ca.itsNumSeparate = 10;
    12     ca.itsMinScale = 0.1;
    13     ca.CalcCoordinate();
    14     CPPUNIT_ASSERT_DOUBLES_EQUAL(0.3,ca.itsSeparateLength,0.001);
    15     CPPUNIT_ASSERT_DOUBLES_EQUAL(2.4,ca.itsCalculateDataRange.itsMaxValue,0.001);
    16     ca.itsGivenDataRange.itsMinValue = 0.14;
    17     ca.itsMinScale = 0.01;
    18     ca.itsNumSeparate = 10;
    19     ca.CalcCoordinate();
    20     CPPUNIT_ASSERT_DOUBLES_EQUAL(0.22,ca.itsSeparateLength,0.001);
    21     CPPUNIT_ASSERT_DOUBLES_EQUAL(0.14,ca.itsCalculateDataRange.itsMinValue,0.001);
    22     CPPUNIT_ASSERT_DOUBLES_EQUAL(2.34,ca.itsCalculateDataRange.itsMaxValue,0.001);
  • 相关阅读:
    阿里云提示Discuz memcache+ssrf GETSHELL漏洞如何解决
    mysql总是无故退出, InnoDB: mmap(68681728 bytes) failed; errno 12
    ElasticSearch6 报错FORBIDDEN/12/index read-only / allow delete (api)
    Yum安装时提示多库版本保护 Multilib version problems found
    redis:CLUSTER cluster is down 解决方法
    虚拟主机如何绑定网站根目录到子目录中
    iscroll遇到的两个坑
    前端之Sass/Scss实战笔记
    JS与jquery书写插件规范
    css命名推荐
  • 原文地址:https://www.cnblogs.com/zhangyonghugo/p/2482613.html
Copyright © 2020-2023  润新知