给定两个数值如(3001,5020),如何做到均匀地标注刻度?
研究matlab图形刻度会发现,在matlab中,图像无论如何缩放,坐标刻度间隔都是以1,2,5为基数,按照10倍或者0.1倍的幂进行放大或缩小也即,刻度间隔为:
…0.1 0.2 0.5 ; 1 2 5; 10 20 50; 100 200 500; 1000 2000 5000….
负刻度也类似:
…-0.1 -0.2 -0.5 ; -1 -2 -5; -10 -20 -50; -100 -200 -500; -1000 -2000 -5000….
在matlab图像放大中,还会发现,坐标轴刻度个数都在4个到10个之间。当数值超过1000或小于0.001,则会采用科学计数法。
从上面的刻度看,无论正刻度还是负刻度,相邻刻度最大倍数为2.5。如果把绘制的刻度最小个数控制为4,则最大个数为4*2.5=10;这即是为什么matlab绘制的刻度个数为什么不会超出4个到10个这个范围。
根据上面的分析,回到最初的问题,如何计算(3001,5020)均匀刻度,让刻度符合matlab的刻度规律。
首先确定寻找的刻度范围,限制最小刻度数4个,则最大刻度数为10个:
(5020-3001)/4=504.75
(5020-3001)/10=201.9
因而,201.9-504.75之间只有500这个刻度值符合matlab规律。下面是C++编程实现的刻度生成器CLabelGenerator类
LabelGenerator.h:
#ifndef _LABELGENERATOR_H
#define _LABELGENERATOR_H
#pragma once
class CLabelGenerator
{
public:
CLabelGenerator(void);
~CLabelGenerator(void);
bool GenerateLabel(float x1,float x2,int minlabelnum,int &reallabelnum,float *&label,CStringArray &labelArr,int &order,int _interal=0,int limitorder=3); // 产生坐标刻度
CString StringCutZeros(CString str); // 对标注字符串进行去零处理
};
#endif
LabelGenerator.cpp:
//**************************** LabelGenerator.cpp *****************************
// 包含功能:坐标刻度生成
//
// 作者: jiangjp2812 1034378054@qq.com
// 单位: 中国地质大学(武汉)
// 日期: 2016/10/01
//*************************************************************************************
#include "StdAfx.h"
#include "LabelGenerator.h"
#include "math.h"
CLabelGenerator::CLabelGenerator(void)
{
}
CLabelGenerator::~CLabelGenerator(void)
{
}
bool CLabelGenerator::GenerateLabel(float x1,float x2,int minlabelnum,int &reallabelnum,
float *&label,CStringArray &labelArr,int &order,int _interal,int limitorder)
// 生成坐标刻度
// x1,x2 :需要计算刻度的数值范围,minlabelnum:最小刻度数目。reallabelnum:得到的刻度个数。
// label:刻度的数值位置。labelArr:刻度字符串。order:刻度数值采用科学计数法的阶次
// _interal:指定刻度间隔,默认采用计算值。limitorder:超过此阶次采用科学计数,默认为3
{
labelArr.RemoveAll();
float labelInterval=0; // 标注间隔
float leftx=(x2-x1)/((float)minlabelnum*2.5); // 求标注间隔范围
if(leftx<0.000001) // 刻度产生失败
return false;
float rightx=leftx*2.5;
int kx=0,ky=0;
float a=0;
float DOne=0; // 1,2,5 不断乘以10判断是否在标注间隔范围内,如果在则终止循环,
float DTwo=0;
float DFive=0;
//----------------- 计算x方向大于1刻度 ----------------------------------------------------------------------
while(a<=(x2-x1)) // 如果没有找到合适的标度,当超出最大数也终止
{
DOne=1*pow((float)10,(float)kx); // 1,2,5 不断乘以10判断是否在标注间隔范围内,如果在则终止循环,
DTwo=2*pow((float)10,(float)kx);
DFive=5*pow((float)10,(float)kx);
if( DOne>=leftx && DOne<=rightx)
{
labelInterval=DOne;
break;
}
if(DTwo>=leftx && DTwo<=rightx)
{
labelInterval=DTwo;
break;
}
if(DFive>=leftx && DFive<=rightx)
{
labelInterval=DFive;
break;
}
kx++;
a=DFive; // 每循环一次是1,2,5同时按倍数扩大,即判断的数扩大到a=DOne
}
//----------- x方向如果没有大于1刻度则计算小于1刻度 --------------------------------------
kx=0;
a=1;
if(labelInterval==0)
{
while(a>0.000001) // 如果没有找到合适的标度,当小于最小数也终止
{
DOne=1*pow((float)0.1,(float)kx); // 1,2,5 不断乘以0.1判断是否在标注间隔范围内,如果在则终止循环,
DTwo=2*pow((float)0.1,(float)kx);
DFive=5*pow((float)0.1,(float)kx);
if( DOne>=leftx && DOne<=rightx)
{
labelInterval=DOne;
break;
}
if(DTwo>=leftx && DTwo<=rightx)
{
labelInterval=DTwo;
break;
}
if(DFive>=leftx && DFive<=rightx)
{
labelInterval=DFive;
break;
}
kx++;
a=DOne; // 每循环一次是1,2,5同时按倍数缩小,即判断的数扩大到a=DOne
}
}
//-----------------------------------------------------------------------------------
if(labelInterval==0 ) return false;
if(_interal!=0) labelInterval=_interal; // 如果指定间隔,则使用指定的间隔
float startx,endx;
float temstartx/*,temendx*/;
temstartx=x1/labelInterval-(int)(x1/labelInterval);
if(temstartx==0) // 如果x1正好位于刻度上,则其实点从x1开始
{
startx=labelInterval*(int)(x1/labelInterval);
}
else // 如果x1不位于刻度上,则起始点从x1之后某点开始
{
if(x1>=0)
startx=labelInterval*((int)(x1/labelInterval)+1);
else
startx=labelInterval*((int)(x1/labelInterval)-1+1); // 当出现负数时,取整向0靠拢,正半轴需加1,负半轴不需要
}
if(x2>=0)
endx=labelInterval*(int)(x2/labelInterval);
else
endx=labelInterval*((int)(x2/labelInterval)-1); // 当出现负数时,取整向0靠拢,正半轴需加1,负半轴不需要
reallabelnum=(endx-startx)/labelInterval+0.5+1; // 真实需要绘制的坐标刻度个数
// 此处(endx-startx)/labelInterval计算得到的数值应为整数,但浮点型计算得不到精确值
// 会略小于理论整数,因此需要加上0.5后再取整
label=new float[reallabelnum]; // 开辟坐标位置容器
int valueorder=0,intervalorder=0; // 采用科学计数法进行刻度标注,绝对值最大值阶数,标注间隔值阶数
float loop=abs(endx);
float valueabs=abs(startx)>abs(endx) ? abs(startx) : abs(endx) ; // 获取最大值,求取阶数
int limitvmax=pow(10.0,limitorder);
float limitvmin=pow(10.0,limitorder*-1);
if(valueabs>=limitvmax) // 求取最大刻度值大于1000的阶次,正阶
{
while(loop>=10)
{
loop=loop/10;
valueorder++;
}
}else if(valueabs<=limitvmin) // 求取最大刻度值小于0.001的阶次,负阶
{
while(loop<=1) //0.1
{
loop=loop*10;
valueorder++;
}
}
for(int i=0;i<reallabelnum;i++)
{
label[i]=startx+i*labelInterval;
CString str;
if(valueorder==0) // 如果绝对值最大值阶次为0,则直接将坐标值输出字符串
{
str.Format(_T("%.4f"),label[i]);
}
else if(valueabs>=limitvmax) // 如果绝对值最大值阶次大于3,则除以对应阶数量级
{
str.Format(_T("%.4f"),label[i]/pow(10.0,valueorder));
}
else if(valueabs<=limitvmin) // 如果绝对值最大值阶次小于-3,则除以对应阶数量级
{
str.Format(_T("%.4f"),label[i]*pow(10.0,valueorder));
}
str=StringCutZeros(str); // 对标注字符串进行尾部去零
labelArr.Add(str);
}
if(valueorder==0) order=0; // 输出阶次
if(endx>=limitvmax) order=valueorder;
if(endx<=limitvmin) order=-valueorder;
return true;
}
CString CLabelGenerator::StringCutZeros(CString str)
// 对标注字符串进行去零处理
{
CString FinalStr;
FinalStr.Empty();
int Length=str.GetLength(); // 字符串长度
int pos=0;
bool IsDot=false;
for(int i=0;i<Length;i++)
{
if(str.GetAt(Length-1-i)=='0') // 反向查找‘0’字符点位,直到找到非‘0’或‘.’点位
{
pos++;
}
else if(str.GetAt(Length-1-i)=='.') // 反向查找‘.’字符点位,如果出现直接终止
{
IsDot=true; break;
}
else // 只要出现非'0'字符点,直接终止
break;
}
if(pos>0)
{
if(IsDot)
FinalStr=str.Left(Length-1-pos);
else
FinalStr=str.Left(Length-1-(pos-1));
}
else
FinalStr=str;
return FinalStr;
}
类中有两个成员函数
boolGenerateLabel(float x1,float x2,int minlabelnum,int &reallabelnum,float*&label,CStringArray &labelArr,int &order,int _interal=0,intlimitorder=3); // 产生坐标刻度
CStringStringCutZeros(CString str); // 对标注字符串进行去零处理
GeneraterLabel函数产生坐标刻度,其中x1,x2是输入的数值,minlabelnum是需要指定的最小刻度个数。如果生成Matlab类似刻度则该数值为4,当然也可以设置其他值。reallabelnum是实际得到刻度个数(注意引用变量,通过引用变量把得到的数据输出),label是该处刻度的数值,labelArr是该处刻度对应的字符串,order是采用科学计数法输出的幂值,_interal是指定刻度值,给定初始值为0,即采用matlab相同刻度。如果不想采用此刻度,可以直接指定_interal值,则会采用指定的刻度生成标注值。Limitorder是指定的科学计数法限制阶数,超过此阶数采用科学计数法。默认为3,即超过1000则采用科学计数法。
StringCutZeros是去除标注字符串尾部的0。因为生成的标注可能尾部有0,如0.3000或1.0。在matlab中就不会出现这样的显示方式。
函数调用:
int xlabelnum;
float *pxlabel;
CStringArray xlabelArr;
int orderx;
CLabelGenerator LabelGenerator;3001,5020
bool sucx=LabelGenerator.GenerateLabel(3001,5020,4,xlabelnum,pxlabel,xlabelArr,orderx); // 产生x刻度和值刻度
if(pxlabel!=NULL) // 清空数组
{
delete m_pxlabel;
pxlabel=NULL;
}
bool sucy=LabelGenerator.GenerateLabel(200001,5340000,5,xlabelnum,pxlabel,xlabelArr,orderx,0,4);
坐标绘制采用GDI编写,被封装成CLabelDrawer类
LabelDrawer.h
#ifndef LABELDRAWER_H
#define LABELDRAWER_H
#pragma once
class CLabelDrawer
{
public:
CLabelDrawer(void);
~CLabelDrawer(void);
public:
bool DrawLabelHor(CDC *pDC,
int xlabelnum,float *pxlabel,CStringArray &xlabelArr,int xorder,
float Desx1,float Desx2,float Desy1,float x1,float x2,CString xtitle,
bool IsUpper=true,COLORREF rgb=RGB(0,0,0));
bool DrawLabelVer(CDC *pDC,
int ylabelnum,float *pylabel,CStringArray &ylabelArr,int yorder,
float Desy1,float Desy2,float Desx1,float y1,float y2,CString ytitle,
bool IsLeft=true,COLORREF rgb=RGB(0,0,0));
};
#endif
LabelDrawer.cpp
//**************************** LabelDrawer.cpp *****************************
// 包含功能:坐标刻度绘制(配合CLabelGenerator类)
//
// 作者: jiangjp2812 1034378054@qq.com
// 单位: 中国地质大学(武汉)
// 日期: 2016/10/01
//**********************************************************************************
#include "StdAfx.h"
#include "LabelDrawer.h"
#include <math.h>
CLabelDrawer::CLabelDrawer(void)
{
}
CLabelDrawer::~CLabelDrawer(void)
{
}
bool CLabelDrawer::DrawLabelHor(CDC *pDC,
int xlabelnum,float *pxlabel,CStringArray &xlabelArr,int xorder,
float Desx1,float Desx2,float Desy1,float x1,float x2,CString xtitle,
bool IsUpper,COLORREF rgb)
// 绘制水平刻度
// pDC: 绘图句柄。xlabelnum: 刻度数目。pxlabel:刻度真实数值。xlabelArr:刻度数值对应的字符串。
// xorder: 采用科学计数法阶次。Desx1,Desx2:标注绘制在窗口中的横坐标。Desy1:水平刻度在窗口中的纵坐标。
// xtitle: 坐标轴名称字符串。IsUpper:若为true,刻度位于刻度线上方,否则位于刻度线下方
// rgb:刻度颜色。
{
if(abs(Desx2-Desx1) <0.0001) return false;
if(pxlabel==NULL) return false;
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(rgb);
CFont font,fontunit,*oldfont; // 创建Times New Roman字体
font.CreatePointFont(100,_T("Times New Roman"),pDC);
fontunit.CreatePointFont(120,_T("Times New Roman"),pDC);
CPen pen(0,1,rgb),*oldpen;
oldpen=pDC->SelectObject(&pen);
float posx,posy;
oldfont=pDC->SelectObject(&font);
TEXTMETRIC tm;
pDC->GetTextMetrics(&tm);
LONG TW=tm.tmAveCharWidth; // 获取输出字符信息,主要是为了使得短线标注对准字符串中间位置
LONG TH=tm.tmHeight; // 用于计算字符相对短线输出位置
double k=(Desx2-Desx1)/(x2-x1);
if(IsUpper==true) // 标注在上边
{
pDC->SelectObject(&font);
pDC->SetTextAlign(TA_CENTER | TA_BOTTOM);
for(int i=0;i<xlabelnum;i++)
{
posx=k*(pxlabel[i]-x1)+Desx1;
pDC->MoveTo(posx,Desy1);
pDC->LineTo(posx,Desy1-TW);
pDC->TextOutW(posx,Desy1-TW-1,xlabelArr.GetAt(i));
}
posx=k*((x1+x2)/2.0-x1)+Desx1;
pDC->SelectObject(&fontunit);
pDC->TextOutW(posx,Desy1-TW-TH,xtitle); // 绘制x方向坐标单位
}
else{ // 标注在下边
pDC->SelectObject(&font);
pDC->SetTextAlign(TA_CENTER | TA_TOP);
for(int i=0;i<xlabelnum;i++)
{
posx=k*(pxlabel[i]-x1)+Desx1;
pDC->MoveTo(posx,Desy1);
pDC->LineTo(posx,Desy1+TW);
pDC->TextOutW(posx,Desy1+TW+1,xlabelArr.GetAt(i));
}
posx=k*((x1+x2)/2.0-x1)+Desx1;
pDC->SelectObject(&fontunit);
pDC->TextOutW(posx,Desy1+TW+TH,xtitle); // 绘制x方向坐标单位
}
if(xorder!=0) // 绘制x方向阶次
{
CFont font1,font2;
font1.CreatePointFont(100,_T("Times New Roman"),pDC);
font2.CreatePointFont(70,_T("Times New Roman"),pDC);
float fontrate=100.0/70.0;
CString tenstr("×10"),orderstr; // ×10字符和阶次字符
orderstr.Format(_T("%d"),xorder);
if(IsUpper==true)
{
pDC->SelectObject(&font1);
pDC->SetTextAlign(TA_BOTTOM | TA_RIGHT);
pDC->TextOutW(Desx2-TW/fontrate*2,Desy1-TW-TH,tenstr);
pDC->SelectObject(&font2);
pDC->SetTextAlign(TA_BOTTOM | TA_LEFT);
pDC->TextOutW(Desx2-TW/fontrate*2,Desy1-TW-TH-TH/2.0,orderstr);
}
else
{
pDC->SelectObject(&font1);
pDC->SetTextAlign(TA_TOP | TA_RIGHT);
pDC->TextOutW(Desx2-TW/fontrate*2,Desy1+TW+TH+TH/2.0,tenstr);
pDC->SelectObject(&font2);
pDC->SetTextAlign(TA_TOP | TA_LEFT);
pDC->TextOutW(Desx2-TW/fontrate*2,Desy1+TW+TH,orderstr);
}
}
pDC->SelectObject(oldfont);
pDC->SelectObject(oldpen);
return true;
}
bool CLabelDrawer::DrawLabelVer(CDC *pDC,
int ylabelnum,float *pylabel,CStringArray &ylabelArr,int yorder,
float Desy1,float Desy2,float Desx1,float y1,float y2,CString ytitle,
bool IsLeft,COLORREF rgb)
// 绘制垂直刻度
// pDC: 绘图句柄。ylabelnum: 刻度数目。pylabel:刻度真实数值。ylabelArr:刻度数值对应的字符串。
// yorder: 采用科学计数法阶次。Desy1,Desy2:标注绘制在窗口中的纵坐标。Desx1:垂直刻度在窗口中的横坐标。
// ytitle: 坐标轴名称字符串。IsLeft:若为true,刻度位于刻度线左边,否则位于刻度线右边
// rgb:刻度颜色。
{
if(abs(Desy2-Desy1)<0.0001) return false;
if(pylabel==NULL) return false;
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(rgb);
CFont font,fontunit,*oldfont; // 创建Times New Roman字体
font.CreatePointFont(100,_T("Times New Roman"),pDC);
fontunit.CreatePointFont(120,_T("Times New Roman"),pDC);
CPen pen(0,1,rgb),*oldpen;
oldpen=pDC->SelectObject(&pen);
float posx,posy;
oldfont=pDC->SelectObject(&font);
TEXTMETRIC tm;
pDC->GetTextMetrics(&tm);
LONG TW=tm.tmAveCharWidth; // 获取输出字符信息,主要是为了使得短线标注对准字符串中间位置
LONG TH=tm.tmHeight; // 用于计算字符相对短线输出位置
int charlength=0;
for(int i=0;i<ylabelArr.GetCount();i++)
{
if(ylabelArr.GetAt(i).GetLength()>charlength) charlength=ylabelArr.GetAt(i).GetLength();
}
charlength=charlength+1;
double k=(Desy2-Desy1)/(y2-y1);
if(IsLeft==true) // 标注左边
{
pDC->SelectObject(&font);
pDC->SetTextAlign(TA_BOTTOM | TA_RIGHT);
for(int i=0;i<ylabelnum;i++)
{
posy=k*(pylabel[i]-y1)+Desy1;
pDC->MoveTo(Desx1,posy);
pDC->LineTo(Desx1-TW,posy);
pDC->TextOutW(Desx1-TW-1,posy+TH/2.0,ylabelArr.GetAt(i));
}
pDC->SetTextAlign(TA_BOTTOM | TA_CENTER); // 纵向字体按纵向的bottom和left设置,即和横向颠倒
posy=k*((y1+y2)/2.0-y1)+Desy1;
LOGFONT logfont;
fontunit.GetLogFont(&logfont);
logfont.lfEscapement =900;
CFont fontemp;
fontemp.CreateFontIndirect(&logfont);
pDC->SelectObject(&fontemp);
pDC->TextOutW(Desx1-TW*charlength-TW,posy,ytitle);
}
else{
pDC->SelectObject(&font);
pDC->SetTextAlign(TA_BOTTOM | TA_LEFT);
for(int i=0;i<ylabelnum;i++)
{
posy=k*(pylabel[i]-y1)+Desy1;
pDC->MoveTo(Desx1,posy);
pDC->LineTo(Desx1+TW,posy);
pDC->TextOutW(Desx1+TW+1,posy+TH/2.0,ylabelArr.GetAt(i));
}
pDC->SetTextAlign(TA_TOP | TA_CENTER);
posy=k*((y1+y2)/2.0-y1)+Desy1;
LOGFONT logfont;
fontunit.GetLogFont(&logfont);
logfont.lfEscapement =900;
CFont fontemp;
fontemp.CreateFontIndirect(&logfont);
pDC->SelectObject(&fontemp);
pDC->TextOutW(Desx1+TW*charlength+TW,posy,ytitle);
}
if(yorder!=0)
{
CFont font1,font2; // 创建Times New Roman字体
font1.CreatePointFont(100,_T("Times New Roman"),pDC);
font2.CreatePointFont(70,_T("Times New Roman"),pDC);
float fontrate=100.0/70.0;
CString tenstr("×10"),orderstr;
orderstr.Format(_T("%d"),yorder);
if(IsLeft==true)
{
pDC->SelectObject(&font1);
pDC->SetTextAlign(TA_BOTTOM | TA_RIGHT);
pDC->TextOutW(Desx1-charlength*TW-TW-TW/fontrate*2,Desy2,tenstr);
pDC->SelectObject(&font2);
pDC->SetTextAlign(TA_BOTTOM | TA_LEFT);
pDC->TextOutW(Desx1-charlength*TW-TW-TW/fontrate*2,Desy2-TH/2.0,orderstr);
}
else
{
pDC->SelectObject(&font1);
pDC->SetTextAlign(TA_BOTTOM | TA_LEFT);
pDC->TextOutW(Desx1+charlength*TW+TW,Desy2,tenstr);
pDC->SelectObject(&font2);
pDC->SetTextAlign(TA_BOTTOM | TA_LEFT);
pDC->TextOutW(Desx1+charlength*TW+TW+(tenstr.GetLength()+1)*TW,Desy2-TH/2.0,orderstr);
}
}
pDC->SelectObject(oldfont);
pDC->SelectObject(oldpen);
return true;
}
CLabelDrawer类中有两个成员函数,DrawLabelHor用于绘制水平刻度,DrawLabelVer用于绘制垂直刻度。函数参数在代码中有说明,这个类必须配合上边的CLabelGenerator类使用。下面是使用实例。
首先利用vs生成一个MFC单文档程序,为了方便说明,直接把数据放在了OnDraw函数中:
void CLabelTestView::OnDraw(CDC* pDC)
{
CLabelTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此处为本机数据添加绘制代码
int labelnum;
float *plabel=NULL;
CStringArray labelArr;
int order;
float v1=-0.0005,v2=-0.0003;
float Desx1=100,Desx2=500,Desy1=100,Desy2=500;
// 生成数值(-0.0005 -0.0003)范围
CLabelGenerator labelgen;
labelgen.GenerateLabel(v1,v2,4,labelnum,plabel,labelArr,order);
CLabelDrawer labeldraw;
// 绘制数值(-0.0005 -0.0003)范围上刻度
labeldraw.DrawLabelHor(pDC,labelnum,plabel,labelArr,order,Desx1,Desx2,Desy1,v1,v2,_T("upper label"));
// 绘制数值(-0.0005 -0.0003)范围下刻度
labeldraw.DrawLabelHor(pDC,labelnum,plabel,labelArr,order,Desx1,Desx2,Desy2,v1,v2,_T("lower label"),false,RGB(0,0,255));
// 绘制数值(-0.0005 -0.0003)范围左刻度
labeldraw.DrawLabelVer(pDC,labelnum,plabel,labelArr,order,Desy1,Desy2,Desx1,v1,v2,_T("left label"));
// 绘制数值(-0.0005 -0.0003)范围右刻度
labeldraw.DrawLabelVer(pDC,labelnum,plabel,labelArr,order,Desy1,Desy2,Desx2,v1,v2,_T("right label"),false,RGB(255,0,0));
// 绘制矩形框
pDC->SelectStockObject(NULL_BRUSH);
pDC->Rectangle(Desx1,Desy1,Desx2,Desy2);
v1=30; v2=803;
Desx1=650;Desx2=950;Desy1=100;Desy2=500;
if(plabel!=NULL) delete plabel;
// 绘制数值(30 803)范围刻度
labelgen.GenerateLabel(v1,v2,5,labelnum,plabel,labelArr,order);
// 绘制数值(30 803)范围水平上刻度
labeldraw.DrawLabelHor(pDC,labelnum,plabel,labelArr,order,Desx1,Desx2,Desy1,v1,v2,_T("hor axis"),true,RGB(0,0,255));
pDC->MoveTo(Desx1,Desy1); pDC->LineTo(Desx2,Desy1);
// 绘制数值(30 803)范围水平上逆向刻度
labeldraw.DrawLabelHor(pDC,labelnum,plabel,labelArr,order,Desx2,Desx1,Desy1+50,v1,v2,_T("reverse hor axis"),true,RGB(0,0,255));
pDC->MoveTo(Desx1,Desy1+50); pDC->LineTo(Desx2,Desy1+50);
Desx1=700;Desx2=1000;Desy1=200;Desy2=550;
// 绘制数值(30 803)范围垂直左刻度
labeldraw.DrawLabelVer(pDC,labelnum,plabel,labelArr,order,Desy1,Desy2,Desx1,v1,v2,_T("ver axis"),true,RGB(255,0,255));
pDC->MoveTo(Desx1,Desy1); pDC->LineTo(Desx1,Desy2);
// 绘制数值(30 803)范围垂直左逆向刻度
labeldraw.DrawLabelVer(pDC,labelnum,plabel,labelArr,order,Desy2,Desy1,Desx1+100,v1,v2,_T("reverse ver axis"),true,RGB(255,0,255));
pDC->MoveTo(Desx1+100,Desy1); pDC->LineTo(Desx1+100,Desy2);
}
生成的效果如图