<?php
namespace Grafika\Gd\DrawingObject;
use Grafika\DrawingObject\CubicBezier as Base;
use Grafika\DrawingObjectInterface;
use Grafika\Gd\Image;
use Grafika\ImageInterface;
class CubicBezier extends Base implements DrawingObjectInterface
{
public function draw($image)
{
$width = $image->getWidth();
$height = $image->getHeight();
$gd = $image->getCore();
list($x0, $y0) = $this->point1;
list($x1, $y1) = $this->control1;
list($x2, $y2) = $this->control2;
list($x3, $y3) = $this->point2;
$this->plot($gd, $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3);
$type = $image->getType();
$file = $image->getImageFile();
return new Image($gd, $file, $width, $height, $type); }
protected function plot($gd, $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
{
$n = 0;
$i = 0;
$xc = $x0 + $x1 - $x2 - $x3;
$xa = $xc - 4 * ($x1 - $x2);
$xb = $x0 - $x1 - $x2 + $x3;
$xd = $xb + 4 * ($x1 + $x2);
$yc = $y0 + $y1 - $y2 - $y3;
$ya = $yc - 4 * ($y1 - $y2);
$yb = $y0 - $y1 - $y2 + $y3;
$yd = $yb + 4 * ($y1 + $y2);
$fx0 = $x0;
$fx1 = 0;
$fx2 = 0;
$fx3 = 0;
$fy0 = $y0;
$fy1 = 0;
$fy2 = 0;
$fy3 = 0;
$t1 = $xb * $xb - $xa * $xc;
$t2 = 0;
$t = array();
if ($xa == 0) {
if (abs($xc) < 2 * abs($xb)) {
$t[$n++] = $xc / (2.0 * $xb);
}
} else {
if ($t1 > 0.0) {
$t2 = sqrt($t1);
$t1 = ($xb - $t2) / $xa;
if (abs($t1) < 1.0) {
$t[$n++] = $t1;
}
$t1 = ($xb + $t2) / $xa;
if (abs($t1) < 1.0) {
$t[$n++] = $t1;
}
}
}
$t1 = $yb * $yb - $ya * $yc;
if ($ya == 0) {
if (abs($yc) < 2 * abs($yb)) {
$t[$n++] = $yc / (2.0 * $yb);
}
} else {
if ($t1 > 0.0) {
$t2 = sqrt($t1);
$t1 = ($yb - $t2) / $ya;
if (abs($t1) < 1.0) {
$t[$n++] = $t1;
}
$t1 = ($yb + $t2) / $ya;
if (abs($t1) < 1.0) {
$t[$n++] = $t1;
}
}
}
for ($i = 1; $i < $n; $i++) {
if (($t1 = $t[$i - 1]) > $t[$i]) {
$t[$i - 1] = $t[$i];
$t[$i] = $t1;
$i = 0;
}
}
$t1 = -1.0;
$t[$n] = 1.0;
for ($i = 0; $i <= $n; $i++) {
$t2 = $t[$i];
$fx1 = ($t1 * ($t1 * $xb - 2 * $xc) - $t2 * ($t1 * ($t1 * $xa - 2 * $xb) + $xc) + $xd) / 8 - $fx0;
$fy1 = ($t1 * ($t1 * $yb - 2 * $yc) - $t2 * ($t1 * ($t1 * $ya - 2 * $yb) + $yc) + $yd) / 8 - $fy0;
$fx2 = ($t2 * ($t2 * $xb - 2 * $xc) - $t1 * ($t2 * ($t2 * $xa - 2 * $xb) + $xc) + $xd) / 8 - $fx0;
$fy2 = ($t2 * ($t2 * $yb - 2 * $yc) - $t1 * ($t2 * ($t2 * $ya - 2 * $yb) + $yc) + $yd) / 8 - $fy0;
$fx0 -= $fx3 = ($t2 * ($t2 * (3 * $xb - $t2 * $xa) - 3 * $xc) + $xd) / 8;
$fy0 -= $fy3 = ($t2 * ($t2 * (3 * $yb - $t2 * $ya) - 3 * $yc) + $yd) / 8;
$x3 = floor($fx3 + 0.5);
$y3 = floor($fy3 + 0.5);
if ($fx0 != 0.0) {
$fx1 *= $fx0 = ($x0 - $x3) / $fx0;
$fx2 *= $fx0;
}
if ($fy0 != 0.0) {
$fy1 *= $fy0 = ($y0 - $y3) / $fy0;
$fy2 *= $fy0;
}
if ($x0 != $x3 || $y0 != $y3) {
$this->plotCubicSegment($gd, $x0, $y0, $x0 + $fx1, $y0 + $fy1, $x0 + $fx2, $y0 + $fy2, $x3, $y3);
}
$x0 = $x3;
$y0 = $y3;
$fx0 = $fx3;
$fy0 = $fy3;
$t1 = $t2;
}
}
protected function plotCubicSegment($gd, $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
{
$f = 0;
$fx = 0;
$fy = 0;
$leg = 1;
$sx = $x0 < $x3 ? 1 : -1;
$sy = $y0 < $y3 ? 1 : -1;
$xc = -abs($x0 + $x1 - $x2 - $x3);
$xa = $xc - 4 * $sx * ($x1 - $x2);
$xb = $sx * ($x0 - $x1 - $x2 + $x3);
$yc = -abs($y0 + $y1 - $y2 - $y3);
$ya = $yc - 4 * $sy * ($y1 - $y2);
$yb = $sy * ($y0 - $y1 - $y2 + $y3);
$ab = 0;
$ac = 0;
$bc = 0;
$ba = 0;
$xx = 0;
$xy = 0;
$yy = 0;
$dx = 0;
$dy = 0;
$ex = 0;
$px = 0;
$py = 0;
$ed = 0;
$ip = 0;
$EP = 0.01;
assert(($x1 - $x0) * ($x2 - $x3) < $EP && (($x3 - $x0) * ($x1 - $x2) < $EP || $xb * $xb < $xa * $xc + $EP));
assert(($y1 - $y0) * ($y2 - $y3) < $EP && (($y3 - $y0) * ($y1 - $y2) < $EP || $yb * $yb < $ya * $yc + $EP));
if ($xa == 0 && $ya == 0) {
$sx = floor((3 * $x1 - $x0 + 1) / 2);
$sy = floor((3 * $y1 - $y0 + 1) / 2);
$this->plotQuadSegment($gd, $x0, $y0, $sx, $sy, $x3, $y3);
return;
}
$x1 = ($x1 - $x0) * ($x1 - $x0) + ($y1 - $y0) * ($y1 - $y0) + 1;
$x2 = ($x2 - $x3) * ($x2 - $x3) + ($y2 - $y3) * ($y2 - $y3) + 1;
do {
$ab = $xa * $yb - $xb * $ya;
$ac = $xa * $yc - $xc * $ya;
$bc = $xb * $yc - $xc * $yb;
$ip = 4 * $ab * $bc - $ac * $ac;
$ex = $ab * ($ab + $ac - 3 * $bc) + $ac * $ac;
$f = $ex > 0 ? 1 : sqrt(1 + 1024 / $x1);
$ab *= $f;
$ac *= $f;
$bc *= $f;
$ex *= $f * $f;
$xy = 9 * ($ab + $ac + $bc) / 8;
$ba = 8 * ($xa - $ya);
$dx = 27 * (8 * $ab * ($yb * $yb - $ya * $yc) + $ex * ($ya + 2 * $yb + $yc)) / 64 - $ya * $ya * ($xy - $ya);
$dy = 27 * (8 * $ab * ($xb * $xb - $xa * $xc) - $ex * ($xa + 2 * $xb + $xc)) / 64 - $xa * $xa * ($xy + $xa);
$xx = 3 * (3 * $ab * (3 * $yb * $yb - $ya * $ya - 2 * $ya * $yc) - $ya * (3 * $ac * ($ya + $yb) + $ya * $ba)) / 4;
$yy = 3 * (3 * $ab * (3 * $xb * $xb - $xa * $xa - 2 * $xa * $xc) - $xa * (3 * $ac * ($xa + $xb) + $xa * $ba)) / 4;
$xy = $xa * $ya * (6 * $ab + 6 * $ac - 3 * $bc + $ba);
$ac = $ya * $ya;
$ba = $xa * $xa;
$xy = 3 * ($xy + 9 * $f * ($ba * $yb * $yc - $xb * $xc * $ac) - 18 * $xb * $yb * $ab) / 8;
if ($ex < 0) {
$dx = -$dx;
$dy = -$dy;
$xx = -$xx;
$yy = -$yy;
$xy = -$xy;
$ac = -$ac;
$ba = -$ba;
}
$ab = 6 * $ya * $ac;
$ac = -6 * $xa * $ac;
$bc = 6 * $ya * $ba;
$ba = -6 * $xa * $ba;
$dx += $xy;
$ex = $dx + $dy;
$dy += $xy;
for ($fx = $fy = $f; $x0 != $x3 && $y0 != $y3;) {
$y1 = min($xy - $dx, $dy - $xy);
$ed = max($xy - $dx, $dy - $xy);
$ed = $f * ($ed + 2 * $ed * $y1 * $y1 / (4 * $ed * $ed + $y1 * $y1));
$y1 = 255 * abs($ex - ($f - $fx + 1) * $dx - ($f - $fy + 1) * $dy + $f * $xy) / $ed;
if ($y1 < 256) {
$this->setPixel($gd, $x0, $y0, $y1 / 255);
}
$px = abs($ex - ($f - $fx + 1) * $dx + ($fy - 1) * $dy);
$py = abs($ex + ($fx - 1) * $dx - ($f - $fy + 1) * $dy);
$y2 = $y0;
do {
if ($ip >= -$EP) {
if ($dx + $xx > $xy || $dy + $yy < $xy) {
goto exits;
}
}
$y1 = 2 * $ex + $dx;
if (2 * $ex + $dy > 0) {
$fx--;
$ex += $dx += $xx;
$dy += $xy += $ac;
$yy += $bc;
$xx += $ab;
} else {
if ($y1 > 0) {
goto exits;
}
}
if ($y1 <= 0) {
$fy--;
$ex += $dy += $yy;
$dx += $xy += $bc;
$xx += $ac;
$yy += $ba;
}
} while ($fx > 0 && $fy > 0);
if (2 * $fy <= $f) {
if ($py < $ed) {
$this->setPixel($gd, $x0 + $sx, $y0, $py / $ed);
}
$y0 += $sy;
$fy += $f;
}
if (2 * $fx <= $f) {
if ($px < $ed) {
$this->setPixel($gd, $x0, $y2 + $sy, $px / $ed);
}
$x0 += $sx;
$fx += $f;
}
}
break;
exits:
if (2 * $ex < $dy && 2 * $fy <= $f + 2) {
if ($py < $ed) {
$this->setPixel($gd, $x0 + $sx, $y0, $py / $ed);
}
$y0 += $sy;
}
if (2 * $ex > $dx && 2 * $fx <= $f + 2) {
if ($px < $ed) {
$this->setPixel($gd, $x0, $y2 + $sy, $px / $ed);
}
$x0 += $sx;
}
$xx = $x0;
$x0 = $x3;
$x3 = $xx;
$sx = -$sx;
$xb = -$xb;
$yy = $y0;
$y0 = $y3;
$y3 = $yy;
$sy = -$sy;
$yb = -$yb;
$x1 = $x2;
} while ($leg--);
$this->plotLine($gd, $x0, $y0, $x3, $y3);
}
protected function plotQuadSegment($gd, $x0, $y0, $x1, $y1, $x2, $y2)
{
$sx = $x2 - $x1;
$sy = $y2 - $y1;
$xx = $x0 - $x1;
$yy = $y0 - $y1;
$xy = $dx = $dy = $err = $ed = 0;
$cur = $xx * $sy - $yy * $sx;
if ($sx * (int)$sx + $sy * (int)$sy > $xx * $xx + $yy * $yy) {
$x2 = $x0;
$x0 = $sx + $x1;
$y2 = $y0;
$y0 = $sy + $y1;
$cur = -$cur;
}
if ($cur != 0) {
$xx += $sx;
$xx *= $sx = $x0 < $x2 ? 1 : -1;
$yy += $sy;
$yy *= $sy = $y0 < $y2 ? 1 : -1;
$xy = 2 * $xx * $yy;
$xx *= $xx;
$yy *= $yy;
if ($cur * $sx * $sy < 0) {
$xx = -$xx;
$yy = -$yy;
$xy = -$xy;
$cur = -$cur;
}
$dx = 4.0 * $sy * ($x1 - $x0) * $cur + $xx - $xy;
$dy = 4.0 * $sx * ($y0 - $y1) * $cur + $yy - $xy;
$xx += $xx;
$yy += $yy;
$err = $dx + $dy + $xy;
do {
$cur = min($dx + $xy, -$xy - $dy);
$ed = max($dx + $xy, -$xy - $dy);
$ed += 2 * $ed * $cur * $cur / (4 * $ed * $ed + $cur * $cur);
$this->setPixel($gd, $x0, $y0, abs($err - $dx - $dy - $xy) / $ed);
if ($x0 == $x2 || $y0 == $y2) {
break;
}
$x1 = $x0;
$cur = $dx - $err;
$y1 = 2 * $err + $dy < 0;
if (2 * $err + $dx > 0) {
if ($err - $dy < $ed) {
$this->setPixel($gd, $x0, $y0 + $sy, abs($err - $dy) / $ed);
}
$x0 += $sx;
$dx -= $xy;
$err += $dy += $yy;
}
if ($y1) {
if ($cur < $ed) {
$this->setPixel($gd, $x1 + $sx, $y0, abs($cur) / $ed);
}
$y0 += $sy;
$dy -= $xy;
$err += $dx += $xx;
}
} while ($dy < $dx); /* gradient negates -> close curves */
}
$this->plotLine($gd, $x0, $y0, $x2, $y2);
}
protected function plotLine($gd, $x0, $y0, $x1, $y1)
{
$dx = abs($x1 - $x0);
$sx = $x0 < $x1 ? 1 : -1;
$dy = -abs($y1 - $y0);
$sy = $y0 < $y1 ? 1 : -1;
$err = $dx + $dy;
$e2 = $x2 = 0;
$ed = $dx - $dy == 0 ? 1 : sqrt((float)$dx * $dx + (float)$dy * $dy);
for (; ;) {
$this->setPixel($gd, $x0, $y0, abs($err - $dx - $dy) / $ed);
$e2 = $err;
$x2 = $x0;
if (2 * $e2 + $dx >= 0) {
if ($x0 == $x1) {
break;
}
if ($e2 - $dy < $ed) {
$this->setPixel($gd, $x0, $y0 + $sy, ($e2 - $dy) / $ed);
}
$err += $dy;
$x0 += $sx;
}
if (2 * $e2 + $dy <= 0) {
if ($y0 == $y1) {
break;
}
if ($dx - $e2 < $ed) {
$this->setPixel($gd, $x2 + $sx, $y0, ($dx - $e2) / $ed);
}
$err += $dx;
$y0 += $sy;
}
}
}
protected function setPixel($gd, $x, $y, $ar)
{
list($r, $g, $b) = $this->color->getRgb();
$c = imagecolorallocatealpha($gd, $r, $g, $b, 127 * $ar);
imagesetpixel($gd, $x, $y, $c);
}
}