Processing开发实战(txt+pdf+epub+mobi电子书下载)


发布时间:2020-07-24 16:43:55

点击下载

作者:黄文恺,吴羽,伍冯洁

出版社:机械工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

Processing开发实战

Processing开发实战试读:

前言

机器人技术是跨学科的综合性技术,涉及的学科比较广泛,包含光学、机电一体化、电子信息、通信技术和计算机编程等专业。在机器人教学实践当中,很难把所有的学科知识都介绍给学生,全部精通更不现实。在搜寻手势控制机器人动作资料的过程中,笔者发现Kinect可以很方便地在Processing中使用,不像在其他开发平台上那么复杂。其他专业的软件开发平台,单单配置环境就要耗费大量时间。有相当一部分机器人爱好者是非计算机专业的人员,对于他们来说,专业软件开发平台的编程技术会成为其学习的障碍,从而导致他们放弃深入探究的计划。

Processing是一门具有革命性和前瞻性的新兴计算机语言,它致力于在电子艺术的环境下介绍程序语言,并将电子艺术的概念介绍给程序员。Processing简单易学的界面和编程风格,使很多机器人爱好者或电子制作爱好者完成机器人的控制,或实现可控的电子产品,例如控制智能家居等。笔者在学习的过程中,对Processing深深着迷,通过动手实践,并将Arduino与之结合,设计了很多有趣的产品。本书是入门书籍,重点引导读者学习Processing的基础知识。除了入门基础知识,本书也会介绍Processing如何与Arduino进行通信,以及如何使用Kinect或Xtion等进阶内容。更多与Arduino互动的例子,以及使用各种传感器开发的小游戏都收录在笔者的另一本书《Processing与Arduino互动编程》中。本书的主要内容及读者对象

本书适合零基础的人学习,没有学过C语言的读者可以从第一篇入门基础篇开始学习,该篇从基本的语法开始(为了能更好地向读者展示程序运行效果,该篇的部分实例会用到后面章节中的函数,读者可以暂不理会,先学习基础知识,等学习到后面章节时再深入理解),再到绘图的数学基础,循序渐进地进行介绍。第一篇的最后部分会介绍面向对象的知识,主要概述类和对象,这是比较抽象的内容,如果初学者感到难以理解可以跳过,不影响其他部分的学习。但该部分有利于读者建立面向对象的思想,建议读者翻阅更多的资料,掌握类和对象的相关知识。第二篇是图像图形篇,有一定编程基础的读者可以直接阅读该篇。它是本书中最具魅力的篇章,学习这些章节有利于读者创造各种各样令人惊艳的图案,或定制自己的软件界面。该篇的结尾是综合实例,读者可以借助这些实例综合运用前述的知识,绘制各种动画或展现出独特的艺术视觉效果。第三篇是互动篇,该篇有鼠标、键盘的互动以及串口通信,通过实例展示Processing与Arduino的互动,包括传感器读取和摇杆的控制程序,让读者掌握两者的交互方式。第四篇是高级应用篇,主要展示如何用Kinect或Xtion进行互动编程,读者可以在此基础上自行扩展,如采用Kinect或华硕的Xtion控制机器人,甚至控制无人飞机等。致谢

首先要感谢刘嘉杰、黄海锋、罗雯钰、肖昌伟、张雯雯、陈思强、潘强,他们牺牲了节假日时间,帮助我整理书稿,并对每一个程序进行验证。在此要感谢你们付出的努力。

其次要感谢“广州市教育局青少年科技教育计划”对本书的撰写、器材的购置提供的资助。

最后要感谢读者朋友们,感谢您花费时间和精力阅读本书。由于时间有限,书中难免存在疏漏与错误,敬请批评指正。希望有更多志同道合的朋友加入到机器人的制作与开发中来!黄文恺2015年8月于广州大学第一篇入门基础篇第1章 Processing简介

Processing是一种开源编程语言,专门为电子艺术和视觉交互设计而创建,其目的是通过可视化的方式辅助编程教学,并在此基础之上表达数字创意。2001年,麻省理工学院(MIT)媒体实验室的Casey Reas和Benjamin Fry发起了此计划,其特定目标之一便是开发一个有效的工具,通过激励性的可视化反馈,帮助非程序员进行编程的入门学习。Processing语言建立在Java语言的基础之上,但使用简化的语法和图形编程模型。

Processing简单易用的特点,以及其丰富的拓展让学习者可以创造出很多富有想象力的作品。因为源自Java,所以大量的Java库都可以添加进来调用,比如Box2D、Unity等引擎都可以在开发时调用。除了强大的Java库,还可以结合Arduino,让图形化界面和硬件产生互动,添加Kinect或Xtion又可以识别人体的肢体动作并进行处理,这些丰富的拓展让Processing的作品充满了想象力。1.1 初识环境

通过访问Processing官网www.Processing.org/download,可以下载Processing,请根据自己的操作系统,选择相应的Mac、Windows、Linux的版本。

相对于其他语言非常复杂的开发环境,Processing开发环境非常简洁明了,如图1-1所示。

Processing开发环境由代码编辑区、消息区域、控制台、管理文件的标签栏、常用指令按钮的工具栏和一排菜单构成。当程序运行时,会弹出一个新的窗口,称为显示窗口。

利用工具栏中的按钮可以实现以下功能。图1-1 Processing开发界面:用于完成程序的检查和编译,并运行程序。:停止运行中的程序,但不关闭显示窗口。:新建一个Processing程序。:打开文件。:保存当前的文件。:将当前程序以Java插件嵌入HTML文件内导出。:建立新的标签页,可以用于创建新的类。

用Processing编写的程序被称作草图(sketch),这些草图是在文本编辑器里完成的。每个草图有独立的文件夹,文件扩展名.pde代表此文件是由Processing开发环境创建的。1.2 绘制第一个图形

Processing提供了很多默认的函数,用来绘制点、线、椭圆、矩形等。先来绘制一个简单的圆,ellipse(x,y,width,height)是椭圆绘制函数,前两个参数设置椭圆圆心的位置,第三个参数设置长轴,第四个参数设置短轴。当参数width和height相等时得到的就是正圆。

例如,以画面中心为原点画一个直径为100像素的圆,如图1-2所示。

示例: 1-1ellipse(50,50,100,100);图1-2 圆1.3 绘制第一个动画

Processing的文本编辑框中默认会有两个函数:setup()和draw()。前者之中的内容只被执行一次,后者内容会被循环执行。如下例中,设置圆的半径为连续增大的值,在画板中连续绘画呈现的结果就是一个圆逐渐变大的动画效果,如图1-3所示。

示例: 1-2int r = 0;void draw(){ ellipse(50,50,r++,r++);}图1-3 逐渐变大的圆1.4 第一个交互

调用鼠标绘制一根直线,可以移动鼠标来进行交互,如图1-4所示。

示例: 1-3void draw(){绘制一条直线 line(mouseX,mouseY,0,0);// }

mouseX和mouseY是系统变量,也是鼠标所在的坐标位置,后面会详细说明。在绘画过程中,不消除原有的绘画留下的痕迹。此时如果想要消除原有的绘画痕迹,需要每次载入一个空白页面。只需加入一行代码即可,如图1-5所示。图1-4 绘制直线图1-5 无痕迹绘图

示例: 1-4void draw(){ background(255);直线 line(mouseX,mouseY,0,0); // }

以上例子介绍了一些基础概念,并没有做详细解释,主要目的是让读者对Processing有初步的认识。接下来将引导读者从语言基础开始,结合简单图案绘制,循序渐进,再进阶到制作动画和交互。为了能得到最大的收获,需要读者尽可能多地上机编程操作,以快速掌握这门语言。第2章 语言基础2.1 变量

变量是储存指定数据类型的空间。在一个程序中,同一变量可以多次引用,变量储存的值也可以更改。你可以把变量理解为存储某种类型东西的箱子,可以把东西放进去,也可以在用的时候取出来。变量的名字就是箱子的标签,这样在取出里面东西的时候会更加方便。使用变量的时候要提前声明。前面是数据类型,后面是变量的名字int variable; // 2.1.1 基础变量类型

不同的数据类型就好像不同大小的箱子,可以放不同的数据。数据类型有基础数据类型和复杂数据类型,如表2-1所示。有些数据类型内存空间大小一样,但是它们的取值范围不一样,这说明类型决定了数据在内存空间中的表示方式。计算机中的数据都是以二进制位来储存的,一个位也就是0或1。boolean类型的数据只有一位,也就是占用一个二进制位来储存这种数据。表2-1 数据类型2.1.2 变量名字

变量是储存数据的箱子,变量名字是箱子的标签。为了更方便地提取数据,变量的命名有一些规则。

·变量名字在同一作用域下是独一无二的。

·变量名字的第一个符号是字母或下划线。

·变量名字区分大小写,不一样的大小写是不同的变量。

·构成变量名字的只能是字母、数字、下划线。

·变量名字最好有实际意义,能表达所储存的内容。2.1.3 变量赋值

把数据存入变量这个箱子叫作变量赋值。通过运算符“=”来把数据存入变量。有以下两种方式来进行变量赋值。 2-1int variable = 2; 2-2int variable;variable = 3;

Processing内置了打印函数,可以用print()或println()函数把变量的数值在控制台输出。

示例: 2-3int variable;variable = 3;输出:println(variable); // 3

执行上面的例子可以在文本框下的控制台上看到输出结果为3。2.1.4 系统变量

有一些变量是系统内置的,不需要声明。这些变量不允许赋值,这些变量的值可以让编程人员获得系统在运行时的一些参数。比如,mouseX、mouseY的值是鼠标在画板上停留的坐标。利用好系统变量可以更方便地设计交互效果。表2-2列出了部分Processing系统变量。表2-2 系统变量举例

示例:

下面例子打印系统变量mouseX、mouseY,并显示在画板上。 2-4void setup(){ size(900,600); textSize(26);}void draw(){ background(0); text("x:"+mouseX,100,200); text("y:"+mouseY,300,200);}

还有一些关键字,比如final,可以让变量变成常量,当后续代码再修改该变量的时候就会报错。通常用于设置不能修改的常量值。

示例:使用final关键字来声明变量。 2-5final int a = 2;出错a = 3; // 2.2 运算符

运算符可以对变量进行基本的运算。运算符包括基本运算符、自增自减运算符、关系运算符逻辑运算符和条件运算符。2.2.1 基本运算符

表2-3所示是Processing的基本运算符。表2-3 运算符

加减乘除是常用的基本运算,求余是两个数相除所得的余数。一般都是同类型的数据通过运算符进行运算得出结果。比如两个int类型的1相加得出2。

示例: 2-6int a = 1;int b = 1;int c = a+b;输出:println(c); // 2

同一数据类型的相加得到相同数据类型的结果。还有一些特例是不同数据类型的数相加,如float类型和int类型的数相加,得到的是float数据类型的结果。后面章节会介绍运算符的其他作用,例如:在字符串中可以连接组合字符串。2.2.2 自增自减运算符

自增“++”、自减“--”运算是非常方便的操作,可以把变量的值自动增加1或者减少1。

示例: 2-7int i = 5;i++;输出:println(i); // 6

此处i++等同于i+=1或i=i+1。变量赋值的顺序非常重要,i++和++i在参与运算的时候,前者i是先参与运算再自增,后者是先自增再参与运算,请读者仔细区分两者的差别。

示例: 2-8int i = 1;int a = 0;a = i++;输出:println(i); // 2输出:println(a); // 1

自减运算同理。2.2.3 关系运算符

关系运算符可以得到两个数据关系判断的真假,例如4>3是真(true),3>4是假(false)。为了区分等于和赋值,要注意等于用了两个等于号“==”,而赋值是一个等于号“=”。关系运算符如表2-4所示。表2-4 关系运算符

基本数据类型的变量和变量进行判断时,检查值是否相等而不是数据类型是否相同。

示例: 2-9int a = 65;float b = 65.0;char c = 'A';输出:println(a == c); // true输出:println(a == c); // true输出:println(b == c); // true2.2.4 逻辑运算符

逻辑运算符有与、或、非3种,对应的符号分别是&&、||、!。它们设定复合判断条件,返回一个boolean值,要么是true,要么是false。1.与运算

语法结构:表达式表达式1 && 2

如果表达式1和表达式2的值都是true,那么整个表达式的值也是true;如果两个表达式中任何一个为false,那么整个表达式的值为false。这个运算符可控制整个子表达式的求值顺序。 2-10a>5 && a<10

&&运算符的优先级比>和<运算符低,所以子表达式是按照下面这种方式进行组合的: 2-11(a>5) && (a<10)

但是尽管&&操作符的优先级较低,但它仍会对两个关系表达式施加控制。它的工作原理如下:&&操作符的左操作数总是首先进行求值,如果它为真,就紧接着对右操作数进行求值。如果左操作数为假,那么右边就不再进行求值,因为整个表达式的值一定是假的。2.或运算

语法结构:表达式表达式1 || 2

两个表达式中任何一个值为true,或者两个都为true,则返回值为true。如果两个表达式都是false,则返回值为false。

逻辑运算符还可以组合使用: 2-12输出:println((2<3) && (4>1 || 2<5)); // true3.非运算

语法结构:!表达式

非运算符可以把一个布尔值变成相反的值,即true变成false,false变成true。

示例: 2-13if(!(2>3)) {输出: print("hello"); // hello}2.2.5 条件运算符

条件运算接收3个表达式,它会控制子表达式的求值顺序。

语法结构:条件表达式表达式:表达式;1 ?23

条件操作符的优先级很低,所以它的各个表达式即使不加括号也没什么问题。但是为了清楚起见,人们还是倾向在它的各个子表达式两端加上括号。

首先计算的是表达式1,如果它的值是true,那么整个表达式的值就是表达式2的值,表达式3不会进行求值。但是,如果表达式1的值是false,那么整个条件语句就是表达式3的值,表达式2不会进行求值。

示例: 2-14a>5 ? b-6 : c/2

可以读作:“a是不是大于5?如果是,就执行b-6,否则执行c/2”。2.3 条件语句

在编程时可能会处理一些较复杂的分支情况,需要进行判断,并跳转到不同的语句中去。下面介绍如何使用两种条件语句。2.3.1 if条件语句

使用条件语句,可以使程序执行某些代码,而跳过另一些代码,可以使程序在符合某些特定条件时才执行特定代码。

if条件语句语法结构: 2-15条件表达式if (){代码 ;}

先判断if后面小括号中表达式的值是真还是假,如果是true执行{}里面的代码,如果是false,则直接跳过{}。

示例: 2-16int a = 2;if(a>1){ a = 0;}输出:println(a); // 0

上例中因为a的初始值是2,所以进入if语句,a被重新赋值为0。

if else语句语法结构: 2-17条件表达式if (){代码 1;}else{代码; 2}

如果if后面的条件表达式值为true,程序会运行代码1,否则执行else后面的代码2。

示例: 2-18void setup(){ size(200,200);}void draw(){ background(255); if (mouseX>100) { fill(0);鼠标在右半屏时画方形 rect(100,100,50,50); // } else { fill(0);鼠标在左半屏时画圆形 ellipse(100,100,50,50); // }}

运行结果如图2-1所示。图2-1 鼠标移动到左半屏时画圆形,移动到右半屏时画方形

上述代码只有两个分支,但是可以通过嵌套else if来插入更多分支的情况。

else if语句语法结构:条件表达式if (1){代码 1;条件表达式}else if (2){代码 2;……}条件表达式 else if(n){代码 n;} else{代码; n+1}

如果条件表达式1为true,只执行代码1;否则判断条件表达式2,如果条件表达式2为true,只执行代码2;否则,判断条件表达式3,以此类推,直到判断条件表达式n为止。如果条件表达式n为false,则执行最后else{}里面的代码n+1。

示例: 2-19int a = 2;if(a>1){ println("t");}else if(a<-5){ println("f");}else{ println("m");}

控制台输出:t。

要注意的是,在使用多个else if条件语句时,程序会按顺序执行判断,从上到下直到返回true。如果在中间某个过程中条件成立,即使后面也有成立的条件,程序都不会执行。2.3.2 Switch条件语句

switch条件语句用于需要进行多次判断才能做出选择的情况。

语法结构:条件表达式switch(){常量 case 1:代码 1; break;常量: case2代码 2;… break;常量 casen:代码 n; break; default:代码 n+1; break;}

switch后面()里的表达式就是要进行判断的条件,switch语句首先计算条件表达式的值,这个值限定了数据类型,只能是byte、char、int三种数据类型,返回其他数据类型程序会报错。

每一个case到break代表一个分支结构,case后面跟的是常量表达式,用来判断是否与条件表达式相等,若相等,就执行分支里面的语句,直到遇见break。若每个分支的值都和表达式的值不相等,程序会执行默认分支default后面的语句。default语句也可以省略,如果分支条件都不成立的话,程序就什么都不执行。

示例: 2-20int a = 2;switch (a){case 1: println("hello"); break;case 2: println("world"); break;}

如果去掉break那么就会从标签位置继续向下执行,而不会执行完分支语句就马上退出。2.4 循环语句

循环结构可以在满足某一个条件之前反复执行一个语句,让更少的代码完成更多的事情。下面是两种循环结构的使用方式。2.4.1 while循环语句

用while来实现循环操作,它的语法结构如下:条件表达式{while()循环体语句;}

条件表达式就是这个循环是否继续进行的条件,而循环体语句就是这个循环需完成的事情。while语句首先判断条件表达式的值(布尔型数据的值)。如果表达式的值为true,则执行循环体语句,否则,结束while语句。当循环语句执行完一次时,会再次判断表达式的值,根据表达式的值决定是否要进行下一次循环。如此不断循环,直到表达式的值为false,此时循环结束。

示例: 2-21int i = 10;void setup(){ size(900,600);}void draw(){ while (i< 900) { ellipse(i,i,50,50); i+=50; }}

运行结果如图2-2所示。图2-2 用while循环画圆2.4.2 for循环语句

for循环语句的语法结构:初始化语句判断循环条件更新语句for(;;){循环体语句 ;}

程序进入for循环语句之后,首先会执行初始化语句,然后计算判断循环条件语句,如果判断循环条件的值为true,则执行循环体语句,再执行更新语句,修改循环控制变量。接着又开始计算判断条件的值,根据其值决定是否需要继续下一次循环:如果判断条件的值为true,则继续下一次循环;反之,则结束整个循环。

示例: 2-22void setup(){ size(900,600);}void draw(){ for (int i = 0; i < 10; i++) { ellipse(50*i,50*i,50,50); }}

运行结果如图2-3所示。图2-3 用for循环画圆2.4.3 加强循环结构1.跳出循环

循环中可以使用break语句来永久终止循环;也可以使用continue语句,用于永久终止当前那次循环,而在执行之后,重新测试表达式的值,决定是否继续执行循环。

下面例子是输出10以内的偶数: 2-23for (int i = 0; i < 10; i++){ if (i%2 != 0) { continue; }输出: println(i); // 0 2 4 6 8}

下面的示例是用break关键字跳出循环: 2-24int a = 0;for (int i = 0; i < 10; i++){ if (i > 5) { break; } a++;}输出:println(a); // 6

两条语句任何一条如果出现在了嵌套的循环内部,它只对最内层的循环起作用,无法使用break或continue语句影响外层循环的执行。2.嵌套循环

嵌套循环指在一个循环之内还有另一个循环。内部循环在外部循环的每个周期做着相同的事情。通过使内部循环的一部分依赖于外部循环,可以使内部循环在每个周期中的表现不同。

示例: 2-25for (int i = 0; i < 5; i++){ for (int j = 0; j < 6; j++) { println(j); }}

输出:012345012345012345012345012345。2.5 函数

函数是用于完成特定任务的程序代码的单元。在Processing中很常见的函数有size()函数、line()函数等。这一节将要展示如何通过编写新的函数,来扩展Processing的功能特性。

为什么要使用函数?当要处理的问题越来越复杂,程序越来越庞大的时候,如果把这些程序代码都放到主函数中,将使主函数异常臃肿,这样会给程序的维护带来麻烦。在这种情况下,可以按照功能的不同,把大问题划分成小问题,把大程序划分成小模块。函数则成为模块划分的基本单元,是对一个复杂问题处理过程的一种抽象。

函数的使用可以省去复杂代码的编写。如果程序中需要多次使用某种特定的功能,那么只需写一个合适的函数即可。程序可以在任何需要的位置调用该函数,并且同一个函数可以在不同的程序中调用。即使某些功能在程序中只使用一次,将其以函数的形式实现也是有必要的,因为函数让程序变成模块化,从而有利于程序的阅读、修改和完善。

函数内部的计算是非常复杂的,特别是由多人共同完成的程序,会创建大量的新函数。但函数的优点在于不必了解它们是如何运作的,只需知道如何使用它们就已经足够了,即了解输入的是什么和它们是如何影响输出的。这种抽象使编程人员能专注于程序整体的设计,而无须考虑过多细节。2.5.1 定义函数

定义函数有3部分:函数名、参数体、返回值。

函数名就是为了标志一个函数而取的名字,通过该函数名可以快速找到这个函数。函数的命名规则和变量的命名规则相同。如果说变量命名重在说明它“是什么”,那么函数的命名则重在说明它要“做什么”。

在程序里函数是可以传入参数的,可以针对函数的不同值,来进行相应的操作。当调用函数时,它会执行{}里的语句,函数{}里的所有语句叫作函数体。

语法结构:返回值类型函数名参数类型参数名参数类型参数名 (1 1,2 2,...)可以接收任意个参数,中间用逗号隔开/**/{语句 ;返回值 return ;}

函数在执行完任务后,往往需要给它的调用者一个返回值,表示函数运行的结果或者其他意义。如下面的示例,在这个加法函数中,函数名为add,返回值类型为int,表示该函数在完成加法计算后,将计算的结果作为返回值返回给它的调用者。若函数只是执行一系列动作,无需返回值,则可以使用void作为返回值类型。

示例: 2-26void draw(){ println(add(5,4));}此处有两个参数,中间用逗号隔开int add(int a,int b) // { return a+b;输出:} // 9

还有一些函数是没有返回值的,类型是void。下面的示例是定义一个名为circle的函数,它的作用是画圆。

示例: 2-27void draw(){ circle();}void circle(){ ellipse(50,50,50,50);}2.5.2 函数重载

有一些函数功能相同,但是接收的参数不一样,它们就可以用相同的名称。比如一个加法函数,接收两个参数相加一起。而另一个加法函数也实现了相同的功能,只不过变为接收3个参数相加。如果起不同的名字,在调用函数时需要起很多名字,也要记录很多名字,会让人觉得很混乱。函数重载可以让其只有一个名字,调用时,根据参数个数和类型自动识别,调用相应的函数。

示例:两个求最小值的函数,第一个是两个数求最小,第二个是3个数求最小。功能相似,用一个名字更容易使用,减少函数数量。 2-28int minNumber(int a,int b) { int temp = a > b? b:a; return temp;}int minNumber(int a,int b,int c){ int temp = a > b? b:a; temp = temp < c ? temp:c; return temp;}2.5.3 函数递归

函数递归就是一个函数直接或间接调用自身。递归有一定的适用条件,在某些情况下使用递归可以方便获得想要的结果。当一个函数自己调用自己时,如果编程没有设定可以终止递归的条件检测,它会无限制地进行递归调用,所以要谨慎使用递归。

递归一般可以用循环来代替,但是递归更为强大,有时是循环无法处理的。递归方法使得程序结构优美,但是执行效率却往往没有循环高。

下面的示例是计算阶乘。

示例: 2-29void setup(){求的阶乘 println(Factorial(8)); // 8}int Factorial(int i){ int num; num=i; if(num==1)return1; else return num *Factorial(--num);}2.6 数组2.6.1 数组的概念

数组是一组有序且数据类型相同的变量的集合,每一个数组里面的项目被称为元素,每一个索引值标记其在数组中的位置。索引值即元素序号从0开始计数。例如,第一个元素在数组中的索引值是0,第二个元素在数组中的索引值是1,以此类推。如果有20个数值在数组中,那么最后一个元素的索引值就是19。如图2-4所示是一个数组的结构概念图。图2-4 数据结构概念图

对集合中数组元素的访问可以采用下标的形式,也可以采用指针的形式进行。数组可以是一维的,也可以是多维的。当数组只有一个下标时,这类数组称为一维数组,当下标不止一个时,称为多维数组。常用的数据集基本在三维以内。

创造一个数组可分为3个步骤:

1)声明和定义数据类型。

2)创建新的关键字和数组长度。

3)给每个元素分配值。

使用数组类似于使用单独的变量,但遵循的是同种模式。如,写一个整数变量x如下:Int x;

而写一个数组,只需在数据类型后加上括号:;int[ ] x

例如,下面的代码表示定义了一个intArray整型数组,下标从0~4,共5个数组元素,如图2-5所示。int[ ] intArray=new int[5];图2-5 长度为5的整型数组

数组的优点在于只需要一行代码就能写多个变量。例如,下面这行代码是2000个整型变量:int[ ] x=new int[2000];

注意 每个数组仅可以储存一种类型的数据(布尔、浮点、整数、PImage等)。不能将不同类型的数据混在一个数组中。如果必须这样,可以使用对象来代替。2.6.2 遍历数组

在Processing中,很多时候需要制作并处理多图片,来达到所需要的图片效果,如果一个一个地对图片的信息进行处理,会消耗很多的精力和时间,并且需要大量的工作量。因此,利用数组,通过for循环语句操作,可以提高工作效率。

1)用for循环遍历数组。

下面就是一个利用for循环语句,绘制单位为10的熊猫来回运动的效果,如图2-6所示。 2-30储存每幅图的横向运动坐标组float [ ] x = new float[10]; // 储存每幅图的纵向运动坐标组float [ ] y = new float[10]; // 储存每幅图的偏移量组float [ ] s = new float[10]; // void setup() {窗口大小 size(300, 300); // 线段光滑 smooth(); // for (int i = 0; i < x.length; i ++) {~随机赋值 x[i] = random(0, 300); // 0300~随机赋值 y[i] = random(0, 300); // 0300偏移量 s[i] = 0.5; // }}void draw() {背景填充 background(200); // for (int i =0; i 300 || x[i] <0) { s[i] = -s[i];防止图片移出窗口 } // 循环运行次,载入次熊猫函数 } // for1010}void panda(float x, float y) {矩阵移动 pushMatrix(); // 移动函数 translate(x, y); // 熊猫头像绘制函数// fill(0); strokeWeight(1);左耳 ellipse(-35, -25, 35, 35); // 右耳 ellipse(35, -25, 35, 35); // fill(255); strokeWeight(1); stroke(0);头部 ellipse(0, 0, 100, 90); // fill(0);左眼 ellipse(-25, 5, 30, 35); // 右眼 ellipse(25, 5, 30, 35); // fill(255);左眼球 ellipse(-25, 0, 6, 6); // 右眼球 ellipse(25, 0, 6, 6); // fill(0);鼻子 ellipse(0, 25, 7, 5); // noFill(); stroke(0); strokeWeight(1);用贝塞尔曲线绘制嘴巴 bezier(-2.5, 35, -2.5, 37, 2.5, 37, 2.5, 35); // 矩阵闭合 popMatrix(); // }图2-6 数组熊猫运动图

2)用for循环的另一种形式来遍历数组。

语法结构:数据类型变量名数组for( :){变量名 println();}

示例: 2-31创建数组int[ ] array={0,1,2,3,4}; // for(int i:array){ println(i);}

输出:0

1

2

3

4

3)通过printArray()函数遍历数组。

printArray()函数用于将信息显示在消息控制台中。每一行都会显示一个数组元素。这个函数只能应用于一维数组。

示例: 2-32float[ ] f = { 0.3, 0.4, 0.5 };printArray(f);

输出:[0]0.3

[1]0.4

[2]0.5

利用数组绘制五角星,如图2-7所示。图2-7 五角星

绘图思路:描绘五角星10个顶点坐标,再通过vertex()函数连接相邻点。

实例点坐标:(50,18)(61,37)(83,43)(69,60)(71,82)(50,73)(29,82)(31,60)(17,43)(39,37)

示例: 2-33int[ ] x={50,61,83,69,71,50,29,31,17,39};数组储存点横坐标// xint[ ] y={18,37,43,60,82,73,82,60,43,37};数组储存点纵坐标// y发起创建新图形信号beginShape(); // for(int i=0;i

注意 避免在draw()内创建数组,因为在每一帧创建一个数组会降低帧速率。2.6.3 二维数组

可以这样理解:一维数组解决的是线性问题,而二维数组解决的是面的问题。定义二维数组的语法格式如下:数据类型变量名[ ][ ] ;

例如,定义一个整型数组,变量名为intArray: 2-34int[][] int Array ;

使用new关键字初始化的语法格式:数据类型数组长度数组长度new [1][2] ;

二维数组可以当作矩阵来看待,二维数组中第1个下标表示行,第2个下标表示列。功能可以看作为该数组分配一块连续数组长度1×数组长度2的存储空间。

例如,定义一个存放3个长度为2的整型数组,如图2-8所示。图2-8 定义一个3行2列的intArray整型数组 2-35数组声明int[ ][ ] intArray; // 数组初始化intArray =new int[3][2]; //

在第二个[]中的数字一般可省略,然后通过下标来分别初始化数组中的数组,以此来得到包含不同长度的二维数组。String[ ][ ] strArray=new String[5][ ];

对第一个数组进行长度初始化。strArray[0]=new String[5];

对第二个数组进行长度初始化。strArray[1]=new String[4];

对第三个数组进行长度初始化。strArray[2]=new String[3];

…………

例如:,int x[2][5]={{1,2,3,4,5}{4,5,6,7,8}};

其等效的形式如下:,int x[2][ ]={{1,2,3,4,5}{4,5,6,7,8}};

注意 数据类型可以是int、float、char等,数组名用标识符充当。数组大小是指构成数组的元素个数。为数组分配的存储空间为一连续的存储空间,这是利用指针访问数组的物理基础。访问数组时,下标始终从0开始。

1)用for循环遍历二维数组。

通过for循环遍历二维数组,编程画一个随机的灰度噪声图,如图2-9所示。 2-36size(300, 300);int cols = width;int rows = height;声明二维数组// int[ ][ ] myArray= new int[cols][rows];初始化二维数组变量// for (int i = 0; i < cols; i++) { for (int j = 0; j < rows; j++) { myArray[i][j] = int(random(255)); }}画点// for (int i = 0; i < cols; i++) { for (int j = 0; j < rows; j++) { stroke(myArray[i][j]); point(i, j); }}图2-9 二维数组绘制灰度图

2)用另一种for循环遍历二维数组。

示例: 2-37String[ ][ ] strArray=new String[3][4];创建一个行列的字符串数组// 34for(int i=0;i

控制台输出:

0_0,0_1,0_2,0_3,

1_0,1_1,1_2,1_3,

2_0,2_1,2_2,2_3,

3)第三种for循环遍历二维数组。

示例: 3-38String[ ][ ] strArray=new String[3][4];创建一个行列的字符串数组 // 34for(int i=0;i

控制台输出:

[0]"0_0"

[1]"0_1"

[2]"0_2"

[3]"0_3"

[0]"1_0"

[1]"1_1"

[2]"1_2"

[3]"1_3"

[0]"2_0"

[1]"2_1"

[2]"2_2"

[3]"2_3"

将一个数组变量值赋值给另一个数组变量,改变其中一个变量的值,会影响另一个变量的值。 2-39int[ ]x={1,3,5,7,9};int[ ]y=x;y[1]=101;输出:println(x[1]); // 1012.6.4 数组综合应用

接下来,本书提供了5个数组综合应用的实用范例。

范例一

功能:利用数组,绘制大小变化的圆。

示例: 2-40定义一个长度为的浮点型数组float[] a=new float[10]; // 10int j=0;void setup() {程序每秒刷新一次画面 frameRate(1); // for (int i=0; i<10; i++)把随机生成的数放入数组 a[i]=random(100); // }void draw() {背景设置为灰色 background(204); // if (j<10) ellipse(50, 50, a[j], a[j]); j++;}

范例二

功能:利用数组的连续遍历功能,制作渐变图效果,如图2-10所示。

示例: 2-41窗口大小size(600, 600); // 600×600float[ ] coswave= new float[width];利用运算符将像素的值赋予数组// newwidthfor (int i = 0; i < width; i++) { float amount= map(i, 0, width, 0, PI);最小值区间最大值区间// map(value,,)绝对值 coswave[i] = abs(cos(amount)); // }for (int i = 0; i

范例三

功能:为数组分配一块连续数组大小1×数组大小2的存储空间

说明:二维数组可以当作矩阵来看待和处理,二维数组中第1个下标表示行,第2个下标表示列。

示例:

在这个例子中,有两个数组来储存鼠标的状态:一个用于x坐标,一个用于y坐标,这些数组储存鼠标在过去帧内的位置。每一帧新的出现,保存最久的x、y坐标的值都会被现在的mouseX和mouseY代替,新的值则被添加到数组的第一个位置,但是在这之前,数组中的每一个值都会被向右移动(从后到前),从而为新的数值腾出空间。

同样,每一帧,所有的60个坐标都被用于在屏幕上画出一系列的圆,如图2-11所示。图2-11 鼠标动态圆绘制

示例: 2-42用于定义数组长度int num=60; // 储存坐标int[ ] x=new int[num]; // x储存坐标int[ ] y=new int[num]; // yvoid setup() {定义窗口大小 size(240, 120); // 线段光滑 smooth(); // 禁止描边 noStroke(); // }void draw() {填充刷新背景色 background(0); // for (int i=x.length-1; i>0; i--) { x[i]=x[i-1]; y[i]=y[i-1];从后往前复制数值到数组 } // 设置第一个元素 x[0]=mouseX; // 设置第二个元素 y[0]=mouseY; // for (int i=0; i

案例分析:

数组存储值分析,如下:

原始数组如下:

开始循环复制第二到最后一个数值到最后的位置。

第二次循环,复制元素2到元素3。

第三次通过循环,复制元素1到元素2。

第四次也是最后一次通过循环,复制元素0到元素1。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载