【Godot4.2】myPoint类 - 基于旋转和移动的点求取方式

概述

记得很久以前(大约17年前)有个用指令绘图的软件(不是LOGO,而是它的一个模仿者,我找半天实在找不到。),基于移动和旋转来绘制折线。每次移动和旋转都是基于当前位置和方向,就像一个人在走路一样。这是一种有别于向量旋转的方式。

所以我就突发奇想,想要自己实现这种独特的绘图方法。

myPoint类

刚开始我想要用编写函数来实现,但发现必须在函数外使用全局变量来记录当前位置和方向。所以直接编写了一个自定义类myPoint

通过它你可以定义一个起始点,然后通过其旋转函数rot和移动函数move,来获取新的点。

最后通过points属性获取所有顶点,然后使用CanvasItem绘图函数绘制和显示。

# ========================================================
# 名称:myPoint
# 类型:自定义类
# 简介:通过记录当前位置和方向进行旋转和移动,获取多个点,可用于绘制
# 多边形、折线
# 作者:巽星石
# Godot版本:v4.2.1.stable.official [b09f793f5]
# 创建时间:20244615:10:13
# 最后修改时间:202441413:39:06
# ========================================================

class_name myPoint

var dir = Vector2.RIGHT:         # 当前方向
	set(val):
		dir = val
		
var start_angle:float:           # 起始角度(与X轴夹角),决定初始方向
	set(val):
		start_angle = val
		dir = dir.rotated(deg_to_rad(val)) # 旋转dir

var pos:= Vector2()               # 当前位置
var _start_pos:= Vector2()       # 初始位置(不会随着移动发生变化)
var points:PackedVector2Array    # 点集合

# 获取初始位置
func get_start_pos() -> Vector2:
	return Vector2(_start_pos)     # 返回副本(避免修改)

# 初始化
func _init(x:float = 0.0,y:float = 0.0) -> void:
	pos = Vector2(x,y)          # 构造初始位置
	points.append(pos)          # 添加初始点
	_start_pos = pos            # 记录初始位置

# 旋转
func rot(deg:float) -> void:
	dir = dir.normalized().rotated(deg_to_rad(deg))

# 移动
func move(length:float) -> void:
	var newpos = pos + dir * length  # 计算新点
	points.append(newpos)              # 添加新点
	pos = newpos                      # 更新位置

# 执行路径,每个Vector2的x表示旋转度数,y表示移动距离
func do_path(path:PackedVector2Array) -> void:
	for do in path:
		rot(do.x)  # 旋转
		move(do.y) # 移动

# 以字符串形式执行路径
func do_path_with_str(path:String) -> void:
	var dos = path.split(" ",false)
	for do in dos:
		var ds = do.split(",",false)
		rot(float(ds[0]))  # 旋转
		move(float(ds[1])) # 移动

绘制简单旗帜

func _draw() -> void:
	var p = myPoint.new(400,200)     # 起始位置
	
	p.move(100)    
	p.rot(90)
	p.move(200)
	p.rot(30)
	p.move(200)
	p.rot(120)
	p.move(200)
	p.rot(30)
	p.move(200)
	p.rot(90)
	p.move(100)
	draw_polyline(p.points,Color.AQUAMARINE,2)

上面代码绘制的图形如下:
image.png

  • var p = myPoint.new(400,200)定义了初始位置(400,200)
  • 默认方向是Vector2.RIGHT,所以第一个p.move(100),是向右移动100像素,也就是(400,200)+(100,0)=(500,100)
  • 第一个p.rot(90),则是在Vector2.RIGHT基础上顺时针旋转90°,也就变成了Vector2.DOWN。依次类推…

image.png
通过简单修改可以获得另一个图形:

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,200)     # 起始位置
	
	p.rot(-30)
	p.move(100/cos(deg_to_rad(30)))
	p.rot(30)
	p.rot(90)
	p.move(200)
	p.rot(30)
	p.move(200)
	p.rot(120)
	p.move(200)
	p.rot(30)
	p.move(200)
	p.rot(120)
	p.move(100/cos(deg_to_rad(30)))
	draw_polyline(p.points,Color.AQUAMARINE,2)

image.png
可以看到,这种方式绘制图形更考验你在平面空间中行走的想象能力。也需要你灵活的计算各种夹角、以及使用三角函数来计算对边和邻边投影等。

但是它的优点是可以只用旋转和移动两种操作,得到一些需要用向量加减法和向量旋转做半天的图形。

用向量表示旋转和移动操作

实际上,因为只有旋转和移动两种操作,所以完全可以用一个Vector2来存储每次的旋转角度移动距离。也就是Vector2(angle,length)

然后用一个PackedVector2Array来记录执行的所有操作。

extends Node2D

# 用于myPoint执行的路径
var path:PackedVector2Array = [
		Vector2(0,100),    # 旋转0度,向前移动100像素
		Vector2(90,200),   # 旋转90度,向前移动200像素
		Vector2(30,200),   # 旋转30度,向前移动200像素
		Vector2(120,200),  # 旋转120度,向前移动200像素
		Vector2(30,200),   # 旋转30度,向前移动200像素
		Vector2(90,100),   # 旋转90度,向前移动100像素
	]

func _draw() -> void:
	var p = myPoint.new(400,200)     # 起始位置
	p.do_path(path)                  # 执行路径
	# 绘制获得的曲线
	draw_polyline(p.points,Color.AQUAMARINE,2)

绘制的效果是一样的。
path数组中的单个Vector2可以看做是一次旋转和移动操作的复合:

  • Vector2(45,100),表示当前方向顺时针旋转45度,然后向前移动100像素
  • 如果要单单表示移动,只需要将旋转设为0,比如Vector2(0,100)只进行移动
  • 而单单表示旋转也类似,只需要将移动设为0,比如Vector2(45,0)只进行旋转

折线描述字符串

我们可以进一步将PackedVector2Array简化为一个字符串,通过解析字符串来还原操作。

extends Node2D

# 用于myPoint执行的路径
var path = "0,100 90,200 30,200 120,200 30,200 90,100"	

func _draw() -> void:
	var p = myPoint.new(400,200)     # 起始位置
	p.do_path_with_str(path)                  # 执行路径
	# 绘制获得的曲线
	draw_polyline(p.points,Color.AQUAMARINE,2)

这种形式的好处是,极致压缩,但又存储了关键信息,可以用于在文件中存储和还原图形。

其他绘制案例

绘制正多边形

你可以使用循环来执行多次的旋转和移动操作,从而获得一些特殊图形,比如正多边形。

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,200)
	var steps := 3   # 步幅
	
	# 旋转和移动
	for i in range(steps):
		p.rot(360.0/steps)
		p.move(100)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

在这里插入图片描述

  • 图中绿色点为实例化myPoint时定义的起始点。
  • 通过将每次行走的线段绘制成向量,可以看到图形基于起始点行走的方向和过程。

绘制螺旋线

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,200)
	var steps := 20   # 步幅
	
	# 旋转和移动
	for i in range(steps):
		p.rot(180.0/steps)
		p.move(10 + 2.0 * i)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,200)
	var steps := 20   # 步幅
	
	# 旋转和移动
	for i in range(steps):
		p.rot(30)
		p.move(5 * i)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,300)
	var steps := 50   # 步幅
	
	# 旋转和移动
	for i in range(steps):
		p.rot(90)
		p.move(10 * i)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,300)
	var steps := 50   # 步幅
	
	# 旋转和移动
	for i in range(steps):
		p.rot(60)
		p.move(5 * i)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,300)
	var steps := 50   # 步幅
	
	# 旋转和移动
	for i in range(steps):
		p.rot(120)
		p.move(10 * i)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,300)
	var steps := 60   # 步幅
	
	# 旋转和移动
	for i in range(steps):
		p.rot(150)
		p.move(10 * i)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,300)
	var steps := 50   # 步幅
	
	# 旋转和移动
	for i in range(steps):
		p.rot(180)
		p.move(10 * i)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

绘制方波

extends Node2D

func _draw() -> void:
	var p = myPoint.new(10,200)
	var steps := 10   # 重复次数
	var length:= 20.0 # 移动距离
	# 旋转和移动
	for i in range(steps):
		p.move(length)
		p.rot(-90)
		p.move(length)
		p.rot(90)
		p.move(length)
		p.rot(90)
		p.move(length)
		p.rot(-90)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

齿轮

# 暂时有Bug!

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,200)
	var steps := 60  # 重复次数
	var length:= 40.0 # 移动距离
	# 旋转和移动
	for i in range(steps):
		p.move(length)
		p.rot(-90 + 360.0/steps)
		p.move(length)
		p.rot(90 + 360.0/steps)
		p.move(length)
		p.rot(90 + 360.0/steps)
		p.move(length)
		p.rot(-90 + 360.0/steps)
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

三角波

extends Node2D

func _draw() -> void:
	var p = myPoint.new(10,200)
	var steps := 10  # 重复次数
	var length:= 40.0 # 移动距离
	# 旋转和移动
	for i in range(steps):
		p.rot(60)
		p.move(length)
		p.rot(-120)
		p.move(length)
		p.rot(60)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,200)
	var steps := 20  # 重复次数
	var length:= 40.0 # 移动距离
	# 旋转和移动
	for i in range(steps):
		p.rot(60)
		p.move(length)
		p.rot(-120 + 360.0/steps)
		p.move(length)
		p.rot(60)
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

锯齿波

extends Node2D

func _draw() -> void:
	var p = myPoint.new(10,200)
	var steps := 10  # 重复次数
	var length:= 40.0 # 移动距离
	# 旋转和移动
	for i in range(steps):
		p.rot(-45)
		p.move(length/sin(deg_to_rad(45)))
		p.rot(135)
		p.move(length)
		p.rot(-90)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

圆锯

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,200)
	var steps := 20  # 重复次数
	var length:= 40.0 # 移动距离
	# 旋转和移动
	for i in range(steps):
		p.rot(-45)
		p.move(length/sin(deg_to_rad(45)))
		p.rot(135 + 360.0/steps)
		p.move(length)
		p.rot(-90)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

总结

  • myPoint类基本上是开辟了另一种几何图形点求取方式,可以很好的作为简单向量旋转和加减法求点形式的补充
  • 通过简单的旋转和移动函数可以绘制很多特殊几何图形,而且也可以通过进一步编写函数来参数化的快速获取图形。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/542950.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

项目4-图书管理系统2+统一功能处理

1. 拦截器(Interceptor) 我们完成了强制登录的功能, 后端程序根据Session来判断用户是否登录, 但是实现⽅法是比较麻烦的。 所需要处理的内容: • 需要修改每个接⼝的处理逻辑 • 需要修改每个接⼝的返回结果 • 接⼝定义修改, 前端代码也需…

分享一个预测模型web APP的功能模块和界面的设计

一个临床预测模型web APP功能模块与界面设计 随着医疗技术的不断进步,web APP是临床预测模型在医学领域的应用的重要形式。这里分享一个web APP的设计,手里有医学预测模型的可以尝试将其构建成webAPP,进而在临床实践中体验预测模型带来的便利…

BugkuCTF:overflow2[WriteUP]

从题目中下载得到pwn文件 使用checksec工具对它进行检查,没有栈溢出保护 再根据题目提示可以知道这道题应该是利用栈溢出漏洞来做 把该文件放到linux中运行,可以看到有一个输入、输出的操作 把pwn丢进IDA里进行反编译分析 先看main函数,分…

Windows Server 2016虚拟机安装教程

一、VMware Workstation虚拟机软件的下载 官网下载入口:​​​​​​Download VMware Workstation Pro - VMware Customer Connect​​​​​ 下载好之后自己看着提示安装软件就好. 二、镜像文件的下载 下载网站入口:MSDN, 我告诉你 - 做一个安静…

关注招聘 关注招聘 关注招聘

🔥关注招聘 🔥关注招聘 🔥关注招聘 🔥开源产品: 1.农业物联网平台开源版 2.充电桩系统开源版 3.GPU池化软件(AI人工智能训练平台/推理平台) 开源版 产品销售: 1.农业物联网平台企业版 2.充电桩系统企业…

BCD BIN 转换

1,BCD是将10进制的每一位转换成2进制 如22 的中数子2的2进制就是0010,那么22的BCD 嘛就是 0010 0010 2,bin 的就是将2进制的每4位转成10进制 如 34的2进制就是0010 0010 高四位和低四位都是 0010 ,0010对应的10进制就是2 那么…

FPGA压缩算法 (一)

压缩算法 简介 压缩算法是通过去除冗余信息来达到的,在图像压缩算法中一般是通过去除编码冗余、像素间冗余、心理视觉冗余这三者之间的一个或多个来完成的。 编码冗余:当所用码字大于最佳编码长度的时候出现的冗余 像素间冗余:因为图像数据间…

Redis:发布和订阅

文章目录 一、介绍二、发布订阅命令 一、介绍 Redis的发布和订阅功能是一种消息通信模式,发送者(pub)发送消息,订阅者(sub)接收消息。这种功能使得消息发送者和接收者不需要直接建立连接,而是通…

【vue】slot 匿名插槽 / 具名插槽

slot父组件向子组件传递数据 匿名插槽–直接写 具名插槽–指定名称 父组件中 子组件中&#xff1a; 代码 App.vue <template><h2>App.vue</h2><!-- 匿名插槽 --><Header><a href"1234567890.com">1234567890</a>&…

【Linux学习笔记】安卓设置内核信息的打印级别

开发环境 开发板&#xff1a;正点原子RK3568开发板安卓版本&#xff1a;11 问题描述 在串口调试过程中经常打印出这样的一些信息 极影响调试&#xff0c;暂时又没什么用&#xff0c;有些时候还不能给它直接关了。尤其是这个信息 healthd: battery l50 v3 t2.6 h2 st3 fc10…

Dubbo面试回答简单版

一、dubbo特性 超时重试机制地址缓存多版本负载均衡&#xff1a;随机、权重轮询、最少活跃调用、一致性哈希集群容错&#xff1a;失败重试、快速失败、失败安全、失败自动恢复、并行调用、广播服务降级&#xff1a;异常时返回mock 集群容错 FailOver 失败重试&#xff0c;读…

Matlab之空间坐标系绘制平面图形

在空间直角坐标系中&#xff0c;绘制指定平面方程的图形 版本说明&#xff1a; 20240413_V1.01&#xff1a;更正代码错误&#xff0c;并修改输入参数类型&#xff08;测试用例得修改&#xff09; 20240413_V1.00&#xff1a;初始版本 一、平面方程 基本形式为&#xff1a;A…

微服务边车模式深度解析:赋能云原生应用的终极指南(自己搞一个简单SideCar?)

什么是SideCar? Sidecar模式定义&#xff1a; Sidecar 模式是一种常用于微服务架构中的设计模式&#xff0c;该模式允许将应用程序的核心功能与辅助功能&#xff08;如日志记录、监控、配置管理、网络通信等&#xff09;分离开来。在这种设计模式中&#xff0c;每个微服务主容…

回归预测 | Matlab基于RIME-SVR霜冰算法优化支持向量机的数据多输入单输出回归预测

回归预测 | Matlab基于RIME-SVR霜冰算法优化支持向量机的数据多输入单输出回归预测 目录 回归预测 | Matlab基于RIME-SVR霜冰算法优化支持向量机的数据多输入单输出回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab基于RIME-SVR霜冰算法优化支持向量机的数…

【数据结构】习题之消失的数字和轮转数组

&#x1f451;个人主页&#xff1a;啊Q闻 &#x1f387;收录专栏&#xff1a;《数据结构》 &#x1f389;前路漫漫亦灿灿 前言 消失的数字这道题目我会和大家分享三种思路。 还有一道题目是轮转数组&#xff0c;&#xff0c;也会分享三种思路&#xff0c;大…

照片和视频一键换脸,ROOP ROPE FaceFusion 中文版汉化版整合包下载安装

AI 人脸替换工具离线版下载地址&#xff1a;点击下载 AI 人脸替换工具离线版&#xff0c;将视频和图片的面孔替换为您选择的面孔。您只需要一张所需脸部的图像。没有数据集&#xff0c;就没有训练。工具包含 ROOP ROPE FaceFusion 三款人脸替换工具解压即用无需安装。 汉化整合…

【SpringBoot实战篇】注册接口

&#x1f495;&#x1f338;&#x1f340;开发接口的流程&#xff1a;明确需求 -》阅读接口文档-》思路分析-》开发-》测试&#x1f340;&#x1f338;&#x1f495; 开始前小知识-配置lombok&#x1f448;&#x1f340; lombok 在编译阶段,为实体类自动生成setter getter toSt…

教你将配置好的conda环境迁移到其它设备

文章目录 问题分析存在的方法环境要求方法步骤1. 下载conda pack2. 打包原环境3. 新设备还原环境4. 查看环境 问题分析 好不容易配置好的conda环境&#xff0c;要在另一个设备上运行&#xff0c;还要重新配置&#xff0c;好麻烦。 存在的方法 pip install -r requirement.txt …

一.表单校验

为什么要表单验证&#xff1f; 是为了减轻服务器的压力&#xff0c;让用户体验更好&#xff0c;保证输入的数据符合要求 常用的表单验证 日期格式 表单元素是否为空 用户名和密码 E-mail 地址 身份证号码 实现验证的思路 问题&#xff1a;当输入的表单数据不符合要求时&…

【在线服务,如何做网络加速】

为什么需要 ToC的业务&#xff0c;由于服务直接提供给用户使用&#xff0c;所以对服务的时延&#xff0c;平响要求比较高。如果因为跨地域的网络传输而造成较高时延&#xff0c;而用户有感的话体验不好&#xff0c;这个时候则需要做网络加速。 服务网络加速 开始之前 先考虑…