Your IP : 216.73.216.48


Current Path : /hdd/hdd11/www/vlevski/enter/con/generate/lib/
Upload File :
Current File : /hdd/hdd11/www/vlevski/enter/con/generate/lib/VMLPath.php

<?php

class VMLPath {

	/**
	 * @param string $type
	 * @param array $coords
	 * @return string
	 */
	private static function commandToString($type, $coords)
	{
		if (!is_array($coords))
		{
			$coords = func_get_args();

			array_shift($coords);
		}

		return $type . implode(',', $coords);
	}

	/**
	 * @param string $path
	 * @return VMLPath
	 */
	public static function fromSVG($path)
	{
		$vml = new VMLPath();

		$matches = array();

		if (!preg_match_all('/([a-zA-Z])([0-9. \-,]*)/', $path, $matches, PREG_SET_ORDER))
		{
			return $vml;
		}

		$at = (object) array('x' => 0, 'y' => 0);
		$cp = (object) array('x' => 0, 'y' => 0);

		$previous = null;

		$zm = false;

		for (; $set = current($matches); next($matches))
		{
			list($cmd, $coords) = array($set[1], array_map('floatval', preg_split('/(?:,|\s+)/', trim($set[2]))));

			switch ($cmd)
			{
				case 'z':
				case 'Z':
					if (strcasecmp($previous, 'm') === 0)
					{
						$vml->pop();
					}
					if ($zm) // ignore chained zm-commands
					{
						$vml->pop();
					}
					$vml->closePath();
					break;
				case 'M':
					if (strcasecmp($previous, 'm') === 0)
					{
						$vml->pop();
					}
					$vml->moveTo(
						$at->x = $coords[0],
						$at->y = $coords[1]
					);
					break;
				case 'L':
					$vml->lineTo(
						$at->x = $coords[0],
						$at->y = $coords[1]
					);
					break;
				case 'l':
					$vml->lineTo(
						$at->x += $coords[0],
						$at->y += $coords[1]
					);
					break;
				case 'H':
					$vml->lineTo(
						$at->x = $coords[0],
						$at->y
					);
					break;
				case 'h':
					$vml->lineTo(
						$at->x += $coords[0],
						$at->y
					);
					break;
				case 'V':
					$vml->lineTo(
						$at->x,
						$at->y = $coords[0]
					);
					break;
				case 'v':
					$vml->lineTo(
						$at->x,
						$at->y += $coords[0]
					);
					break;
				case 'C':
					$vml->bezierCurveTo(
						$coords[0],
						$coords[1],
						$cp->x = $coords[2],
						$cp->y = $coords[3],
						$at->x = $coords[4],
						$at->y = $coords[5]
					);
					break;
				case 'c':
					$vml->bezierCurveTo(
						$at->x + $coords[0],
						$at->y + $coords[1],
						$cp->x = $at->x + $coords[2],
						$cp->y = $at->y + $coords[3],
						$at->x += $coords[4],
						$at->y += $coords[5]
					);
					break;
				case 'S':
					if (!$previous || !preg_match('/^[CcSs]$/', $previous))
					{
						$cp->x = $at->x;
						$cp->y = $at->y;
					}
					$vml->bezierCurveTo(
						$at->x + ($at->x - $cp->x),
						$at->y + ($at->y - $cp->y),
						$cp->x = $coords[0],
						$cp->y = $coords[1],
						$at->x = $coords[2],
						$at->y = $coords[3]
					);
					break;
				case 's':
					if (!$previous || !preg_match('/^[CcSs]$/', $previous))
					{
						$cp->x = $at->x;
						$cp->y = $at->y;
					}
					$vml->bezierCurveTo(
						$at->x + ($at->x - $cp->x),
						$at->y + ($at->y - $cp->y),
						$cp->x = $at->x + $coords[0],
						$cp->y = $at->y + $coords[1],
						$at->x += $coords[2],
						$at->y += $coords[3]
					);
					break;
				case 'Q':
					$vml->quadraticCurveTo(
						$cp->x = $coords[0],
						$cp->y = $coords[1],
						$at->x = $coords[2],
						$at->y = $coords[3]
					);
					break;
				case 'q':
					$vml->quadraticCurveTo(
						$cp->x = $at->x + $coords[0],
						$cp->y = $at->y + $coords[1],
						$at->x += $coords[2],
						$at->y += $coords[3]
					);
					break;
				case 'T':
					if (!$previous || !preg_match('/^[QqTt]$/', $previous))
					{
						$cp->x = $at->x;
						$cp->y = $at->y;
					}
					$vml->quadraticCurveTo(
						$cp->x = $at->x + ($at->x - $cp->x),
						$cp->y = $at->y + ($at->y - $cp->y),
						$at->x = $coords[0],
						$at->y = $coords[1]
					);
					break;
				case 't':
					if (!$previous || !preg_match('/^[QqTt]$/', $previous))
					{
						$cp->x = $at->x;
						$cp->y = $at->y;
					}
					$vml->quadraticCurveTo(
						$cp->x = $at->x + ($at->x - $cp->x),
						$cp->y = $at->y + ($at->y - $cp->y),
						$at->x += $coords[0],
						$at->y += $coords[1]
					);
					break;
				case 'A':
				case 'a':
					break;

			}

			$zm = strcasecmp($cmd, 'm') === 0 && strcasecmp($previous, 'z') === 0;

			$previous = $cmd;
		}

		$vml->endPath();

		return $vml;
	}

	private $parts = array();

	private $atX = 0;
	private $atY = 0;

	private $cpX = 0;
	private $cpY = 0;

	private $remainderX = 0;
	private $remainderY = 0;

	/**
	 * @return void
	 */
	public function __construct()
	{
	}

	/**
	 * @return string
	 */
	public function __toString()
	{
		return implode('', $this->parts);
	}

	/**
	 * @param float $cp1x
	 * @param float $cp1y
	 * @param float $cp2x
	 * @param float $cp2y
	 * @param float $toX
	 * @param float $toY
	 * @return VMLPath
	 */
	public function bezierCurveTo($cp1x, $cp1y, $cp2x, $cp2y, $toX, $toY)
	{
		$this->parts[] = $this->draw('v', $cp1x, $cp1y, $cp2x, $cp2y, $toX, $toY);

		return $this;
	}

	/**
	 * @return VMLPath
	 */
	public function closePath()
	{
		$this->parts[] = 'x';

		return $this;
	}

	/**
	 * Returns a shortened, relative VML path part
	 *
	 * @param string $type
	 * @param mixed $coords
	 * @return string
	 */
	private function draw($type, $coords)
	{
		if (!is_array($coords))
		{
			$coords = func_get_args();

			array_shift($coords);
		}

		$toY = array_pop($coords);
		$toX = array_pop($coords);

		$parts = array();

		foreach ($coords as $i => $coord)
		{
			$parts[] = $coord === 0 ? '' : round($coord - ($i % 2 ? $this->atY : $this->atX));
		}

		$parts[] = $this->moveX($toX);
		$parts[] = $this->moveY($toY);

		return self::commandToString($type, $parts);
	}

	/**
	 * @return VMLPath
	 */
	public function endPath()
	{
		$this->parts[] = 'e';

		return $this;
	}

	/**
	 * @param float $toX
	 * @param float $toY
	 * @return VMLPath
	 */
	public function lineTo($toX, $toY)
	{
		$this->parts[] = $this->draw('r', $toX, $toY);

		return $this;
	}

	/**
	 * @param float $toX
	 * @param float $toY
	 * @return VMLPath
	 */
	public function moveTo($toX, $toY)
	{
		$this->moveX($toX);
		$this->moveY($toY);

		$this->parts[] = self::commandToString('m', $this->atX, $this->atY);

		return $this;
	}

	/**
	 * @param float $to
	 * @return int
	 */
	private function moveX($to)
	{
		$delta = $to - $this->atX;

		$rounded = round($delta);

		$this->atX += $rounded;

		return $rounded;
	}

	/**
	 * @param float $to
	 * @return int
	 */
	private function moveY($to)
	{
		$delta = $to - $this->atY;

		$rounded = round($delta);

		$this->atY += $rounded;

		return $rounded;
	}

	/**
	 * Removes the last point from the path. Note: this does NOT
	 * reset atX, atY and others to their prior values.
	 *
	 * @return VMLPath
	 */
	public function pop()
	{
		array_pop($this->parts);

		return $this;
	}

	/**
	 * IE has some trouble drawing quadratic beziers so we'll just convert
	 * them to cubic ones which it can handle just fine.
	 *
	 * @link http://developer.mozilla.org/en/Canvas_tutorial/Drawing_shapes#Firefox_1.5_quadraticCurveTo()_bug_workaround
	 * @param float $atX
	 * @param float $atY
	 * @param float $cpX
	 * @param float $cpY
	 * @param float $toX
	 * @param float $toY
	 * @return VMLPath
	 */
	private function quadraticCurveTo($cpX, $cpY, $toX, $toY)
	{
		$cp1 = (object) array(
			'x' => $this->atX + 2 / 3 * ($cpX - $this->atX),
			'y' => $this->atY + 2 / 3 * ($cpY - $this->atY)
		);

		$cp2 = (object) array(
			'x' => $cp1->x + ($toX - $this->atX) / 3,
			'y' => $cp1->y + ($toY - $this->atY) / 3
		);

		return $this->bezierCurveTo(
			$cp1->x,
			$cp1->y,
			$cp2->x,
			$cp2->y,
			$toX,
			$toY
		);
	}

}