利用VC绘制对数坐标系
目标:绘制以任意频率为起点、任意频率为终点的对数坐标,并能对坐标进行任意缩放。
实现方法:
======【Update 2013-12-18】:=========================================================
- 今天发现以前的方法其实是很笨的方法。其实只要知道任意两个点的X坐标及其对应的频率f, 就可以根据第三点的频率确定第三点的坐标,或者根据第三点的坐标确定该点的频率。 个人感觉 对数坐标的本质就是,对于频率取对数之后,该坐标映射成为线性坐标,即屏幕坐标x可以看做log(f)的线性函数。
-
- 给定三点(x0,f0), (x1,f1),(x2,f2), 其中x是X坐标,f是对应的频率,有对应关系
- (x2-x0)/(x1-x0) = (log(f2)-log(f0))/(log(f1)-log(f0)) = log(f2/f0)/log(f1/f0),
- 于是,可以求得
- x1=log(f1/f0)/log(f2/f0)*(x2-x0)+x0 ,
- 和
- log(f1) = 1/C* log(f2/f0)+log(f0), where C=(x2-x0)/(X1-x0)。
-
- 根据这两个公式,写成两个函数,一个是根据频率计算x坐标,一个根据x坐标计算频率,然后指定坐标轴的位置,就可以很容易绘制出对数坐标系了。
以下内容已经过时,仅供参考。
1 、定义
标准频率:频率值为 10 N 的相应的频率点,其中 N=…-2,-1,0,1,2,3… ,单位 MHz 。如 0.01MHZ 、 0.1MHz 、 1MHz 、 10MHz 、 100MHz 等都是标准频率。
2 、思路
首先找出标准频率,画出标准频率线,然后根据标准频率画出其他频率线。
3 、实现
( 1 )对数坐标系数学基础
如图所示的对数坐标系中:
由此,若知道上图中的三个点的坐标,可以求出另一点的坐标。
( 2 )找出起始 | 终止标准频率的代码:
double stdStartFreq,stdStopFreq; // 标准频率起始点、终止点
// 格式化标准起始频率
for ( int i=0;i<7;++i)
{
if (StartFreq==pow(( double )10,i-2))
{
stdStartFreq=StartFreq;
break ;
}
else if (StartFreq>pow(( double )10,i-2) && StartFreq<pow(( double )10,i-1))
{
stdStartFreq=pow(( double )10,i-1);
break ;
}
}
// 格式化标准终止频率
for ( int i=0;i<7;++i)
{
if (StopFreq==pow(( double )10,i-2))
{
stdStopFreq=StopFreq;
break ;
}
else if (StopFreq>pow(( double )10,i-2) && StopFreq<pow(( double )10,i-1))
{
stdStopFreq=pow(( double )10,i-2);
break ;
}
}
起始标准频率 就是第一个大于起始频率的标准频率;终止标准频率就是最后一个小于终止频率的标准频率。
举例:如果起始频率为 1.9MHz ,终止频率为 201MHz ,那么起始标准频率为 10MHz ,终止标准频率为 100MHz ;如果起始频率为 1.9MHz ,终止频率为 20.1MHz ,那么起始标准频率为 10MHz ,终止标准频率为 10MHz ;如果起始频率为 1.9MHz ,终止频率为 2.01MHz ,那么起始标准频率为 10MHz ,终止标准频率为 1MHz 。
( 3 )三种情况
① 起始频率≤起始标准频率 < 终止标准频率≤终止频率
在这种情况下,首先画出起始 | 终止标准频率线。对于起始频率和起始标准频率之间的部分,由起始标准频率递减向起始频率画出虚线;对于终止标准频率和终止频率之间的部分,由终止标准频率向终止频率递增画出虚线;对于起始标准频率和终止标准频率之间的部分,首先找到二者之间的标准频率,然后根据标准频率(包括起始 | 终止标准频率)画出其间的虚线。
② 起始频率 < 起始标准频率 = 终止标准频率 < 终止频率
这种情况是第一种情况的特例。因为起始标准频率 = 终止标准频率,所以第一种情况里只有两种情况来画虚线。
③ 终止标准频率 < 起始频率 < 终止频率 < 起始标准频率
在这种情况下,先找出终止标准频率,然后依据终止标准频率递增画出起始频率和终止频率之间的虚线。
4 、其他问题
( 1 )细化间隔
由于对数坐标相同频率段的间隔不同,如 10 ~ 20MHz 的间隔与 80 ~ 90MHz 的间隔不同,前者大于后者;另外,考虑到坐标系放大时,如 200MHz ~ 300MHz 频率段,整个坐标系中无虚线,只有横线,很难对测试曲线中某个点的频率进行粗略判断。因此,对于比较大的间隔,应该进行细化。
方法就是利用相邻两虚线的间隔所占坐标横轴的比例 进行判断,若大于某个数值则有必要进行细化。由于间隔有大有小,考虑到细化的间隔不能影响整体对数坐标的效果,因此细化的竖线用灰色虚线表示,且将间隔分成不同等级,不同等级画不同条灰色虚线。
( 2 )坐标标注
坐标标注同样利用相邻两虚线的间隔所占坐标横轴的比例 来进行判断,大于某个设定值后才进行标注,否则不进行标注。
【源代码如下:】
//
绘制对数坐标
void
CEMCView::DrawLogCoords(CDC
*
pDC,
double
StartFreq,
double
StopFreq)
...
{
CString note;
CRect rect;
this
->
GetClientRect(
&
rect);
ORIGIN_X
=
rect.left
+
rect.Width()
/
10
;
//
坐标原点X坐标
ORIGIN_Y
=
rect.bottom
-
rect.Height()
/
10
;
//
坐标原点Y坐标
COORD_TOP
=
rect.top
+
rect.Height()
/
10
;
//
坐标系最高点Y坐标
COORD_RIGHT
=
rect.right
-
rect.Width()
/
10
;
//
坐标系最右点X坐标
pDC
->
MoveTo(ORIGIN_X,ORIGIN_Y);
pDC
->
LineTo(ORIGIN_X,COORD_TOP);
//
画纵坐标轴
pDC
->
MoveTo(ORIGIN_X,ORIGIN_Y);
pDC
->
LineTo(COORD_RIGHT,ORIGIN_Y);
//
画横坐标轴
note.Format(L
"
%4.2fM
"
,StartFreq);
pDC
->
TextOutW(ORIGIN_X
-
15
,ORIGIN_Y
+
5
,note);
//
原点标注
//
画横格线
double
yInc
=
(ORIGIN_Y
-
COORD_TOP)
/
9.0
;
double
tmp
=
(
double
)ORIGIN_Y
-
yInc;
for
(
int
i
=
0
;i
<
8
;
++
i)
...
{
pDC
->
MoveTo(ORIGIN_X,(
int
)tmp);
pDC
->
LineTo(COORD_RIGHT,(
int
)tmp);
note.Format(L
"
%d
"
,
10
*
(i
+
1
));
pDC
->
TextOutW(ORIGIN_X
-
25
,(
int
)tmp
-
8
,note);
//
z纵坐标标注
tmp
-=
yInc;
}
pDC
->
MoveTo(ORIGIN_X,COORD_TOP);
pDC
->
LineTo(COORD_RIGHT,COORD_TOP);
note.Format(L
"
%d
"
,
90
);
pDC
->
TextOutW(ORIGIN_X
-
25
,COORD_TOP
-
8
,note);
//
z纵坐标标注
note.Format(L
"
[dBuV]
"
);
pDC
->
TextOutW(ORIGIN_X
-
25
,COORD_TOP
-
28
,note);
//
z纵坐标标注
//
画竖格线
//
| . .| .
//
| . .| .
//
| . .| .
//
| . .| .
//
| . .| .
//
| . .| .
//
定义:标准频率,是指0.01MHz、0.1MHz、1MHz
//
10MHZ、100MHz、1000MHz等频率
double
stdStartFreq,stdStopFreq;
//
标准频率起始点、终止点
//
格式化标准起始频率
for
(
int
i
=
0
;i
<
7
;
++
i)
...
{
if
(StartFreq
==
pow((
double
)
10
,i
-
2
))
...
{
stdStartFreq
=
StartFreq;
break
;
}
else
if
(StartFreq
>
pow((
double
)
10
,i
-
2
)
&&
StartFreq
<
pow((
double
)
10
,i
-
1
))
...
{
stdStartFreq
=
pow((
double
)
10
,i
-
1
);
break
;
}
}
//
格式化标准终止频率
for
(
int
i
=
0
;i
<
7
;
++
i)
...
{
if
(StopFreq
==
pow((
double
)
10
,i
-
2
))
...
{
stdStopFreq
=
StopFreq;
break
;
}
else
if
(StopFreq
>
pow((
double
)
10
,i
-
2
)
&&
StopFreq
<
pow((
double
)
10
,i
-
1
))
...
{
stdStopFreq
=
pow((
double
)
10
,i
-
2
);
break
;
}
}
double
totalRatio
=
log10(StopFreq
/
StartFreq);
//
整个频率段所占的比例
int
totalLen
=
COORD_RIGHT
-
ORIGIN_X;
//
总长度
if
(stdStartFreq
<=
stdStopFreq)
//
起始频率和终止频率相差大于等于一个数量级,如StartFreq=2.5,StopFreq=200
...
{
double
startRatio
=
log10(stdStartFreq
/
StartFreq)
/
totalRatio;
//
起始频率与标准起始频率之间的部分所占的比例
double
stdRatio
=
log10(stdStopFreq
/
stdStartFreq)
/
totalRatio;
//
标准起始频率和标准终止频率之间部分所占的比例
double
stopRatio
=
log10(StopFreq
/
stdStopFreq)
/
totalRatio;
//
标准终止频率和终止频率之间部分所占的比例
double
xStdStart
=
ORIGIN_X
+
startRatio
*
totalLen;
//
标准起始频率所在的X坐标
pDC
->
MoveTo((
int
)xStdStart,ORIGIN_Y);
pDC
->
LineTo((
int
)xStdStart,COORD_TOP);
//
绘制标准起始频率所在的垂直实线
note.Format(L
"
%4.2fM
"
,stdStartFreq);
pDC
->
TextOutW((
int
)xStdStart
-
15
,ORIGIN_Y
+
5
,note);
//
坐标标注
double
xStdStop
=
xStdStart
+
stdRatio
*
totalLen;
//
标准终止频率所在的X坐标
pDC
->
MoveTo((
int
)xStdStop,ORIGIN_Y);
pDC
->
LineTo((
int
)xStdStop,COORD_TOP);
//
绘制标准终止频率所在的垂直实线
note.Format(L
"
%4.2fM
"
,stdStopFreq);
pDC
->
TextOutW((
int
)xStdStop
-
15
,ORIGIN_Y
+
5
,note);
//
坐标标注
double
xtmp
=
xStdStart;
for
(
int
i
=
0
;i
<
(
int
)(stdRatio
*
totalRatio);
++
i)
//
绘制标准起始频率和标准终止频率之间的垂直实线
...
{
xtmp
+=
totalLen
/
totalRatio;
pDC
->
MoveTo((
int
)xtmp,ORIGIN_Y);
pDC
->
LineTo((
int
)xtmp,COORD_TOP);
//
绘制个关键频率所在的垂直实线
note.Format(L
"
%4.2fM
"
,stdStartFreq
*
pow((
double
)
10
,i
+
1
));
pDC
->
TextOutW((
int
)xtmp
-
15
,ORIGIN_Y
+
5
,note);
//
坐标标注
}
pDC
->
MoveTo(COORD_RIGHT,ORIGIN_Y);
//
终止频率X坐标为COORD_RIGHT
pDC
->
LineTo(COORD_RIGHT,COORD_TOP);
note.Format(L
"
%4.2fM
"
,StopFreq);
pDC
->
TextOutW(COORD_RIGHT
-
15
,ORIGIN_Y
+
5
,note);
//
坐标标注
CPen dotPen(PS_DOT,
1
,RGB(
0
,
0
,
0
));
CPen
*
pOldPen
=
pDC
->
SelectObject(
&
dotPen);
//
绘制起始频率与标准起始频率之间的虚线
double
tmpFreqL
=
stdStartFreq
-
stdStartFreq
/
10
;
//
低频率
double
tmpFreqH
=
stdStartFreq;
//
高频率
double
tmpRatio;
xtmp
=
xStdStart;
while
((tmpFreqL
-
StartFreq)
>
0.000001
)
/**/
///
// 由于double型浮点数精度的问题,不得不采用这种方法进行判断
///
//
...
{
tmpRatio
=
log10(tmpFreqH
/
tmpFreqL)
/
totalRatio;
xtmp
-=
tmpRatio
*
totalLen;
pDC
->
MoveTo((
int
)xtmp,ORIGIN_Y);
pDC
->
LineTo((
int
)xtmp,COORD_TOP);
//
绘制虚线
if
(tmpRatio
>=
0.06
)
...
{
note.Format(L
"
%4.2fM
"
,tmpFreqL);
pDC
->
TextOutW(xtmp
-
15
,ORIGIN_Y
+
5
,note);
//
坐标标注
}
//
如果两个频率之间的间隔所占长度大于等于横坐标全长的10%,
//
用灰色虚线画出中间频率线
if
(tmpRatio
>=
0.10
)
...
{
CPen grayPen(PS_DOT,
0.5
,RGB(
192
,
192
,
192
));
CPen
*
pDotPen
=
pDC
->
SelectObject(
&
grayPen);
//
灰色画笔
double
grayInc;
if
(tmpRatio
<
0.15
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
2
;
//
画1条灰色虚线
else
if
(tmpRatio
>=
0.15
&&
tmpRatio
<
0.20
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
4
;
//
画3条灰色虚线
else
if
(tmpRatio
>=
0.20
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
6
;
//
画5条灰色虚线
double
grayFreqH
=
tmpFreqL
+
grayInc;
double
grayFreqL
=
tmpFreqL;
double
grayLen
=
tmpRatio
*
totalLen;
double
xgray
=
xtmp;
double
grayRatio;
while
(tmpFreqH
-
grayFreqH
>
0.000001
)
...
{
grayRatio
=
log10(grayFreqH
/
grayFreqL)
/
log10(tmpFreqH
/
tmpFreqL);
xgray
+=
grayRatio
*
grayLen;
pDC
->
MoveTo(xgray,ORIGIN_Y);
pDC
->
LineTo(xgray,COORD_TOP);
//
绘制灰色虚线
grayFreqH
+=
grayInc;
grayFreqL
+=
grayInc;
}
pDC
->
SelectObject(pDotPen);
}
tmpFreqH
-=
stdStartFreq
/
10
;
tmpFreqL
-=
stdStartFreq
/
10
;
}
//
起始频率和第一条黑色虚线之间的灰色虚线
//
如果两个频率之间的间隔所占长度大于等于横坐标全长的10%
//
用灰色虚线画出全部中间频率线
tmpRatio
=
log10(tmpFreqH
/
StartFreq)
/
totalRatio;
if
(tmpRatio
>=
0.10
)
...
{
CPen grayPen(PS_DOT,
0.5
,RGB(
192
,
192
,
192
));
CPen
*
pDotPen
=
pDC
->
SelectObject(
&
grayPen);
//
灰色画笔
double
grayInc;
if
(tmpRatio
<
0.15
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
2
;
//
画1条灰色虚线
else
if
(tmpRatio
>=
0.15
&&
tmpRatio
<
0.20
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
4
;
//
画2条灰色虚线
else
if
(tmpRatio
>=
0.20
&&
tmpRatio
<
0.25
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
6
;
//
画5条灰色虚线
else
if
(tmpRatio
>=
0.25
&&
tmpRatio
<
0.30
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
8
;
//
画7条灰色虚线
else
if
(tmpRatio
>=
0.30
&&
tmpRatio
<
0.4
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
9
;
//
画8条灰色虚线
else
if
(tmpRatio
>=
0.40
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
10
;
//
画8条灰色虚线
double
grayFreqH
=
tmpFreqH;
double
grayFreqL
=
tmpFreqH
-
grayInc;
double
xgray
=
xtmp;
double
grayRatio;
while
(grayFreqL
-
StartFreq
>
0.000001
)
...
{
grayRatio
=
log10(grayFreqH
/
grayFreqL)
/
log10(tmpFreqH
/
StartFreq);
xgray
-=
(xtmp
-
ORIGIN_X)
*
grayRatio;
pDC
->
MoveTo(xgray,ORIGIN_Y);
pDC
->
LineTo(xgray,COORD_TOP);
//
绘制灰色虚线
grayFreqH
-=
grayInc;
grayFreqL
-=
grayInc;
}
pDC
->
SelectObject(pDotPen);
}
//
绘制标准起始频率和标准终止频率之间的垂直虚线
tmp
=
xStdStart;
double
tmpFreq
=
stdStartFreq;
for
(
int
i
=
0
;i
<
(
int
)(stdRatio
*
totalRatio);
++
i)
...
{
xtmp
=
tmp;
//
tmp代表关键频率所在的X坐标,xtmp代表将要绘制的虚线的X坐标
tmpFreqL
=
tmpFreq;
tmpFreqH
=
2
*
tmpFreq;
//
由低频向高频绘制虚线
for
(
int
j
=
0
;j
<
8
;
++
j)
...
{
tmpRatio
=
log10(tmpFreqH
/
tmpFreqL)
/
totalRatio;
xtmp
+=
tmpRatio
*
totalLen;
pDC
->
MoveTo(xtmp,ORIGIN_Y);
pDC
->
LineTo(xtmp,COORD_TOP);
//
绘制虚线
if
(tmpRatio
>=
0.06
)
...
{
note.Format(L
"
%4.2fM
"
,tmpFreqH);
pDC
->
TextOutW(xtmp
-
15
,ORIGIN_Y
+
5
,note);
//
坐标标注
}
//
如果两个频率之间的间隔所占长度大于等于横坐标全长的10%,
//
用灰色虚线画出中间频率线
if
(tmpRatio
>=
0.10
)
...
{
CPen grayPen(PS_DOT,
0.5
,RGB(
192
,
192
,
192
));
CPen
*
pDotPen
=
pDC
->
SelectObject(
&
grayPen);
//
灰色画笔
double
grayInc;
if
(tmpRatio
<
0.15
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
2
;
//
画1条灰色虚线
else
if
(tmpRatio
>=
0.15
&&
tmpRatio
<
0.20
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
4
;
//
画3条灰色虚线
else
if
(tmpRatio
>=
0.20
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
6
;
//
画3条灰色虚线
double
grayFreqH
=
tmpFreqL
+
grayInc;
double
grayFreqL
=
tmpFreqL;
double
grayLen
=
tmpRatio
*
totalLen;
double
xgray
=
xtmp
-
grayLen;
double
grayRatio;
while
(tmpFreqH
-
grayFreqH
>
0.00001
)
...
{
grayRatio
=
log10(grayFreqH
/
grayFreqL)
/
log10(tmpFreqH
/
tmpFreqL);
xgray
+=
grayRatio
*
grayLen;
pDC
->
MoveTo(xgray,ORIGIN_Y);
pDC
->
LineTo(xgray,COORD_TOP);
//
绘制灰色虚线
grayFreqH
+=
grayInc;
grayFreqL
+=
grayInc;
}
pDC
->
SelectObject(pDotPen);
}
tmpFreqL
+=
tmpFreq;
tmpFreqH
+=
tmpFreq;
}
tmpFreq
*=
10
;
//
tmpFreq代表关键频率
tmp
+=
totalLen
/
totalRatio;
}
//
绘制标准终止频率和终止频率之间的垂直虚线
xtmp
=
xStdStop;
tmpFreqL
=
stdStopFreq;
tmpFreqH
=
2
*
stdStopFreq;
while
((StopFreq
-
tmpFreqH)
>
0.000001
)
...
{
tmpRatio
=
log10(tmpFreqH
/
tmpFreqL)
/
totalRatio;
xtmp
+=
tmpRatio
*
totalLen;
pDC
->
MoveTo(xtmp,ORIGIN_Y);
pDC
->
LineTo(xtmp,COORD_TOP);
//
绘制虚线
if
(tmpRatio
>=
0.06
)
...
{
note.Format(L
"
%4.2fM
"
,tmpFreqH);
pDC
->
TextOutW(xtmp
-
15
,ORIGIN_Y
+
5
,note);
//
坐标标注
}
//
如果两个频率之间的间隔所占长度大于等于横坐标全长的10%
//
用灰色虚线画出全部中间频率线
if
(tmpRatio
>=
0.10
)
...
{
CPen grayPen(PS_DOT,
0.5
,RGB(
192
,
192
,
192
));
CPen
*
pDotPen
=
pDC
->
SelectObject(
&
grayPen);
//
灰色画笔
double
grayInc;
if
(tmpRatio
<
0.15
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
2
;
//
画1条灰色虚线
else
if
(tmpRatio
>=
0.15
&&
tmpRatio
<
0.20
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
4
;
//
画3条灰色虚线
else
if
(tmpRatio
>=
0.20
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
6
;
//
画5条灰色虚线
double
grayFreqH
=
tmpFreqL
+
grayInc;
double
grayFreqL
=
tmpFreqL;
double
grayLen
=
tmpRatio
*
totalLen;
//
要画灰色虚线频率之间的距离
double
xgray
=
xtmp
-
grayLen;
double
grayRatio;
while
(tmpFreqH
-
grayFreqH
>
0.000001
)
...
{
grayRatio
=
log10(grayFreqH
/
grayFreqL)
/
log10(tmpFreqH
/
tmpFreqL);
xgray
+=
grayRatio
*
grayLen;
pDC
->
MoveTo(xgray,ORIGIN_Y);
pDC
->
LineTo(xgray,COORD_TOP);
//
绘制灰色虚线
grayFreqH
+=
grayInc;
grayFreqL
+=
grayInc;
}
pDC
->
SelectObject(pDotPen);
}
tmpFreqL
+=
stdStopFreq;
tmpFreqH
+=
stdStopFreq;
}
//
黑色虚线和终止频率之间的灰色虚线
//
如果两个频率之间的间隔所占长度大于等于横坐标全长的10%
//
用灰色虚线画出全部中间频率线
tmpRatio
=
log10(StopFreq
/
tmpFreqL)
/
totalRatio;
if
(tmpRatio
>=
0.10
)
...
{
CPen grayPen(PS_DOT,
0.5
,RGB(
192
,
192
,
192
));
CPen
*
pDotPen
=
pDC
->
SelectObject(
&
grayPen);
//
灰色画笔
double
grayInc;
if
(tmpRatio
<
0.15
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
2
;
//
画1条灰色虚线
else
if
(tmpRatio
>=
0.15
&&
tmpRatio
<
0.20
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
4
;
//
画2条灰色虚线
else
if
(tmpRatio
>=
0.20
&&
tmpRatio
<
0.25
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
6
;
//
画5条灰色虚线
else
if
(tmpRatio
>=
0.25
&&
tmpRatio
<
0.30
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
8
;
//
画7条灰色虚线
else
if
(tmpRatio
>=
0.30
&&
tmpRatio
<
0.4
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
9
;
//
画8条灰色虚线
else
if
(tmpRatio
>=
0.40
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
10
;
//
画9条灰色虚线
double
grayFreqH
=
tmpFreqL
+
grayInc;
double
grayFreqL
=
tmpFreqL;
double
xgray
=
xtmp;
double
grayRatio;
while
(StopFreq
-
grayFreqL
>
0.000001
)
...
{
grayRatio
=
log10(grayFreqH
/
grayFreqL)
/
log10(StopFreq
/
tmpFreqL);
xgray
+=
(COORD_RIGHT
-
xtmp)
*
grayRatio;
pDC
->
MoveTo(xgray,ORIGIN_Y);
pDC
->
LineTo(xgray,COORD_TOP);
//
绘制灰色虚线
grayFreqH
+=
grayInc;
grayFreqL
+=
grayInc;
}
pDC
->
SelectObject(pDotPen);
}
pDC
->
SelectObject(pOldPen);
}
else
//
起始频率和终止频率在同一数量级,如StartFreq=200,StopFreq=500
...
{
pDC
->
MoveTo(COORD_RIGHT,ORIGIN_Y);
pDC
->
LineTo(COORD_RIGHT,COORD_TOP);
//
最右端边界线
note.Format(L
"
%4.2fM
"
,StopFreq);
pDC
->
TextOutW(COORD_RIGHT
-
15
,ORIGIN_Y
+
5
,note);
//
坐标标注
CPen dotPen(PS_DOT,
1
,RGB(
0
,
0
,
0
));
CPen
*
pOldPen
=
pDC
->
SelectObject(
&
dotPen);
double
unitLen
=
totalLen
/
totalRatio;
//
标准频率之间应有的距离
double
xStdStop
=
ORIGIN_X
-
log10(StartFreq
/
stdStopFreq)
*
unitLen;
//
标准终止频率(此时在起始频率的左边)的X坐标
double
xtmp
=
xStdStop;
double
tmpFreqH
=
2
*
stdStopFreq;
double
tmpFreqL
=
stdStopFreq;
double
tmpRatio;
//
根据标准终止频率(此时在起始频率的左边)绘制标准虚线
while
((StartFreq
-
tmpFreqH
>
0.000001
))
//
找到第一根标准虚线的X坐标
...
{
tmpRatio
=
log10(tmpFreqH
/
tmpFreqL);
xtmp
+=
tmpRatio
*
unitLen;
tmpFreqH
+=
stdStopFreq;
tmpFreqL
+=
stdStopFreq;
}
while
(StopFreq
-
tmpFreqL
>
0.000001
)
...
{
tmpRatio
=
log10(tmpFreqH
/
tmpFreqL);
if
(tmpFreqL
>
StartFreq)
...
{
pDC
->
MoveTo(xtmp,ORIGIN_Y);
pDC
->
LineTo(xtmp,COORD_TOP);
//
绘制虚线
if
(tmpRatio
>=
0.06
)
...
{
note.Format(L
"
%4.2fM
"
,tmpFreqL);
pDC
->
TextOutW(xtmp
-
15
,ORIGIN_Y
+
5
,note);
//
坐标标注
}
}
//
如果两个频率之间的间隔所占长度大于等于横坐标全长的10%
//
用灰色虚线画出全部中间频率线
double
grayTmpRatio
=
tmpRatio
/
log10(StopFreq
/
StartFreq);
if
(grayTmpRatio
>=
0.10
)
...
{
CPen grayPen(PS_DOT,
0.5
,RGB(
192
,
192
,
192
));
CPen
*
pDotPen
=
pDC
->
SelectObject(
&
grayPen);
//
灰色画笔
double
grayInc;
if
(grayTmpRatio
<
0.15
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
2
;
//
画1条灰色虚线
else
if
(grayTmpRatio
>=
0.15
&&
grayTmpRatio
<
0.20
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
4
;
//
画2条灰色虚线
else
if
(grayTmpRatio
>=
0.20
&&
grayTmpRatio
<
0.25
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
6
;
//
画5条灰色虚线
else
if
(grayTmpRatio
>=
0.25
&&
grayTmpRatio
<
0.30
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
8
;
//
画7条灰色虚线
else
if
(grayTmpRatio
>=
0.30
&&
grayTmpRatio
<
0.35
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
9
;
//
画8条灰色虚线
else
if
(grayTmpRatio
>=
0.35
)
grayInc
=
(tmpFreqH
-
tmpFreqL)
/
10
;
//
画9条灰色虚线
double
grayFreqH
=
tmpFreqL
+
grayInc;
double
grayFreqL
=
tmpFreqL;
double
grayLen
=
tmpRatio
*
unitLen;
//
要画的灰色虚线频率之间的距离
double
xgray
=
xtmp;
double
grayRatio;
while
((tmpFreqH
-
grayFreqH
>
0.000001
)
&&
grayFreqH
<
StopFreq)
...
{
grayRatio
=
log10(grayFreqH
/
grayFreqL)
/
log10(tmpFreqH
/
tmpFreqL);
xgray
+=
grayRatio
*
grayLen;
pDC
->
MoveTo(xgray,ORIGIN_Y);
pDC
->
LineTo(xgray,COORD_TOP);
//
绘制灰色虚线
grayFreqH
+=
grayInc;
grayFreqL
+=
grayInc;
}
pDC
->
SelectObject(pDotPen);
}
xtmp
+=
tmpRatio
*
unitLen;
tmpFreqH
+=
stdStopFreq;
tmpFreqL
+=
stdStopFreq;
}
pDC
->
SelectObject(pOldPen);
}
}
目标:绘制以任意频率为起点、任意频率为终点的对数坐标,并能对坐标进行任意缩放。
附录:
1 读者来信问道:
您好,我是在校的学生,看了您关于“利用VC绘制对数坐标系”http://blog.csdn.net/bonny95/archive/2008/02/21/2111368.aspx 的博文,于是自己写了一个在不同坐标系(直角坐标系和对数坐标系)上描点并选择一些点进行线性拟合的C++程序,结果发现我的点不能很好的在对数坐标坐标上显示出来。
因此我想问您关于实际点到对数坐标系上的页面坐标点转换的过程,您的想法是什么样的呢?
比如点(1998,200)落在1000-10000的标准频率段内,并且是在1000-2000之间,这其中的如何正确地把该点在对数坐标上绘制出来呢。如果您有时间能给我这个解答的话不胜感激。
【答】首先我觉得有必要明确一下直角坐标系和对数坐标系的区别: 直角坐标系的X轴、Y轴全部为线性坐标轴,也即1、2、3、…… 10、……100,1和2,2和3, 99和100之间的距离是相等的;而对数坐标系( 确切而言是半对数坐标系)的X轴(横坐标)是对数坐标, 是非线性的,而Y轴是线性变化的,X轴上1、10、 100之间的距离是相等的,而1和2,2和3, 99和100之间的距离是不等的。