2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




 
 Рамка на координатной сетке
Сообщение17.08.2020, 15:11 
Пусть есть бесконечная координатная сетка (для определенности, с шагом 2). Также есть некая рамка (просто прямоугольник шириной в $1$ и высотой в $\frac12$) с центром где-то на плоскости (мы знаем координаты центра, они нецелы). Квадраты координатной сетки заполнены какими-то изображениями (один квадрат - одно изображение, заполняющее его целиком, без перекрытий и наложений). Надо заполнить рамку соответствующими фрагментами изображений (рамка показывается пользователю). Рамка заполняется через функции https://developer.mozilla.org/en-US/doc ... gContext2D (это тут не слишком принципально, можно использовать эту ссылку просто как список аргументов функцци).
Для иллюстрации:
Изображение
Большие черные квадраты - квадраты сетки. Черные прямоугольники - рамки. Черные точки внутри рамок - их центры (на рисунке я нарисовал их не очень центральными, правда...). Красная и желтая линии - координаты центра (относительно угла соответствущего квадрата). Синие и зеленые линии ограничивают области точек, где ситуация существенно меняется (в зеленом прямоугольнике - только один фрагмент изображения получается, в зелено-сине-черных - два, сине-черных - четыре).

Есть ли возможность это сделать проще, чем простым перебором if'ами, в каком из синих/зеленых/черных прямоугольников находится центр (тогда получается пара сотен строк примерно такого кода:
код: [ скачать ] [ спрятать ]
Используется синтаксис Javascript
if ((remainderCoordinates[0] >= canvasSize[0] / 2) && (remainderCoordinates[0] <= backgroundSize - canvasSize[0] / 2) &&
          (remainderCoordinates[1] >= canvasSize[1] / 2) && (remainderCoordinates[1] <= backgroundSize - canvasSize[1] / 2)) {
                try {
                        let img = await getBackground(divisionCoordinates[0], divisionCoordinates[1]);
                        let sx = remainderInPixels[0] - canvas.width / 2;
                        let sy = backgroundSize * coordinateInPixels - remainderInPixels[1] - canvas.height / 2;
                        let width = canvas.width;
                        let height = canvas.height;
                        let dx = 0;
                        let dy = 0;
                        context.drawImage(img, sx, sy, width, height, dx, dy, width, height);
                } catch {};
        } else if ((remainderCoordinates[1] >= canvasSize[1] / 2) &&
          (remainderCoordinates[1] <= backgroundSize - canvasSize[1] / 2)) {
                let sx1 = (remainderInPixels[0] - canvas.width / 2) % (backgroundSize * coordinateInPixels);
                let sy1 = backgroundSize * coordinateInPixels - remainderInPixels[1] - canvas.height / 2;
                let width1 = backgroundSize * coordinateInPixels - sx1;
                let height1 = canvas.height;
                let dy1 = 0;
                try {
                        let image1 = await getBackground(divisionCoordinates[0] - (remainderCoordinates[0] <= canvasSize[0] / 2),
                      divisionCoordinates[1]);
                        let dx1 = 0;
                        context.drawImage(image1, sx1, sy1, width1, height1, dx1, dy1, width1, height1);
                } catch {};
                try {
                        let image2 = await getBackground(divisionCoordinates[0] + (remainderCoordinates[0] >= canvasSize[0] / 2),
                          divisionCoordinates[1]);
                        let sx2 = 0;
                        let sy2 = sy1;
                        let width2 = canvas.width - width1;
                        let height2 = height1;
                        let dx2 = width1;
                        let dy2 = dy1;
                        context.drawImage(image2, sx2, sy2, width2, height2, dx2, dy2, width2, height2);
                } catch {};
        } else if ((remainderCoordinates[0] >= canvasSize[0] / 2) &&
          (remainderCoordinates[0] <= backgroundSize - canvasSize[0] / 2)) {
                let sx1 = remainderInPixels[0] - canvas.width / 2;
                let sy1 = (-canvas.height / 2 - remainderInPixels[1] * (remainderCoordinates[1] <= canvasSize[1] / 2 ? 1 : -1)) %
                  (backgroundSize * coordinateInPixels);
                let width1 = canvas.width;
                let height1 = backgroundSize * coordinateInPixels - sy1;
                let dx1 = 0;
                try {
                        let image1 = await getBackground(divisionCoordinates[0], divisionCoordinates[1] +
                          (remainderCoordinates[1] >= backgroundSize - canvasSize[1] / 2));
                        let dy1 = 0;
                        context.drawImage(image1, sx1, sy1, width1, height1, dx1, dy1, width1, height1);
                } catch {};
                try {
                        let image2 = await getBackground(divisionCoordinates[0], divisionCoordinates[1] -
                          (remainderCoordinates[1] <= canvasSize[1] / 2));
                        let sx2 = sx1;
                        let sy2 = 0;
                        let width2 = width1;
                        let height2 = canvas.height - height1;
                        let dx2 = dx1;
                        let dy2 = height1;
                        context.drawImage(image2, sx2, sy2, width2, height2, dx2, dy2, width2, height2);
                } catch {};
      } перебор остальных вариантов

(remainderCoordinates[0] и [1] - длина красной и желтой линий в координатах, canvasSize - размер рамки в координатах, backgroundSize - размер квадратов сетки в координатах, coordinateInPixels, remainderInPixels, и canvas.width/height - размеры координаты, красной и желтой линий, а так же размеры рамки в пикселях, это не так важно))?

 
 
 
 Re: Рамка на координатной сетке
Сообщение17.08.2020, 19:57 
Посмотрите оптимизацию отсечения невидимых линий в компьютерной графике (я помню про методы разбиения пространства), кажется это 1-в-1 та задача, решённая ещё при появлении первых версий графической оболочки (и уж точно не позже первых многозадачных Windows). Вариантов решения может быть больше одного, смотря сколько рамок и сколько объектов всего в сцене, какое соотношение размеров рамки и клеток, кучкования объектов в клетках и т.д.
Ну и полный перебор вроде бы не обязателен, можно поделить объекты на списки для каждой клетки и по координатам центра рамки перебирать лишь максимум 4 клеток (если размер рамки не больше размера клетки). Если клеток сильно больше десятка, то это выгодно.
Ну и зачем кучи if-ов не совсем ясно, можно же сложить объекты в массив по клеткам и получать номер клетки из координат рамки просто делением с округлением двух чисел.
Или я опять не вник в смысл задачи. :-(

 
 
 
 Re: Рамка на координатной сетке
Сообщение17.08.2020, 20:03 
Вы не поняли, похоже... Координаты легко получаются, это и есть красная и желтая линии. If'ы служат для различия девяти принципиально разных ситуаций.

 
 
 
 Re: Рамка на координатной сетке
Сообщение17.08.2020, 20:21 
Аватара пользователя
Я бы предложил для каждого из углов прямоугольника скопировать тот кусок, который в него попадает. При желании можно оптимизировать - обходим углы в фиксированном порядке, и очередной берем, если он не попал в тот же квадрат, что один из предыдущих.

 
 
 
 Re: Рамка на координатной сетке
Сообщение17.08.2020, 20:27 
Аватара пользователя
Dmitriy40
Насколько понял, ТС нужно узнать в какой из 9 областей клетки оказался центр рамки, что выбрать правильный способ заполнения рамки (из одной, двух или четырех картинок).

kotenok gav
9 областей формируются тремя полосами по вертикали и тремя по горизонтали.

1. Делая всего два сравнения, Вы получаете номер полосы по вертикали, $i$.
2. Делая точно так же еще два сравнения, Вы получаете номер полосы по вертикали, $j$.
3. Считаете $k = i +3(j-1)$, получаете номер области внутри клетки, от 1 до 9. Вот Вам девять отличающихся вариантов.

Если внимательно на них посмотреть, то принципиально различающися там 3 типа:
1. Номер $5$ - нет соседей.
2. Нечетные номера, кроме $5$ - четыре соседа.
3. Четные номера - два соседа.

Можно даже такую нумерацию ввести, что она сразу даст количество соседей.

 
 
 
 Re: Рамка на координатной сетке
Сообщение17.08.2020, 20:40 
EUgeneUS в сообщении #1479626 писал(а):
Насколько понял, ТС нужно узнать в какой из 9 областей клетки оказался центр рамки, что выбрать правильный способ заполнения рамки (из одной, двух или четырех картинок).

Нет. Это-то как раз и делается if'ами. Просто каждый способ заполнения весьма длинн, и поэтому я не хочу повторять его несколько раз по всем if'ам.
mihaild в сообщении #1479625 писал(а):
Я бы предложил для каждого из углов прямоугольника скопировать тот кусок, который в него попадает. При желании можно оптимизировать - обходим углы в фиксированном порядке, и очередной берем, если он не попал в тот же квадрат, что один из предыдущих.

Не совсем понял... Можно пример?

 
 
 
 Re: Рамка на координатной сетке
Сообщение17.08.2020, 20:57 
Аватара пользователя
Проще всего как-то так: делаем из координат центра список координат левых нижних углов прямоугольников, целиком лежащих в одной ячейке, которые нам надо скопировать:
код: [ скачать ] [ спрятать ]
Используется синтаксис C++
int round_to_smaller(float x); // округляет вниз до кратного 2
vector<pair<float, float>> get_corners(float xc, float yc) {
  vector<pair<float, float>> result;
  float x0  = xc - 0.5, x1 = xc + 0.5, y0 = yc - 0.25, y1 = yc + 0.25;
  result.emplace_back(x0, y0);
  if (round_to_smaller(x0) != round_to_smaller(x1)) {
    result.emplace_back(round_to_smaller(x1), y0);
  }
  if (round_to_smaller(y0) != round_to_smaller(y1)) {
    result.emplace_back(x0, round_to_smaller(y1));
  }
  if (round_to_smaller(x0) != round_to_smaller(x1) && round_to_smaller(y0) != round_to_smaller(y1)) {
    result.emplace_back(round_to_smaller(x0), round_to_smaller(y1));
  }
  return result;
}
 

Дальше для каждого из углов копируем нужный кусок картинки:
Используется синтаксис C++
void copy_part(float x0, float y0, float x1, float y1); // копирует прямоугольник с плоскости с углами (x0, y0) и (x1, y1) в нашу рамку; нужно, чтобы этот прямоугольник лежал в одной клетке и накрывался рамкой
void fill_image(float xc, float yc) {
  auto corners = get_corners(xc, yc);
  float x1 = xc + 0.5, y1 = yc + 0.25;
  for (auto [x, y] : corners) {
    copy_part(x, y, min(x1, round_to_smaller(x) + 2), min(y1, round_to_smaller(y) + 2);
  }
}
 

 
 
 
 Re: Рамка на координатной сетке
Сообщение17.08.2020, 21:24 
mihaild, спасибо! Кажется, так получится...

 
 
 [ Сообщений: 8 ] 


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group