【Godot 4.2】常见几何图形、网格、刻度线点求取函数及原理总结

概述

  • 本篇为ShapePoints静态函数库的补充和辅助文档。
  • ShapePoints函数库是一个用于生成常见几何图形顶点数据(PackedVector2Array)的静态函数库。
  • 生成的数据可用于_drawLine2DPolygon2D等进行绘制和显示。
  • 因为不断地持续扩展,ShapePoints函数库的函数数目在不断增加,同时涉及的图形类型也在发生变化。
  • 本篇按照一定的分类,阐述每个图形函数的原理和具体实现,以及具体使用。

注意:本篇基础内容写于2023年7月,由3篇文章汇总而成。ShapePoints函数库及其使用会单独发文贴出。本篇更接近原理讲解。


基础原理

  • 在一个平面中,确定一个直角坐标系后,平面上任意一点位置就可以用(x,y)这样的值对来表示,(x,y)可以被称为这个点的坐标。
  • 同样这个点(x,y)也可以理解为相对于坐标系原点(0,0),水平移动了x,垂直移动了y,也就是一个由原点指向(x,y)的向量。
  • 通过平面向量的加减乘除以及旋转操作,我们获得新的点的位置,一系列点的位置可以被顺序用线段连接起来,构成PolyLine(折线)或PolyGon(多边形,闭合的折线)
  • 这些点数据可以用于_drawLine2DPolygon2D等绘制和显示几何图形

基础图形

矩形

矩形最简单,计算出4个顶点就行。其运算不过是一些简单的向量加减法。
image.png

实现代码
# 返回矩形的顶点
static func rect(size:Vector2,offset:Vector2 = Vector2.ZERO) -> PackedVector2Array:
	var points:PackedVector2Array = [
		offset,
		offset + Vector2.RIGHT * size.x,
		offset + size,
		offset + Vector2.DOWN * size.y,
		offset]
	return points

正多边形

求正多边形的顶点,其本质是求圆上等分的点。可以通过向量旋转法求取。起始角度不同,图形发生相应旋转。
在这里插入图片描述

实现代码
# 返回正多边形顶点
static func regular_polygon(start_angle:int,edges:int,r:float,offset:Vector2 = Vector2.ZERO):
	var points:PackedVector2Array
	var vec  = Vector2.RIGHT.rotated(deg_to_rad(start_angle)) * r
	for i in range(edges):
		points.append(vec.rotated(2* PI/edges * i) + offset)
	return points

圆是边数很多的正多边形。问题在于这个边数计算,怎样才能保证任何半径下圆都看起来很平滑。

我的方法简单粗暴,边数直接等于2πr,也就是周长。这等于无论圆的半径是多少,它都要包含2πr个顶点。

实现代码
# 返回圆顶点
static func circle(r:float,offset:Vector2 = Vector2.ZERO):
	var points = regular_polygon(0,2 * PI * r,r,offset)
	points.append(points[0])
	return points

扇形

扇形是圆的一部分,起始和终止点都是圆心,从而组成闭合图形。

圆弧部分可以通过向量旋转求取的,具体调用弧形函数arc就可以。
image.png

实现代码
# 返回扇形顶点
# 注意start_angle和end_angle都是角度
static func sector(start_angle:int,end_angle:int,r:float):
	var points:PackedVector2Array
	
	points.append(Vector2.ZERO)
	points.append_array(arc(start_angle,end_angle,r))
	points.append(Vector2.ZERO)
	return points

弧形

弧形是扇形去掉起始点也就是圆心之后的图形。

同样为了始终保持平滑效果,绘制的点个数是与r的大小相关的,即始终绘制θ×r个点。

θ为起始角度和结束角度之间的夹角的弧度值。
image.png

实现代码
# 弧形
# 注意start_angle和end_angle都是角度
static func arc(start_angle:int,end_angle:int,r:float,offset:Vector2 = Vector2.ZERO):
	var points:PackedVector2Array
	var angle = deg_to_rad(end_angle - start_angle)
	var edges:float = ceilf(angle * r) # 要绘制的点的个数 = θ * r
	var vec  = Vector2.RIGHT.rotated(deg_to_rad(start_angle)) * r
	for i in range(edges+1):
		points.append(vec.rotated(angle/edges * i) + offset)
	return points

星形

星形是在两个半径不同的同心圆上求正多边形顶点。也是采用向量旋转法。
image.png

实现代码
# 星形
static func star(start_angle:int,edges:int,r:float,r2:float = 0,offset:Vector2 = Vector2.ZERO):
	if r2 == 0:
		r2 = r/2.0
	var points:PackedVector2Array
	# 外部半径
	var vec  = Vector2.RIGHT.rotated(deg_to_rad(start_angle)) * r
	# 内部半径
	var vec2  = Vector2.RIGHT.rotated(deg_to_rad(start_angle + 180/edges)) * r2
	for i in range(edges):
		points.append(vec.rotated(2 * PI/edges * i) + offset)
		points.append(vec2.rotated(2 * PI/edges * i) + offset)
	return points

圆角矩形

本质是在矩形四个角上绘制1/4圆弧。
image.png

实现代码
# 返回圆角矩形的顶点
# 注意:以(0,0)为几何中心
static func round_rect(size:Vector2,r1:float,r2:float,r3:float,r4:float,offset:Vector2 = Vector2.ZERO) -> PackedVector2Array:
	var points:PackedVector2Array
	points.append_array(arc(180,270,r1,Vector2(r1,r1) + offset))
	points.append_array(arc(270,360,r2,Vector2(size.x - r2,r2) + offset))
	points.append_array(arc(0,90,r3,Vector2(size.x - r3,size.y -r3) + offset))
	points.append_array(arc(90,180,r4,Vector2(r4,size.y - r4) + offset))
	points.append(Vector2(0,r1)+offset)
	return points

倒角矩形

倒角矩形跟圆角矩形很像,只是更简单了,不用在四个角上画圆弧了,而是从矩形的4个顶点变为计算8个顶点。
image.png

实现代码
# 返回倒角矩形的顶点
# 注意:以(0,0)为几何中心
static func chamfer_rect(size:Vector2,a:float,b:float,c:float,d:float,offset:Vector2 = Vector2.ZERO) -> PackedVector2Array:
	var points:PackedVector2Array = [
		Vector2(0,a) + offset,Vector2(a,0) + offset,
		Vector2(size.x-b,0) + offset,Vector2(size.x,b) + offset,
		Vector2(size.x,size.y-c) + offset,Vector2(size.x-c,size.y) + offset,
		Vector2(d,size.y) + offset,Vector2(0,size.y-d) + offset
	]
	points.append(points[0]) # 闭合
	return points

胶囊形

胶囊形的本质是两个水平或垂直方向上的半圆弧+一定的偏移距离。
限定在矩形范围内的胶囊形示意图

实现代码
# 返回胶囊形的顶点
static func capsule(size:Vector2,offset:Vector2 = Vector2.ZERO) -> PackedVector2Array:
	var points:PackedVector2Array = []
	var r:float = min(size.x,size.y)/2.0
	if size.x>size.y: # 横向
		points.append_array(arc(90,270,r,Vector2(r,r) + offset))
		points.append_array(arc(-90,90,r,Vector2(size.x-r,r) + offset))
	else: # 纵向
		points.append_array(arc(180,360,r,Vector2(r,r) + offset))
		points.append_array(arc(0,180,r,Vector2(r,size.y-r) + offset))
	points.append(points[0]) # 闭合
	return points
@tool
extends Control

func _draw():
	var size = get_rect().size
	draw_polyline(ShapePoints.capsule(size),Color.GREEN_YELLOW,1)

效果:
胶囊形演示

梭形

梭形的本质是绘制两段在X轴或Y轴上对称的圆弧。而圆弧需要的就是半径、起始角度和结束角度。

所以问题就变成了求半径和角度的问题。
梭形一侧圆弧示意图
可以知道: r 2 = d y 2 + ( r − d x ) 2 r^2 = dy^2+(r-dx)^2 r2=dy2+(rdx)2

也就是: r 2 = d y 2 + r 2 − 2 r d x + d x 2 r^2 = dy^2+r^2-2rdx + dx^2 r2=dy2+r22rdx+dx2

两侧消去 r 2 r^2 r2,就变成 2 r d x = d y 2 + d x 2 2rdx = dy^2+dx^2 2rdx=dy2+dx2

最终半径 r = ( d y 2 + d x 2 ) / 2 d x r = (dy^2+dx^2)/2dx r=(dy2+dx2)/2dx

而因为 s i n θ = d y / r sinθ = dy/r sinθ=dy/r,所以 θ = a r c s i n ( d y / r ) θ = arcsin(dy/r) θ=arcsin(dy/r)

有了半径r和二分之一的夹角θ,就可以求圆弧了,反向的圆弧也可以求出。

实现代码
# 返回梭形的顶点
# 注意:以(0,0)为几何中心
static func spindle(size:Vector2,offset:Vector2 = Vector2.ZERO) -> PackedVector2Array:
	var points:PackedVector2Array = []
	var dx:float = size.x/2.0
	var dy:float = size.y/2.0
	
	var d_max = max(dx,dy)
	var d_min = min(dx,dy)
	
	var r = (pow(d_max,2.0) + pow(d_min,2.0))/(2.0 * d_min) # 圆弧半径
	var angle = rad_to_deg(asin(d_max/r))
	
	if dx<dy:
		points.append_array(arc(180-angle,180+angle,r,Vector2(r,dy)))
		points.append(Vector2(dx,0))
		points.append_array(arc(-angle,angle,r,Vector2(-r+2*dx+1,dy)))
		points.append(points[0]) # 闭合
	else:
		points.append_array(arc(270-angle,270+angle,r,Vector2(dx,r)))
		points.append(Vector2(size.x,dy))
		points.append_array(arc(90-angle,90+angle,r,Vector2(dx,-r+2*dy+1)))
		points.append(points[0]) # 闭合
	return points
效果测试
@tool
extends Control

func _draw():
	var size = get_rect().size
	draw_polyline(ShapePoints.spindle(size),Color.GREEN_YELLOW,1)

效果:
梭形绘图演示

特殊图形

太极图

最主要的阴阳鱼,几何组成却十分简单:可以看成是一个大半圆弧和两个反向的小半圆弧连接形成的。
1875660110.jpg

函数
# 太极
static func taiji(r:float,offset:Vector2 = Vector2.ZERO) -> Dictionary:
	var dict = {
		pan = circle(r,offset), # 底部圆盘
		yin = [], # 阴鱼
		yang = [], # 阳鱼
		yin_eye = circle(r/10,Vector2(0,-r/2)+ offset), # 阴鱼眼
		yang_eye = circle(r/10,Vector2(0,r/2)+ offset), # 阳鱼眼
	}
	# 阴鱼
	dict["yin"].append_array(arc(90,270,r,offset))
	dict["yin"].append_array(arc(-90,90,r/2,Vector2(0,-r/2)+offset))
	var ac = arc(90,270,r/2,Vector2(0,r/2)+offset)
	ac.reverse()
	dict["yin"].append_array(ac)
	# 阳鱼
	dict["yang"].append_array(arc(-90,90,r,offset))
	dict["yang"].append_array(arc(90,270,r/2,Vector2(0,r/2)+offset))
	var ac2 = arc(-90,90,r/2,Vector2(0,-r/2)+offset)
	ac2.reverse()
	dict["yang"].append_array(ac2)
	return dict
绘制测试
@tool
extends Control

func _draw():
	var rect = get_rect()
	var center = rect.get_center()
	var r = rect.size.y/2
	var w = rect.size.x - 10
	var offset = center
	
	
	var taiji = ShapePoints.taiji(r,offset)
	for point in taiji["yin"]:
		draw_circle(point,0.5,Color.CHARTREUSE)
	draw_polyline(taiji["yin"],Color.AQUA,1)
	draw_polyline(taiji["yang"],Color.AQUA,1)
	draw_polyline(taiji["yin_eye"],Color.AQUA,1)
	draw_polyline(taiji["yang_eye"],Color.AQUA,1)

效果:
image.png

螺旋线

暂时还不是很完美。

函数
# 螺旋线
static func helix(start_angle:int,start_r:float,end_r:float,
			step:int =1,offset:Vector2 = Vector2.ZERO) -> PackedVector2Array:
	var points:PackedVector2Array
	var steps = end_r - start_r
	for i in range(steps):
		points.append(Vector2.RIGHT.rotated(deg_to_rad(start_angle + step * i)) * (start_r+i) + offset)
	return points
测试
@tool
extends Control

func _draw():
	var rect = get_rect()
	var center = rect.get_center()
	draw_polyline(ShapePoints.helix(0,0,rect.size.y * 2,1,center),Color.GREEN_YELLOW,2)

效果:
image.png

各种网格

矩形网格

最好是能够将网格绘制也像刻度线求取函数一样,封装成函数,通过传入参数后返回横线竖线线的集合,然后具体绘制可以在任何节点中进行。
函数化的好处还在于,你可以求取不同参数下的网格线,然后具体绘制的时候使用不同的粗细、颜色等。搭配起来可以绘制更复杂的网格线,比如心电图纸的大小格设计。

# 方形 - 网格线求取函数
static func rect_grid_lines(size:Vector2,cell_size:Vector2) -> Dictionary:
	var lines = {
		v_lines = [], # 垂直的网格线
		h_lines = []  # 水平的网格线
	}
	var v_line1 = [Vector2.ZERO,Vector2.DOWN * cell_size.y * size.y]
	var h_line1 = [Vector2.ZERO,Vector2.RIGHT * cell_size.x * size.x]
	lines["v_lines"].append(v_line1)
	lines["h_lines"].append(h_line1)
	for x in range(1,size.x+1):
		var offset_x = Vector2(cell_size.x,0) * x
		lines["v_lines"].append([v_line1[0] + offset_x,v_line1[1] + offset_x])
	for y in range(1,size.y+1):
		var offset_y = Vector2(0,cell_size.y) * y
		lines["h_lines"].append([h_line1[0] + offset_y,h_line1[1] + offset_y])
	return lines
@tool
extends Control

func _draw():
	var grid = ShapePoints.rect_grid_lines(Vector2(10,10),Vector2(50,50))
	# 绘制垂直线
	for line in grid["v_lines"]:
		draw_line(line[0],line[1],Color.GREEN_YELLOW,2)
	# 绘制水平线
	for line in grid["h_lines"]:
		draw_line(line[0],line[1],Color.GREEN_YELLOW,2)

用横竖线而不是矩形绘制的网格

绘制函数

因为_draw和draw_*之类的只能在CanvasItem类型及其子节点中使用,并且不能用于编写静态函数,所以好的办法就剩下将点、线之类的求取做成函数,而在实际的扩展节点中在基于这些求取函数编写进一步的绘制函数。

@tool
extends Control

func _draw():
	draw_grid(Vector2(10,10),Vector2(50,50))
	

# 绘制网格函数
func draw_grid(size:Vector2,cell_size:Vector2,border_color:Color = Color.GREEN_YELLOW,border_width = 1) -> void:
	var grid = ShapePoints.rect_grid_lines(size,cell_size)
	# 绘制垂直线
	for line in grid["v_lines"]:
		draw_line(line[0],line[1],border_color,border_width)
	# 绘制水平线
	for line in grid["h_lines"]:
		draw_line(line[0],line[1],border_color,border_width)

网格线求取和网格线绘制函数的好处是,你可以轻松的基于其创建复杂的网格,比如下面这样的:

@tool
extends Control

func _draw():
	draw_grid(Vector2(50,50),Vector2(10,10),Color.ORANGE)
	draw_grid(Vector2(10,10),Vector2(50,50),Color.ORANGE_RED,2)

双层网格叠加实现的网格

矩形点网格

再绘制原点网格或十字网格的时候,要的不再是一条条的线,而是网格的交点。

# 方形 - 网格点求取函数
static func rect_grid_points(size:Vector2,cell_size:Vector2) ->PackedVector2Array:
	var points:PackedVector2Array
	for x in range(size.x + 1):
		for y in range(size.y + 1):
			points.append(Vector2(x,y) * cell_size)
	return points
绘制函数

同样的我们可以在自定义控件内部定义参数化的点网格绘制函数:

# 绘制点网格函数
func draw_point_grid(size:Vector2,cell_size:Vector2,point_color:Color = Color.GREEN_YELLOW,r = 2) -> void:
	for point in ShapePoints.rect_grid_points(size,cell_size):
		draw_circle(point,r,point_color)

使用:

@tool
extends Control

func _draw():
	draw_point_grid(Vector2(10,10),Vector2(50,50),Color.GREEN_YELLOW,5)

简单的点网格绘制效果
也可以使用不同参数的多个点网格叠加:

@tool
extends Control

func _draw():
	draw_point_grid(Vector2(50,50),Vector2(10,10),Color.ORANGE)
	draw_point_grid(Vector2(10,10),Vector2(50,50),Color.ORANGE_RED,5)

双层点网格叠加效果

十字线网格

# 返回指定点为中心,给定长度的两条互相垂直线段,可以用于绘制十字坐标线
static func line_cross(position:Vector2,length:float,start_angle:int = 0) -> Array:
	# 水平线段俩端点
	var h_line = [
			Vector2.LEFT.rotated(start_angle) * length/2.0 + position,
			Vector2.RIGHT.rotated(start_angle) * length/2.0 + position,
		]
	# 水平线段俩端点
	var v_line = [
			Vector2.UP.rotated(start_angle) * length/2.0 + position,
			Vector2.DOWN.rotated(start_angle) * length/2.0 + position,
		]
	return [h_line,v_line]
绘制函数

控件内部绘制十字线网格函数:

# 绘制十字网格函数
func draw_line_cross_grid(size:Vector2,cell_size:Vector2,color:Color = Color.GREEN_YELLOW,length = 10,start_angle:int = 0):
	for point in ShapePoints.rect_grid_points(size,cell_size):
		var line_cross = ShapePoints.line_cross(point,length,start_angle)
		draw_line(line_cross[0][0],line_cross[0][1],Color.GREEN_YELLOW,1)
		draw_line(line_cross[1][0],line_cross[1][1],Color.GREEN_YELLOW,1)

使用:

@tool
extends Control

func _draw():
	draw_line_cross_grid(Vector2(10,10),Vector2(50,50))

简单的十字网格

旋转45度

因为设定了start_angle参数,所以理论上你可以任意设定十字的旋转角度,甚至将其做成动画。

@tool
extends Control

func _draw():
	draw_line_cross_grid(Vector2(10,10),Vector2(50,50),Color.GREEN_YELLOW,10,45)

45度旋转后的十字组成的网格

三角点网格

image.png
特点是:

  • 偶数行不偏移,绘制n+1个点
  • 奇数行向右半偏移,并且点数比奇数行少1
# 三角 - 网格点求取函数
static func triangle_grid_points(size:Vector2,cell_size:Vector2) ->PackedVector2Array:
	var points:PackedVector2Array
	for y in range(size.y + 1):
		if y % 2 == 0: # 偶数行
			for x in range(size.x + 1):
				points.append(Vector2(x,y) * cell_size)
		else: # 奇数行
			for x in range(size.x):
				points.append(Vector2(x,y) * cell_size + Vector2(cell_size.x/2,0))
	return points
绘制函数
# 绘制三角网格 - 点网格函数
func draw_triangle_point_grid(size:Vector2,cell_size:Vector2,point_color:Color = Color.GREEN_YELLOW,r = 2) -> void:
	for point in ShapePoints.triangle_grid_points(size,cell_size):
		draw_circle(point,r,point_color)

三角网格点网格绘制

六边形点网格

image.png
六边形网格的顶点可以在三角网格点的基础上轻松获取,规律就是:

  • 将奇偶行调换一下位置,也就是偶数行进行半偏移,而奇数行不进行偏移
  • 偶数行:(x+1) % 3 == 0时不画点
  • 奇数行:x % 3 == 1时不画点
# 六边形 - 网格点求取函数
static func hex_grid_points(size:Vector2,cell_size:Vector2) ->PackedVector2Array:
	var points:PackedVector2Array
	for y in range(size.y + 1):
		if y % 2 == 0: # 偶数行
			for x in range(size.x):
				if (x+1)% 3 != 0:
					points.append(Vector2(x,y) * cell_size + Vector2(cell_size.x/2,0))
		else: # 奇数行
			for x in range(size.x + 1):
				if x % 3 != 1:
					points.append(Vector2(x,y) * cell_size)
	return points
绘制函数
# 绘制六边形网格 - 点网格函数
func draw_hex_point_grid(size:Vector2,cell_size:Vector2,point_color:Color = Color.GREEN_YELLOW,r = 2) -> void:
	for point in ShapePoints.hex_grid_points(size,cell_size):
		draw_circle(point,r,point_color)

使用:

@tool
extends Control

func _draw():
	draw_hex_point_grid(Vector2(10,10),Vector2(50,50),Color.GREEN_YELLOW,5)

六边形网格-点网格绘制

@tool
extends Control

func _draw():
	draw_hex_point_grid(Vector2(30,30),Vector2(20,20),Color.ORANGE,2)

image.png

棋盘格

# 矩形网格 - 棋盘格矩形求取函数
static func checker_board_rects(size:Vector2,cell_size:Vector2) -> Array:
	var rects_yang:Array[Rect2]
	var rects_yin:Array[Rect2]
	for x in range(size.x):
		for y in range(size.y):
			var pos = Vector2(x,y) * cell_size
			if (x % 2 == 0 and y % 2 == 0) or (x % 2 == 1 and y % 2 == 1):
				rects_yang.append(Rect2(pos,cell_size))
			else:
				rects_yin.append(Rect2(pos,cell_size))
	return [rects_yang,rects_yin]

绘制函数

# 矩形棋盘格绘制函数
func draw_checker_board_grid(
		size:Vector2,
		cell_size:Vector2,
		yang_color:Color = Color.WHITE,
		yin_color:Color = Color.DIM_GRAY,
		draw_grid_lines:bool = false,
		border_color:Color = Color.DIM_GRAY.darkened(0.5),
		border_width:int = 1
	) -> void:
	var grid = ShapePoints.checker_board_rects(size,cell_size)
	var rects_yang:Array[Rect2] = grid[0]
	var rects_yin:Array[Rect2] = grid[1]
	for rect in rects_yang:
		draw_rect(rect,yang_color)
	for rect in rects_yin:
		draw_rect(rect,yin_color)
	if draw_grid_lines: # 绘制网格线
		draw_line_grid(size,cell_size,border_color,border_width)

使用:

@tool
extends Control

func _draw():
	draw_checker_board_grid(Vector2(9,9),Vector2(20,20))

默认样式

@tool
extends Control

func _draw():
	draw_checker_board_grid(Vector2(9,9),Vector2(20,20),Color.ORANGE_RED,Color.ORANGE,true)

自定义颜色+绘制边线

@tool
extends Control

func _draw():
	draw_checker_board_grid(Vector2(9,9),Vector2(20,20),Color.GREEN_YELLOW,Color.YELLOW,true,Color.GREEN_YELLOW.darkened(0.2),1)

自定义颜色+自定义边线颜色

刻度线

弧形刻度线

概述

在制作一些钟表、压力表以及其他一些控件时,存在弧形刻度或圆形刻度线绘制需求,为了减少重复造轮子,搞了一个函数。
它可以轻松的求取和返回指定参数的弧形刻度线集合。

# 返回指定范围的弧形刻度线起始点坐标集合
# start_angle:起始角度
# end_angle:结束角度
# steps:切分次数
# r:半径
# length:刻度线长
func arc_scale(start_angle:int,end_angle:int,steps:int,r:float,length:float) -> Array:
	var scales:Array = []
	var vec1 = (Vector2.RIGHT  * (r-length)).rotated(deg_to_rad(start_angle))
	var vec2 = (Vector2.RIGHT  * r).rotated(deg_to_rad(start_angle))
	var angle = deg_to_rad(end_angle - start_angle) # 夹角
	for i in range(steps+1):
		var line = [vec1.rotated((angle/steps) * i),vec2.rotated((angle/steps) * i)]
		scales.append(line)
	return scales

通过遍历返回的刻度线起始坐标,就可以绘制刻度线了。

绘制钟表刻度
@tool
extends Control

func _draw():
	var rect = get_rect()
	var center = rect.get_center()
	var r = rect.size.y/2
	draw_circle(center,r,Color.AZURE)
	# 绘制基础刻度
	var lines = arc_scale(-90,270,12,r,10)
	for line in lines:
		draw_line(line[0]+center,line[1]+center,Color.AQUA,2)
	
	var lines2 = arc_scale(-90,270,60,r,5)
	for line in lines2:
		draw_line(line[0]+center,line[1]+center,Color.AQUA,1)

image.png

绘制压力表刻度
@tool
extends Control

func _draw():
	var rect = get_rect()
	var center = rect.get_center()
	var r = rect.size.y/2
	draw_circle(center,r,Color.AZURE)
	
	# 最细刻度
	var lines3 = arc_scale(-(270-45),90-45,60,r,4)
	for line in lines3:
		draw_line(line[0]+center,line[1]+center,Color.AQUA,1)
	# 中刻度
	var lines2 = arc_scale(-(270-45),90-45,12,r,8)
	for line in lines2:
		draw_line(line[0]+center,line[1]+center,Color.AQUA,1)
	# 大刻度
	var lines = arc_scale(-(270-45),90-45,6,r,10)
	for line in lines:
		draw_line(line[0]+center,line[1]+center,Color.CADET_BLUE,2)

在这里插入图片描述

直线刻度

函数
# 返回指定范围的直线刻度线起始点坐标集合
func line_scale(ruler_width:float,steps:int,length:float):
	var scales:Array = []
	var vec1 = Vector2.ZERO
	var vec2 = Vector2.DOWN * length
	var space = ruler_width/steps  # 单位间隔
	for i in range(steps+1):
		var line = [vec1 + Vector2(space,0) * i,vec2 + Vector2(space,0) * i]
		scales.append(line)
	return scales
绘制直尺刻度
@tool
extends Control

func _draw():
	var rect = get_rect()
	var center = rect.get_center()
	var r = rect.size.y/2
	var w = rect.size.x - 10
	var offset = Vector2(5,5)
	
	draw_rect(rect,Color("orange").lightened(0.2))
	draw_rect(Rect2(Vector2.ZERO,Vector2(rect.size.x,20)),Color("orange").lightened(0.4))
	
	# 最细刻度
	var lines = line_scale(w,100,5)
	for line in lines:
		draw_line(line[0]+offset,line[1]+offset,Color("#444").lightened(0.5),1)
	# 最细刻度
	var lines2 = line_scale(w,20,8)
	for line in lines2:
		draw_line(line[0]+offset,line[1]+offset,Color("#444").lightened(0.4),1)
	# 最细刻度
	var lines3 = line_scale(w,10,10)
	for line in lines3:
		draw_line(line[0]+offset,line[1]+offset,Color("#444").darkened(0.4),2)

image.png

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

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

相关文章

Orbit 使用指南 03 | 与刚体交互 | Isaac Sim | Omniverse

如是我闻&#xff1a; “在之前的指南中&#xff0c;我们讨论了独立脚本&#xff08; standalone script&#xff09;的基本工作原理以及如何在模拟器中生成不同的对象&#xff08;prims&#xff09;。在指南03中&#xff0c;我们将展示如何创建并与刚体进行交互。为此&#xf…

机器学习周记(第三十周:文献阅读-SageFormer)2024.3.11~2024.3.17

目录 摘要 ABSTRACT 1 论文信息 1.1 论文标题 1.2 论文摘要 1.3 论文背景 2 论文模型 2.1 问题描述 2.2 模型信息 2.2.1 Series-aware Global Tokens&#xff08;序列感知全局标记&#xff09; 2.2.2 Graph Structure Learning&#xff08;图结构学习&#xff09; …

大数据面试题之SQL题

大数据面试题之SQL题 1.有一个录取学生人数表&#xff0c;记录的是每年录取学生人数和入学学生的学制 以下是表结构&#xff1a; CREATE TABLE admit ( id int(11) NOT NULL AUTO_INCREMENT, year int(255) DEFAULT NULL COMMENT ‘入学年度’, num int(255) DEFAULT NULL COMM…

交流互动系统|基于springboot框架+ Mysql+Java+Tomcat的交流互动系统设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java&#xff0c;ssm&#xff0c;springboot的平台设计与实现项目系统开发资源&#xff08;可…

【医学图像处理】ECAT和HRRT格式转nii格式【超简单】

之前从ADNI上下载PET数据的时候发现有许多数据的格式不是DICOM的而是ECAT或者是HRRT格式&#xff0c;这对原本就少的PET数据是血上加霜啊。 当然只使用DICOM格式的数据也会得到不少的数据&#xff0c;我一开始也是只使用DICOM格式的样本&#xff0c;后来为了得到更多的数据&a…

2024年值得创作者关注的十大AI动画创新平台

别提找大型工作室制作动画了。如今,AI平台让我们就可以轻松制作动画。从简单的文本生动画功能到复杂的角色动作,这些平台为各种类型的创作者提供了不同的功能。 AI已经有了长足的发展,现在它可以理解复杂的人类动作和艺术意图,将简单的输入转化成丰富而详细的动画。 下面…

RoketMQ主从搭建

vim /etc/hosts# IP与域名映射&#xff0c;端口看自己的#nameserver 192.168.126.132 rocketmq-nameserver1 192.168.126.133 rocketmq-nameserver2# 注意主从节点不在同一个主机上 #broker 192.168.126.132 rocketmq-master1 192.168.126.133 rocketmq-master2#broker 192.168…

HarmonyOS(鸿蒙)不再适合JS语言开发

ArkTS是鸿蒙生态的应用开发语言。它在保持TypeScript&#xff08;简称TS&#xff09;基本语法风格的基础上&#xff0c;对TS的动态类型特性施加更严格的约束&#xff0c;引入静态类型。同时&#xff0c;提供了声明式UI、状态管理等相应的能力&#xff0c;让开发者可以以更简洁、…

用Python 3 开发的摄像头拍照程序

在当今数字化的世界中&#xff0c;使用摄像头进行拍照已成为日常生活的重要组成部分。无论是用于个人用途还是专业用途&#xff0c;能够使用电脑摄像头轻松拍照都是一项有用的技能。本文将指导您使用 Python 3 编写一个简单的程序&#xff0c;让您能够使用电脑摄像头拍照并将其…

如果网络不好 如何下载huggingface上的模型

很多朋友网络不太好&#xff0c;有时候上不了huggingface这样的国外网站&#xff1b; 或者网络流量不太够&#xff0c;想要下载一些stable diffusion模型&#xff0c;或者其他人工智能的大模型的时候&#xff0c;看到动辄几个G的模型文件&#xff0c;不太舍得下载&#xff1b;…

9. 综合案例-ATM系统 (1~7节知识综合练习)

ATM系统_综合大练习 今天的任务是对之前所有的学习的知识, 进行一个综合性的大练习. 老师说的好, 键盘敲烂 这个项目我写了大量的注释给大家参考, 如果有同学是跟着我的系列学习的, 一定动手练一练. 下面的代码只要按着敲是可以直接运行起来的, 我也把完整代码上传到了CSDN上…

Fritzing 简单使用

文章目录 1 Fritzing 资源2 Fritzing 简单使用3 添加自已的元器件3.1 面包板3.1.1 新建面包板 svg 文件3.1.2 新建面包板 3.2 原理图3.3 PCB3.4 图标3.5 使用 1 Fritzing 资源 1&#xff09;官网&#xff1a; 开源的电子设计和原型平台&#xff1a;https://fritzing.org/免费开…

机试:砍树修路

问题描述 代码示例&#xff1a; //一坐标轴表示某道路&#xff0c;从0开始 到L&#xff0c;整数位置上都种有一颗树。现在该路修建地铁&#xff0c;要砍掉铁路线路上的树木。例如&#xff1a;L等于10&#xff0c;铺设4条铁路&#xff0c;坐标是1到2,2到3,2到8&#xff0c;3到…

【SpringBoot】解决数据库时间和返回时间格式不一致的问题

先看问题: 类中的属性中有Date类型的属性 数据库表中的数据: 可以看到也没问题 但是在返回实体类对象时,数据类型是这样的: 虽然数据是成功返回了,但这显然不是我们想要的结果.也不符合我们的日常使用习惯. 这个问题虽然前端,后端都能处理,但最好还是后端来进行处理.前端主…

openEuler学习总结1(仅供学习参考)

华为的openEuler内核是源于Linux。 openEuler操作系统安装流程 第一步&#xff1a;开启虚拟化 第二步&#xff1a;安装一个虚拟化软件virtualbox 第三步&#xff1a;镜像 第四步&#xff1a;配置 设置虚拟机所在的目录 把网卡类型选择成桥接网卡 挂载镜像 设置完成&#xff0…

玩转键盘鼠标,自动化你的电脑操作 —— 定时执行专家

简介 “定时执行专家”是一款功能强大的定时任务执行软件&#xff0c;除了支持常见的定时关机、重启、执行程序等功能外&#xff0c;还拥有模拟键盘按键和模拟鼠标操作功能&#xff0c;可以让你轻松实现各种自动化操作。 模拟键盘按键功能可以模拟用户的键盘输入&#xff0c;让…

BufferedOutputStream类讲解

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java IO相关知识点了&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好…

GEE错误——Line 12: xxx.size is not a function(计算列表长度出现错误)

简介 这里我们再计算研究区面积的时候出现了一个错误,这里的问题是Line 12: points8.size is not a function 主要问题是xxx不是一个数组或者对象,无法调用size方法。这里的问题是我们要获取这个对象的时候出现了问题,也就说你给函数传输的并不是一个对象,而不知道是什么…

基于Springboot+Vue+Sercurity实现的大学生健康管理平台

1.项目介绍 大学生健康档案管理系统&#xff0c;通过电子健康档案管理系统这个平台&#xff0c;可以实现人员健康情况的信息化、网络化、系统化、规范化管理&#xff0c;从繁杂的数据查询和统计中解脱出来&#xff0c;更好的掌握人员健康状况。系统的主要功能包括&#xff1a;…

7.JavaWebHTML:构建数字世界的语言和结构

目录 导语&#xff1a; 第一部分&#xff1a;Web概念与作用 1.1 Web的定义 1.2 Web的作用 1.3 JavaWeb 第二部分&#xff1a;HTML概念与内容 2.1 HTML的定义 2.2 HTML的内容 第三部分&#xff1a;HTML的作用 3.1 HTML的作用 3.2 HTML在现代Web开发中的角色 …