自作図面の座標軸

22037 ワード

コードを書く前に、座標軸のいくつかの属性を理解しなければなりません.座標軸には範囲があり、数値情報がどれだけ表示されますか.しかし、間隔情報が不確定な場合があり、設定が不正確になるとグラフィックが乱れてしまうことがあります.最良の方法は、セパレータの総数という別のパラメータを使用することです.これにより、座標範囲を用いて間隔を算出することができる.
まず範囲を定義する必要があります.
 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つの除算だけでできます.コードは次のとおりです.
 1 template <class T>
 2 class CoordinateAttribute
 3 {
 4 public:
 5     CoordinateAttribute(){}
 6     //attribute
 7     int itsNumSeparate;
 8     DataRange itsGivenDataRange;
 9     T itsSeparateLength;
10     //operators
11     void CalcCoordinate()
12     {
13         //calculate separate length
14         itsSeparateLength = itsGivenDataRange.Length() / itsNumSeparate;
15     }
16 };

問題は、座標軸の範囲がセパレータで割り切れない場合、表示される座標には小さな数桁が多く、私たちが望んでいないことが多いということです.座標軸にもう一つのパラメータが必要です.それは最小目盛りです.たとえば、整数を表示する必要がある場合や、小数を1桁保持する必要がある場合があります.
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 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を使用して座標軸の情報を伝達することができる.
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(iT)));
10     }
11 }

ConvertToString関数は次のとおりです.
1 template 
2 std::string ConvectToString(const T& v)
3 {
4     std::strstream str;
5     str<'\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 itsGivenDataRange;
 9     T itsMinScale;
10     //the param calculate
11     T itsSeparateLength;
12     DataRange 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);

転載先:https://www.cnblogs.com/zhangyonghugo/archive/2012/05/04/2482613.html