diff --git a/project.godot b/project.godot index 77b0bbb..d6286c6 100644 --- a/project.godot +++ b/project.godot @@ -95,6 +95,46 @@ version_control/autoload_on_startup=true enabled=PackedStringArray() +[file_customization] + +folder_colors={ +"res://addons/": "blue", +"res://modbus/": "blue", +"res://scenes/": "teal", +"res://scenes/bip/": "blue", +"res://scenes/button-flat/": "blue", +"res://scenes/button-scale/": "blue", +"res://scenes/cell/": "blue", +"res://scenes/count-all/": "blue", +"res://scenes/count-danger/": "blue", +"res://scenes/diagram/": "blue", +"res://scenes/frame-ecm-list/": "blue", +"res://scenes/frame-ecm-params/": "blue", +"res://scenes/frame-params/": "blue", +"res://scenes/frame-threats/": "blue", +"res://scenes/frame/": "blue", +"res://scenes/grid-buttons-ecm/": "blue", +"res://scenes/grid-side-panel/": "blue", +"res://scenes/pribor-afsp/": "purple", +"res://scenes/pribor-prd-k/": "purple", +"res://scenes/pribor-prd-n/": "purple", +"res://scenes/pribor-prd-v/": "purple", +"res://scenes/pribor-rtr/": "purple", +"res://scenes/pribor-uf/": "purple", +"res://scenes/pribor-uyep/": "purple", +"res://scenes/tabs-switch/": "blue", +"res://scenes/tilemap/": "blue", +"res://scenes/журнал/": "green", +"res://scenes/контроль/": "green", +"res://scenes/настройки/": "green", +"res://scenes/работа/": "green", +"res://scenes/эмс/": "green", +"res://scenes/эмс2/": "green", +"res://scripts/": "pink", +"res://shaders/": "pink", +"res://table/": "blue" +} + [global] map_server_addr=false diff --git a/scenes/button-flat/button-flat.tscn b/scenes/button-flat/button-flat.tscn index 5e1913c..955e3f4 100644 --- a/scenes/button-flat/button-flat.tscn +++ b/scenes/button-flat/button-flat.tscn @@ -13,7 +13,7 @@ shader_parameter/rotation = 0.0 shader_parameter/color = Vector4(1, 1, 1, 0.1) shader_parameter/width = 3.0 shader_parameter/speed = 0.0 -shader_parameter/disabled = true +shader_parameter/disabled = false shader_parameter/scale = Vector2(1, 1) [sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_6nup5"] @@ -64,7 +64,6 @@ texture = SubResource("PlaceholderTexture2D_6nup5") centered = false script = SubResource("GDScript_uyl5d") speed = 0.0 -disabled = true [node name="state" type="NinePatchRect" parent="."] layout_mode = 2 diff --git a/scenes/diagram/diagram.gd b/scenes/diagram/diagram.gd index 3ae62cc..a383a98 100644 --- a/scenes/diagram/diagram.gd +++ b/scenes/diagram/diagram.gd @@ -23,9 +23,9 @@ func get_strob_width() -> float: return $строб.material.get('shader_par func get_strob_band() -> float: return $строб.material.get('shader_parameter/band') * 125.0 func get_strob_radius() -> float: return $строб.material.get('shader_parameter/radius') * 125.0 -func get_radius_outter() -> float: return material.get('shader_parameter/radius_outter') -func get_radius_inner_0() -> float: return material.get('shader_parameter/radius_inner_0') -func get_radius_inner_1() -> float: return material.get('shader_parameter/radius_inner_1') +func get_radius_outter() -> float: return material.get('shader_parameter/radius_outter') +func get_radius_inner_0() -> float: return material.get('shader_parameter/radius_inner_0') +func get_radius_inner_1() -> float: return material.get('shader_parameter/radius_inner_1') func set_view_mode(val: int): material.set('shader_parameter/mode', val); func set_band_colors(val: Array): material.set('shader_parameter/ant_band_c', val) diff --git a/scenes/tilemap/tilemap.gd b/scenes/tilemap/tilemap.gd index 2445c07..ca742a9 100644 --- a/scenes/tilemap/tilemap.gd +++ b/scenes/tilemap/tilemap.gd @@ -1,173 +1,670 @@ -class_name main_tilemap extends Sprite2D +@tool +extends CanvasGroup -## Возможные состояния конечного автомата заданий. -enum TASK_STATE { - DONE, ## Выполнено. - WAIT, ## Ожидание плитки. - RUN, ## Запуск выполнения. - CHECK, ## Проверка пустого места. - ERROR ## Ошибка. -} +## Отображение карт WEB Mercator + +const MIN_ZOOM: float = 2.0 +const MAX_ZOOM: float = 19.0 +const TEST_COORDS: = [ Vector2(0, 0), Vector2(1, 0), Vector2(0, 1), Vector2(1, 1) ] +var test_coords_i: = 3 -## Координаты для проверок [lng, lat]. -const TEST_DATA: = { - 'Калиниград': [ 20.51095, 54.70649 ], - 'Москва': [ 37.6156, 55.7522 ], - 'Нью-Йорк': [ -74.006, 40.7143 ], - 'Сидней': [ 151.20732, -33.8678500 ], - 'Таганрог': [ 38.8969, 47.2362 ], - 'Хельсинки': [ 24.9354, 60.1695 ], - 'Азорские о-ва': [ 28.3996582, 38.5397568 ], - 'Мехико': [ -99.12766, 19.42847 ], - 'Белое море': [ 68.136606, 42.045546 ], - 'море Беллинсгаузена': [ -72.406587, -74.857997 ], - 'Фиджи': [ 178.065032, -17.713371 ], - 'Ноль': [ 0.0, 0.0 ], - 'Точка1': [ 170.0, 0.0 ], - 'Точка2': [-170.0, 0.0 ], - 'Море' : [ 19.4, 54.91 ] } +#http://10.1.1.220/osm_tiles/0/0/0.png +#https://tile.openstreetmap.org/{z}/{x}/{y}.png +@export var base_url: String = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png': + set(v): + if base_url != v: + base_url = v + _clean_all() + queue_redraw() + +@export var zoom_step: float = 0.025 +@export var tile_width: float = 256.0 +@export var tile_height: float = 256.0 +@export_range(1, 1000, 1) var max_concurrent_requests:int = 5 +@export_range(100, 100000, 1) var max_cached_tiles: int = 200 +@export_range(0, 25, 1) var max_zoom_level: int = 21 +@export_color_no_alpha() var back_color: = Color.BLACK +@export var animation_time: float = 1.0 -const MAP_MODES: = { - 0: 'application/config/map_image_size_rto', - 1: 'application/config/map_image_size_rls' } +@export var size: Vector2i = Vector2i(400, 400): + set(v): + $canvas.size = v + _on_resize() + queue_redraw() + get: + return $canvas.size -const MAP_RELATIVE_RADIUSES: = { - 0: 0.39, - 1: 0.466 -} +@export var ax: float = 0.0: + set(v): + _xyz.x = v + queue_redraw() + get: + return _xyz.x -var map_image: Image ## Изображение с картой. -var map_tile_state: TASK_STATE ## Текущее состояние конечного автомата плитки. -var map_bcs: Vector3 ## Яркость, контраст насыщенность. -var map_server_addr: String ## Адрес сервера изображений карт в проекции Меркатора. -var map_tile_format: String ## -var map_tile_stack: Array ## Стек для хранения плиток карты. -var map_request: HTTPRequest ## Обработчик запросов http. -var map_zoom: int ## Уровень детализации карты. -var map_tile: Rect2i ## Область одной плитки: [x-координата области, y-координата области, x-индекс плитки, y-индекс плитки]. -var map_mode: int ## Режим карты: 0 - приоритет РТО, 1 - приоритет РЛС. -var prev_tiles_dic: Dictionary ## -var lnglat: Array ## Текущие координаты. -var root_rect: Rect2i +@export var ay: float = 0.0: + set(v): + _xyz.y = v + queue_redraw() + get: + return _xyz.y + + +@export var az: float = 0.0: + set(v): + _xyz.z = v + queue_redraw() + get: + return _xyz.z + + +var _xyz: Vector3 = Vector3(0, 0, MIN_ZOOM) +var _cache: Dictionary = {} +var _queue: Dictionary = {} +var _dragging: bool = false +var _drag_pos: Vector2 = Vector2.ZERO +var _last_cache_check: float = 0 +var _rollover: bool = false +var _marks: Dictionary = {} +var _req_count: = 0 +var image: = Image.new() +var image_load_proc: Callable +var now: = 0 +var spr_mask: = Sprite2D.new() +var visible_marks_count: = 0 +var mouse_wheel_count: = 0 +var map_server_available: = false + + +## Вызывается после того, как загрузка плиток завершена +signal load_completed() + +## Вызывается при запуске загрузки плиток +signal load_started() + +## Вызывается при получении ответа от сервера карт +signal map_server_online() + +## Вызывается при отсутствии ответа от сервера карт +signal map_server_offline() + +const image_load_proc_names = { + 'png': 'load_png_from_buffer', + 'jpg': 'load_jpg_from_buffer', + 'tga': 'load_tga_from_buffer', + 'webp': 'load_webp_from_buffer' } + + +@export_enum('png', 'jpg', 'jpeg', 'tga', 'webp') var image_type: String = 'png': + set(v): image_load_proc = Callable(image, image_load_proc_names[v]) + func _ready(): - lnglat = TEST_DATA['Таганрог'] - map_tile_state = TASK_STATE.DONE - map_request = HTTPRequest.new() - add_child(map_request) - map_request.request_completed.connect(self._map_request_completed) - map_bcs = ProjectSettings.get_setting('application/config/map_bcs', Vector3(1.0, 1.0, 1.0)) - map_server_addr = ProjectSettings.get_setting('application/config/map_server_addr') - map_tile_format = ProjectSettings.get_setting('application/config/map_tile_format', '%s/%d/%d/%d.png') - var map_image_size = ProjectSettings.get_setting(MAP_MODES[0], Vector2i(512, 512)) - var map_tile_size = ProjectSettings.get_setting('application/config/map_tile_size', 256) - map_image = Image.create(map_image_size.x, map_image_size.y, true, Image.FORMAT_RGB8) - map_tile = Rect2i(0, 0, map_tile_size, map_tile_size) - signaller.connect('zoom_changed', Callable(self, '_on_zoom_changed')) - signaller.connect('map_mode_changed', Callable(self, '_on_map_mode_changed')) - log.info('Адрес сервера поставщика карт: \"%s\"' % map_server_addr) - log.info('Модуль географической карты готов') + $canvas.connect('mouse_entered', func(): _rollover = true) + $canvas.connect('mouse_exited', func(): _rollover = false) + _on_resize() + image_type = 'png' + spr_mask.name = StringName('spr_mask') + spr_mask.material = CanvasItemMaterial.new() + spr_mask.material.blend_mode = CanvasItemMaterial.BLEND_MODE_SUB + spr_mask.add_to_group('gui_items') + $canvas.add_child(spr_mask) -func _on_map_mode_changed(mode: int) -> void: - var map_image_size = ProjectSettings.get_setting(MAP_MODES[mode]) - material.set('shader_parameter/radius', MAP_RELATIVE_RADIUSES[mode]) - map_image.fill(Color(0, 0, 0, 0)) - map_image.resize(map_image_size.x, map_image_size.y) - map_tile_state = TASK_STATE.DONE - map_mode = mode - var tile: = mercator.create_tile(lnglat[0], lnglat[1], map_zoom) - map_tile_stack.clear() - map_tile_stack.append(tile) - prev_tiles_dic.clear() +func _on_resize() -> void: + var img_mask: = Image.create($canvas.size.x, $canvas.size.y, false, Image.FORMAT_RGBA8) + var c = $canvas.size / 2.0 + var r = min($canvas.size.x, $canvas.size.y) / 2.0 + var color = Color.WHITE + var sz = img_mask.get_size() + for x in sz.x: + for y in sz.y: + var dist = c.distance_to(Vector2(x, y)) + color.a = smoothstep(r - 2.0, r + 2.0, dist) + img_mask.set_pixel(x, y, color) + spr_mask.texture = ImageTexture.create_from_image(img_mask) + spr_mask.position = $canvas.position + $canvas.size / 2.0 -func mark_image_center(image: Image, color: Color = Color.RED): - var c = image.get_size() / 2 - map_image.set_pixel(c.x - 1, c.y - 1, color) - map_image.set_pixel(c.x - 1, c.y, color) - map_image.set_pixel(c.x, c.y - 1, color) - map_image.set_pixel(c.x, c.y, color) +## Создаёт новую отметку из координат экрана +func add_mark_from_screen_pos(id: int, x: float, y: float, mark: Node): + if _marks.has(id): + return false + var wrld_pos = screen_to_world(_xyz, x, y) + var p = world_to_tile(wrld_pos.x, wrld_pos.y, _xyz.z) + var tile_index = xyz_to_idx(p.x, p.y, _xyz.z) + var dot = { 'position': wrld_pos, 'tile_index': tile_index, 'mark': mark } + _marks[id] = dot + add_child(mark) + fix_mark_position(dot) + return true -func _on_zoom_changed(zoom: int): - if map_tile_stack.size() != 0: +func get_marks_count(): return _marks.size() + + +func delete_mark(id: int): + if not _marks.has(id): + return false + _marks[id].mark.queue_free() + _marks.erase(id) + queue_redraw() + + +## Создаёт новую отметку из долготы и широты +func add_mark_from_lon_lat(id: int, lon: float, lat: float, mark: Node, gap_size: float = 32): + if _marks.has(id): + return false + var wrld_pos = lonlat_to_world(lon, lat) + var scr_pos = world_to_screen(wrld_pos.x, wrld_pos.y) + mark.set_global_position(scr_pos) + _marks[id] = {'position': wrld_pos, 'mark': mark, 'gap_size': gap_size } + add_child(mark) + queue_redraw() + return true + + +## Возвращает новую позицию для точки, если она выходит из поля видимости +func fix_mark_position(dot): + if is_mark_visible(dot): return - map_zoom = zoom - var tile: = mercator.create_tile(lnglat[0], lnglat[1], zoom) - map_tile_stack.clear() - map_tile_stack.append(tile) - prev_tiles_dic.clear() + var v = 2 * tile_width * pow(2, _xyz.z) + if dot.position.x < _xyz.x - $canvas.size.x / v: + dot.position.x = _xyz.x - $canvas.size.x / v + elif dot.position.x > _xyz.x + $canvas.size.x / v: + dot.position.x = _xyz.x + $canvas.size.x / v -func img_tile_to_map_image(image: Image, dst_pos: Vector2i): - image.adjust_bcs(map_bcs.x, map_bcs.y, map_bcs.z) - map_image.blit_rect(image, map_tile, dst_pos) +func is_mark_visible(dot): + var screen_pos = world_to_screen(dot.position.x, dot.position.y) + return screen_pos.x > 0 and screen_pos.x < $canvas.size.x -func _process(_delta: float): - if (map_tile_state == TASK_STATE.DONE) and map_tile_stack.size(): - var tile: mercator.Tile = map_tile_stack[-1] - var tile_addr: = map_tile_format % [map_server_addr, tile.z, tile.x , tile.y] - var rc = map_request.request(tile_addr) - if rc == OK: - map_tile_state = TASK_STATE.WAIT +func _input(e): + var lmp = get_local_mouse_position() + if not _rollover: + return + if e is InputEventMouseButton: + if e.pressed: + if e.button_index == MOUSE_BUTTON_WHEEL_DOWN: + mouse_wheel_count -= 1 + queue_redraw() + elif e.button_index == MOUSE_BUTTON_WHEEL_UP: + mouse_wheel_count += 1 + queue_redraw() + + if e.button_index == MOUSE_BUTTON_LEFT: + _dragging = e.pressed + _drag_pos = screen_to_world(_xyz, lmp.x, lmp.y) + + + elif e is InputEventMouseMotion: + if _dragging: + var wp = screen_to_world(_xyz, lmp.x, lmp.y) + var diff = _drag_pos - wp + _xyz.x += diff.x + _xyz.y += diff.y + queue_redraw() + + +## Возвращает true, если видна область за краем карты +func is_border_visible(): + var z = min(max_zoom_level, _xyz.z) + return screen_to_tile(0, 0, z).y != 0 + + +func multiply_zoom(multiplier: float, pivot: Vector2): + var p1: = screen_to_world(_xyz, pivot.x, pivot.y) + _xyz.z = clampf(_xyz.z * multiplier, MIN_ZOOM, MAX_ZOOM) + var p2: = screen_to_world(_xyz, pivot.x, pivot.y) + _xyz.x -= p2.x - p1.x + _xyz.y -= p2.y - p1.y + + +func set_zoom(zoom: float, pivot=null): + if pivot == null: + pivot = $canvas.size / 2.0 + + var p1: = screen_to_world(_xyz, pivot.x, pivot.y) + _xyz.z = clampf(zoom, MAX_ZOOM, MIN_ZOOM) + var p2: = screen_to_world(_xyz, pivot.x, pivot.y) + _xyz.x -= p2.x - p1.x + _xyz.y -= p2.y - p1.y + + +func _draw(): + + var lmp = get_local_mouse_position() + if mouse_wheel_count > 0: + multiply_zoom(1.0 + zoom_step, lmp) + mouse_wheel_count -= 1 + elif mouse_wheel_count < 0: + multiply_zoom(1.0 - zoom_step, lmp) + mouse_wheel_count += 1 + + var z = min(max_zoom_level, _xyz.z) + var t1 = screen_to_tile(0, 0, z) + var t2 = screen_to_tile($canvas.size.x, $canvas.size.y, z) + for tx in range(t1.x, t2.x + 1): + for ty in range(t1.y, t2.y + 1): + _draw_tile(tx, ty, z) + + var n = pow(2, z) + var dx = n * tile_width + var c = $canvas.size / 2.0 + var r = min($canvas.size.x, $canvas.size.y) / 2.0 + visible_marks_count = 0 + for dot in _marks.values(): + var pos = world_to_screen(dot.position.x, dot.position.y) + if pos.x >= 0: + pos.x = fmod(pos.x, dx) else: - log.error('%s: Невозможно сформировать запрос HTTP' % self) - map_tile_state = TASK_STATE.ERROR - elif map_tile_state == TASK_STATE.CHECK: - var root_tile: = mercator.Tile.new(root_rect.size.x, root_rect.size.y, map_zoom) - var neighbors_tiles = mercator.get_perimetr_tiles(root_tile, (map_image.get_size().x / map_tile.size.x) / 2) - var neighbors_tiles_dic = {} - for tile in neighbors_tiles: - neighbors_tiles_dic[[tile.x, tile.y]] = false - if neighbors_tiles_dic != prev_tiles_dic: - for tile in neighbors_tiles: - tile.is_root = false - map_tile_stack.push_front(tile) - prev_tiles_dic[[tile.x, tile.y]] = false - else: - if map_tile_stack.size() == 0: - signaller.emit_signal('zoom_complete', map_zoom) - map_tile_state = TASK_STATE.DONE + pos.x = -pos.x - 1 + pos.x = int(dx - 1) - fmod(pos.x, dx) + dot.mark.position = pos + dot.mark.visible = c.distance_to(pos) < (r - dot.gap_size) + visible_marks_count += int(dot.mark.visible) -func _map_request_completed(result, response_code, headers, data): +func _clean_all(): + _queue.clear() + _cache.clear() + _marks.clear() + _req_count = 0 + for c in get_children().filter(func (item): return not item.is_in_group('gui_items')): + remove_child(c) + c.queue_free() + + +# get the tile from queue with the newest timestamp +func get_next_in_queue(): + var tile = null + for idx in _queue: + var t = _queue.get(idx) + if not tile or t.t > tile.t: + tile = t + return tile + + +# delete oldest tiles from cache +func _clean_cache(): + var overflow = _cache.size() - max_cached_tiles + if overflow <= 0: + return + + var vals = _cache.values() + vals.sort_custom(func(t1, t2): return t1.t < t2.t) + for i in overflow: + _cache.erase(vals[i].i) + + +func _process(delta): + now += delta + + # if cache is overflowing - clean it + if not _last_cache_check or now - _last_cache_check > 5: + _last_cache_check = now + _clean_cache() + + while not _queue.is_empty() and (_req_count < max_concurrent_requests): + + var tile = get_next_in_queue() + if not tile: + break + + var req = HTTPRequest.new() + _req_count += 1 + req.set_meta('tile', tile) + add_child(req) + req.name = NodePath(str(tile.i)) + + _queue.erase(tile.i) + req.request_completed.connect(_response.bind(req, tile)) + + if req.request(tile.url) != OK: + tile.t = now + remove_child(req) + req.queue_free() + _req_count -= 1 + push_error('ошибка: запрос плитки \"%s\" не отправлен' % tile.url) + if _queue.is_empty(): + emit_signal('load_completed') + + +# result, response_code, headers, body +func _response(result, code, headers: Array, body: PackedByteArray, req, tile: Tile): + + _req_count -= 1 + remove_child(req) + req.queue_free() + if result != HTTPRequest.RESULT_SUCCESS: - log.error('%s: Запрос не обработан. result: \"%s\", response_code: \"%s\", headers: \"%s\"' % [self, result, response_code, headers]) - map_tile_state = TASK_STATE.ERROR + if map_server_available: + map_server_available = false + emit_signal('map_server_offline') + _queue[tile.i] = tile return - var img_tile = Image.new() - var error = img_tile.load_png_from_buffer(data) - if error != OK: - log.error('%s: Полученые данные невозможно использовать для формироания изображения. result: \"%s\", response_code: \"%s\", headers: \"%s\"' % [self, result, response_code, headers]) - map_tile_state = TASK_STATE.ERROR + if not map_server_available: + map_server_available = true + emit_signal('map_server_online') + + if code == 404: + push_error('ошибка: на сервере нет \"%s\"' % tile.url) return - var tile = map_tile_stack.pop_back() - map_tile_state = TASK_STATE.CHECK + if Error.OK != image_load_proc.call(body): + push_error('ошибка: не удалось получить изображение (неверный формат?) из \"%s\"' % tile.url) + return - if tile.is_root: - # Отображение корневой плитки - map_image.fill(Color(0, 0, 0, 0)) - var bounds: = mercator.bounds(tile) - var lng_c = (bounds.east + bounds.west) / 2.0 - var lat_c = (bounds.north + bounds.south) / 2.0 - var sx: float = float(map_tile.size.x) / (bounds.east - bounds.west) * (lng_c - tile.lng) + map_tile.size.x / 2.0 # Cмещение в пискселях центра плитки от заданной точки по горизонтали. - var sy: float = float(map_tile.size.y) / (bounds.north - bounds.south) * (lat_c - tile.lat) - map_tile.size.y / 2.0 # Cмещение в пискселях центра плитки от заданной точки по вертикали. - var dst_pos: Vector2i = Vector2i(int(sx), int(-sy)) + map_tile.size * map_mode - img_tile_to_map_image(img_tile, dst_pos) - root_rect = Rect2i(dst_pos.x, dst_pos.y, tile.x, tile.y) + # create texture from image and add it to the cache + tile.texture = ImageTexture.create_from_image(image) + _cache[tile.i] = tile + + # redraw the map + queue_redraw() + + +func _draw_subtile(tx: int, ty: int, tz: int, origx: int, origy: int, origz: float) -> bool: + var subtile = get_tile(tx, ty, tz) + if not subtile: + return false + + var p1 = tile_to_screen(origx, origy, origz) + var p2 = tile_to_screen(origx + 1, origy + 1, origz) + var x1 = tile_to_screen(tx, ty, tz) + var x2 = tile_to_screen(tx + 1, ty + 1, tz) + + var xdiff = x2.x - x1.x + var xrat1 = (p1.x - x1.x) / xdiff + var xrat2 = (p2.x - x1.x) / xdiff + var xwidth = xrat2 - xrat1 + + var ydiff = x2.y - x1.y + var yrat1 = (p1.y - x1.y) / ydiff + var yrat2 = (p2.y - x1.y) / ydiff + var yheight = yrat2 - yrat1 + + var rect = Rect2(xrat1 * tile_width, yrat1 * tile_height, xwidth * tile_width, yheight * tile_height) + if subtile.texture: + draw_texture_rect_region(subtile.texture, Rect2(p1, p2 - p1), rect) + return true + return false + + +func _draw_tile(tx: int, ty: int, z: float): + var tz = floor(z) + var p1 = tile_to_screen(tx, ty, z) + var p2 = tile_to_screen(tx + 1, ty + 1, z) + var tile = get_tile(tx, ty, tz) + if tile: + if tile.texture: + draw_texture_rect(tile.texture, Rect2(p1, p2 - p1), false, Color.WHITE, false) + else: + var zzz = tz + var txx = tx + var tyy = ty + while zzz > 1: + zzz -= 1 + txx = floor(txx / 2.0) + tyy = floor(tyy / 2.0) + if _draw_subtile(txx, tyy, zzz, tx, ty,z): + break + + +## convert lon/lat to world coords +func lonlat_to_world(lon: float, lat: float) -> Vector2: + var x = lon / 180.0 + var latsin = sin(deg_to_rad(lat) * sign(lat)) + var y = (sign(lat) * (log((1.0 + latsin) / (1.0 - latsin)) / 2.0)) / PI + return Vector2(x, y) + + +## convert lon/lat to screen coords +func lonlat_to_screen(lon: float, lat: float) -> Vector2: + var w = lonlat_to_world(lon, lat) + return world_to_screen(w.x, w.y) + + +## convert lon/lat to tile coords +func lonlat_to_tile(lon: float, lat: float, z: float) -> Vector2: + var w = lonlat_to_world(lon,lat) + return world_to_tile(w.x, w.y, z) + + +## convert world coords to lon/lat +func world_to_lonlat(wx: float, wy: float) -> Vector2: + var lon = wx * 180.0 + var lat = rad_to_deg(atan(sinh(wy * PI))) + return Vector2(lon, lat) + + +## convert screen coords to lon/lat +func screen_to_lonlat(sx: float, sy: float) -> Vector2: + var w = screen_to_world(_xyz, sx, sy) + return world_to_lonlat(w.x, w.y) + + +## convert tile coords to lon/lat +func tile_to_lonlat(tx: float, ty: float, tz: float) -> Vector2: + var w = tile_to_world(tx, ty, tz) + return world_to_lonlat(w.x, w.y) + + +## convert screen coords to world coords +func screen_to_world(xyz: Vector3, sx: float, sy: float) -> Vector2: + var n = pow(2.0, xyz.z) + var span_w = n * tile_width + var span_h = n * tile_height + var px = sx - $canvas.size.x / 2 + span_w / 2 + var py = sy - $canvas.size.y / 2 + span_h / 2 + var xr = px / span_w + var yr = py / span_h + var x = (xr * 2.0 - 1.0) + xyz.x + var y = ((-yr * 2.0) + 1.0) + xyz.y + return Vector2(x, y) + + +## convert screen coords to tile coords +func screen_to_tile(sx: float, sy: float, z: float) -> Vector2: + var world = screen_to_world(_xyz, sx, sy) + return world_to_tile(world.x, world.y, z) + + +## convert tile coords to screen coords +func tile_to_screen(tx: float, ty: float, tz: float) -> Vector2: + var w = tile_to_world(tx, ty, tz) + return world_to_screen(w.x, w.y) + + +## convert tile coords to world coords +func tile_to_world(tx: float, ty: float, tz: float) -> Vector2: + var n = pow(2.0, floor(tz)) + var x = (tx / n) * 2.0 - 1.0 + var y = -((ty / n) * 2.0 - 1.0) + return Vector2(x, y) + + +## convert world coords to tile coords +func world_to_tile(wx: float, wy: float, z: float) -> Vector2: + var n: = pow(2.0, floor(z)) + var tx: = ((wx + 1.0) / 2.0) * n + var ty: = ((-wy + 1.0) / 2.0) * n + tx = floor(tx) + ty = floor(ty) + return Vector2(tx, ty) + + +## convert world coords to screen coords +func world_to_screen(wx: float, wy: float) -> Vector2: + var n: = pow(2.0, _xyz.z) + var w: = n * tile_width + var h: = n * tile_height + var xr: = ((wx - _xyz.x) + 1.0) / 2.0 + var yr: = (-(wy - _xyz.y) + 1.0) / 2.0 + var x = w * xr - w / 2.0 + $canvas.size.x / 2.0 + var y = h * yr - h / 2.0 + $canvas.size.y / 2.0 + return Vector2(x, y) + + +## get the tile index from xyz tile coords +func xyz_to_idx(x: float, y: float, z: float) -> int: + var i: = (pow(4.0, z) - 1.0) / 3.0 + var n: = pow(2.0, z) + return int(i + (y * n + x)) + + +func get_tile(x: int, y: int, z: int, create: bool = true) -> Tile: + + var n = pow(2, z) + if x >= 0: + x %= int(n) else: - # Отображение соседних плиток - var delta: = Vector2i(tile.x - root_rect.size.x, tile.y - root_rect.size.y) - var dst_pos: Vector2i = root_rect.position + delta * map_tile.size - img_tile_to_map_image(img_tile, dst_pos) - mark_image_center(map_image) - texture = ImageTexture.create_from_image(map_image) + x = -x - 1 + x = int(n - 1) - (x % int(n)) + # out of bounds + if z < 0 or y < 0 or y >= n: + return null + + # get tile index + var idx = xyz_to_idx(x, y, z) + var tile = null + + # retrieve from current requests... + var req = get_node_or_null(NodePath(str(idx))) + if req: + tile = req.get_meta('tile') + tile.t = now + return tile + + # retrieve from cache + tile = _cache.get(idx) + if tile: + tile.t = now + return tile + + # retrieve from queue + tile = _queue.get(idx) + if tile: + tile.t = now + return tile + + # create a new tile - add it to queue + var queue_size: = _queue.size() + if create and (not $canvas/player.is_playing()): + tile = Tile.new(idx, x, y, z) + tile.url = base_url.replace('{x}', str(x)).replace('{y}', str(y)).replace('{z}', str(z)) + tile.t = now + _queue[idx] = tile + + if queue_size == 0 and _queue.size(): + emit_signal('load_started') + + return tile + + +func lonlat_to_dms(lon: float, lat: float) -> String: + var pf = 'N' if lat >= 0 else 'S' + lat = abs(lat) + var deg = floor(lat) + var minute = (lat - deg) * 60 + var second = (minute - floor(minute)) * 60 + var text = ('%02d' % deg) + ('%02d' % floor(minute)) + ('%02d' % floor(second)) + pf + ' ' + + pf = 'E' if lon >= 0 else 'W' + lon = abs(lon) + deg = floor(lon) + minute = (lon - deg) * 60 + second = (minute - floor(minute)) * 60 + text += ('%02d' % deg) + ('%02d' % floor(minute)) + ('%02d' % floor(second)) + pf + + return text + + +class Tile: + var i: int = 0 + var x: int = 0 + var y: int = 0 + var z: int = 0 + var t: int = 0 + var url: String = '' + var texture: Texture2D = null + + func _init(i0: int, x0: int, y0: int, z0: int): + self.i = i0 + self.x = x0 + self.y = y0 + self.z = z0 + + +func haversine_m(lat1, lon1, lat2, lon2): + # Радиус Земли в метрах + var R: = 6372795.0 + # Преобразование градусов в радианы + var rlat1: = deg_to_rad(lat1) + var rlon1: = deg_to_rad(lon1) + var rlat2: = deg_to_rad(lat2) + var rlon2: = deg_to_rad(lon2) + var cl1: = cos(rlat1) + var cl2: = cos(rlat2) + var sl1: = sin(rlat1) + var sl2: = sin(rlat2) + var delta: = rlon2 - rlon1 + var cdelta: = cos(delta) + var sdelta: = sin(delta) + # вычисления длины большого круга + var y: = sqrt(pow(cl2 * sdelta, 2) + pow(cl1 * sl2 - sl1 * cl2 * cdelta, 2)) + var x: = sl1 * sl2 + cl1 * cl2 * cdelta + var ad: = atan2(y, x) + var dist: = ad * R + return dist + + +func remove_closest_dot(screen_pos): + var closest_dot = find_at_screen_position(screen_pos) + if not closest_dot: + return + remove_child(closest_dot.mark) + closest_dot.mark.queue_free() + _marks.erase(closest_dot) + + +func find_at_screen_position(screen_pos: Vector2): + var closest_dot = null + var closest_l = null + var closest_distance = INF + for mark in _marks.values(): + var dot_screen_pos = world_to_screen(mark.position.x, mark.position.y) + var distance = dot_screen_pos.distance_to(screen_pos) + if distance < closest_distance: + closest_distance = distance + closest_dot = mark + return closest_dot + + +func clear() -> void: + _clean_all() + queue_redraw() + + +func set_coordinates(lon: float, lat: float): + var anim: Animation = $canvas/player.get_animation('goto') + var wrld_pos = lonlat_to_world(lon, lat) + + anim.track_insert_key(0, 0, _xyz.x) + anim.track_insert_key(1, 0, _xyz.y) + + anim.track_insert_key(0, animation_time, wrld_pos.x) + anim.track_insert_key(1, animation_time, wrld_pos.y) + + anim.length = animation_time + $canvas/player.play('goto') + + +func blend_mask(val: bool) -> void: + spr_mask.visible = val + queue_redraw() diff --git a/scenes/tilemap/tilemap.tscn b/scenes/tilemap/tilemap.tscn index 406784f..b67f330 100644 --- a/scenes/tilemap/tilemap.tscn +++ b/scenes/tilemap/tilemap.tscn @@ -1,14 +1,26 @@ -[gd_scene load_steps=4 format=3 uid="uid://bbq6nklh36yij"] +[gd_scene load_steps=3 format=3 uid="uid://ceknxxfu1loo5"] -[ext_resource type="Script" path="res://scenes/tilemap/tilemap.gd" id="1_mkfgj"] -[ext_resource type="Shader" path="res://shaders/tilemap.gdshader" id="1_tsdui"] +[ext_resource type="Script" path="res://scenes/tilemap/tilemap.gd" id="1_l64tp"] -[sub_resource type="ShaderMaterial" id="ShaderMaterial_pmqvj"] -resource_local_to_scene = true -shader = ExtResource("1_tsdui") -shader_parameter/radius = 0.39 +[sub_resource type="CanvasTexture" id="CanvasTexture_58cl1"] -[node name="tilemap" type="Sprite2D"] -material = SubResource("ShaderMaterial_pmqvj") -position = Vector2(560, 560) -script = ExtResource("1_mkfgj") +[node name="tilemap" type="CanvasGroup" groups=["gui_items"]] +script = ExtResource("1_l64tp") +az = 2.0 + +[node name="canvas" type="TextureRect" parent="." groups=["gui_items"]] +clip_contents = true +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_right = 400.0 +offset_bottom = 400.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +texture = SubResource("CanvasTexture_58cl1") + +[node name="player" type="AnimationPlayer" parent="canvas" groups=["gui_items"]] + +[connection signal="resized" from="canvas" to="." method="_on_resize"] diff --git a/scenes/работа/работа.gd b/scenes/работа/работа.gd index 59ee497..98c701d 100644 --- a/scenes/работа/работа.gd +++ b/scenes/работа/работа.gd @@ -73,9 +73,14 @@ const fs_freq: Dictionary = { 5: [4400.0, 5200.0], 6: [5200.0, 6000.0] } -var fs_band = fs_band_rto # Текущий режим блоков ФС по частоте -var fs_selected: Dictionary # Словарь выбранных секторов -var fs_active: Dictionary # Словарь секторов назначенных на подавление + +## Размеры карты для разных режимов РТО и РЛС соответственно +const map_sizes = [ + Rect2i(360, 360, 400, 400), + Rect2i(82, 82, 955, 955)] +var fs_band = fs_band_rto ## Текущий режим блоков ФС по частоте +var fs_selected: Dictionary ## Словарь выбранных секторов +var fs_active: Dictionary ## Словарь секторов назначенных на подавление ## Состояние перетаскивания. enum DragFSM { @@ -88,9 +93,10 @@ enum DragFSM { enum BtnSel { SECTOR, ## В процессе. THREAT, ## Не активно, ожидание. - FS ## Выключено. + FS ## Выключено. } + ## Обработчик сигнала изменения списка целей. func on_threats_resized(threats: Dictionary): $count_all_pad/count_all.text = '%02d' % threats.size() @@ -102,13 +108,13 @@ func on_threat_selected(th, ecm_btns): on_rto_threat_sel(th.id, ecm_btns) ## Обработчик нажатия на кнопку вид. func on_button_view_toggled(toggled: bool): signaller.emit_signal('map_mode_changed', int(toggled)) - if toggled: - fs_band = fs_band_rls - else: - fs_band = fs_band_rto + fs_band = fs_band_rls if toggled else fs_band_rto + var s = map_sizes[int(toggled)] + $tilemap.size = s.size + $tilemap.position = s.position -## +## Встроенный обратный вызов. func _enter_tree() -> void: var timer1 = Timer.new() timer1.name = 'timer1' @@ -118,6 +124,7 @@ func _enter_tree() -> void: timer1.start(1.0) +## Встроенный обратный вызов. func _exit_tree() -> void: var timer1 = get_node('timer1') remove_child(timer1) diff --git a/scenes/работа/работа.tscn b/scenes/работа/работа.tscn index 9540a75..c9dfd15 100644 --- a/scenes/работа/работа.tscn +++ b/scenes/работа/работа.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=15 format=3 uid="uid://b276iygic5itk"] +[gd_scene load_steps=13 format=3 uid="uid://b276iygic5itk"] [ext_resource type="Script" path="res://scenes/работа/работа.gd" id="1_niok4"] [ext_resource type="PackedScene" uid="uid://nl1vklubr5kr" path="res://scenes/bip/bip.tscn" id="2_br3s6"] @@ -8,16 +8,10 @@ [ext_resource type="PackedScene" uid="uid://bwti82byphl68" path="res://scenes/count-danger/count_danger_pad.tscn" id="7_2arls"] [ext_resource type="PackedScene" uid="uid://da7w3vkhadfwe" path="res://scenes/button-flat/button-flat.tscn" id="8_k0iv2"] [ext_resource type="PackedScene" uid="uid://0uqi1ghf7sma" path="res://scenes/button-scale/btn-scale.tscn" id="9_oey3x"] -[ext_resource type="PackedScene" uid="uid://bbq6nklh36yij" path="res://scenes/tilemap/tilemap.tscn" id="10_gtwyg"] +[ext_resource type="PackedScene" uid="uid://ceknxxfu1loo5" path="res://scenes/tilemap/tilemap.tscn" id="10_gtwyg"] [ext_resource type="PackedScene" uid="uid://b5kjdyxuwsot5" path="res://scenes/diagram/diagram.tscn" id="11_b6su4"] -[ext_resource type="Shader" path="res://shaders/tilemap.gdshader" id="11_ekapd"] [ext_resource type="Script" path="res://scenes/работа/sld-mode.gd" id="12_gqdkl"] -[sub_resource type="ShaderMaterial" id="ShaderMaterial_4p7y1"] -resource_local_to_scene = true -shader = ExtResource("11_ekapd") -shader_parameter/radius = 0.39 - [sub_resource type="ImageTexture" id="ImageTexture_cnmvt"] [node name="Работа" type="Panel"] @@ -41,7 +35,8 @@ metadata/_edit_vertical_guides_ = [] [node name="canvas" parent="." instance=ExtResource("11_b6su4")] [node name="tilemap" parent="." instance=ExtResource("10_gtwyg")] -material = SubResource("ShaderMaterial_4p7y1") +self_modulate = Color(1, 1, 1, 0.415686) +position = Vector2(360, 360) [node name="grd_side" parent="." instance=ExtResource("3_s2j0e")] layout_mode = 0