226 lines
9.8 KiB
GDScript
226 lines
9.8 KiB
GDScript
@tool
|
||
class_name Tools extends Node
|
||
|
||
## Простые часто используемые функции.
|
||
|
||
|
||
## Возвращает округлённое значение [param v] с точностью [param a].
|
||
func round_to(v: float, a: float) -> float: return snappedf(v, -a if v < 0 else a)
|
||
|
||
|
||
## Возвращает [b]true[/b], если [param val] число, [b]false[/b] - если иначе.
|
||
func type_is_num(val) -> bool: return (val is int) or (val is float)
|
||
|
||
|
||
## Возвращает значение в радианах для угла в градусах [param val].
|
||
func radians(val: float): return (val * PI) / 180.0
|
||
|
||
|
||
## Возвращает значение из байтового массива в 16 битном слове.[br]
|
||
## [i]Следует использовать встроенные в[/i] [b]PackedByteArray[/b] [i]методы вместо этой функции.[/i]
|
||
func ibytes_to_word(buf: PackedByteArray, i: int) -> int: return buf[i] + (buf[i + 1] * 256)
|
||
|
||
|
||
## Возвращает расстояние от центра координат до точки.
|
||
func hypot(x: float, y: float) -> float: return sqrt(x * x + y * y)
|
||
|
||
|
||
## Проверяет пересечение диапазона [param x1], [param x2] с диапазоном [param y1], [param y2]
|
||
func is_overlap(x1, x2, y1, y2) -> bool: return max(x1, y1) <= min(x2, y2)
|
||
|
||
|
||
## Закрывает программу если [param val] установлен в [b]false[/b][br]
|
||
## [param msg] - сообщение об ошибке
|
||
func allways(val: bool, msg: String = '<не указано>'):
|
||
if val: return
|
||
push_error('%s\n' % msg)
|
||
|
||
|
||
## Преобразует путь в виде целой строки в массив отдельных элементов пути.[br]
|
||
## [param solid] - Входная строка содержащая путь, которая имеет следующий примерный вид:[br]
|
||
## [code]SelfTest[1].DevList[2]['CurrentValue'][/code][br]
|
||
## [param paths] - Массив содержащий отдельные элементы пути.[br]
|
||
## [param tokens] - Разделители используемые во входной строке для разделения элементов пути.[br]
|
||
func map_solid_to_array(solid: String, paths: Array, tokens: Array = ['[', ']', '.']) -> void:
|
||
paths.clear()
|
||
var i_chs = []
|
||
for sep in tokens:
|
||
var i = 0
|
||
while i != -1:
|
||
i = solid.find(sep, i + 1)
|
||
i_chs.append(i)
|
||
i_chs.pop_back()
|
||
i_chs.sort()
|
||
|
||
var i0 = 0
|
||
for i1 in i_chs:
|
||
var ss: = [String()]
|
||
get_string_slice(solid, ss, i0, i1)
|
||
if ss[0].is_valid_int():
|
||
paths.append(ss[0].to_int())
|
||
else:
|
||
if ss[0]:
|
||
paths.append(ss[0])
|
||
i0 = i1 + 1
|
||
|
||
|
||
func check_index(obj, index) -> bool:
|
||
if obj is Dictionary:
|
||
if obj.has(index):
|
||
return true
|
||
elif obj is Array:
|
||
if index is int:
|
||
if (index >= 0) and (index < obj.size()):
|
||
return true
|
||
return false
|
||
|
||
|
||
## Возвращает значение поля хранящегося по указанному пути внутри объекта.[br]
|
||
## [param obj] - Объект в котором хранится поле, должен быть [b]Dictionary[/b].[br]
|
||
## [param paths] - Массив содержащий узлы пути к полю значения, включая его имя.[br]
|
||
## [param ret_val] - Массив из двух элементов, служит для получения возвращаемого значения:[br]
|
||
## [param ret_val[0]] - содержит значение искомого поля;[br]
|
||
## [param ret_val[1]] - устанвлено в [b]true[/b], если поле найдено, и установлено в [b]false[/b],
|
||
## если искомое поле не найдено.[br]
|
||
## [param ret_val[2]] - содержит индекс массива узла пути, который был не найден (имеет смысл, если
|
||
## [param ret_val[1]] установлен в [b]false[/b]
|
||
func get_value_by_path(obj, paths: Array, ret_val: Array):
|
||
var index = paths.pop_at(0)
|
||
ret_val[1] = false
|
||
if paths.size():
|
||
if check_index(obj, index):
|
||
ret_val[2] += 1
|
||
get_value_by_path(obj[index], paths, ret_val)
|
||
else:
|
||
if check_index(obj, index):
|
||
ret_val[0] = obj[index]
|
||
ret_val[1] = true
|
||
|
||
|
||
## Возвращает часть от входной строки ограниченную индексами символов.[br]
|
||
## [param s_in] - Входная строка.[br]
|
||
## [param s_out] - Массив содержащий выходную строку.[br]
|
||
## [param i0] - Индекс начала части строки.[br]
|
||
## [param i1] - Индекс конца части строки. Возвращает все символы до этого индекса.
|
||
func get_string_slice(s_in: String, s_out: Array, i0: int, i1: int):
|
||
for i in range(i0, i1):
|
||
if s_in[i] != '\'':
|
||
s_out[0] += s_in[i]
|
||
|
||
|
||
## Производит преобразование из декартовой в полярную систему координат.
|
||
func pos_calc(freq: float, aoa: float, pos0: Vector2, r0: float, r1: float, f0: float, f1: float):
|
||
var r = remap(freq, f0, f1, r0, r1)
|
||
var aoa_rad = radians(aoa) - PI / 2.0
|
||
return Vector2(cos(aoa_rad), sin(aoa_rad)) * r + pos0
|
||
|
||
|
||
## Отсекает значение до заданного диапазона.
|
||
func clip_value(val, val_min, val_max):
|
||
if val < val_min: val = val_min
|
||
if val > val_max: val = val_max
|
||
return val
|
||
|
||
|
||
## Возвращает массив с именами членов [param obj].
|
||
func get_members_names(obj: Object) -> Array:
|
||
var pl = obj.get_property_list()
|
||
var names: = Array()
|
||
for item in pl: names.append(item.name)
|
||
return names
|
||
|
||
|
||
## Проверяет, что заданный массив [param val] похож на ip-адрес,
|
||
## в формате ['0.0.0.0', 1000]
|
||
func is_array_ipaddr(val: Array):
|
||
return (val.size() == 2) \
|
||
and (val[0] is String) \
|
||
and (val[0].split('.').size() == 4) \
|
||
and (val[1] is int) \
|
||
and (0 < val[1]) \
|
||
and (val[1] < 65536)
|
||
|
||
|
||
## Преобразует декартовые координаты в полярные.[br]
|
||
## [param pos0] - координаты центра.[br]
|
||
## [param pos] - координаты точки.[br]
|
||
func map_dec_to_rad(pos0: Vector2, pos: Vector2) -> Vector2:
|
||
var x1: float = pos.x - pos0.x
|
||
var y1: float = pos.y - pos0.y
|
||
var ang: float = (atan2(y1, x1) * 180.0) / PI
|
||
return Vector2(ang + 360.0 * float(ang < 0.0), hypot(x1, y1))
|
||
|
||
|
||
## Возвращает [b]true[/b], если точка внутри заданного полигона.[br]
|
||
## [param pos] - Проверяемая точка.[br]
|
||
## [param p] - Координаты вершин полигона.[br]
|
||
func is_point_in_polygon(pos: Vector2, p: PackedVector2Array) -> bool:
|
||
var sz: = p.size()
|
||
assert(sz)
|
||
var j: int = sz - 1
|
||
var odd_nodes: int = 0
|
||
for i in sz:
|
||
if ((p[i].y < pos.y and p[j].y >= pos.y or p[j].y < pos.y and p[i].y >= pos.y) and (p[i].x <= pos.x or p[j].x <= pos.x)):
|
||
odd_nodes ^= int((p[i].x + (pos.y - p[i].y) / (p[j].y - p[i].y) * (p[j].x - p[i].x) < pos.x))
|
||
j = i
|
||
return odd_nodes > 0
|
||
|
||
|
||
## Записывает слово [param val] в элемент байтовго массива [param buf].
|
||
## расположенный по индексу [param index] (младшее слово).[br]
|
||
## [i]Следует использовать встроенные в[/i] [b]PackedByteArray[/b] [i]методы вместо этой функции.[/i]
|
||
func set_wordi(buf: PackedByteArray, index: int, val: int) -> void:
|
||
buf[index] = val & 0xff
|
||
buf[index + 1] = (val >> 8) & 0xff
|
||
|
||
|
||
## Устанавливает бит в соответствии с маской
|
||
## [param bits] - Прежние значения бит
|
||
## [param index] - Индекс нового значения бита
|
||
## [param val] - Новое значение бита
|
||
## [param mask] - Маска изменяемых бит
|
||
func set_bit(bits, index, val, mask = 0xfffffffffffffff):
|
||
bits &= mask
|
||
if val:
|
||
bits |= 1 << index
|
||
else:
|
||
bits &= ~(1 << index)
|
||
return (bits & mask)
|
||
|
||
|
||
## Устанавливает биты в соответствии с маской
|
||
## [param v] - Прежние значения бит
|
||
## [param a] - Новые значения бит
|
||
## [param m] - Маска изменяемых бит
|
||
func set_bits(v, a, m):
|
||
return (~m) & v | a & m
|
||
|
||
|
||
func fill_files_table(tbl: table, folder: String, rows_types: Array):
|
||
var dir: = DirAccess.open(folder)
|
||
if dir == null:
|
||
return
|
||
var exclude_names: = ['.', '..']
|
||
dir.list_dir_begin() # TODO: GODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547
|
||
while true:
|
||
var file: = dir.get_next()
|
||
if file == '':
|
||
break
|
||
if file in exclude_names:
|
||
continue
|
||
var file_name: = folder + file
|
||
var fd: = FileAccess.open(file_name, FileAccess.READ)
|
||
if not fd:
|
||
continue
|
||
var glob_path: = ProjectSettings.globalize_path(file_name)
|
||
var sz: = fd.get_length()
|
||
var ut: = FileAccess.get_modified_time(file_name)
|
||
fd.close()
|
||
var mt: = Time.get_datetime_dict_from_unix_time(ut)
|
||
var file_row = [file, '%d' % sz, '%d-%02d-%02d %02d:%02d' % [mt.year, mt.month, mt.day, mt.hour, mt.minute]]
|
||
tbl.add_row(rows_types)
|
||
var i_row: = tbl.get_rows_count() - 1
|
||
tbl.set_row_text(i_row, file_row)
|
||
tbl.set_node_meta(0, i_row, 'file_path', glob_path)
|
||
dir.list_dir_end()
|