Товарищи программисты-математики, отдаю на ваш справедливый суд данный класс.
Некоторые очевидные методы, вроде lineTo я убрал, чтобы не было tl;dr, то есть, это выжимка интересующих меня моментов.
/**
* @param $x
* @param $y
*/
function __construct($x=false,$y=false){
$this->x = $x?$x:rand(1,IMG_SIZE_X);
$this->y = $y?$y:rand(1,IMG_SIZE_Y);
}
/**
* @param point $p
* @param float $dist
*
* @return point
*/
function midPoint(point $p,$dist=0.5){
$new_x = $this->x+($p->x-$this->x)*$dist;
$new_y = $this->y+($p->y-$this->y)*$dist;
$new_point = new point($new_x,$new_y);
return $new_point;
}
}
/**
* Class bezierimage
*/
class bezierimage{
private $img;
public $color;
public $bgcolor;
public $brush_position;
function __construct(){
$this->img = imagecreatetruecolor(IMG_SIZE_X,IMG_SIZE_Y);
$this->color = imagecolorallocate($this->img,255,255,255);
}
/**
* @param point[] $points
* @param float $dist
*
* @return point[]
*/
function getBezierPoints($points,$dist=0.5){
if(count($points)==1){
return $points[0];
}
$new_points = array();
for($i=1;$i
Ответ
По поводу getBezierPoints — можно сделать в один проход без рекурсии.
Смотрите. Пусть в начале у нас есть точки p[0], ..., p[n-1]. После первого шага у нас получается q[0] = (p[0] + p[1]) / 2, q[1] = (p[1] + p[2]) / 2, ..., q[n-2] = (p[n-2] + p[n-1]) / 2, то есть
q[i] = (p[i] + p[i+1]) / 2.
На следующем шаге
r[i] = (q[i] + q[i+1]) / 2 = (p[i] + 2p[i+1] + p[i+2]) / 4.
Видно, что возникают биномиальные коэффициенты:
r[i] = (C_2^0 p[i + 0] + C_2^1 p[i + 1] + C_2^2 p[i + 2]) / 2^2.
(Немного математики: По индукции легко доказать, что биномиальные коэффициенты будут и дальше: если на j-м уровне
s[i] = (\sum_k=0^{j-1} C_j^k p[i + k]) / 2^j,
то на (j+1)-м
t[i] = (s[i] + s[i+1]) / 2 =
= (\sum_k=0^j (C_j^k + C_j^{k + 1}) p[i + k]) / 2^{j + 1}
= (\sum_k=0^j C_{j + 1}^k p[i + k]) / 2^{j + 1}.
База индукции очевидна.)
Итак, нам нужны биномиальные коэффициенты. Их можно тоже легко вычислить на лету. Конечный алгоритм должен быть таким:
int l = count($points);
int coeff = 1;
point result = $points[0];
for (int idx = 1; idx < l; idx++)
{
coeff *= (l - idx);
coeff /= idx;
result += $points[idx] * coeff;
}
result /= (1 << (l - 1)); // степень двойки
Если делить надо не обязательно на 2, аналогично получается вот что:
int l = count($points);
double alpha = 0.333333; // или любое другое значение между 0 и 1
double beta = 1 - alpha;
double beta2alpha = beta / alpha;
int coeff = alpha ** (l - 1); // степень
point result = $points[0] * coeff;
for (int idx = 1; idx < l; idx++)
{
coeff *= (l - idx);
coeff /= idx;
coeff *= beta2alpha;
result += $points[idx] * coeff;
}
Комментариев нет:
Отправить комментарий