我试图在PHP中使用Imagick创建一些bezier。 直到它到目前为止,我唯一的问题是如何在其他点(不是0,0)启动bezier并连接起点和终点? 任何帮助表示赞赏:)
这是我使用的代码:
$image = new Imagick();
$image->newImage(500, 500, 'none', 'png');
$bezier1 = new ImagickDraw();
$bezier1->setFillColor('#B42AAF');
$bezier1->setStrokeColor('black');
$bezier1->setStrokeWidth(1);
$bezier2 = new ImagickDraw();
$bezier2->setFillColor('FB9407');
$bezier2->setStrokeColor('black');
$bezier2->setStrokeWidth(1);
$coordinates_1 = Array
(
[0] => Array
(
[x] => 250
[y] => 46
)
[1] => Array
(
[x] => 394
[y] => 166
)
[2] => Array
(
[x] => 316
[y] => 288
)
[3] => Array
(
[x] => 250
[y] => 324
)
[4] => Array
(
[x] => 163
[y] => 299
)
[5] => Array
(
[x] => 163
[y] => 200
)
)
$coordinates_2 = Array
(
[0] => Array
(
[x] => 250
[y] => 123
)
[1] => Array
(
[x] => 437
[y] => 141
)
[2] => Array
(
[x] => 410
[y] => 342
)
[3] => Array
(
[x] => 250
[y] => 405
)
[4] => Array
(
[x] => 169
[y] => 296
)
[5] => Array
(
[x] => 101
[y] => 164
)
)
$bezier1->pathStart();
$bezier2->pathStart();
for($i = 0; $i < count($coordinates_1); $i++)
{
$bezier1->pathCurveToQuadraticBezierSmoothAbsolute($coordinates_1[$i]['x'], $coordinates_1[$i]['y']);
$bezier2->pathCurveToQuadraticBezierSmoothAbsolute($coordinates_2[$i]['x'], $coordinates_2[$i]['y']);
}
$bezier1->pathClose();
$bezier2->pathClose();
$image->drawImage($bezier1);
$image->drawImage($bezier2);
header('Content-Type: image/png');
echo $image;
1e结果用于显示点(带多边形):
2e结果是贝塞尔问题(左上角0,0):
3e结果是使用pathMoveToAbsolute尝试将“铅笔”移动到起始位置。哪个更难失败:(
答案 0 :(得分:1)
虽然I have opened an issue的功能似乎确实存在问题,但我认为这个功能不会像你希望的那样做,假设你想要一个平滑的轮廓。
基本上,在该功能中没有足够的信息来产生平滑的轮廓。
特别是:
我认为你需要做的是:
下面是一个非常粗略的代码示例。输入值只是极坐标系中的半径,并且它们之间的角度相等。它有两种绘制形状的方法,或者使用如上所述的贝塞尔曲线,或者通过对角度周围的值进行插值(产生一个非常“圆形”的图形,然后使用直线版本插值...代码可能比描述更有意义。
无论如何,产生的一些示例图像是:
贝塞尔
曲线插值
内插曲线
<?php
define('debug', false);
$image = new Imagick();
$image->newImage(500, 500, 'white', 'png');
function interpolate($fraction, $value1, $value2) {
return ((1 - $fraction) * $value1) + ($fraction * $value2);
}
function interpolateArray($fraction, $curvedPosition, $linearPosition) {
$result = [];
for ($i=0 ; $i<count($curvedPosition) ; $i++) {
$result[$i] = interpolate($fraction, $curvedPosition[$i], $linearPosition[$i]);
}
return $result;
}
class SpiderChart {
/**
* @var \ImagickDraw
*/
private $draw;
private $chartWidth = 500;
private $chartHeight = 500;
private $segments = 100;
function __construct() {
$draw = new ImagickDraw();
$draw->setFillColor('#B42AAF');
$draw->setStrokeColor('black');
$draw->setStrokeWidth(1);
$this->draw = $draw;
}
/**
* @return ImagickDraw
*/
public function getDraw() {
return $this->draw;
}
function drawChartBackground() {
$this->draw->line(
25,
$this->chartHeight / 2,
$this->chartWidth - 25,
$this->chartHeight / 2
);
$this->draw->line(
$this->chartWidth / 2,
25,
$this->chartWidth / 2,
$this->chartHeight - 25
);
$this->draw->setFillColor('none');
$this->draw->circle(
$this->chartWidth / 2,
$this->chartHeight / 2,
$this->chartWidth / 2,
$this->chartHeight / 2 + 200
);
$this->draw->circle(
$this->chartWidth / 2,
$this->chartHeight / 2,
$this->chartWidth / 2,
$this->chartHeight / 2 + 100
);
}
public function getInterpolatedPosition($p, $i, $points) {
$angleBetweenPoints = 2 * M_PI / count($points);
$fraction = $i / $this->segments;
$angle = ($p + ($fraction)) * $angleBetweenPoints;
$firstValue = $points[$p];
$secondValue = $points[($p + 1) % count($points)];
$averageValue = interpolate($fraction, $firstValue, $secondValue);
$positionX = sin($angle) * $averageValue ;
$positionY = -cos($angle) * $averageValue ;
if (debug) {
echo "angle $angle positionX $positionX, positionY $positionY \n";
}
return [$positionX, $positionY];
}
public function getLinearPosition($p, $i, $points) {
$angleBetweenPoints = 2 * M_PI / count($points);
$fraction = $i / $this->segments;
$startAngle = $p * $angleBetweenPoints;
$endAngle = ($p + 1) * $angleBetweenPoints;
$startPositionX = sin($startAngle) * $points[$p];
$startPositionY = -cos($startAngle) * $points[$p];
$endPositionX = sin($endAngle) * $points[($p + 1)];
$endPositionY = -cos($endAngle) * $points[($p + 1) % count($points)];
return [
interpolate($fraction, $startPositionX, $endPositionX),
interpolate($fraction, $startPositionY, $endPositionY),
];
}
public function drawBlendedChart($points, $curveLinearBlend) {
$this->draw->setFillColor('#B42AAF9F');
$this->draw->translate(
$this->chartWidth / 2,
$this->chartHeight / 2
);
$this->draw->pathStart();
list($nextPositionX, $nextPositionY) = $this->getInterpolatedPosition(0, 0, $points);
$this->draw->pathMoveToAbsolute(
$nextPositionX,
$nextPositionY
);
for ($p=0 ; $p<count($points) ; $p++) {
for ($i = 0; $i < $this->segments; $i++) {
$curvedPosition = $this->getInterpolatedPosition($p, $i, $points);
$linearPosition = $this->getLinearPosition($p, $i, $points);
list($nextPositionX,$nextPositionY) = interpolateArray(
$curveLinearBlend,
$curvedPosition,
$linearPosition
);
$this->draw->pathLineToAbsolute(
$nextPositionX,
$nextPositionY
);
}
}
$this->draw->pathClose();
$this->draw->pathFinish();
}
/**
* @param $points
* @return array
*/
private function getPointTangents($points) {
$angleBetweenPoints = 2 * M_PI / count($points);
$tangents = [];
for ($i=0; $i<count($points) ; $i++) {
$angle = ($i * $angleBetweenPoints) + M_PI_2;
$unitX = sin($angle);
$unitY = -cos($angle);
$tangents[] = [$unitX, $unitY];
}
return $tangents;
}
/**
* @param $points
* @return array
*/
private function getPointPositions($points) {
$angleBetweenPoints = 2 * M_PI / count($points);
$positions = [];
for ($i=0; $i<count($points) ; $i++) {
$angle = ($i * $angleBetweenPoints);
$positions[] = [
sin($angle) * $points[$i],
-cos($angle) * $points[$i]
];
}
return $positions;
}
/**
* @param $position
* @param $tangent
* @param $direction - which sign the control point should use to multiple the unit vector
* @return array
*/
private function getControlPoint($position, $tangent, $direction, $roundness) {
//TODO - this scale needs to be done properly. The factors should be
// i) the value of the current point.
// ii) The curviness desired - done
// iii) The cosine exterior angle.
// top-tip - the interior angles sum to 180, so exterior is (180 - 180/n)
// for an n-sided polygon
$scale = 60 * $roundness;
$resultX = $position[0] + $direction * $tangent[0] * $scale;
$resultY = $position[1] + $direction * $tangent[1] * $scale;
return [$resultX, $resultY];
}
function drawBezierChart($points, $roundness) {
//Calculate the tangent vector for each point that you're drawing.
$tangents = $this->getPointTangents($points);
$positions = $this->getPointPositions($points);
$numberOfPoints = count($points);
$this->draw->setFillColor('#B42AAF9F');
$this->draw->translate(
$this->chartWidth / 2,
$this->chartHeight / 2
);
$this->draw->pathStart();
$this->draw->pathMoveToAbsolute($positions[0][0], $positions[0][4]);
//Scale that by the 'value' of each point aka the distance from the chart's centre.
//Also scale it by how rounded you want the chart.
for ($i=0 ; $i<$numberOfPoints ; $i++) {
list($nextPositionX, $nextPositionY) = $positions[($i + 1) % $numberOfPoints];
list($controlPoint1X, $controlPoint1Y) =
$this->getControlPoint(
$positions[$i],
$tangents[$i],
1,
$roundness
);
list($controlPoint2X, $controlPoint2Y) =
$this->getControlPoint(
$positions[($i + 1) % $numberOfPoints],
$tangents[($i + 1) % $numberOfPoints],
-1,
$roundness
);
$this->draw->pathCurveToAbsolute(
$controlPoint1X, $controlPoint1Y,
$controlPoint2X, $controlPoint2Y,
$nextPositionX, $nextPositionY
);
}
$this->draw->pathClose();
$this->draw->pathFinish();
}
}
function getValues() {
$coordinates_1 = [
145,
80,
125,
165,
145
];
return $coordinates_1;
}
$spiderChart = new SpiderChart();
$spiderChart->drawChartBackground();
$points = getValues();
//$spiderChart->drawBlendedChart($points, 0.2);
$spiderChart->drawBezierChart($points, 0.5);
$image->drawImage($spiderChart->getDraw());
if (!debug) {
header('Content-Type: image/png');
echo $image;
}
答案 1 :(得分:1)
图片的原因:
看起来很疯狂,当你使用pathCurveToQuadraticBezierSmoothAbsolute作为路径上的第一个元素时,有一个bug in ImageMagick。
应该在ImageMagick的下一个版本中修复它。