你好。我的旧图形框架 (GD) 无法绘制三次贝塞尔曲线(它根本无法绘制)。为了从 TTF 字体中绘制字符(任务不允许直接使用它),我想以某种方式部分地用弧线替换贝塞尔曲线。但是怎么做?我不需要现成的科学作品(我知道它们存在并具有工业意义),它允许使用一组弧线使结果尽可能准确地接近贝塞尔曲线。两条弧线对我来说就足够了。
点 P1 和 P2 的坐标是已知的。控制点 C₁ 和 C₂ 也是如此。从点 P1 和 P2 我画出垂直于红色线段 P1C1 和 P2C2 的直线。所需弧的中心将位于这些线上,否则点 P 1 和 P 2 处的切线将不重合,并且违反了问题的条件。
然后我决定这样做。通过点 P₁ 和 P₂ 之间的中点 P₀,我画了一条垂直于它们之间的线段的直线(灰色),然后是“灰色垂直线”。然后我添加第一个圆圈(绿色):
可以看出,根据它的半径R1,它的中心简单地沿着红色直线“行进”,而与灰色垂线的交点和交角发生变化。
所以,我需要找到半径R₁和R₂的这样的值,使两个圆与灰色垂线相交于一点,此外,它们在这一点具有相同的切角。然后我会以某种方式自己将它们切成弧形。
也就是说,初始数据是P1,P2,C1,C2,其坐标(x,y)是已知的。你需要找到 R₁ 和 R₂(圆半径,标量数)。
我不能再用一堆三角函数分别求解带有 X 和 Y 的方程。也许在向量代数的帮助下,这会更容易、更优雅地解决?
对于这个问题,我自己已经做了什么。当预先知道第一个弧的半径时,我解决了一个稍微不同的问题。
<?php
$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),(Xr1,Yr1)和(Xr2,Yr2)位于同一条线上。简单比例:
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γ
结果是:
具有三个未知(R1、R2、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 程序),包括部分结果。事实证明,没有解析解。而且数值方法不适合,因为我打算用弧线从字体中绘制符号,你能想象它会以什么速度出现。
因此,宣布比赛。只有问题的标题很重要。然后我们根据需要做出决定,只是分析性的。注意我在文本中所说的所有废话是没有必要的,而且可能是有害的。
PS,除其他外,我对基于弧(半径)之间的某种关系的“奇偶校验”感兴趣。用给定的半径制作一个小圆圈并将第二个“附加”到它 - 这已经在问题的文本中完成了。
顺便说一下,这里是 R₂ 的负值。基于前两个公式在 PHP GD 中渲染,没有任何附加条件:
也就是说,与灰色垂线相交的圆与点 P₀ 相交的距离没有限制。
在这里,以防万一,谁需要它,点 P1、P2 以及需要它的控制点 C1 和 C2 上的三次贝塞尔曲线,用红色虚线表示:
它不需要近似,而是简单地替换为基于两条弧线的平滑值。
我会在问题中添加动画,否则很少关注它。
$r2 = .5 * (pow($r1, 2.) - pow($x2-$r1x, 2.) - pow($y2-$r1y, 2.)) /
(cos($ap2)*($x2-$r1x) + sin($ap2)*($y2-$r1y) + $r1);
$ap2 就是 P2 处切线的垂线。








您已将问题表述为在一般情况下没有解决方案。
Если 2 окружности пересекаются, то в точке пересечения они имеют одинаковый угол касательной тогда и только тогда, когда точка пересечения одна:
В примере выше, в первых 2 случаях угол касательной одинаковый, в 3 случае, никак нельзя совместить 2 дуги.
Я нарисовал в редакторе решение для примера, как на ваших рисунках. Решение не учитывает ваше условие про пересечение на перпендикуляре:
Если я продолжу увеличивать радиус большей окружности, и подбирать радиус меньшей, что бы они пересекались в одной точке, эта точка никогда не будет на перпендикуляре. Даже если мы увеличим радиус второй окружности до бесконечности (она станет прямой), пересечение все еще не достигнет перпендикуляра:
Решение без вложенных окружностей не подходит, потому что дуги не с правильной стороны выходят:
Тоже самое и с решением, когда вторая окружность внутри первой:
作为一个简单的选择,我建议光栅化。
中间点很容易计算:
通过用线连接中间点,我们已经在 8 个点处得到了一个不错的结果: