turtle(海龟作图),C++版

    科技2022-07-12  131

    海龟作图

    引言

    turtle来源

    Logo的原型来自另一个计算机语言LISP,派普特修改了LISP的语法使其更易于阅读。Logo常被称作没有括号的Lisp。

    Logo是一种解释型语言,和其他语言不同的是,它内置一套海龟绘图(Turtle Graphics)系统,通过向海龟发送命令,用户可以直观地学习程序的运行过程,因此很适于儿童学习。它亦适合用作数学教学。

    海龟绘图使得Logo用户可以通过简单的编程创作出丰富多彩的视觉效果或图案。假想一只带着画笔的海龟可以接受简单的命令,例如向前走100步,或者左转30度。通过对这只海龟发送命令,可以让它绘制出较为复杂的图形,例如正方形,三角形,圆等。

    海龟的移动相对于它本身所在的位置。例如,命令"左90"意味着让海龟左转90度,学生可以站在海龟的角度来思考它将如何执行命令,这使得程序设计更加形象化,也更易于理解。 来自:wiki https://zh.wikipedia.org/wiki/Logo_(程序语言) python上直接有turtle的接口,而C++的turtle暂时还没有一个人官方的库。所以本次任务就是做一个初步的turtle接口

    本次实现的功能

    (1)设置海龟类型的基本操作为: void StartTurtleGraphics() //显示作图窗口,并在窗口内写出本人的姓名。 void StartTurtle() //令海龟处于作图的初始状态。即显示作图窗口,并将海龟定位在窗口正中; //置画笔状态为落笔、龟头朝向为0度(正东方向) void PenUp() //改变画笔状态为抬笔·从此时起,海龟移动将不在屏幕上作图。 void PenDown() //改变画笔状态为落笔。从此时起,海龟移动将在屏幕上作图。 int TurtleHeading() //返回海龟头当前朝向的角度。 aPoint * TurtlePos() //返回海龟的当前位置。 void Move(intsteps) //依照海龟头的当前朝向,向前移动海龟steps步. void Turn(intdegrees) //改变海龟头的当前朝向,逆时针旋转degrees度。 void MoveTTo(aPoint newPos) //将海龟移动到新的位置newPos。如果是落笔状态,则同时作图。 void TurnTTo(float angle) //改变海龟头的当前朝向为,从正东方向起的angle度。 void SetTurtleColor(intcolor) 设置海龟画笔的颜色为color 完成这些功能的建立,即可做出一个初步的turtle框架

    具体实现

    turtle.h

    #pragma once #include <graphics.h> #include <iostream> #include <conio.h> #include <cstdio> #include <string> #include <easyx.h> #include <cstdlib> #include <cmath> #include <graphics.h> using namespace std; #define UP 0 #define DOWN 1 #define PI 3.14159 typedef int penState; //取值UP或DOWN typedef struct { float x, y; } aPoint; //位置 typedef struct { double heading; //海龟头方向 penState pen; //画笔状态 int color; //画笔当前颜色 aPoint Pos; //海龟当前位置 } newTurtle; class turtle { public: //复制turtle类中的数据到另一个类中 void copy(turtle& C); //显示作图窗口,并在窗口内写出本人的姓名。 void StartTurtleGraphics(); //令海龟处于作图的初始状态。即显示作图窗口,并将海龟定位在窗口正中; //置画笔状态为落笔、龟头朝向为0度(正东方向) void StartTurtle(); //改变画笔状态为抬笔·从此时起,海龟移动将不在屏幕上作图。 void PenUp(); //改变画笔状态为落笔。从此时起,海龟移动将在屏幕上作图。 void PenDown(); //返回海龟头当前朝向的角度。 int TurtleHeading(); //返回海龟的当前位置。 aPoint* TurtlePos(); //依照海龟头的当前朝向,向前移动海龟steps步. void Move(int steps); //改变海龟头的当前朝向,逆时针旋转degrees度。 void Turn(double degrees); //将海龟移动到新的位置newPos。如果是落笔状态,则同时作图。 void MoveTTo(aPoint newPos); //改变海龟头的当前朝向为,从正东方向起的angle度。 void TurnTTo(double angle); //设置海龟画笔的颜色为color void SetTurtleColor(int color); private: newTurtle A; };

    turtle.cpp

    #include "turtle.h" //显示作图窗口,并在窗口内写出本人的姓名。 void turtle::StartTurtleGraphics() { initgraph(880, 640); wchar_t a[]= L"输入你的姓名: "; outtext(a); wchar_t s[10]; wcin >> s; outtext(s); } //令海龟处于作图的初始状态。即显示作图窗口,并将海龟定位在窗口正中; //置画笔状态为落笔、龟头朝向为0度(正东方向) void turtle::StartTurtle() { A.Pos.x = 440; A.Pos.y = 320; A.pen = DOWN; A.heading = 0; } //改变画笔状态为抬笔·从此时起,海龟移动将不在屏幕上作图。 void turtle::PenUp() { A.pen = UP; } //改变画笔状态为落笔。从此时起,海龟移动将在屏幕上作图。 void turtle::PenDown() { A.pen = DOWN; } //返回海龟头当前朝向的角度。 int turtle::TurtleHeading() { return A.heading; } //返回海龟的当前位置。 aPoint* turtle::TurtlePos() { aPoint* apt = new(aPoint); apt->x = A.Pos.x; apt->y = A.Pos.y; return apt; } //依照海龟头的当前朝向,向前移动海龟steps步. void turtle::Move(int steps) { double x1=A.Pos.x; double y1=A.Pos.y; A.Pos.x = x1 + steps * cos((A.heading / 360) * 2 * PI); A.Pos.y = y1 + steps * (-1) * sin((A.heading / 360) * 2 * PI); } //改变海龟头的当前朝向,逆时针旋转degrees度。 void turtle::Turn(double degrees) { A.heading += degrees; while (A.heading > 0) { A.heading -= 360; } } //将海龟移动到新的位置newPos。如果是落笔状态,则同时作图。 void turtle::MoveTTo(aPoint newPos) { if (A.pen == UP) { A.Pos.x = newPos.x; A.Pos.y = newPos.y; } else if (A.pen == DOWN) { double x1 = A.Pos.x; double y1 = A.Pos.y; A.Pos.x = newPos.x; A.Pos.y = newPos.y; line(x1, y1, A.Pos.x, A.Pos.y); } } //改变海龟头的当前朝向为,从正东方向起的angle度。 void turtle::TurnTTo(double angle) { A.heading = angle; } //设置海龟画笔的颜色为color void turtle::SetTurtleColor(int color) { setlinecolor(color); } void turtle::copy(turtle& C) { A.color = C.A.color; A.heading = C.A.heading; A.pen = C.A.pen; A.Pos.x = C.A.Pos.x; A.Pos.y = C.A.Pos.y; }

    test.cpp

    #include "turtle.h" //自行指定线段(的长度)、矩形(的长度和宽度)及圆(的半径)等参数。 //画线 turtle B; void drawline(double length) { turtle C; C.copy(B); C.Move(length); aPoint* newpos_1; newpos_1 = C.TurtlePos(); aPoint newpos_2; newpos_2.x = newpos_1->x; newpos_2.y = newpos_1->y; B.MoveTTo(newpos_2); } //以乌龟当前点出发,调用drawline(),画矩形 void drawtangle(double length,double width) { for (int i = 0; i < 2; i++) { drawline(length); B.Turn(90); drawline(width); B.Turn(90); } } //以乌龟当前点出发,调用drawline(),画圆 //由于圆形只能不断逼近,所以本次采用180次分割,使肉眼见图形为圆形 //将圆分成180份,运用三角函数计算每一段的段长为2*sin(1)*r //待优化,由于是使用line函数直接画线逼近圆,所以当半径较大时需要调整分割次数以使圆形较为圆润 //而这需要一个度量标准,即当半径多大时分割次数为多少 //还有一点问题就是当分割次数过大时,sin过小,math提供的sin函数无法满足计算 //所以画圆最为理想的解法应该为画点来做圆 void drawcircle(double r) { double k = 2 * sin((1.0 / 360)*2.0*PI) * r; cout << k << endl; for (int i = 0; i < 180; i++) { drawline(k); B.Turn(2); } } //test函数,试验海龟作图及相关函数是否正确 int main() { B.StartTurtleGraphics(); B.StartTurtle(); B.PenUp(); B.SetTurtleColor(YELLOW); B.PenDown(); drawline(200); B.Turn(30); drawtangle(100, 100); drawcircle(200); aPoint pos; pos.x = 440; pos.y = 320; B.MoveTTo(pos); drawcircle(200); drawtangle(100, 100); B.PenUp(); drawtangle(100, 100); while (!_kbhit()) { ; } closegraph(); // 关闭绘图窗口 return 0; }

    待优化,由于是使用line函数直接画线逼近圆,所以当半径较大时需要调整分割次数以使圆形较为圆润,而这需要一个度量标准,即当半径多大时分割次数为多少。 还有一点问题就是当分割次数过大时,sin过小,math提供的sin函数无法满足计算。所以画圆最为理想的解法应该为画点来做圆,感兴趣的话可以自己尝试一下用点法画圆

    画圆算法(Bresenham + 中点)

    文末

    这个turtle框架还是很粗糙的,仅供参考,希望可以给一些萌新们一些思路上的启发。如果有什么疑问,可以在讨论区讨论。 另:如果有兴趣的话可以阅读python turtle模板,写一个自己的功能强大的C++ turtle。 github上关于各种语言的turtle有很多,感兴趣的话可以下载学习。

    最后

    相信很多看到这个blog的都是为了写作业。我就是为了写作业才开始了解turtle的,但是搜了很多blog都没有满意的,所以就干脆自己写一个。不要谢哦!!

    没有将这篇文传到下载区,我简直就是良心创作者/dog. 不要吝惜一键三连,喝水不忘挖井人嘛!

    Processed: 0.010, SQL: 8