当前位置: 首页 > 编程日记 > 正文

自制绘图之坐标轴

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

首先需要定义范围:

 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);

转载于:https://www.cnblogs.com/zhangyonghugo/archive/2012/05/04/2482613.html

相关文章:

DotNET(C#) Socket基本编程 (1)

Socket基本编程 服务端&#xff1a; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; Thread mythread ; Socket socket; // 清理所有正在使用的资源。 protected override void Dispose( bool disposing ) { try {    socket.Clos…

CC2540 OSAL 学习其中原理,以及 给任务 添加 一个事件(定时发送串口消息)

参考学习大神博客&#xff1a; http://blog.csdn.net/feilusia/article/details/51083953 &#xff1a; http://blog.csdn.net/xiaoleiacmer/article/details/41723583 1、TI 的 CC2540跑了一个 OSAL (Operating System Abstraction Layer) 心得&#xff1a;大概 就是 一个循…

Strategy_Level3

以下代码是“策略模式”的第六个例子&#xff1a;

SDK开发日积月累(二)

WM_NOTIFY消息和WM_COMMAND消息在一个对话框中&#xff0c;子控件可以有两种方式与父对话框通信。1.向父对话框发送WM_COMMAND消息&#xff0c;但这种消息传递的信息量比较少。2.向父对话框发送WM_NOTIFY消息&#xff0c;信息量比较大。idCtrl (int) wParam; pnmh (LPNMHDR) …

关于IP地址的分类

我们说过因特网是把全世界的无数个网络连接起来的一个庞大的网间网&#xff0c;每个网络中的计算机通过其自身的IP地址而被唯一标识的&#xff0c;据此我们也可以设想&#xff0c;在INTERNET上这个庞大的网间网中&#xff0c;每个网络也有自己的标识符。这与我们日常生活中的电…

Visual Studio UML Activity Diagram(1)

数理系LSGO软件技术团队能够存活下来&#xff0c;并形成战斗力的根本原因&#xff0c;归结为我们的价值观。我们“只问收获&#xff0c;不问耕耘”&#xff0c;对知识是贪婪的&#xff0c;我们会把所学的知识放到场景中去应用&#xff0c;我们一定要做出成品。我们强调“要构建…

VMware 克隆Linux后找不到eth0

VMware 克隆Linux,ifconfig 不出现eth0解决方案:1)删除 /etc/udev/rules.d/70-persistent-net.rules/rm -rf /etc/udev/rules.d/70-persistent-net.rules2)重启init 6 3)ifconfig 查看转载于:https://blog.51cto.com/ictedu/1917185

笔试算法题(58):二分查找树性能分析(Binary Search Tree Performance Analysis)

议题&#xff1a;二分查找树性能分析&#xff08;Binary Search Tree Performance Analysis&#xff09; 分析&#xff1a; 二叉搜索树&#xff08;Binary Search Tree&#xff0c;BST&#xff09;是一颗典型的二叉树&#xff0c;同时任何节点的键值大于等于该节点左子树中的所…

定义自定义的异常

首先我们建立自己的异常类CustomException&#xff0c;它要继承自ApplicationException类&#xff08;这是一个在发生非致命的应用程序错误时抛出的通用异常&#xff0c;它又继承于更为通用的Exception类&#xff09;&#xff0c;将其用作为应用程序定义的任何自定义异常的基类…

python3 的 round 函数的 练习

python3 的 round 函数感觉很别扭&#xff0c;其运算结果与习惯不相符。特记录下来&#xff1a; 代码 python 3的 round 函数 是“四舍六入五成双”的https://www.zhihu.com/question/20128906print(python 3的 round 函数&#xff1a;四舍六入五成双)print(\nround(-3.5) , …

Visual Studio UML Activity Diagram(2)

昨天的图文介绍了Visual Studio UML Activity Diagram中所涉及的对象&#xff0c;今天图文我们来介绍这些对象的属性部分并给出UML关于Activity Diagram的元模型类图。通常情况下&#xff0c;我们在做一套软件系统的时候&#xff0c;对甲方业务流程并不熟悉&#xff0c;如果直接…

Go 语言中手动内存管理

2019独角兽企业重金招聘Python工程师标准>>> Go 语言是自带GC的, 相对C语言等的手动内存管理省事很多, 弊端便是会消耗更多的内存, 以及在GC时导致整个程序的停顿. 在某些特殊场合, 如果能够可选地手动进行内存管理, 效果会好不少. Go 目前的 GC 实现比较简单(mark-…

依赖倒转原则(Dependency Inversion Principle,DIP)

前面两篇图文介绍了“开闭原则”和“里氏替换原则”。开发出对扩展开放&#xff0c;对修改封闭的系统是程序员的目标&#xff0c;而今天所介绍的“依赖倒转原则”正是实现这一目标的途径之一&#xff0c;而“里氏替换原则”为这一途径提供了保证。大家或许发现&#xff0c;我写…

细说浏览器特性检测(2)-通用事件检测

在上一篇中介绍了jQuery1.4版本新增的几个浏览器特性检测方案和具体的目的&#xff0c;本文将以事件为中心&#xff0c;介绍一个较为完整、通用的事件检测方案。 事件检测&#xff0c;即检测某一事件在不同的浏览器中是否存在&#xff08;可用&#xff09;&#xff0c;这在编写…

robot简单功能测试脚本设计(例子)

以学生管理系统的添加一个学生信息为例子页面对象&#xff1a;editbox&#xff08;姓名&#xff09;,button&#xff08;添加&#xff09;数据要求&#xff1a;1 姓名不能为空2 姓名不能重复程序结构1 点button&#xff0c;弹出对话框“姓名不能为空”2 输入姓名&#xff0c;点…

里氏替换原则(Liskov Substitution Principle,LSP)

昨天图文介绍了软件设计的一个基本原则“开闭原则”&#xff0c;而“开闭原则”的核心就是通过抽象把需求变化进行隔离&#xff0c;这种想法可以通过“里氏替换原则”进行保证。理解“里氏替换原则”也是理解面向对象中“运行时多态”的关键。希望大家仔细体会。

在IIS7里配置 ISAPI,运行dll程序,总提示下载dll

在IIS7里配置 ISAPI&#xff0c;运行dll程序&#xff0c;总提示下载dll&#xff0c;只需要把对应站点应用程序池里面的高级设置里的启用32位应用程序&#xff0c;设为“true"即可。

MySQL数据库高可用集群搭建-PXC集群部署

Percona XtraDB Cluster&#xff08;下文简称PXC集群&#xff09;提供了MySQL高可用的一种实现方法。集群是有节点组成的&#xff0c;推荐配置至少3个节点&#xff0c;但是也可以运行在2个节点上。 PXC原理描述&#xff1a; 分布式系统的CAP理论&#xff1a; C&#xff1a;一致…

搭建Jupyter学习环境

python notebook是一个基于浏览器的python数据分析工具&#xff0c;使用起来非常方便&#xff0c;具有极强的交互方式和富文本的展示效果。jupyter是它的升级版&#xff0c;它的安装也非常方便&#xff0c;一般Anaconda安装包中会自带。安装好以后直接输入jupyter notebook便可…

[转贴]2006十大经典语句

1. 骑白马的不一定是王子&#xff0c;他可能是唐僧&#xff1b; 2. 带翅膀的也不一定是天使&#xff0c;他可能是鸟人。 3. 站的更高&#xff0c;尿的更远。 4. 穿别人的鞋&#xff0c;走自己的路&#xff0c;让他们找去吧&#xff0c; 5. 我不是随便的人。我随便起来不是人 6.…

开放-封闭原则(The Open-Closed Principle,OCP)

自己设计的软件系统“易于维护”、“扩展性好”、“可重用”、“具有灵活性”&#xff0c;这是每位程序员所追求的目标。“开闭原则”为我们指明了方向&#xff0c;即我们所设计的软件尽量满足“开闭原则–对扩展开放&#xff0c;对修改关闭”&#xff0c;这样就能降低需求不断…

Interesting visualization tools for profiling.

Interesting visualization tools for profiling. http://dtrace.org/blogs/brendan/2012/03/17/linux-kernel-performance-flame-graphs/ http://dtrace.org/blogs/brendan/2013/07/01/detecting-outliers/转载于:https://www.cnblogs.com/kungfupanda/p/3245651.html

javascript网页开发 第二章

HTML高级部分 2.1. 表格标签 2.1.1 <table></table> Bgcolor 设置表格的背景色 Border 设置边框的宽度 Bordercolor 设置边框的颜色 Bordercolorlight 设置边框明亮部分的颜色 Bordercolordark 设置边框昏暗部分的颜色 Cellspacing 设置单元格之间的间隔大小 Cel…

ORACLE JET BASIC TABLE

转载于:https://blog.51cto.com/feitai/1917581

Visual Studio UML Use Case Diagram(1)

前几天我们介绍了Visual Studio UML Activity Diagram&#xff0c;今天我们介绍Visual Studio UML Use Case Diagram的内容。通常RUP按照动态划分&#xff0c;分为周期、阶段、里程碑、迭代&#xff0c;按照静态划分&#xff0c;分为角色、制品、工作流、活动&#xff0c;在Wor…

可以左右移动多选下拉列表的javaScipt(可以兼容IE和firefox)

自己在项目业余时间总结了一份可以左右移动&#xff08;Add和remove&#xff09;多选下拉列表的javaScipt,可以兼容IE和firefox,并且经过测试&#xff0c;只是代码略显臃肿&#xff0c;希望各位网友参考后给一些指点&#xff0c;特别是在简化代码方面。我在让其兼容 firefox很是…

OLAP和OLTP的区别(基础知识)

联机分析处理 (OLAP) 的概念最早是由关系数据库之父E.F.Codd于1993年提出的&#xff0c;他同时提出了关于OLAP的12条准则。OLAP的提出引起了很大的反响&#xff0c;OLAP作为一类产品同联机事务处理 (OLTP) 明显区分开来。当今的数据处理大致可以分成两大类&#xff1a;联机事务…

如何让phpmyadmin输入密码再进入

分类&#xff1a;wamp对于很多不熟悉PHP环境安装的朋友来说&#xff0c;用集成环境可以更快的上手&#xff0c;更方便的搭建PHP的运行环境&#xff0c;但是&#xff0c;WAMP的集成环境仅仅是将底层基础工作做好了&#xff0c;有些个别关键的配置操作并没有集成到环境安装中&…

Visual Studio UML Use Case Diagram(2)

Use Case Model是捕获用户需求确定系统边界最流行的方法。Use Case Model由两部分组成Use Case Diagram和Use Case Specification&#xff0c;对于不方便描述的部分可以放在Supplementary Specification中&#xff0c;通过Glossary统一大家的用词规范。昨天我们介绍了Visual St…

Delphi下利用WinIo模拟鼠标键盘详解

本文最早在编程论坛上发表&#xff0c;文章地址&#xff1a;http://programbbs.com/bbs/view12-17207-1.htm&#xff0c;相关文件可以在上述地址的页面中下载。转载时请注明出处。 前言 一日发现SendInput对某程序居然无效&#xff0c;无奈只好开始研究WinIo。上网查了很多资料…