PHP教程:用直线绘制各种形状的抛物线曲线

在PHP中用直线绘制抛物线曲线

注意:这篇文章发布已超过两年,因此其中包含的信息可能已过时。如果您发现了问题,请留下评论,我公司将尽力更正。

抛物线是一种曲线,其上的每个点到一个焦点和一条准线的距离相等。有许多不同的数学方法可以生成这种曲线,但最简单的方法之一是用直线来营造出曲线的视觉效果。

绘制这种曲线的方法是,将两条坐标轴划分为相等的点,然后从一条轴上的第一个点向相对轴上的最后一个点画一条线。曲线的每条线都以相同的方式绘制,沿着坐标轴为每个点重复在相对点之间画线的过程。

我们在图表中添加的线条越多,曲线就越明显,但只需几条线,我们就能创作出看似能使直线弯曲的复杂线条画。

以下是一个用直线创建抛物线曲线的简单示例。此曲线的焦点位于图像的右上角,实际上就在卡片的另一侧。

在本文中,我们将探讨如何在PHP中创建一个示例图像,然后如何仅使用直线在图像上绘制不同的抛物线曲线。同时,在Drupal开发Drupal模块开发以及Drupal升级等相关工作中,有时候也会用到类似的图形绘制技巧,特别是在Drupal11版本中,可能会有更多的应用场景。

一、创建图像

在本文的其余部分,我们将使用以下代码,借助PHP的GD库生成一个尺寸为500x500、纯黑色背景的图像。我们创建的白色将用于绘制曲线的线条。

<?php

$height = 500;
$width = 500;

// 创建图像引用。
$img = imagecreatetruecolor($width, $height);

// 创建一种颜色。
$white = imagecolorallocate($img, 255, 255, 255);

// -- 在此处添加生成图像中线条的代码。 --

// 生成图像。
imagepng($img, 'parabolic_curve.png');

// 销毁图像引用。
imagedestroy($img);

在PHP中,在图像上绘制线条非常简单,我们只需使用 imageline() 函数,传入图像资源、线条起点和终点的坐标以及线条的颜色即可。PHP中图像的坐标系以图像左上角的坐标“0,0”为基准。

例如,要绘制一条从左上角到右下角的线条,我们可以使用以下代码。

imageline($img, 0, 0, 1000, 1000, $white);

第一个参数是图像资源,接下来的两个参数是线条的起点坐标,之后是线条的终点坐标。最后,我们传入想要的线条颜色,该颜色是通过调用 imagecolorallocate() 函数创建的。

将这段代码组合起来,就会生成以下图像。现在我们有了一个可以绘图的画布,让我们开始绘制线条吧。

二、简单曲线

我们可以绘制的第一条抛物线是相对于单个x轴和y轴的简单曲线。在文章开头的图像中,我们已经看到了一个这样的示例。

为了绘制这条曲线,我们首先需要计算一个偏移值,该值用于将图表从屏幕边缘偏移,同时也用于计算抛物线本身将有多少条线。我们根据图像的高度除以一个数字来计算这个值。实际上,我们设定的数字越大,图像边缘的偏移量就越小,曲线中绘制的线条也就越多。

// 计算偏移量。
$offset = $height / 10;

这是一种简化曲线坐标轴创建方式以及线条与该曲线关系的方法。

利用这个偏移值,我们可以绘制图表的基线,这只需调用两次 imageline() 函数来绘制线条。

// 绘制曲线的基线。
imageline($img, $offset, $width - $offset, $offset, $offset, $white);
imageline($img, $offset, $width - $offset, $width - $offset, $height - $offset, $white);

完成这些后,我们就可以绘制抛物线的线条了。

为此,我们只需对基线高度对应的偏移值迭代次数进行循环,并在两条基线之间绘制线条。实际上,我们在这里所做的是,在第一次迭代中,在y轴上的第10个点和x轴上的第1个点之间绘制一条线。在第二次迭代中,我们在y轴上的第9个点和x轴上的第2个点之间绘制一条线。然后我们重复这个过程,直到结束。

// 在图表上绘制线条。
for ($i = $offset; $i < $height - $offset; $i = $i + $offset) {
  imageline($img, $offset, $i, $i, $width - $offset, $white);
}

使用偏移值10意味着我们在图表上绘制了7条线,它看起来是这样的。

例如,以下是使用偏移值30绘制的抛物线。您可以尝试不同的偏移值,看看能创建出什么样的抛物线。如果将数字设置得非常高,所有的线条将会重叠,基本上会形成一条实心曲线。

三、创建菱形

既然我们已经掌握了基本的抛物线绘制方法,那么可以考虑通过排列4条抛物线来创建一个菱形或星形。其过程基本相同,但需要绘制4条基线,然后绘制曲线的4个部分。

以下是完整的代码,我已对抛物线的不同角进行了标注,这样就可以很容易地看出哪些线条在绘制曲线的哪些部分。

// 计算偏移量。
$offset = $height / 10;

// 假设 $centerPoint 是一个对象,这里需要先初始化
$centerPoint = (object) [
  'x' => $width / 2,
  'y' => $height / 2
];

// 绘制曲线的基线。
imageline($img, $centerPoint ->x, $centerPoint ->y, $offset, $centerPoint ->x, $white);
imageline($img, $centerPoint ->x, $centerPoint ->y, $centerPoint ->y, $height - $offset, $white);
imageline($img, $centerPoint ->x, $centerPoint ->y, $width - $offset, $centerPoint ->x, $white);
imageline($img, $centerPoint ->x, $centerPoint ->y, $centerPoint ->y, $offset, $white);

// 在图表上绘制线条。
for ($i = $offset; $i < $centerPoint ->y; $i = $i + $offset) {
  // 左下角。
  imageline($img, $i, $centerPoint ->y, $centerPoint ->x, $centerPoint ->y + $i, $white);

  // 左上角。
  imageline($img, $i, $centerPoint ->y, $centerPoint ->x, $centerPoint ->y - $i, $white);

  // 右上角。
  imageline($img,  $centerPoint ->x + $i, $centerPoint ->y, $centerPoint ->x, $i, $white);

  // 右下角。
  imageline($img, $centerPoint ->x + $i,$centerPoint ->y,  $centerPoint ->x,$width - $i, $white);
}

这将生成以下图像。这只是四条曲线首尾相连排列而成的。

四、倾斜曲线

到目前为止,我只介绍了以水平线为基线的曲线,并且在结合图表的偏移量和曲线上的线条数量时稍微取了点巧。以这种方式绘制曲线意味着我们只能局限于方形或水平的基线,但我们可以轻松解决这两个问题。

第一步是创建一个函数,该函数允许我们从给定的x、y坐标处以特定的角度和长度绘制一条线。

/**
 * 从给定的y、x坐标绘制一条指定长度的线
 *
 * @param resource $image
 *   图像资源。
 * @param int $x1
 *   线条起点的X坐标。
 * @param int $y1
 *   线条起点的Y坐标。
 * @param int $length
 *   线条的长度,以像素为单位。
 * @param float $angle
 *   线条的角度,以度为单位。
 * @param int $color
 *   颜色资源,通过imagecolorallocate()创建。
 */
function imageAngledLine($image, $x1, $y1, $length, $angle, $color) {
  $x2 = $x1 + sin(deg2rad($angle)) * $length;
  $y2 = $y1 + cos(deg2rad($angle + 180)) * $length;
  imageline($image, $x1, $y1, $x2, $y2, $color);
}

使用这个函数(并对涉及的一些数学计算进行调整),我们可以创建一个通用函数,在任意位置绘制具有给定属性的抛物线曲线。

/**
 * 在两条倾斜的线之间绘制一条抛物线曲线。
 *
 * @param resource $img
 *   图像资源。
 * @param int $centerX
 *   基线的x坐标。
 * @param int $centerY
 *   基线的y坐标。
 * @param int $length
 *   基线的长度,以像素为单位。
 * @param float $xAngle
 *   x基线的角度,以度为单位。
 * @param float $yAngle
 *   y基线的角度,以度为单位。
 * @param int $increment
 *   每条线之间的增量值。值为1将形成一条实心曲线。
 * @param int $colour
 *   绘制线条使用的颜色。
 */
function drawCurve($img, int $centerX, int $centerY, int $length, float $xAngle, float $yAngle, int $increment, $colour) {
  imageAngledLine($img, $centerX, $centerY, $length, $xAngle, $colour);
  imageAngledLine($img, $centerX, $centerY, $length, $yAngle, $colour);

  for ($i = $increment; $i < $length; $i = $i + $increment) {
    $x3 = $centerX + sin(deg2rad($xAngle)) * $i;
    $y3 = $centerY + cos(deg2rad($xAngle + 180)) * $i;

    $x4 = $centerX + sin(deg2rad($yAngle)) * ($length - $i);
    $y4 = $centerY + cos(deg2rad($yAngle + 180)) * ($length - $i);

    imageline($img, $x3, $y3, $x4, $y4, $colour);
  }
}

我们可以用以下方式使用这个函数,在坐标50,450处绘制一条抛物线曲线。

drawCurve($img, 50, 450, 400, 21, 64, 20, $white);

这将生成以下图像。

现在我们有了一个可以用于绘制抛物线的通用函数,就可以用它做更多有趣的事情了。例如,我们可以使用以下循环,在图像的随机位置绘制10条具有随机角度和增量的曲线(假设我们的画布尺寸仍然是500像素)。

for ($i = 0; $i < 10; $i++) {
  drawCurve($img, rand(0, 450), rand(0, 450), rand(100, 500), rand(0, 360), rand(0, 360), rand(15, 50), $white);
}

这个循环会生成类似以下的图像,但每次运行代码时结果都会不同。

对于水平直线构成的抛物线曲线,我们能做的大概就这么多了,剩下的就是以不同的排列方式绘制曲线,而现在我们可以借助上述代码轻松做到这一点。那么,让我们尝试用不同的形状来绘制曲线吧。

五、圆内的抛物线曲线

抛物线的神奇之处在于,它们并不局限于在两条直线上绘制。也可以在其他结构上创建抛物线,比如圆。

要在圆内绘制抛物线,我们需要先绘制一个圆作为基线,然后计算圆周长上各点的位置。有了这些信息后,我们就可以在不同的点对之间绘制线条了。

让我们先设置一些绘图所需的变量。

// 设置一些变量。
$length = 400;
$radius = $length / 2;
$points = [];
$numberOfPoints = 40;
$slice = 2 * pi() / $numberOfPoints;

在PHP中绘制圆,我们需要使用 imageellipse() 函数。这个函数需要传入圆的中心以及椭圆的高度和宽度。由于我们想在图像中间绘制一个圆,所以将坐标设置为高度和宽度的一半,并将椭圆的高度和宽度设置为相同的值,这样就形成了一个圆。

// 绘制基线。
imageellipse($img, $height / 2, $width /2, $length, $length, $white);

完成这些后,我们需要计算圆周长上各点的坐标。我们使用 $numberOfPoints$slice 变量来计算这些坐标,但在这种情况下,我们只是将坐标填充到一个数组中,并不绘制任何线条。

// 为每个点找到圆上的一个位置。
for ($i = 0; $i < $numberOfPoints; $i++) {
  $angle = $slice * $i;
  $x = ($width / 2 + $radius * cos($angle));
  $y = ($height / 2 + $radius * sin($angle));
  $points[] = ['x' => $x, 'y' => $y];
}

现在我们已经有了圆周围各点的坐标,只需使用 imageline() 函数在每对点之间绘制一条线即可。为了计算坐标列表中的下一个点,我们使用模运算,这样如果超出数组的末尾,就会回到数组的开头。

// 在基线上的点对之间绘制线条。
$nextIncrement = $numberOfPoints / 2.5;
for ($i = 0; $i < count($points); $i++) {
  $next = (($i + $nextIncrement) + (count($points))) % (count($points));
  imageline($img, $points[$i]['x'], $points[$i]['y'], $points[$next]['x'], $points[$next]['y'], $white);
}

这段代码将生成以下图像。

关于抛物线,还有很多可以探索的地方。我们可以用不同颜色的线条绘制,或者用颜色填充空白处,以创建复杂的图案。目前我就介绍到这里,您可以自由使用这里的代码进行实验,创建自己的抛物线曲线。