欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

利用c++实现数值坐标刻度生成,并利用GDI绘制

程序员文章站 2022-12-29 08:07:20
给定两个数值如(3001,5020),如何做到均匀地标注刻度? 研究matlab图形刻度会发现,在matlab中,图像无论如何缩放,坐标刻度间隔都是以1,2,5为基数,按照10倍或者0.1倍的幂进行...

给定两个数值如(3001,5020),如何做到均匀地标注刻度?

研究matlab图形刻度会发现,在matlab中,图像无论如何缩放,坐标刻度间隔都是以1,2,5为基数,按照10倍或者0.1倍的幂进行放大或缩小也即,刻度间隔为:

…0.1 0.2 0.5 ; 12 5; 1020 50; 100200 500; 10002000 5000….

负刻度也类似:

…-0.1 -0.2 -0.5 ; -1-2 -5; -10 -20 -50; -100 -200 -500; -1000 -2000 -5000….

利用c++实现数值坐标刻度生成,并利用GDI绘制

 

在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 loop1=labelinterval,loop2=abs(endx);

    if(labelinterval>=1)                            //  求取标注间隔阶次,间隔大于1,正阶
    {
        while(loop1>=10)
        {
            loop1=loop1/10;
            intervalorder++;
        }
    }else if(labelinterval<1)                       //  标注小于1,负阶
    {
        while(loop1<0.1)
        {
            loop1=loop1*10;
            intervalorder++;
        }
    }
    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(loop2>=10)
        {
            loop2=loop2/10;
            valueorder++;
        }
    }else if(valueabs<=limitvmin)                      //  求取最大刻度值小于0.001的阶次,负阶
    {
        while(loop2<=0.1)
        {
            loop2=loop2*10;
            valueorder++;
        }
    }

    for(int i=0;i=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;i0)
    {
        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  

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;imoveto(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;imoveto(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;icharlength) 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;imoveto(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;imoveto(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);  
}
生成的效果如图

利用c++实现数值坐标刻度生成,并利用GDI绘制