【Godot4.3】数学函数曲线点求取与绘制

概述

曲线可以通过描点,然后连线得来。并不是所有曲线都有数学函数,但是有表达式的数学函数,都可以用描点法求出来。

数学函数的特征属性

  • expression:String,存储表达式的字符串版本,可以用Latex数学公式形式
  • delta_x:x的间隔
  • min_x:定义域最小值
  • max_x:定义域最大值
  • 还需要指定定义域区间是否闭合,或者,有多个区间的定义域

y = kx

# 一次函数
func kx(
	k:float,               # 斜率
	b:float,               # 常数
	min_x:int,max_x:int,   # 定义域
	delta_x:float = 1.0,   # 曲线点的x增量值,越小曲线上点越多,曲线越光滑
	scale:float=10.0       # 坐标轴刻度间隔的实际像素值,默认10像素表示长度1
) -> PackedVector2Array:
	var points:PackedVector2Array
	var dx = abs(max_x - min_x)   # 水平距离
	var steps = dx/delta_x        # 曲线点个数
	for x in range(steps+1):
		var p = Vector2(min_x,k * min_x + b) + Vector2(x,k * x)
		points.append(p * scale)
	return points

测试代码:

extends Node2D


var k:float = 0   # 斜率
var b:float = 0   # 常数

func _draw() -> void:
	var points = kx(k,b,0,10)
	draw_circle(Vector2(200,200),5,Color.AQUAMARINE)
	draw_set_transform(Vector2(200,200))

	#draw_polyline(points,Color.WHITE,1)
	for p in points:
		draw_circle(p,1,Color.AQUAMARINE)

直角坐标系类Coordinate2D

为了方便数学函数曲线的绘图,不得不首先设计一个2D直角坐标系类型Coordinate2D,世上没有新鲜事,之前用绘图函数写过一个显示2D直角坐标系的控件,淹没在了测试项目中。

这次基于类,而不是控件或自定义节点的好处是,它可以有更多灵活的使用场景。

在这里插入图片描述

代码:

# 直角坐标系
class Coordinate2D:
	var scale:float = 10.0    # 以10像素为刻度1的长度
	var up_dir := Vector2.UP  # x轴正方向
	var position := Vector2() # 左飙戏原点位置
	var min_x:= -10          # x最小值
	var max_x:= 10           # x最大值
	var min_y:= -10          # y最小值
	var max_y:= 10           # y最大值
	var scale_line_length:= 6.0           # 刻度线长度
	
	func draw(canvas:CanvasItem):
		# 绘制背景颜色填充矩形
		var width = abs(max_x - min_x) * scale
		var height = abs(max_y - min_y) * scale
		var rect  = Rect2(-Vector2(width,height)/2.0,Vector2(width,height))
		canvas.draw_rect(Transform2D(0,position) * rect,Color.WHITE)
		# 绘制Xvar x1 = position + Vector2.RIGHT * scale * min_x
		var x2 = position + Vector2.RIGHT * scale * max_x
		canvas.draw_line(x1,x2,Color.BLACK,1) # 绘制Xfor i in range(abs(max_x - min_x)+1):  # 绘制X轴刻度线
			var x = x1 + Vector2.RIGHT * scale * i 
			canvas.draw_line(x + up_dir * (scale_line_length / 2.0),x - up_dir * (scale_line_length / 2.0),Color.BLACK,1)
		# 绘制Yvar y1 = position + up_dir * scale * min_y
		var y2 = position + up_dir * scale * max_y
		canvas.draw_line(y1,y2,Color.BLACK,1) # 绘制Yfor i in range(abs(max_x - min_x)+1):  # 绘制X轴刻度线
			var y = y1 + up_dir * scale * i 
			canvas.draw_line(y + Vector2.RIGHT * (scale_line_length / 2.0),y - Vector2.RIGHT * (scale_line_length / 2.0),Color.BLACK,1)

结合坐标系和数学函数进行图形绘制

extends Node2D
		
var k:float = 2   # 斜率
var b:float = 3   # 常数

func _draw() -> void:
	# 创建并绘制坐标系
	var c = Coordinate2D.new()
	c.scale = 10
	c.position = Vector2(300,300)
	c.draw(self)
	draw_set_transform(c.position)
	
	# 求函数曲线点
	var points = kx(k,b,c.min_x,c.max_x,1,c.scale)
	# 如果坐标系的Y轴正方向向上
	if c.up_dir == Vector2.UP:
		points = Transform2D.FLIP_Y *  points  # 对所有求得的点进行基于X轴的镜像翻转
	# 绘制折线
	draw_polyline(points,Color.BLACK,1)
	# 绘制曲线点
	for p in points:
		draw_circle(p,2,Color.BLACK)

绘制效果:

在这里插入图片描述

可以看到通过结合数学函数曲线求点函数和直角坐标系类,可以轻松的绘制出函数曲线的一部分。

数学曲线函数类MathCurveFuction

通过抽象出一个MathCurveFuction类,可以轻松的扩展和重写出遵循类似结构,又有自己的参数和求点方法的数学曲线函数类。

在这里插入图片描述

# 数学曲线函数类
class MathCurveFuction:
	var latex_expression:String           # 公式的LaTex公式形式字符串
	var domian:Array[PackedFloat32Array]  # 定义域(x取值范围)
	var delta_x:float = 1.0               # 曲线点的x增量值,越小曲线上点越多,曲线越光滑
	var scale:float=10.0                  # 坐标轴刻度间隔的实际像素值,默认10像素表示长度1
	
	# 获取曲线点
	func get_points() -> PackedVector2Array:
		return []
  • domian的设计,可以使用不连续的多段定义域,甚至是定义散点

基于MathCurveFuction重写的y = kx +b函数

在这里插入图片描述

# y = kx +b
class MathCurve_kx extends MathCurveFuction:
	var k:float # 斜率
	var b:float # 偏移
	var min_x:int
	var max_x:int
	
	func _init() -> void:
		self.latex_expression = "y=kx + b"
	
	# 获取曲线点
	func get_points() -> PackedVector2Array:
		var points:PackedVector2Array
		var dx = abs(max_x - min_x)   # 水平距离
		var steps = dx/delta_x        # 曲线点个数
		for x in range(steps+1):
			var p = Vector2(min_x,k * min_x + b) + Vector2(x,k * x)
			points.append(p * scale)
		return points

测试代码:

extends Node2D


func _draw() -> void:
	# 创建并绘制坐标系
	var c = Coordinate2D.new()
	c.scale = 10
	c.position = Vector2(300,300)
	c.draw(self)
	draw_set_transform(c.position)
	
	# 求函数曲线点
	var kx = MathCurve_kx.new()
	kx.k = 2
	kx.b = 3
	kx.min_x = c.min_x
	kx.max_x = c.max_x
	var points = kx.get_points()
	# 如果坐标系的Y轴正方向向上
	if c.up_dir == Vector2.UP:
		points = Transform2D.FLIP_Y *  points  # 对所有求得的点进行基于X轴的镜像翻转
	# 绘制折线
	draw_polyline(points,Color.BLACK,1)
	# 绘制曲线点
	for p in points:
		draw_circle(p,2,Color.BLACK)

创建MathCure类

通过将上文中创建的两个自定义类放入一个MathCure类内部,则可以简化类的名称,并统一由MathCure调用。

在这里插入图片描述

class_name MathCurve

# 数学曲线函数类
class Fuction:
	var latex_expression:String           # 公式的LaTex公式形式字符串
	var domian:Array[PackedFloat32Array]  # 定义域(x取值范围)
	var delta_x:float = 1.0               # 曲线点的x增量值,越小曲线上点越多,曲线越光滑
	var scale:float=10.0                  # 坐标轴刻度间隔的实际像素值,默认10像素表示长度1
	
	# 获取曲线点
	func get_points() -> PackedVector2Array:
		return []
	

# y = kx +b
class kx_b extends Fuction:
	var k:float # 斜率
	var b:float # 偏移
	var min_x:int
	var max_x:int
	
	func _init() -> void:
		self.latex_expression = "y=kx + b"
	
	# 获取曲线点
	func get_points() -> PackedVector2Array:
		var points:PackedVector2Array
		var dx = abs(max_x - min_x)   # 水平距离
		var steps = dx/delta_x        # 曲线点个数
		for x in range(steps+1):
			var p = Vector2(min_x,k * min_x + b) + Vector2(x,k * x)
			points.append(p * scale)
		return points

申明kx_b类就变成了:

var kx = MathCurve.kx_b.new()

Coordinate2D也可以放入MathCurve类中做为辅助类型使用。

var c = MathCurve.Coordinate2D.new()

内部类还是很有用的:

  • 相关类型很容易放在一起,方便创建和使用
  • 可以简化内部类的名称,以及减少全局注册类的数量,避免和减少全局类重名。
  • 使用时代码可以大大简化,类的相关性很强,使用更连贯。
extends Node2D


func _draw() -> void:
	# 创建并绘制坐标系
	var c = MathCurve.Coordinate2D.new()
	c.scale = 10
	c.position = Vector2(300,300)
	c.draw(self)
	draw_set_transform(c.position)
	
	# 求函数曲线点
	var kx = MathCurve.kx_b.new()
	kx.k = 2
	kx.b = 3
	kx.min_x = c.min_x
	kx.max_x = c.max_x
	var points = kx.get_points()
	# 如果坐标系的Y轴正方向向上
	if c.up_dir == Vector2.UP:
		points = Transform2D.FLIP_Y *  points  # 对所有求得的点进行基于X轴的镜像翻转
	# 绘制折线
	draw_polyline(points,Color.BLACK,1)
	# 绘制曲线点
	for p in points:
		draw_circle(p,2,Color.BLACK)

下一步

  • 绘制坐标系网格
  • 让坐标系可以自适应函数图像的大小
  • 使用刻度数字
  • 求鼠标在曲线上最近点,如果距离小于一定值,动态显示该点和坐标
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巽星石

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值