Вместо псевдокода я использовал python, но он должен быть применим. Для этого алгоритма я предполагаю, что startAngle < endAngle
и оба находятся в пределах [-2 * PI, 2 * PI]
. Если вы хотите использовать оба в [0, 2 * PI]
и позволить startAngle> endAngle, я бы сделал:
if (startAngle > endAngle):
startAngle = startAngle - 2 * PI
Итак, алгоритм, который приходит на ум, - это рассчитать границы единичной дуги, а затем масштабировать, чтобы соответствовать вашему прямоугольнику.
Первая часть сложнее. Вам необходимо рассчитать 4 числа:
Left: MIN(cos(angle), 0)
Right: MAX(cos(angle), 0)
Top: MIN(sin(angle),0)
Bottom: MAX(sin(angle),0)
Конечно, угол - это диапазон, поэтому он не так прост, как этот. Тем не менее, вам действительно нужно включить до 11 баллов в этот расчет. Начальный угол, конечный угол и, возможно, кардинальные направления (их 9 от -2 * PI
до 2 * PI
.) Я собираюсь определить boundingBoxes
как списки из 4 элементов, упорядоченные [left, right, top, bottom]
def IncludeAngle(boundingBox, angle)
x = cos(angle)
y = sin(angle)
if (x < boundingBox[0]):
boundingBox[0] = x
if (x > boundingBox[1]):
boundingBox[1] = x
if (y < boundingBox[2]):
boundingBox[2] = y
if (y > boundingBox[3]):
boundingBox[3] = y
def CheckAngle(boundingBox, startAngle, endAngle, angle):
if (startAngle <= angle and endAngle >= angle):
IncludeAngle(boundingBox, angle)
boundingBox = [0, 0, 0, 0]
IncludeAngle(boundingBox, startAngle)
IncludeAngle(boundingBox, endAngle)
CheckAngle(boundingBox, startAngle, endAngle, -2 * PI)
CheckAngle(boundingBox, startAngle, endAngle, -3 * PI / 2)
CheckAngle(boundingBox, startAngle, endAngle, -PI)
CheckAngle(boundingBox, startAngle, endAngle, -PI / 2)
CheckAngle(boundingBox, startAngle, endAngle, 0)
CheckAngle(boundingBox, startAngle, endAngle, PI / 2)
CheckAngle(boundingBox, startAngle, endAngle, PI)
CheckAngle(boundingBox, startAngle, endAngle, 3 * PI / 2)
CheckAngle(boundingBox, startAngle, endAngle, 2 * PI)
Теперь вы вычислили ограничивающую рамку дуги с центром 0,0
и радиусом 1
. Чтобы заполнить поле, нам нужно решить линейное уравнение:
boundingBox[0] * xRadius + xOffset = 0
boundingBox[1] * xRadius + xOffset = w
boundingBox[2] * yRadius + yOffset = 0
boundingBox[3] * yRadius + yOffset = h
И мы должны решить для xRadius и yRadius. Вы заметите, что здесь есть два радиуса. Причина этого заключается в том, что для заполнения прямоугольника мы должны умножить на разные величины в двух направлениях. Поскольку ваш алгоритм запрашивает только один радиус, мы просто выберем меньшее из двух значений.
Решение уравнения дает:
xRadius = w / (boundingBox[1] - boundingBox[0])
yRadius = h / (boundingBox[2] - boundingBox[3])
radius = MIN(xRadius, yRadius)
Здесь вы должны проверить, что boundingBox[1] - boundingBox[0]
является 0
, и установить xRadius
в бесконечность в этом случае. Это даст правильный результат, так как yRadius
будет меньше. Если у вас нет бесконечности, вы можете просто установить ее на 0
, а в функции MIN
проверить на 0
и использовать другое значение в этом случае. xRadius
и yRadius
не могут быть оба 0
, потому что оба sin
и cos
должны быть равны 0
для всех включенных выше углов, чтобы это имело место.
Теперь мы должны разместить центр дуги. Мы хотим, чтобы это было сосредоточено в обоих направлениях. Теперь мы создадим еще одно линейное уравнение:
(boundingBox[0] + boundingBox[1]) / 2 * radius + x = xCenter = w/2
(boundingBox[2] + boundingBox[3]) / 2 * radius + y = yCenter = h/2
Решение для x
и y
, центр дуги, дает
x = w/2 - (boundingBox[0] + boundingBox[1]) * radius / 2
y = h/2 - (boundingBox[3] + boundingBox[2]) * radius / 2
Это должно дать вам центр дуги и радиус, необходимый для помещения наибольшего круга в данный прямоугольник.
Я не тестировал ни один из этого кода, поэтому этот алгоритм может иметь огромные дыры, или, возможно, крошечные, вызванные опечатками. Я хотел бы знать, работает ли этот алгоритм.
редактирование:
Объединение всего кода дает:
def IncludeAngle(boundingBox, angle)
x = cos(angle)
y = sin(angle)
if (x < boundingBox[0]):
boundingBox[0] = x
if (x > boundingBox[1]):
boundingBox[1] = x
if (y < boundingBox[2]):
boundingBox[2] = y
if (y > boundingBox[3]):
boundingBox[3] = y
def CheckAngle(boundingBox, startAngle, endAngle, angle):
if (startAngle <= angle and endAngle >= angle):
IncludeAngle(boundingBox, angle)
boundingBox = [0, 0, 0, 0]
IncludeAngle(boundingBox, startAngle)
IncludeAngle(boundingBox, endAngle)
CheckAngle(boundingBox, startAngle, endAngle, -2 * PI)
CheckAngle(boundingBox, startAngle, endAngle, -3 * PI / 2)
CheckAngle(boundingBox, startAngle, endAngle, -PI)
CheckAngle(boundingBox, startAngle, endAngle, -PI / 2)
CheckAngle(boundingBox, startAngle, endAngle, 0)
CheckAngle(boundingBox, startAngle, endAngle, PI / 2)
CheckAngle(boundingBox, startAngle, endAngle, PI)
CheckAngle(boundingBox, startAngle, endAngle, 3 * PI / 2)
CheckAngle(boundingBox, startAngle, endAngle, 2 * PI)
if (boundingBox[1] == boundingBox[0]):
xRadius = 0
else:
xRadius = w / (boundingBox[1] - boundingBox[0])
if (boundingBox[3] == boundingBox[2]):
yRadius = 0
else:
yRadius = h / (boundingBox[3] - boundingBox[2])
if xRadius == 0:
radius = yRadius
elif yRadius == 0:
radius = xRadius
else:
radius = MIN(xRadius, yRadius)
x = w/2 - (boundingBox[0] + boundingBox[1]) * radius / 2
y = h/2 - (boundingBox[3] + boundingBox[2]) * radius / 2
редактирование:
Одна из проблем заключается в том, что sin[2 * PI]
не будет точно 0
из-за ошибок округления. Я думаю, что решение состоит в том, чтобы избавиться от CheckAngle
вызовов и заменить их чем-то вроде:
def CheckCardinal(boundingBox, startAngle, endAngle, cardinal):
if startAngle < cardinal * PI / 2 and endAngle > cardinal * PI / 2:
cardinal = cardinal % 4
if cardinal == 0:
boundingBox[1] = 1
if cardinal == 1:
boundingBox[3] = 1
if cardinal == 2:
boundingBox[0] = -1
if cardinal == 3:
boundingBox[2] = -1
CheckCardinal(boundingBox, startAngle, endAngle, -4)
CheckCardinal(boundingBox, startAngle, endAngle, -3)
CheckCardinal(boundingBox, startAngle, endAngle, -2)
CheckCardinal(boundingBox, startAngle, endAngle, -1)
CheckCardinal(boundingBox, startAngle, endAngle, 0)
CheckCardinal(boundingBox, startAngle, endAngle, 1)
CheckCardinal(boundingBox, startAngle, endAngle, 2)
CheckCardinal(boundingBox, startAngle, endAngle, 3)
CheckCardinal(boundingBox, startAngle, endAngle, 4)
Вам все еще нужно IncludeAngle(startAngle)
и IncludeAngle(endAngle)