Привет. Мой старый графический фреймворк (GD) не умеет рисовать кубические кривые Безье (вообще никакие не умеет). С целью отрисовки символов из TTF шрифтов (напрямую задача не позволяет), хочу как-то частично неадекватно заменить кривые Безье на дуги. Но как? Мне не нужны готовые научные работы (я знаю, они есть и имеют промышленный смысл), которые позволяют набором дуг приблизить результат к кривой Безье максимально точно. Мне достаточно двух дуг. Вот что у меня есть: Координаты точек P₁ и P₂ известны. Координаты контрольных точек C₁ и C₂ тоже. Из точек P₁ и P₂ я провел прямые перпендикулярно отрезкам P₁C₁ и P₂C₂ красного цвета. Центры искомых дуг будут лежать на этих прямых, иначе касательные в точках P₁ и P₂ не совпадут, и нарушается условие задачи. Дальше я решил действовать так. Через среднюю точку P₀ между точками P₁ и P₂ я провел перпендикулярную отрезку между ними прямую (серый цвет), далее "серый перпендикуляр". Потом добавляю первую окружность (зеленый цвет):
Видно, что в зависимости от ее радиуса R₁ ее центр просто "ездит" по красной прямой, при этом изменяется точка пересечения с серым перпендикуляром и угол пересечения. Аналогично все и с второй окружностью радиуса R₂ (синий цвет): Так вот, мне нужно найти такие значения радиусов R₁ и R₂, чтобы обе окружности пересекались с серым перпендикуляром в одной точке, кроме того, имели в этой точке одинаковый угол касательной. Дальше я уж на дуги их сам как-нибудь порежу. То есть, исходные данные это P₁, P₂, C₁, C₂, координаты которых (x, y) известны. А найти надо R₁ и R₂ (радиусы окружностей, скалярные числа). Я уже не в силах решать уравнения с X и Y по-отдельности, с кучей тригонометрии. Может, с помощью векторной алгебры это как-то попроще-изящнее решается? К вопросу, а что я уже сделал сам. Я решил немного другую задачу, когда заранее известен радиус первой дуги. <?php<br>$width = 600 $height = 600 function demo($im) { $x1 = 124 $y1 = 253 imagefilledarc($im, $x1, $y1, 10, 10, 0, 0, 0x000000, 0) imagestring($im, 2, $x1-30, $y1+10, "x1, y1", 0x000000) $x2 = 445 $y2 = 428 imagefilledarc($im, $x2, $y2, 10, 10, 0, 0, 0x000000, 0) imagestring($im, 2, $x2+0, $y2+10, "x2, y2", 0x000000) imageline($im, $x1, $y1, $x2, $y2, 0x000000) $xc1 = $x1 + 50 $yc1 = $y1 - 200 imageline($im, $x1, $y1, $xc1, $yc1, 0x000000) imagefilledarc($im, $xc1, $yc1, 10, 10, 0, 0, 0x000000, 0) $xc2 = $x2 -5 $yc2 = $y2 -200 imageline($im, $x2, $y2, $xc2, $yc2, 0x000000) imagefilledarc($im, $xc2, $yc2, 10, 10, 0, 0, 0x000000, 0) $d1 = 10 * sqrt( pow($x1-$xc1, 2) + pow($y1-$yc1, 2) ) $ac1 = atan2($yc1-$y1, $xc1-$x1) $ap1 = $ac1 + pi() / 2. $xp1 = $x1 + $d1*cos($ap1) $yp1 = $y1 + $d1*sin($ap1) imageline($im, $x1, $y1, $xp1, $yp1, 0x808080) $d2 = 10 * sqrt( pow($x2-$xc2, 2) + pow($y2-$yc2, 2) ) $ac2 = atan2($yc2-$y2, $xc2-$x2) $ap2 = $ac2 - pi() / 2. $xp2 = $x2 + $d2*cos($ap2) $yp2 = $y2 + $d2*sin($ap2) imageline($im, $x2, $y2, $xp2, $yp2, 0x808080) /** Принудительно задается радиус первой (красной) дуги, что неправильно!!! */ $r1 = 135 /** Из него вычисляется положение центра красной дуги, обозначен красной точкой. */ $r1x = $x1 + $r1*cos($ap1) $r1y = $y1 + $r1*sin($ap1) imagefilledarc($im, $r1x, $r1y, 10, 10, 0, 0, 0xFF0000, 0) imagestring($im, 2, $r1x-40, $r1y-30, "r1x, r1y", 0x000000) /** Это результат решения уравнения для заданного радиуса красной дуги */ $L = ( 2.*($r1x*$x2 + $r1y*$y2) - pow($x2, 2) - pow($y2, 2) - pow($r1x, 2) - pow($r1y, 2) + pow($r1, 2) ) / 2. / ( cos($ap2)*($x2-$r1x) + sin($ap2)*($y2-$r1y) + $r1 ) $xl = $x2 + $L*cos($ap2) $yl = $y2 + $L*sin($ap2) imagefilledarc($im, $xl, $yl, 10, 10, 0, 0, 0x00FF00, 0) $aspl = atan2($r1y-$yl, $r1x-$xl) imagearc($im, $r1x, $r1y, 2*$r1, 2*$r1, rad2deg($ap1)+180, rad2deg($aspl), 0xFF0000) imagearc($im, $xl, $yl, 2*$L, 2*$L, rad2deg($aspl), rad2deg($ap2)-180, 0x0000FF) } $im = imagecreatetruecolor($width, $height) imagefilledrectangle($im, 0, 0, $width-1, $height-1, 0xc0c0c0) demo($im) header('Content-Type: image/png') imagepng($im, null, 9)
В данном случае первая дуга красная. Но это не то, что нужно. Мне нужно, чтобы между дугами был некий "паритет", а не чтобы радиус первой задавался принудительно. Собираюсь начать конкурс. Поэтому, чтобы не вводить участников в заблуждение, сейчас изложу то, к каким выводам за прошедшее время пришел сам.
Я получил формулу для радиуса окружности от координаты любой точки (Xz, Yz), через которую она должна проходить: (Xz-X₁)² + (Yz-Y₁)² R₁ = 2 --------------------------- (Xz-X₁)Cosβ + (Yz-Y₁)Sinβ (Xz-X₂)² + (Yz-Y₂)² R₂ = 2 --------------------------- (Xz-X₂)Cosγ + (Yz-Y₂)Sinγ Дальше обозначаю углы (нет на картинке) между осью абсцисс и 1) красным перпендикуляром из точки P₁ -- β 2) красным перпендикуляром из точки P₂ -- γ 3) серым перпендикуляром из точки P₀ -- δ (угол α я зарезервировал на всякий случай для наклона самой прямой P₁P₂, но оно не понадобилось). Дальше добавляется условие равенства касательных окружностей в точке (Xz, Yz). Это просто значит, что (Xz, Yz), (Xr₁, Yr₁) и (Xr₂, Yr₂) лежат на одной прямой. Простая пропорция: Xz - Xr₁ Xz - Xr₂ -------- = -------- Yz - Yr₁ Yz - Yr₂ Потом я выражаю координаты точек через углы и расстояния от других точек: Xz = X₀ + ZCosδ Yz = Y₀ + ZSinδ Xr₁ = X₁ + R₁Cosβ Yr₁ = Y₁ + R₁Sinβ Xr₂ = X₂ + R₂Cosγ Yr₂ = Y₂ + R₂Sinγ В итоге получается: СИСТЕМА УРАВНЕНИЙ С ТРЕМЯ НЕИЗВЕСТНЫМИ (R₁, R₂, Z): (Xz-X₁)² + (Yz-Y₁)² R₁ = 2 --------------------------- (Xz-X₁)Cosβ + (Yz-Y₁)Sinβ (Xz-X₂)² + (Yz-Y₂)² R₂ = 2 --------------------------- (Xz-X₂)Cosγ + (Yz-Y₂)Sinγ Xz - Xr₁ Xz - Xr₂ -------- = -------- Yz - Yr₁ Yz - Yr₂ Xz = X₀ + ZCosδ Yz = Y₀ + ZSinδ Xr₁ = X₁ + R₁Cosβ Yr₁ = Y₁ + R₁Sinβ Xr₂ = X₂ + R₂Cosγ Yr₂ = Y₂ + R₂Sinγ При том, все эти синусы и косинусы - вообще константы, поскольку углы (их аргументы) заданы как константы. Как только я эту систему ни крутил. Раскрывал скобки, использовал компьютерную алгебру (программа Maxima), в том числе для частичных результатов. Выходит, что аналитического решения нет. А численные методы не подходят, я ведь символы из шрифтов собирался дугами рисовать, представляете, какая скорость получится. Поэтому объявляется конкурс. Имеет значение только заголовок вопроса. Дальше решаем как хотим, только аналитически. Принимать к сведению все глупости, изложенные мною в тексте - не обязательно и может быть вредно. P.S. Меня, кроме прочего, интересует "паритет" на основе некоторого соотношения между дугами (радиусами). Сделать маленькую окружность с заданными радиусом и "нацепить" на нее вторую -- это уже сделано в тексте вопроса. Вот, кстати, отрицательное значение R₂. Отрисовано на PHP GD на основе первых двух формул без всяких дополнительных условий:
То есть никакого ограничения на то, как далеко от точки P₀ может пересекаться окружность с серым перпендикуляром - нет. Вот, на всякий случай, кому нужно, кубическая кривая Безье по точкам P₁, P₂ и контрольным точкам C₁ и C₂, кому нужно, красным пунктиром:
Её не нужно аппроксимировать, а просто нужно заменить чем-то гладким на основе двух дуг. Добавлю анимацию в вопрос, а то что-то мало к нему внимания.
$r2 = .5 * (pow($r1, 2.) - pow($x2-$r1x, 2.) - pow($y2-$r1y, 2.)) / (cos($ap2)*($x2-$r1x) + sin($ap2)*($y2-$r1y) + $r1) Где $ap2 - просто перпендикуляр к касательной в точке P2.
Ответ Вы сформулировали задачу так, что она не имеет решения в общем случае. Так вот, мне нужно найти такие значения радиусов R₁ и R₂, чтобы обе окружности пересекались с серым перпендикуляром в одной точке, кроме того, имели в этой точке одинаковый угол касательной. Если 2 окружности пересекаются, то в точке пересечения они имеют одинаковый угол касательной тогда и только тогда, когда точка пересечения одна:
В примере выше, в первых 2 случаях угол касательной одинаковый, в 3 случае, никак нельзя совместить 2 дуги. Я нарисовал в редакторе решение для примера, как на ваших рисунках. Решение не учитывает ваше условие про пересечение на перпендикуляре:
Если я продолжу увеличивать радиус большей окружности, и подбирать радиус меньшей, что бы они пересекались в одной точке, эта точка никогда не будет на перпендикуляре. Даже если мы увеличим радиус второй окружности до бесконечности (она станет прямой), пересечение все еще не достигнет перпендикуляра:
Решение без вложенных окружностей не подходит, потому что дуги не с правильной стороны выходят:
Тоже самое и с решением, когда вторая окружность внутри первой: