CorelDraw插件开发教程(6):循环

  • 0

CorelDraw插件开发教程(6):循环

Category:COREL插件 Tags : 

 刘肖健创意之代码 1周前

CorelDraw插件开发教程(6):循环

 

 

刘肖健

浙江工业大学工业设计研究院

 8.   双循环

有了画一行圆的经验,我们就可以画很多行的圆,把整个页面铺满。下面是界面上增加的部分。

从下往上把整个页面铺满,圆的半径和刚才的算法一样,按页面宽度除以圆的个数来计算,每行圆左右相切,且第一个和最后一个与页面两侧边缘相切。

由于页面高度不一定是圆直径的整数倍,所以页面最上端可能会留出一段空白,而页面下边缘则会与最下一行圆相切。

程序代码如下:

 

Private Sub DrawCirclePage_Click()

Dim i As Integer, j As Integer, nRows As Integer

Dim s1 As Shape, x As Double, y As Double, r AsDouble

r = 0.5 * ActivePage.SizeWidth / nCircles3.Value    ‘圆半径

nRows = Int(ActivePage.SizeHeight / (2 * r))  ‘计算行数

For j = 1 To nRows ‘第二层循环:每次画一行圆

‘圆心y坐标

y = ActivePage.BottomY + (j – 1) * 2 * r + r

‘第一层循环:每次画一个圆

For i = 1 To nCircles3.Value

‘圆心x坐标

x = ActivePage.LeftX + (i – 1) * 2 * r +r

Set s1 = ActiveLayer.CreateEllipse2(x, y, r,r, 90#, 90#, False)

‘填RGB随机色

s1.Fill.UniformColor.RGBAssign (i /nCircles3.Value) * 255, (1 – i / nCircles3.Value) * 255, (1 – i /nCircles3.Value) * 255

Next

‘第一层循环结束

Next

End Sub

上述代码如果是从刚才的代码直接拷贝过来的,记得要把所有的“nCircles2.Value”都改为“nCircles3.Value”。

nRows是一个整数,是圆的行数,同步页面高度除以圆直径再取整获得。函数Int()是对括号中的数值进行取整,即抹掉小数点后的数位,不做四舍五入。带四舍五入的取整函数是CInt()。

圆的色彩每列都是一样的,因为给圆赋色的那句代码里面只有i没有j,就是说,色彩只与圆心x坐标有关,而在y坐标上没有变化。

圆心x坐标是i的函数:

 

x = ActivePage.LeftX + (i – 1) * 2 * r + r

 

圆心y坐标是j的函数:

 

y = ActivePage.BottomY + (j – 1) * 2 * r + r

 

我们想让色彩在两个方向收都有变化,赋色语句可以改为(注意第二个色彩分量):

 

s1.Fill.UniformColor.RGBAssign (i / nCircles3.Value)* 255, (j / nRows) * 255, (1 – i /nCircles3.Value) * 255

9.   条件循环:次数不确定的循环

现在来做这样一个任务:画一行顺序相切的圆,撑满页面,圆的半径值在一个范围内变化。

这个循环和刚才的几个程序的不同之处是循环次数不确定,循环终止的条件是到达右页面边界。我们采用Do While语句来实现这个任务。

9.1 Do While条件循环

在页面下端建立一个新的按钮控件,命名为DrawRndRadiumCircles。

代码如下:

 

Private Sub DrawRndRadiumCircles_Click()

Dim i As Integer, s1 As Shape, x As Double, y As Double,r As Double

Dim w As Double: w = ActivePage.SizeWidth   ‘页宽

‘生成第一个圆的半径

r = minR.Value * w + Rnd * (maxR.Value – minR.Value)* w

‘生成第一个圆的圆心坐标

x = ActivePage.LeftX + r: y = ActivePage.BottomY +0.5 * ActivePage.SizeHeight

Set s1 = ActiveLayer.CreateEllipse2(x, y, r, r, 90#,90#, False)

Do While x + r < ActivePage.RightX

x = x + r ‘此时x为上一个圆的右界坐标

‘生成第一个圆的半径

r = minR.Value * w + Rnd * (maxR.Value -minR.Value) * w

‘此时x为新圆的圆心坐标(注意两个r不一样)

x = x + r

Set s1 = ActiveLayer.CreateEllipse2(x, y, r,r, 90#, 90#, False)

Loop

End Sub

 

最大最小半径用的还是原来面板上的半径上下限两个输入框:

上述代码的运行逻辑可表示为如下图所示:

几点说明:

1)第一个圆单独画,没有放在循环里,是因为需要确定第一个圆的圆心作为后续圆心计算的基准;

2)Do While…Loop的用法:While后面的条件满足时,执行Do While和Loop之间的语句,否则结束跳出;

3)While条件是“x + r < ActivePage.RightX”,即当前圆心的x坐标加上一个半径(圆的右边界x坐标)仍在页面内,未超出右边界;页面右边界为ActivePage.RightX;

4)Do While循环内,当前圆心的x坐标是通过旧圆心x坐标加上上一个圆的半径和本次要画的新圆半径得到,使用了两次x=x+r语句:第一次使用时r表示上一个圆的半径,将其叠加到x上;第二次使用时r已经是新圆半径了,将其再次叠加到x上;注意:VB语言中的等号表示“赋值”而不是相等,x=x+r表示把变量x的值加上r后重新赋给x。

 

上述代码运行结果:

9.2  用条件判断完善图形绘制

这个结果好像跟我们想的不太一样,因为右侧最后一个圆跑出了页面。这是因为画最后一个圆时并未考虑将其与右边界相切。如果顾及这点,则最后一个圆的半径就不是随机的了,而是根据右边界的位置计算出来的。

因此,要想正确地画出最后一个圆,需要做三件事:

1)  判断什么时候是最后一个圆;

2)  计算该圆的半径和圆心坐标;

3)  画圆。

由于画最后一个圆的流程与其他不一样,所以要在Do While…Loop循环里设置一个If…Then判断机制,如果是最后一个圆,则按上述流程画,否则还是按刚才的流程。

判断最重要。判断是否最后一个圆的条件是:页面右侧的剩余空隙不大于最大圆的直径。也就是说,如果剩余的空隙只放一个圆,则它的半径不会比最大半径maxR.Value大;否则至少还能放两个圆,当前要画的就不是最后一个了。

页面右侧剩余空隙的计算方法:

 

Dim span As Double ‘页面右侧空隙

span=ActivePage.RightX – (x+r)

 

其中x+r是当前圆的右边界,x是圆心横坐标,r是半径。

判断语句可以写为:

 

If span < 2 * maxR.Value * wThen

 

因此,程序可以改为:

 

Private Sub DrawRndRadiumCircles_Click()

Dim i As Integer, s1 As Shape, x As Double, y AsDouble, r As Double

Dim w As Double: w = ActivePage.SizeWidth   ‘页宽

‘页面右侧空隙

Dim span AsDouble

‘生成第一个圆的半径

r = minR.Value * w + Rnd * (maxR.Value – minR.Value)* w

‘生成第一个圆的圆心坐标

x = ActivePage.LeftX + r: y = ActivePage.BottomY +0.5 * ActivePage.SizeHeight

Set s1 = ActiveLayer.CreateEllipse2(x, y, r, r, 90#,90#, False)

Do While x + r < ActivePage.RightX

‘计算空隙

span =ActivePage.RightX – (x + r)

  If span< 2 * maxR.Value * w Then

    x = x +r

    r =span / 2

    x = x +r

    Set s1= ActiveLayer.CreateEllipse2(x, y, r, r, 90#, 90#, False)

    Exit Do

Else

‘此时x为上一个圆的右界坐标

x = x + r

‘生成第一个圆的半径

r = minR.Value * w + Rnd * (maxR.Value -minR.Value) * w

‘此时x为新圆的圆心坐标(注意两个r不一样)

x = x + r

Set s1 = ActiveLayer.CreateEllipse2(x, y, r,r, 90#, 90#, False)

End If

Loop

End Sub

 

两点说明:

1)程序在开始部分定义了一个span变量,用于表达空隙,这样就不用每次用到“空隙”这个值都重新计算一遍了,以节省计算时间;

2)最后一个圆做完后,增加了一个Exit Do语句,强迫跳出DoWhile循环,因为已经是最后一个圆了;这句没有也可以,但是加上可以以防万一造成死循环。

程序执行结果:

如果需要画圆结束后统计一下画了多少个圆,可以设置一个计数变量。程序修改如下:

 

Private Sub DrawRndRadiumCircles_Click()

Dim i As Integer, s1 As Shape, x As Double, y AsDouble, r As Double

Dim w As Double: w = ActivePage.SizeWidth ‘页宽

‘页面右侧空隙

Dim span As Double

‘计数变量

Dim n As Integer

‘生成第一个圆的半径

r = minR.Value * w + Rnd * (maxR.Value – minR.Value)* w

‘生成第一个圆的圆心坐标

x = ActivePage.LeftX + r: y = ActivePage.BottomY + 0.5* ActivePage.SizeHeight

Set s1 = ActiveLayer.CreateEllipse2(x, y, r, r, 90#,90#, False)

n = 1

Do While x + r < ActivePage.RightX

‘计算空隙

span = ActivePage.RightX – (x + r)

If span < 2 * maxR.Value * w Then

x = x + r

r = span / 2

x = x + r

Set s1 = ActiveLayer.CreateEllipse2(x, y, r,r, 90#, 90#, False)

n = n +1

Exit Do

Else

‘此时x为上一个圆的右界坐标

x = x + r

‘生成第一个圆的半径

r = minR.Value * w + Rnd * (maxR.Value -minR.Value) * w

‘此时x为新圆的圆心坐标(注意两个r不一样)

x = x + r

Set s1 = ActiveLayer.CreateEllipse2(x, y, r,r, 90#, 90#, False)

n = n +1

End If

Loop

MsgBox “共计” & n& “个圆。

End Sub

 

计数语句出现在三个地方:

1)开始时设置初值n=1,此时已经画好了第一个圆;

2)正常画圆时:n=n+1;

3)画最后一个圆时:n=n+1。

最后会弹出对话框:

Msgbox()函数的详细用法请查阅VBA帮助文档。

上述代码的第三行:

 

Dim w As Double: w = ActivePage.SizeWidth   ‘页宽

 

VB语言允许在一行里写多个语句,语句之间用英文冒号隔开即可。

 

修改后的流程图如下:

应该指出的是,最后一个圆的半径最大值虽然保证了在maxR.Value之内,但是不能保证其最小值在minR.Value之上。要实现这点,需要采用一些不同的机制。因为如果最后的空隙小于最小圆直径,则倒数第二个圆就要重新生成了,而且这种情况下两个圆也不能100%保证其大小都在上下限之内。

这道题留着慢慢思考,这里给出一种处理思路:

1)如果空隙span小于2*minR.Value*w,则删掉上一个圆;

2)重新计算span,即:span=span+2*r;

3)判断新span的大小,并作出相应的动作:

4)如果span大于2*minR.Value*w且小于2*maxR.Value*w,则画一个圆,半径r=span/2;

5)如果span大于2*maxR.Value*w,则画两个圆;为了保证两个圆的半径都合法,可以令其中一个圆的半径为最小值r=minR.Value*w,另一个半径则为r=span/2-minR.Value*w。

上述步骤中还有一种情况没提到:有没有可能新的空隙span小于2*minR.Value*w呢?答案是不可能,因为我们删掉的圆的直径已经大于这个值了,这个直径加入新span后,只会令其更大。

 

写完这个程序,我们大概就能明白了,为什么前面说写代码不需要很高的数学能力,但是需要缜密的逻辑头脑。一个设计师可以大言不惭地承认自己数学差,但是要承认自己逻辑也差就有点难为情了。写代码确实能训练一个人有条有理地思考,和规划各种复杂行为。

http://mip.i3geek.com

Leave a Reply

搜索

分类目录

公 告

本网站学习论坛:

www.zhlisp.com

lisp中文学习源码:

https://github.com/zhlisp/

欢迎大家来到本站,请积极评论发言;

加QQ群学习交流。