设为首页 加入收藏

TOP

利用c++实现数值坐标刻度生成,并利用GDI绘制(一)
2016-10-08 11:31:15 】 浏览:1322
Tags:利用 实现 数值 坐标 刻度 生成 GDI 绘制

给定两个数值如(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….

\

在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)
首页 上一页 1 2 3 4 5 下一页 尾页 1/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇【C++研发面试笔记】1. C++常见关.. 下一篇Effective C++ 简要条款分析(一)

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目