前段时间写过几篇<致新手>,对广大青菜还是有用的,现在给有一定基础的写几篇
以前我不怎么重视数学,尤其是高等数学,高考时候就是因为数学不及格,与重点大学擦肩而过...
近期搞些3D啊什么的算法,才发现高级的数学知识还是挺有用的~
也证明了那句话:书到用时方恨少! 还好我只缺数学,而且重视一下问题就可以解决了.
第一个例子,我们自己画一个圆角的窗口.
首先,任何窗口(Window)在Windows下都是一个矩形的区域,常见的做法是:
透明不想要的区域,将该区域设为某一种颜色,然后透明该颜色
圆角其实只要在四个顶角画一个圆,圆以外部分透明即可

然而做一件事情,我们的追求往往是这样,做得了->做得好->做得妙!
最笨的方法当然是二重循环,一个个点的算出距离,然后距离大于R的画上紫色
对于无边框的窗体,代码就应该是
'x0,y0正好在边界上,边界不画,否则不显出圆角,而显出圆形 For j = Y1 To Y2 For i = X1 To X2 If (i - x0) ^ 2 + (j - y0) ^ 2 > r ^ 2 Then Me.PSet (i, j), vbMagenta '16711935 (&HFF00FF),RGB(255,0,255) End If Next i Next j
然而,直观看出半径以外的区域占的面积是较小的,因此有一部分我们其实不必要判断的
那就是这条直线以内的,用两点可以求出方程,对于右上角就是Y=R-X

如果是逐列(从左到右)处理,直接代入,我习惯逐行,因此变换一下为:X=R-Y
那么处理右上角的代码就应该是:
For j = Y1 To Y2 For i = r - Y1 To r - Y2 If (i - x0) ^ 2 + (j - y0) ^ 2 > r ^ 2 Then Me.PSet (i, j), vbMagenta '16711935 (&HFF00FF),RGB(255,0,255) End If Next i Next j
至此,我们有一半的面积没有参与计算,速度就提高了将近一倍!
对于其他的顶角,一样的道理
然而这是又有另外一个问题,半径的大小怎么定?
如果是标题栏高度的一半,那么显得像个帽子,1/4有点感觉,还是没到点上
我们换一个半径,都市的白领喜欢搞三角关系,我们也用三角形说明问题

如图,BC=AB/2, CD=BC, AE=AD
我相信有人知道我想说什么,不错,黄金分割点,约为0.618
确切的值是:根号5-1的差的一半
分割比K有很多奇妙的东西,比如1/1+K=K,1+K/1=1/K
我们用1-K来做为圆角直径,实际上微软的XP窗口就是这么干的,好了贴代码
Option Explicit Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long Private Declare Function SetLayeredWindowAttributes Lib "user32" (ByVal hWnd As Long, ByVal crKey As Long, ByVal bAlpha As Byte, ByVal dwFlags As Long) As Long Private lpEx As Long Private Sub Form_Load() 'Form的BorderStyle设为0,对C/C++则用CreateWindow(Ex)创建无边框的窗口 With Me .Caption = "Draw Window Demo" lpEx = GetWindowLong(.hWnd, -20) .AutoRedraw = True .ScaleMode = 3 .Show End With SetWindowAlpha 128 End Sub Private Sub Form_Resize() ClipCorner End Sub Public Function ClipCorner(Optional lRad As Long = 6) As Long 'XP标题栏高度30像素, 1-0.618=0.382, 0.382/2=0.191, 30*0.191=6 '其实微软的XP窗口圆角就是这样子的 Dim lW As Long, lH As Long Dim i As Long, j As Long lH = Me.ScaleHeight - 1 lW = Me.ScaleWidth - 1 Me.Cls On Local Error GoTo ErrHandle '左上角 X = Y + R,圆心(R,R),因Y向下为正,方程:X = R - Y For j = 0 To lRad - 1 For i = 0 To lRad - j If (lRad - i) ^ 2 + (lRad - j) ^ 2 > lRad ^ 2 Then Me.PSet (i, j), vbMagenta End If Next i Next j '右上角 X = Y - R,圆心(W-R,R) For j = 0 To lRad - 1 For i = lW + j - lRad To lW If (i - lW + lRad) ^ 2 + (j - lRad) ^ 2 > lRad ^ 2 Then Me.PSet (i, j), vbMagenta End If Next i Next j '左下角 X = R - Y,圆心(R,H-R) For j = lH - lRad To lH For i = 0 To lH + lRad - j If (lRad - i) ^ 2 + (lRad - lH + j) ^ 2 > lRad ^ 2 Then Me.PSet (i, j), vbMagenta End If Next i Next j '右下角 X = -(Y + R),圆心(W-R,H-R) For j = lH - lRad To lH For i = lW + lH - (j + lRad) To lW If (i - lW + lRad) ^ 2 + (j - lH + lRad) ^ 2 > lRad ^ 2 Then Me.PSet (i, j), vbMagenta End If Next i Next j '后面可以用lRad/2作为边框的宽度画出边框 ClipCorner = 1 ErrHandle: Err.Clear End Function Public Function SetWindowAlpha(Optional bAlpha As Byte = 255) As Long Dim ret As Long ret = lpEx Or &H80000 '如果不想让窗口接受焦点,加 Or &H20 SetWindowLong Me.hWnd, -20, ret SetLayeredWindowAttributes Me.hWnd, vbMagenta, bAlpha, &H3 'Const LWA_COLORKEY = &H1 'Const LWA_ALPHA = &H2 'SetLayeredWindowAttributes Me.hWnd, vbMagenta, bAlpha, &H1 '&H2 End Function
文章写的有点久,丢失了一次,影响心情,C/C++的代码就不写了,郁闷

1074

被折叠的 条评论
为什么被折叠?



