/** * Get a instance of a Path. * * @param string or array $d the points * @param string $id of element * @param string or SVGStyle object $style of element * * @return SVGPath */ public static function getInstance($d, $id, $style) { $path = new SVGPath('<path></path>'); //if is as array make implode to glue it if (is_array($d)) { $d = implode(' ', $d); } $path->setAttribute('d', $d); $path->setId($id); $path->setAttribute('style', $style); return $path; }
private static function parseNode($node) { $type = $node->getName(); if ($type === 'g') { $element = new SVGGroup(); $children = $node->children(); foreach ($children as $child) { $element->addChild(self::parseNode($child)); } } else { if ($type === 'rect') { $w = isset($node['width']) ? $node['width'] : 0; $h = isset($node['height']) ? $node['height'] : 0; $x = isset($node['x']) ? $node['x'] : 0; $y = isset($node['y']) ? $node['y'] : 0; $element = new SVGRect($x, $y, $w, $h); } else { if ($type === 'circle') { $cx = isset($node['cx']) ? $node['cx'] : 0; $cy = isset($node['cy']) ? $node['cy'] : 0; $r = isset($node['r']) ? $node['r'] : 0; $element = new SVGCircle($cx, $cy, $r); } else { if ($type === 'ellipse') { $cx = isset($node['cx']) ? $node['cx'] : 0; $cy = isset($node['cy']) ? $node['cy'] : 0; $rx = isset($node['rx']) ? $node['rx'] : 0; $ry = isset($node['ry']) ? $node['ry'] : 0; $element = new SVGEllipse($cx, $cy, $rx, $ry); } else { if ($type === 'line') { $x1 = isset($node['x1']) ? $node['x1'] : 0; $y1 = isset($node['y1']) ? $node['y1'] : 0; $x2 = isset($node['x2']) ? $node['x2'] : 0; $y2 = isset($node['y2']) ? $node['y2'] : 0; $element = new SVGLine($x1, $y1, $x2, $y2); } else { if ($type === 'polygon' || $type === 'polyline') { $element = $type === 'polygon' ? new SVGPolygon() : new SVGPolyline(); $points = isset($node['points']) ? preg_split('/[\\s,]+/', $node['points']) : array(); for ($i = 0, $n = floor(count($points) / 2); $i < $n; $i++) { $element->addPoint($points[$i * 2], $points[$i * 2 + 1]); } } else { if ($type === 'path') { $d = isset($node['d']) ? $node['d'] : ''; $element = new SVGPath($d); } } } } } } } if (!isset($element)) { return false; } $attributes = $node->attributes(); $ignoredAttributes = array('x', 'y', 'width', 'height', 'x1', 'y1', 'x2', 'y2', 'cx', 'cy', 'r', 'rx', 'ry', 'points', 'style'); foreach ($attributes as $attribute => $value) { if (in_array($attribute, $ignoredAttributes)) { continue; } $element->setStyle($attribute, $value); } if (isset($node['style'])) { $styles = self::parseStyles($node['style']); foreach ($styles as $style => $value) { $element->setStyle($style, $value); } } return $element; }
for ($x = 0; $x < $imgW; $x++) { for ($y = 0; $y < $imgH; $y++) { $rgb = imagecolorat($img, $x, $y); $color = imagecolorsforindex($img, $rgb); if ($color['alpha'] < 126) { $hex = RGBToHex($color['red'], $color['green'], $color['blue']); //$rect = SVGRect::getInstance( $x, $y, null, 1, 1, new SVGStyle( array( 'fill' => $hex ) ) ); //$d = "m $x,$y 1,0 0,1 -1,0 z"; $x1 = $x + 1; $y1 = $y + 1; $d = "M {$x},{$y} {$x},{$y1} {$x1},{$y1} {$x1},{$y} "; @($paths[$hex] .= $d); /* @$paths[$hex][$x . ',' . $y] = $x . ',' . $y; @$paths[$hex][$x . ',' . $y1] = $x . ',' . $y1; @$paths[$hex][$x1 . ',' . $y1] = $x1 . ',' . $y1; @$paths[$hex][$x1 . ',' . $y] = $x1 . ',' . $y; */ //M 25,20 25,21 26,21 26,20 z //$path = SVGPath::getInstance( $d, null, new SVGStyle( array( 'fill' => $hex ) ) ); //$svg->append( $path ); } } } echo '<pre>'; foreach ($paths as $hex => $d) { //$d = implode( ' L ', $res ); $path = SVGPath::getInstance($d . ' z', null, new SVGStyle(array('fill' => $hex))); $svg->append($path); } $svg->asXML($svgPath, TRUE); echo "<img src='{$imagePath}'/>"; echo '<embed style="border:solid 1px gray;" src="' . $svgPath . '" type="image/svg+xml" pluginspage="http://www.adobe.com/svg/viewer/install/" /><br / >';
private function parseLines() { /* Set standard line options */ $this->svgObjects->pushGroup('lines'); $this->svgObjects->setOption('stroke', 'black'); $this->svgObjects->setOption('stroke-width', '2'); $this->svgObjects->setOption('fill', 'none'); /* The grid is not uniform, so we need to determine the longest row. */ $maxCols = 0; $bound = count($this->grid); for ($r = 0; $r < $bound; $r++) { $maxCols = max($maxCols, count($this->grid[$r])); } for ($c = 0; $c < $maxCols; $c++) { for ($r = 0; $r < $bound; $r++) { /* This gets set if we find a line-start here. */ $dir = false; $line = new SVGPath(); /* * Since the column count isn't uniform, don't attempt to handle any * rows that don't extend out this far. */ if (!isset($this->grid[$r][$c])) { continue; } $char = $this->getChar($r, $c); switch ($char) { /* * Do marker characters first. These are the easiest because they are * basically guaranteed to represent the start of the line. */ case '<': $e = $this->getChar($r, $c + 1); if ($this->isEdge($e, self::DIR_RIGHT) || $this->isCorner($e)) { $line->addMarker($c, $r, Point::IMARKER); $dir = self::DIR_RIGHT; } else { $se = $this->getChar($r + 1, $c + 1); $ne = $this->getChar($r - 1, $c + 1); if ($se == "\\") { $line->addMarker($c, $r, Point::IMARKER); $dir = self::DIR_SE; } elseif ($ne == '/') { $line->addMarker($c, $r, Point::IMARKER); $dir = self::DIR_NE; } } break; case '^': $s = $this->getChar($r + 1, $c); if ($this->isEdge($s, self::DIR_DOWN) || $this->isCorner($s)) { $line->addMarker($c, $r, Point::IMARKER); $dir = self::DIR_DOWN; } elseif ($this->getChar($r + 1, $c + 1) == "\\") { /* Don't need to check west for diagonals. */ $line->addMarker($c, $r, Point::IMARKER); $dir = self::DIR_SE; } break; case '>': $w = $this->getChar($r, $c - 1); if ($this->isEdge($w, self::DIR_LEFT) || $this->isCorner($w)) { $line->addMarker($c, $r, Point::IMARKER); $dir = self::DIR_LEFT; } /* All diagonals come from west, so we don't need to check */ break; case 'v': $n = $this->getChar($r - 1, $c); if ($this->isEdge($n, self::DIR_UP) || $this->isCorner($n)) { $line->addMarker($c, $r, Point::IMARKER); $dir = self::DIR_UP; } elseif ($this->getChar($r - 1, $c + 1) == '/') { $line->addMarker($c, $r, Point::IMARKER); $dir = self::DIR_NE; } break; /* * Edges are handled specially. We have to look at the context of the * edge to determine whether it's the start of a line. A vertical edge * can appear as the start of a line in the following circumstances: * * +------------- +-------------- +---- | (s) * | | | | * | | (s) +-------+ |(s) | * +------+ | (s) * * From this we can extrapolate that we are a starting edge if our * southern neighbor is a vertical edge or corner, but we have no line * material to our north (and vice versa). This logic does allow for * the southern / northern neighbor to be part of a separate * horizontal line. */ /* * Edges are handled specially. We have to look at the context of the * edge to determine whether it's the start of a line. A vertical edge * can appear as the start of a line in the following circumstances: * * +------------- +-------------- +---- | (s) * | | | | * | | (s) +-------+ |(s) | * +------+ | (s) * * From this we can extrapolate that we are a starting edge if our * southern neighbor is a vertical edge or corner, but we have no line * material to our north (and vice versa). This logic does allow for * the southern / northern neighbor to be part of a separate * horizontal line. */ case ':': $line->setOption('stroke-dasharray', '5 5'); /* FALLTHROUGH */ /* FALLTHROUGH */ case '|': $n = $this->getChar($r - 1, $c); $s = $this->getChar($r + 1, $c); if (($s == '|' || $s == ':' || $this->isCorner($s)) && $n != '|' && $n != ':' && !$this->isCorner($n) && $n != '^') { $dir = self::DIR_DOWN; } elseif (($n == '|' || $n == ':' || $this->isCorner($n)) && $s != '|' && $s != ':' && !$this->isCorner($s) && $s != 'v') { $dir = self::DIR_UP; } break; /* * Horizontal edges have the same properties for search as vertical * edges, except we need to look east / west. The diagrams for the * vertical case are still accurate to visualize this case; just * mentally turn them 90 degrees clockwise. */ /* * Horizontal edges have the same properties for search as vertical * edges, except we need to look east / west. The diagrams for the * vertical case are still accurate to visualize this case; just * mentally turn them 90 degrees clockwise. */ case '=': $line->setOption('stroke-dasharray', '5 5'); /* FALLTHROUGH */ /* FALLTHROUGH */ case '-': $w = $this->getChar($r, $c - 1); $e = $this->getChar($r, $c + 1); if (($w == '-' || $w == '=' || $this->isCorner($w)) && $e != '=' && $e != '-' && !$this->isCorner($e) && $e != '>') { $dir = self::DIR_LEFT; } elseif (($e == '-' || $e == '=' || $this->isCorner($e)) && $w != '=' && $w != '-' && !$this->isCorner($w) && $w != '<') { $dir = self::DIR_RIGHT; } break; /* * We can only find diagonals going north or south and east. This is * simplified due to the fact that they have no corners. We are * guaranteed to run into their westernmost point or their relevant * marker. */ /* * We can only find diagonals going north or south and east. This is * simplified due to the fact that they have no corners. We are * guaranteed to run into their westernmost point or their relevant * marker. */ case '/': $ne = $this->getChar($r - 1, $c + 1); if ($ne == '/' || $ne == '^' || $ne == '>') { $dir = self::DIR_NE; } break; case "\\": $se = $this->getChar($r + 1, $c + 1); if ($se == "\\" || $se == "v" || $se == '>') { $dir = self::DIR_SE; } break; /* * The corner case must consider all four directions. Though a * reasonable person wouldn't use slant corners for this, they are * considered corners, so it kind of makes sense to handle them the * same way. For this case, envision the starting point being a corner * character in both the horizontal and vertical case. And then * mentally overlay them and consider that :). */ /* * The corner case must consider all four directions. Though a * reasonable person wouldn't use slant corners for this, they are * considered corners, so it kind of makes sense to handle them the * same way. For this case, envision the starting point being a corner * character in both the horizontal and vertical case. And then * mentally overlay them and consider that :). */ case '+': case '#': $ne = $this->getChar($r - 1, $c + 1); $se = $this->getChar($r + 1, $c + 1); if ($ne == '/' || $ne == '^' || $ne == '>') { $dir = self::DIR_NE; } elseif ($se == "\\" || $se == "v" || $se == '>') { $dir = self::DIR_SE; } /* FALLTHROUGH */ /* FALLTHROUGH */ case '.': case "'": $n = $this->getChar($r - 1, $c); $w = $this->getChar($r, $c - 1); $s = $this->getChar($r + 1, $c); $e = $this->getChar($r, $c + 1); if (($w == '=' || $w == '-') && $n != '|' && $n != ':' && $w != '-' && $e != '=' && $e != '|' && $s != ':') { $dir = self::DIR_LEFT; } elseif (($e == '=' || $e == '-') && $n != '|' && $n != ':' && $w != '-' && $w != '=' && $s != '|' && $s != ':') { $dir = self::DIR_RIGHT; } elseif (($s == '|' || $s == ':') && $n != '|' && $n != ':' && $w != '-' && $w != '=' && $e != '-' && $e != '=' && ($char != '.' && $char != "'" || $char == '.' && $s != '.' || $char == "'" && $s != "'")) { $dir = self::DIR_DOWN; } elseif (($n == '|' || $n == ':') && $s != '|' && $s != ':' && $w != '-' && $w != '=' && $e != '-' && $e != '=' && ($char != '.' && $char != "'" || $char == '.' && $s != '.' || $char == "'" && $s != "'")) { $dir = self::DIR_UP; } break; } /* It does actually save lines! */ if ($dir !== false) { $rInc = 0; $cInc = 0; if (!$this->isMarker($char)) { $line->addPoint($c, $r); } /* * The walk routine may attempt to add the point again, so skip it. * If we don't, we can miss the line or end up with just a point. */ if ($dir == self::DIR_UP) { $rInc = -1; $cInc = 0; } elseif ($dir == self::DIR_DOWN) { $rInc = 1; $cInc = 0; } elseif ($dir == self::DIR_RIGHT) { $rInc = 0; $cInc = 1; } elseif ($dir == self::DIR_LEFT) { $rInc = 0; $cInc = -1; } elseif ($dir == self::DIR_NE) { $rInc = -1; $cInc = 1; } elseif ($dir == self::DIR_SE) { $rInc = 1; $cInc = 1; } /* * Walk the points of this line. Note we don't use wallFollow; we are * operating under the assumption that lines do not meander. (And, in * any event, that algorithm is intended to find a closed object.) */ $this->walk($line, $r + $rInc, $c + $cInc, $dir); /* * Remove it so that we don't confuse any other lines. This leaves * corners in tact, still. */ $this->clearObject($line); $this->svgObjects->addObject($line); /* We may be able to find more lines starting from this same point */ if ($this->isCorner($char)) { $r--; } } } } $this->svgObjects->popGroup(); }