Обобщите найдите решение наименьшего размера для всех четырех сторон прямоугольника

#javascript #typescript

Вопрос:

В приложении пользователь может щелкнуть по краю прямоугольного поля, чтобы изменить его размер до «наименьшего допустимого размера» в этом направлении. Например, если они щелкнут по правому краю, размер поля изменится на наименьшую допустимую ширину, оставив левый край там, где он есть. Если они щелкнут по нижней части, она уменьшит нижний край как можно дальше, не став недействительной. Критерии того, что такое «действительный», не имеют значения, но для расчета требуется некоторая работа.

Теперь я написал некоторый код, чтобы сделать эту работу для правого края коробки, используя двоичный поиск, чтобы приблизиться к наименьшему допустимому размеру. Но мне нужно общее решение, которое не включает в себя повторение моего кода в четырех различных вариантах, и я не могу понять, как это сделать.

Код в том виде, в каком он есть сейчас (поддерживает только правый край):

 interface Zone {
  x: number;
  y: number;
  width: number;
  height: number;
}

enum Side {
  'top',
  'left',
  'bottom',
  'right',
}

enum ResizeDirection {
  NEGATIVE = -1,
  POSITIVE = 1,
}

async function findMinValidZone(
  zone: Zone,
  minWidth: number,
  maxWidth: number,
  side: Side,
  validator: (z: Zone) => Promise<boolean>
): Promise<Zone> {
  const area = {...zone};
  let d = maxWidth;
  let step = maxWidth - minWidth;
  let currentDirection = ResizeDirection.NEGATIVE; // positive means widen the area, negative means narrow it
  let lastGood: number = d;
  do {
    d  = step * currentDirection;
    if (d < minWidth || d > maxWidth) {
      break;
    }
    area.width = d;
    const ok = await validator(area);
    if (ok) {
      currentDirection = ResizeDirection.NEGATIVE;
      lastGood = d;
    } else {
      currentDirection = ResizeDirection.POSITIVE;
    }
    step = step / 2;
  } while (step >= 1);
  area.width = lastGood;
  return area;
}
 

Ответ №1:

Один из способов, которым я могу это сделать, — повернуть прямоугольник на любое число, кратное 90 градусам, необходимое для преобразования прямоугольника, чтобы вы всегда работали с правым краем, чем поворачивать его обратно для проверки и при возврате результата. Фактическое вращение можно было бы выполнить с помощью матричных преобразований, но пока я оставлю фактическую функцию поворота для вас. Тогда ваш обобщенный код будет выглядеть следующим образом:

 function findMinValidZone(
  zone: Zone,
  minwidth: number,
  maxWidth: number,
  side: Side,
  validator: (z: Zone) => Promise<boolean>
): Promise<Zone> {
  let rotationAngle: number;
  switch (side) {
    case Side.bottom:
      rotationAngle = -Math.PI / 2;
      break;
    case Side.left:
      rotationAngle = Math.PI;
      break;
    case Side.right:
      rotationAngle = 0;
      break;
    case Side.top:
      rotationAngle = Math.PI / 2;
  }
  let d = maxWidth;
  let step = maxWidth - minwidth;
  // rotate the area so that you are always working on the width dimension
  // while keeping other dimensions fixed. Then rotate area back again
  // when validating and when returning the results
  const area = rotateZone({...zone}, rotationAngle);
  let currentDirection = ResizeDirection.NEGATIVE; // positive means widen the area, negative means narrow it
  let lastGood: number = d;
  do {
    d  = step * currentDirection;
    if (d < minwidth || d > maxWidth) {
      break;
    }
    area.width = d;
    const ok = await validator(rotateZone(area, -rotationAngle));
    if (ok) {
      currentDirection = ResizeDirection.NEGATIVE;
      lastGood = d;
    } else {
      currentDirection = ResizeDirection.POSITIVE;
    }
    step = step / 2;
  } while (step >= 1);
  area.width = lastGood;
  return rotateZone(area, -rotationAngle);
}
 

Я подумаю о самой функции rotateZone, но в принципе она должна быть достаточно простой, используя такую библиотеку, как https://www.npmjs.com/package/transformation-matrix

Комментарии:

1. Спасибо — мне это нравится! Любая помощь с функцией поворота будет признательна.