turtle(海龟作图),C++版「建议收藏」

turtle(海龟作图),C++版「建议收藏」海龟作图引言turtle来源Logo的原型来自另一个计算机语言LISP,派普特修改了LISP的语法使其更易于阅读。Logo常被称作没有括号的Lisp。Logo是一种解释型语言,和其他语言不同的是,它内置一套海龟绘图(TurtleGraphics)系统,通过向海龟发送命令,用户可以直观地学习程序的运行过程,因此很适于儿童学习。它亦适合用作数学教学。海龟绘图使得Logo用户可以通过简单的编程创作出丰富多彩的视觉效果或图案。假想一只带着画笔的海龟可以接受简单的命令,例如向前走100步,或者左转30度。

大家好,又见面了,我是你们的朋友全栈君。

海龟作图

引言

turtle来源

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

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

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

海龟的移动相对于它本身所在的位置。例如,命令”左90″意味着让海龟左转90度,学生可以站在海龟的角度来思考它将如何执行命令,这使得程序设计更加形象化,也更易于理解。
来自:wiki
https://zh.wikipedia.org/wiki/Logo_(%E7%A8%8B%E5%BA%8F%E8%AF%AD%E8%A8%80)
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.
不要吝惜一键三连,喝水不忘挖井人嘛!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/154095.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 在服务器上排除问题的头五分钟

    在服务器上排除问题的头五分钟

    2021年9月8日
    57
  • imfinfo怎么用_matlablimit函数

    imfinfo怎么用_matlablimit函数imfinifo获取图像的相关信息!例如:>>Ipath=’F:\actionrecognition\code2\DB\JPEGImages\action0002.jpg’;>>info=imfinfo(Ipath)info=       Filename:’F:\actionrecognition\code2\DB\JPEGImages\action

    2022年10月5日
    2
  • 激光slam学习日记——基于滤波器的激光SLAM方法

    激光slam学习日记——基于滤波器的激光SLAM方法激光slam学习日记——基于滤波器的基于滤波的方法不适合大型场景,因为误差慢慢累积,没办法修复。贝叶斯与频率学派相对应,贝叶斯估计概率,频率估计数值粒子滤波:贝叶斯中的一个特例一、贝叶斯估计独立:我们知道其中一个,并不能对求解另一个产生任何帮助;条件独立:在某种情况下,才相互独立;假设,我们知道上一时刻位姿和上一个位姿的运动情况,那我们可以根据这两个得到此时刻的位姿,进而得到此时刻的观测情况,但是我们一旦知道这时刻的位姿,那上一时刻的运动情况与观测便不再有关系了。(好啰嗦)全概率:积分号内部

    2022年8月23日
    7
  • mysql分析慢查询_开启慢查询日志

    mysql分析慢查询_开启慢查询日志一、生成实验数据原理:sql蠕虫复制(这种生成数据方式同样适用于数据表中有主键的情况)。insertintocomic(name,pen_name,cover)selectname,pen_name,coverfromcomic 二、慢查询日志设置当语句执行时间较长时,通过日志的方式进行记录,这种方式就是慢查询的日志。1、临时开启慢查询日志(如果需要长时间…

    2022年10月14日
    3
  • Docker Compose详解

    Docker Compose详解使用 DockerCompos 预计阅读时间 11 分钟 DockerCompos 是一种用于帮助定义和共享多容器应用程序的工具 使用 Compose 我们可以创建一个 YAML 文件来定义服务 并且使用一个命令 可以启动所有内容或将其全部关闭 使用 Compose 的最大优势是您可以在一个文件中定义您的应用程序堆栈 将其保存在项目 repo 的根目录中 它现在是版本控制的 并且可以轻松地让其他人为您的项目做出贡献 有人只需要克隆您的存储库并启动撰写应用程序 事实上 你现在可能会在 GitHub Gi

    2025年6月17日
    2
  • redis和memcache区别_redis和数据库的区别

    redis和memcache区别_redis和数据库的区别1redis做存储,可以持久化,memcache做缓存,数据易丢失。2redis支持多数据类型,memcache存放字符串。3redis服务端仅支持单进程、单线程访问,也就是先来后到的串行模式,避免线程上下文切换,自然也就保证数据操作的原子性。Memcache服务端是支持多线程访问的。4redis虽然是单进程单线程模式,但是redis使用了IO多路复用技术做到一个线程可以处理很多个请求来保证高性能。Redis的主从复制1在Slave启动并连接到Master之后,它将主动发送

    2025年8月21日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号