KAIST CS109 编程实践教程(三)
发布时间:2026/9/26 20:56:30来源:尧图网络
setLineWidth(width: Double)设置轮廓绘制的笔宽度drawRectangle(x: Double, y: Double, width: Double, height: Double, s: DrawStyle) 绘制矩形drawCircle(x: Double, y: Double, radius: Double, s: DrawStyle) 在 ((x,y)) 处以半径 (r) 绘制一个圆setFont(size: Double, face: String) 设置文本绘制操作的字体参见上面的示例;drawText(text: String, x: Double, y: Double, a: TextAlign) 在位置 ((x, y)) 绘制对齐的字符串;done() 在完成绘制时建议调用此方法。更复杂的形状要绘制除了矩形和圆之外的形状您需要使用 ImageCanvas 的 shape 方法描述形状时沿着轮廓走。首先调用 beginShape()然后调用 moveTo(x, y) 前往轮廓的起点。现在通过一系列的 lineTo(x, y) 调用来描述形状的边界。您可以通过调用 closePath() 来完成轮廓这将返回到起点您在调用 moveTo(x, y) 时设置的位置。对于填充形状通常要调用 closePath对于轮廓描边形状这取决于您是否希望该形状是封闭曲线。最后调用 drawShape 来绘制形状。变换您可以修改坐标系统以便更容易地指定您的对象translate(x: Double, y: Double) 使点 ((x, y)) 成为新坐标系的原点;rotate(degrees: Double) 顺时针旋转坐标系;scale(sx: Double, sy: Double) 缩放 (x) 和 (y) 方向的坐标系统因此调用 scale(2.0, 2.0) 会使您的绘图尺寸加倍。通常您只想临时修改坐标系并在完成绘图的某个部分时返回到原始坐标系请参阅上面代码中的四边形。通过在更改坐标系之前调用 save()并在想要返回到先前设置时调用 restore() 来实现这一点。透明度通常后绘制的形状会覆盖先前绘制的形状计算机图形学中的“画家模型”。但是您可以调用 setAlpha(alpha) 来设置未来绘制操作的透明度。当 alpha 255 时绘图是不透明的并完全覆盖以前绘制的内容。当 alpha 128 时您以 50% 透明度进行绘制结果是原始颜色的一半与新绘制颜色的一半的混合。测量文本方法 textWidth(text: String) 返回在当前变换和字体设置下绘制文本的宽度。CS109UI 模块我编写了一个简单的模块叫做 cs109ui它使得可以编写具有简单图形用户界面的程序而无需了解 Swing 库或担心基于事件的编程。我们在CS109中的几个项目中使用这个模块。基本用法要使用该模块你需要按照 installation page 中的说明进行安装。该模块创建一个窗口。你可以通过绘制图像使用这里解释的操作来填充这个窗口然后调用 show。setTitle 命令设置窗口的标题。这是一个基本示例uitest1.kt。函数 draw 绘制到一个 java.awt.image.BufferedImage与 drawing example 中的绘图代码完全相同。import org.otfried.cs109ui.* import org.otfried.cs109ui.ImageCanvas import org.otfried.cs109.Color import org.otfried.cs109.DrawStyle import java.awt.image.BufferedImage fun draw(image: BufferedImage) { // get ImageCanvas for the image val g ImageCanvas(image) // clear background g.clear(Color.WHITE) // draw two filled circles g.setColor(Color.RED) g.drawCircle(50.0, 50.0, 20.0) // FILL is the default g.setColor(Color.ORANGE) g.drawCircle(250.0, 400.0, 20.0) // draw an unfilled circle with a pen of width 3 g.setColor(Color.MAGENTA) g.setLineWidth(3.0) g.drawCircle(415.0, 50.0, 15.0, DrawStyle.STROKE) // draw a filled and an unfilled Rectangle g.setColor(Color.CYAN) g.drawRectangle(20.0, 400.0, 50.0, 20.0, DrawStyle.FILL) g.drawRectangle(400.0, 400.0, 50.0, 20.0, DrawStyle.STROKE) // draw a line g.setLineWidth(1.0) // reset to default g.setColor(Color(0, 0, 255)) // same as Color.BLUE g.beginShape() g.moveTo(50.0, 50.0) g.lineTo(250.0, 400.0) g.drawShape(DrawStyle.STROKE) // draw a non-convex quadrilateral: g.save() // save current coordinate system g.translate(360.0, 260.0) // move origin to here g.rotate(-30.0) // rotate 30 degrees counter-clockwise g.beginShape() g.moveTo(0.0, 0.0) g.lineTo(30.0, -40.0) g.lineTo(60.0, 0.0) g.lineTo(30.0, -100.0) g.closePath() g.drawShape() g.restore() // restore current coordinate system // draw some text g.setColor(Color(0, 128, 0)) // a darker green g.setFont(20.0, Batang) g.drawText(Hello World!, 155.0, 225.0) g.drawText(안녕 하세요, 175.0, 245.0) // done with drawing g.done() } fun main(args: ArrayString) { setTitle(CS109 UI Test #1) val image BufferedImage(500, 500, BufferedImage.TYPE_INT_RGB) draw(image) show(image) }我们编译并运行程序$ ktc uitest1.kt $ kt Uitest1Kt程序会打开一个像这样的新窗口https://github.com/OpenDocCN/cs-notes-pt3-zh/raw/master/docs/kaist-cs109-prog-prac/img/670ee8e22b35961a1f0a52eb01dc4370.jpg请注意尽管主函数已经返回程序尚未终止。要结束程序你必须手动使用鼠标关闭窗口。更新显示你可以通过绘制一个新图像并再次调用 show 来更改窗口的内容。你可以绘制到之前的相同图像或使用新图像。在窗口变化之间等待一段时间你可以执行一些简单的动画比如让对象在窗口内闪烁或移动。这里有一个简单的示例展示了一个闪烁的正方形。首先正方形以红色出现一秒钟然后以蓝色出现一秒钟。五秒后程序会自动终止uitest2.ktimport org.otfried.cs109ui.* import org.otfried.cs109ui.ImageCanvas import org.otfried.cs109.Color import org.otfried.cs109.DrawStyle import java.awt.image.BufferedImage fun draw(image: BufferedImage, color: Color) { val g ImageCanvas(image) g.clear(Color.WHITE) g.setColor(color) g.drawRectangle(100.0, 100.0, 300.0, 300.0) g.done() } fun showWait(image: BufferedImage, color: Color, ms: Int) { draw(image, color) // draw rectangle show(image) waitForMs(ms) // wait ms milliseconds } fun main(args: ArrayString) { setTitle(CS109 UI Blinking Rectangle) val image BufferedImage(500, 500, BufferedImage.TYPE_INT_RGB) showWait(image, Color.WHITE, 500) // 0.5 sec white picture showWait(image, Color.RED, 1000) // 1 sec red rectangle showWait(image, Color.WHITE, 500) // 0.5 sec white picture showWait(image, Color.BLUE, 1000) // 1 sec blue rectangle showWait(image, Color.WHITE, 5000) // 5 secs white picture close() // close window and terminate program }同样你可以使用以下命令运行$ ktc uitest2.kt $ kt Uitest2Kt现在你可以编写有趣的程序比如Simon project你可以使用终端进行文本输出和文本输入使用窗口进行图形输出。动画通过快速更新窗口我们可以编写一些简单的平滑动画。这里有一个示例让一个红色球在屏幕上平滑移动uitest-animation.ktimport org.otfried.cs109ui.* import org.otfried.cs109ui.ImageCanvas import org.otfried.cs109.Color import org.otfried.cs109.DrawStyle import java.awt.image.BufferedImage fun draw(image: BufferedImage, x: Double, y: Double) { val g ImageCanvas(image) g.clear(Color.WHITE) g.setColor(Color.RED) g.drawCircle(x, y, 40.0) g.done() } fun main(args: ArrayString) { setTitle(CS109 UI Animation test) val image BufferedImage(500, 500, BufferedImage.TYPE_INT_RGB) var x 30.0 var y 30.0 while (x 500.0) { draw(image, x, y) x 2 y 1 show(image) waitForMs(10) } }键盘输入如果我们想要更进一步我们可以通过窗口与用户进行所有交互。我们可以使用 drawString 函数在窗口中显示文本。下一步是允许用户通过在窗口中按键来控制程序。这是使用 waitKey 函数完成的。它会等待用户按下一个键然后返回所按下的字符。这里有一个简单的测试程序uitest3.ktimport org.otfried.cs109ui.* import org.otfried.cs109ui.ImageCanvas import org.otfried.cs109.Color import org.otfried.cs109.DrawStyle import java.awt.image.BufferedImage fun draw(image: BufferedImage, color: Color) { val g ImageCanvas(image) g.clear(Color.WHITE) g.setColor(color) g.drawRectangle(100.0, 100.0, 300.0, 300.0) g.done() } fun main(args: ArrayString) { setTitle(CS109 UI Keyboard Input Test) val image BufferedImage(500, 500, BufferedImage.TYPE_INT_RGB) draw(image, Color.RED) show(image) println(Now press some keys inside the CS109 UI windows) println(Pressing q will terminate the program) while (true) { val ch waitKey() println(Got character $ch) if (ch q) close() // close window and terminate program } }窗口出现后尝试在窗口上聚焦时键入键。您应该在终端上看到消息“Got character”。按下“q”键将终止程序。对话框我们可以通过使用额外的弹出窗口通常称为“对话框”使我们的程序更加专业。最简单的方法只是向用户显示一条消息。用户必须按下“确定”才能继续程序。例如使用这段代码showMessage(This is a message)我们将得到这个弹出窗口https://github.com/OpenDocCN/cs-notes-pt3-zh/raw/master/docs/kaist-cs109-prog-prac/img/453fea90b7ab9f6f3881b6d6d7f3e08c.jpg稍微有趣的是我们可以询问一个是/否问题用户可以通过按下两个按钮中的一个来决定val yesno: Boolean askYesNo(Do you like this?)它看起来像这样https://github.com/OpenDocCN/cs-notes-pt3-zh/raw/master/docs/kaist-cs109-prog-prac/img/e3a3598d89fa3a7ed69e338b5ba99108.jpgaskYesNo 函数将用户的选择作为布尔值返回。最后我们可以要求用户输入一个字符串val name: String inputString(What is your name?)它看起来像这样https://github.com/OpenDocCN/cs-notes-pt3-zh/raw/master/docs/kaist-cs109-prog-prac/img/a7a4104c2748836445daaaeca60213b5.jpg再次该函数返回用户输入的字符串。如果用户按下“取消”或关闭弹出窗口则返回空字符串。高级功能在模块中还有一些更多的可能性从窗口接收鼠标按钮点击另外两个对话框以及设置用户输入的超时。当这些对项目有需要时我会记录这些内容但你也可以随时查看示例代码。编译成 JavaScript 并在浏览器中运行Kotlin 自带一个编译器将 Kotlin 源代码编译成 JavaScript准备好包含在网页中。因此你可以创建完全在 Web 浏览器中运行的应用程序。为了帮助你入门这里有一些提示和一个示例项目。我们首先创建一个新目录。我们将在此目录中创建三个文件canvas.html浏览器加载的网页canvas.js从网页加载的 JavaScript 代码jscanvas.js我们将使用的 org.otfried.cs109js.JsCanvas 类的 JavaScript 代码kotlin.jsJavaScript 的 Kotlin 标准库。让我们从最后一个开始kotlin.js 包含在 Kotlin 编译器附带的 kotlin-jslib.jar 文件中。用你喜欢的工具提取 kotlin.js 并将其放在你的新目录中jar 文件只是被简单压缩的存档。文件 jscanvas.js 也很容易获得它包含在你之前安装过的档案 cs109-jslib.jar 中。这是一个示例 HTML 文件canvas.htmlhtml head meta charsetutf-8 script srckotlin.js/script script srcjscanvas.js/script script srccanvas.js/script /head body onloadjavascript:Kotlin.modules[canvas].canvas.start(); canvas width600 height300 idcanvas/canvas div idtextThis is a fun paragraph. /div /body /html在文档的头部我们加载两个库和我们自己的 JavaScript 代码 canvas.js我们仍然需要生成。在 body 中我们包含两个元素一个 HTML 画布这是一个我们可以从 JavaScript 自由绘制的矩形区域和一个文本分区稍后我们可以从 JavaScript 中更新。现在从你的浏览器加载此文件你应该看到一个空白的白色矩形和下面的文本。此时我们应该在 Web 浏览器中打开 JavaScript 控制台。这是 JavaScript 的所有输出以及任何错误消息将会显示的地方。在 Firefox 中使用菜单、开发者、Web 控制台。在 Safari 中转到设置并勾选“显示开发菜单”复选框然后转到新出现的开发菜单并打开控制台。到目前为止控制台应该只显示一个错误消息TypeError: Kotlin.modules.canvas is undefined这是 body 标签中 onload 属性的响应。在这里我们指示 JavaScript 在网页完全加载后这一点很重要因为我们希望所有 HTML 元素在代码运行时都存在在 canvas.js 脚本中执行包 canvas 中的函数 start。剩下的是编写这个 start 函数并生成 canvas.js。这里是一个第一个示例canvas1.ktpackage canvas import org.otfried.cs109js.JsCanvas import org.otfried.cs109.Color import kotlin.browser.document import org.w3c.dom.* fun start() { println(Hello World from Javascript) val canvas JsCanvas(canvas) canvas.clear(Color.GREEN) val text document.getElementById(text) text?.appendChild(document.createTextNode(Was du hier liest ist kein Gedicht.)) }我们需要通过以下方式将该文件编译成 JavaScript$ kotlinc-js -output canvas.js canvas1.ktkotlinc-js 是 Kotlin 到 JavaScript 的编译器。我指定输出文件名在从 JavaScript 调用时对应于模块 Kotlin.modules[‘canvas’]。开始处的包声明将 start 函数放在 canvas 包中因此需要从 JavaScript 中调用它作为Kotlin.modules[canvas].canvas.start();这正是我们在 onload 属性中编写的内容。现在重新加载网页画布矩形会变成绿色底部的文本也会改变。在 JavaScript 控制台中你应该会看到 println 语句的输出。让我们通过添加一些颜色、文本和透明度来使我们的绘图更加有趣canvas2.ktpackage canvas import org.otfried.cs109js.JsCanvas import org.otfried.cs109.Color object Controller { val canvas JsCanvas(canvas) var x 30.0 var y 50.0 var alpha 45.0 fun draw() { canvas.clear(Color.WHITE) canvas.setAlpha(48) canvas.setColor(Color.GREEN) canvas.drawRectangle(10.0, 10.0, 100.0, 100.0) canvas.setAlpha(255) // opaque for (i in 0 .. 5) { for (j in 0 .. 5) { canvas.setColor(Color(Math.floor(255-42.5*i), Math.floor(255-42.5*j), 0)) canvas.drawRectangle(150.0 j*25.0, i*25.0, 25.0, 25.0) } } canvas.translate(x, y) canvas.setAlpha(128) canvas.rotate(alpha) canvas.setColor(Color.RED) canvas.setFont(32.0) canvas.drawText(Lovely, 0.0, 0.0) } } fun start() { println(Canvas2 starting...) Controller.draw() }我们再次编译$ kotlinc-js -output canvas.js canvas2.kt现在重新加载网页画布应该包含一个透明的绿色正方形在上面有一些半透明的红色文本右侧还有一个漂亮的颜色图案。到目前为止都很顺利现在让我们添加一些交互性。当你在画布上点击鼠标时文本应该移动到这个位置。按下键盘上的 ‘a’、‘s’、‘w’ 和 ‘z’ 键可以用来移动文本按下 ‘j’ 和 ‘k’ 键可以旋转它canvas3.ktpackage canvas import org.otfried.cs109js.JsCanvas import org.otfried.cs109.Color import kotlin.browser.window import org.w3c.dom.events.* object Controller { val canvas JsCanvas(canvas) var x 30.0 var y 50.0 var alpha 45.0 fun draw() { canvas.save() canvas.clear(Color.WHITE) canvas.setAlpha(48) canvas.setColor(Color.GREEN) canvas.drawRectangle(10.0, 10.0, 100.0, 100.0) canvas.setAlpha(255) // opaque for (i in 0 .. 5) { for (j in 0 .. 5) { canvas.setColor(Color(Math.floor(255-42.5*i), Math.floor(255-42.5*j), 0)) canvas.drawRectangle(150.0 j*25.0, i*25.0, 25.0, 25.0) } } canvas.translate(x, y) canvas.setAlpha(128) canvas.rotate(alpha) canvas.setColor(Color.RED) canvas.setFont(32.0) canvas.drawText(Lovely, 0.0, 0.0) canvas.restore() } fun keyDown(e: Event) { val ek e as KeyboardEvent var k ek.key if (k undefined) k ${ek.keyCode.toChar().toLowerCase()} when (k) { a - x - 3 s - x 3 w - y - 3 z - y 3 j - alpha 10.0 k - alpha - 10.0 else - return } draw() e.preventDefault() } fun mouseDown(e: Event) { val em e as MouseEvent x em.offsetX y em.offsetY draw() } } fun start() { println(Canvas3 starting...) println(Active keys are aswzjk) Controller.draw() window.addEventListener(keydown, { Controller.keyDown(it) }, true) window.addEventListener(mousedown, { Controller.mouseDown(it) }, true) }我们再次编译$ kotlinc-js -output canvas.js canvas3.kt重新加载网页。现在你应该能够点击画布并看到文本移动到这个位置。当你按下其中一个活动键时文本应该移动和旋转。最后让我们给我们的绘图添加一些动画效果。按下 ‘g’ 键开始动画并再次暂停。现在的代码还会调整画布大小以填满整个浏览器窗口只留下一点空间给滚动条和底部的文本canvas4.ktpackage canvas import org.otfried.cs109js.JsCanvas import org.otfried.cs109.Color import kotlin.browser.window import org.w3c.dom.events.* object Controller { val canvas JsCanvas(canvas) // change canvas size to fill entire browser window init { canvas.canvas.width window.innerWidth.toInt() - 20 canvas.canvas.height window.innerHeight.toInt() - 50 } var x 30.0 var y 50.0 var alpha 45.0 var animate false var timeStamp 0.0 fun draw() { canvas.save() canvas.clear(Color.WHITE) canvas.setAlpha(48) canvas.setColor(Color.GREEN) canvas.drawRectangle(10.0, 10.0, 100.0, 100.0) canvas.setAlpha(255) // opaque for (i in 0 .. 5) { for (j in 0 .. 5) { canvas.setColor(Color(Math.floor(255-42.5*i), Math.floor(255-42.5*j), 0)) canvas.drawRectangle(150.0 j*25.0, i*25.0, 25.0, 25.0) } } canvas.translate(x, y) canvas.setAlpha(128) canvas.rotate(alpha) canvas.setColor(Color.RED) canvas.setFont(32.0) canvas.drawText(Lovely, 0.0, 0.0) canvas.restore() if (animate) window.requestAnimationFrame { animate(it) } } fun animate(s: Double) { val delta s - timeStamp timeStamp s x delta / 2.0 if (x canvas.width) x 0.0 alpha 0.3 * delta if (alpha 360.0) alpha 0.0 draw() } fun keyDown(e: Event) { val ek e as KeyboardEvent var k ek.key if (k undefined) k ${ek.keyCode.toChar().toLowerCase()} when (k) { a - x - 3 s - x 3 w - y - 3 z - y 3 j - alpha 10.0 k - alpha - 10.0 g - { if (!animate) timeStamp window.performance.now() animate !animate } else - return } draw() e.preventDefault() } fun mouseDown(e: Event) { val em e as MouseEvent x em.offsetX y em.offsetY draw() } } fun start() { println(Canvas3 starting...) println(Active keys are aswzjk and g for animation) Controller.draw() window.addEventListener(keydown, { Controller.keyDown(it) }, true) window.addEventListener(mousedown, { Controller.mouseDown(it) }, true) }现在你可以编写自己的 web 应用程序了。或者尝试一下 我的实现 的 2048 游戏。移动键是 ‘u’、‘d’、‘l’ 和 ‘r’。直接使用 Javascript Canvas API我的 JsCanvas 类很方便因为你可以使用相同的绘图代码来绘制位图和 CS109 Android 框架当然你也可以从 HTML 文件中移除 jscanvas.js 文件并直接使用 JavaScript 函数来绘制到画布上。查看 canvas-nojscanvas.kt 可以看到没有 JsCanvas 的最新版本是什么样子的。你可能会发现 Mozilla 文档 有所帮助虽然它是为 JavaScript 设计的但其中很多代码在 Kotlin 中也可以不变地使用。Android 迷你应用框架Android 软件开发工具包非常复杂超出了本教程的范围。相反我们将看到如何仅使用我们迄今学到的工具使用“KAIST CS109 迷你应用框架”为 Android 编写基于画布的应用程序。第一个示例绘图轻点、双击和轻扫对话框定时器和动画菜单使用手机传感器参考资料Kotlin 文档在线尝试 Kotlin《Kotlin 实战》目前是唯一一本关于 Kotlin 的书甚至还没有完成。这是一本好书但假定读者已经了解 Java。Java 库参考文档本文档使用 Java 语法和类型名称。如果有任何不理解的地方请在课程论坛上提问
网站建设高端定制企业官网