Спасибо
mihaild,
sergey zhukov,
zgemm,
B@R5uk и
12d3 мне кажется ваши решения более затратны по вычислениям.
12d3 да тангенс угла я тоже к этому пришел.
Опишу в общем что делаю.
Делаю картографическую библиотеку, подобные используются в гугл, яндекс, ОСМ карт. Для лучшей гибкости и контроля.
Задача которую щас решаю это вращение карты, тоесть когда север не сверху карты.
Экран это по сути прямоугольник, а карта состоит из плиток(квадратных картинок).
Таким образом задача находить плитки которые внутри области экрана прямоугольника. так как загрузка плитки наиболее затратна по времени, лучше определить точно те плитки которые требуется загрузить.
Сделал для тестов выше на скрине, где зеленый прямоугольник имитирует экран, красная точка верхний левый угол экрана, справа угол поворота экрана, квадратики это имитация плиток, которые как и плитки карты пронумерованы по рядам(горизонтально), и столбцам вертикально. закрашенные это те которые нужно загружать.
При решении задачи пошел достаточно примитивным путем.
Далее относительно координат плиток.
1. определяю верхний угол прямоугольника(он же экран). Это чтобы потом в циклах перебирать плитки с верхнего ряда вниз, с лева направо.
2. определяю высоту прямоугольника(сумма на син, кос, сторон прямоугольника)
3. определяю правую и левую сторону прямоугольника(при вращении они могут менятся).
4. определяю правое и левое расстояние прямоугольника по оси x относительно верхнего угла прямоугольника(минимум и максимум по x).
5. определяю смещение верхнего угла относительно плитки.
6. определяю смещение в верхнем ряде(ряд верхнего угла).
7. определяю смещение на один ряд, для каждой стороны(смещение между углами).
8. определяю смещение для правого и левого угла прямоугольника.
9. проходим каждый ряд, с лева на право по определенным смещениям(минимум максимум плитка для каждого ряда сверху вниз).
10. при привышении левой или правой границы меняю направление смещения и само смещение.
11. в итоге заполняю массив с номерами(рядов и столбцов) плиток, для загрузки. в тестовом примере они окрашены в серый цвет.
Мне кажется должен быть более элегантный и/или быстрый способ решения данной задачи.
При скорости стоит учитывать что всякие син, кос, более затратны, умножений делений, которве более затратны сложений вычитаний, которые более затратны бинарных операций.
Понимаю решений много данной задачи, но хотелось бы найти самое быстрое и/или самое элегантное решение. В этом и вопрос.
За ваши решения всеравно спасибо рассмотрю их более подробно. Но по вычислениям на вскидку они мне кажутся более затратными пока.
Моё решение именно в коде JavaScript с комментариями попробую отправить в отдельном сообщении, нето как непробовал тут отправлять сообщение с кодом не отправлялось, как увидите оно достаточно громоздкое в коде.
-- 26.06.2025, 13:59 --Код:
function getTileArr(){
let arr_tile = [];// возвращаемый функцией массив номеров(рядов и столбцов) плиток// надо заполнить.
/*
для работы функции используются внешние данные.
a_view = 0;// угол поворота дисплея(Дисплей это прямоугольник).
win_out_w = 300;// ширина дисплея.
win_out_h = 200;// высота дисплея.
center_x = win_out_x + win_out_w / 2 | 0;// центер дисплея относительно которого вращается
center_y = win_out_y + win_out_h / 2 | 0;// центер дисплея относительно которого вращается
point_radius = (win_out_w ** 2 + win_out_h ** 2) ** .5 / 2;// радиус описывающей окружности, расстояние от центра до углов прямоугольника
point_a = hf_pi - Math.atan(win_out_h / win_out_w);// угол диагонали дисплея
tile_size = 32;// размер высоты и ширины плитки
hf_pi = Math.PI / 2;// half PI // половина Пи // угол 90 градусов в радианах
*/
let size_left_border, size_right_border;// относительно координат плиток, размер левой и правой стороны дисплея
let a_relative;// угол поворота экрана, относительно верхнего угла дисплея, относительно координат плиток
// определяем верхний угол.
//*
let x, y;// координаты верхнего угла дисплея, относительно координат плиток
if(a_view >= 0){// определяем соответствующие значения относительно верхнего угла дисплея
if(a_view <= hf_pi){
a_relative = a_view;
size_left_border = win_out_h;
size_right_border = win_out_w;
x = center_x + point_radius * Math.sin(a_view - point_a);
y = center_y - point_radius * Math.cos(a_view - point_a);
}else{
a_relative = a_view - hf_pi;
size_left_border = win_out_w;
size_right_border = win_out_h;
x = center_x - point_radius * Math.sin(a_view + point_a);
y = center_y + point_radius * Math.cos(a_view + point_a);
}
}else{
if(a_view >= -hf_pi){
a_relative = a_view + hf_pi;
size_left_border = win_out_w;
size_right_border = win_out_h;
x = center_x + point_radius * Math.sin(a_view + point_a);
y = center_y - point_radius * Math.cos(a_view + point_a);
}else{
a_relative = a_view + Math.PI;
size_left_border = win_out_h;
size_right_border = win_out_w;
x = center_x - point_radius * Math.sin(a_view - point_a);
y = center_y + point_radius * Math.cos(a_view - point_a);
}
}
//*/
// определяем left right в ряде
let sin_a = Math.sin(a_relative);
let cos_a = Math.cos(a_relative);
let tan_a = sin_a / cos_a;
// относительно координат плиток
let shift_y_tile = tile_size - y % tile_size;// смещение верхнего угла дисплея относительно плитки по оси y
let shift_x_tile = x % tile_size;// смещение верхнего угла дисплея относительно плитки по оси x
let max_left_shift_w = size_left_border * sin_a;// размер левой стороны дисплея по оси x
let max_left_shift_h = size_left_border * cos_a;// размер левой стороны дисплея по оси y
let max_right_shift_w = size_right_border * cos_a;// размер правой стороны дисплея по оси x
let max_right_shift_h = size_right_border * sin_a;// размер правой стороны дисплея по оси y
let max_h = max_left_shift_h + max_right_shift_h;// высота дисплея по оси y
let tile_y = y / tile_size | 0;// ряд верхней плитки дисплея.
let tile_bottom = Math.ceil((max_h - shift_y_tile) / tile_size) + tile_y;// ряд нижней плитки дисплея.
//let tile_x = x / tile_size | 0;
let x_min = x - max_left_shift_w;// минимальная координата дисплея по оси x
let x_max = x + max_right_shift_w;// максимальная координата дисплея по оси x
let l = tile_size * tan_a;// смещение по оси x на один ряд с левой стороны
let l_1 = shift_y_tile * tan_a;// смещение по оси x на один ряд, относительно верхнего угла дисплея с левой стороны
let r_2 = tan_a === 0 ? Infinity: (tile_size - (y + max_left_shift_h) % tile_size) / tan_a;// смещение по оси x на один ряд, относительно левого угла дисплея с левой стороны в право
let r = tan_a === 0 ? Infinity: tile_size / tan_a;// смещение по оси x на один ряд с правой стороны
let r_1 = tan_a === 0 ? Infinity: shift_y_tile / tan_a;// смещение по оси x на один ряд, относительно верхнего угла дисплея с правой стороны
let l_2 = (tile_size - (y + max_right_shift_h) % tile_size) * tan_a;// смещение по оси x на один ряд, относительно правого угла дисплея с правой стороны в лево
let l_pos = x - l_1;// левая позиция в ряде.
let r_pos = x + r_1;// правая позиция в ряде.
let l_shift = -l;// смещение левой позиции в ряде, относительно позиции в предыдущем ряде.
let r_shift = r;// смещение правой позиции в ряде, относительно позиции в предыдущем ряде.
let flag_l = false;// флаг достижения левого края дисплея и смены направления
let flag_r = false;// флаг достижения правого края дисплея и смены направления
if(l_pos < x_min){// если достигнут левый край экрана
l_pos = x_min;
l_shift = r_2;
flag_l = true;
}
if(r_pos > x_max){// если достигнут правый край экрана
r_pos = x_max;
r_shift = -l_2;
flag_r = true;
}
if(l_pos < x_min)l_pos = x_min;
if(r_pos > x_max)r_pos = x_max;
// определяем плиток с лева и права.
let left_tile_xx = l_pos / tile_size | 0;// номер левой плитки
let right_tiles_xx = r_pos / tile_size | 0;// номер правой плитки
for(let t_x = left_tile_xx; t_x <= right_tiles_xx; t_x++)arr_tile.push([t_x, tile_y]);
for(let t_y = tile_y + 1; t_y <= tile_bottom; t_y++){
l_pos += l_shift;
r_pos += r_shift;
if(flag_l){
l_shift = r;
flag_l = false;
}
if(flag_r){
r_shift = -l;
flag_r = false;
}
if(l_pos < x_min){// если достигнут левый край экрана
l_pos = x_min;
l_shift = r_2;
flag_l = true;
}
if(r_pos > x_max){// если достигнут правый край экрана
r_pos = x_max;
r_shift = -l_2;
flag_r = true;
}
// определяем плиток с лева и права.
let left_tile = l_pos / tile_size | 0;// номер левой плитки
let right_tiles = r_pos / tile_size | 0;// номер правой плитки
for(let t_x = left_tile; t_x <= right_tiles; t_x++)arr_tile.push([t_x, t_y]);
}
tileColor(arr_tile);// return arr_tile;
}