├── .gitignore ├── .idea └── php.xml ├── LICENSE ├── README.md ├── Simplify.php ├── composer.json └── test.php /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | -------------------------------------------------------------------------------- /.idea/php.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Vladimir Agafonkin 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are 5 | permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this list of 8 | conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 | of conditions and the following disclaimer in the documentation and/or other materials 12 | provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 15 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 16 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 17 | COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 21 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | simplify-php 2 | ============ 3 | 4 | PHP port of https://github.com/mourner/simplify-js 5 | 6 | 7 | Run 8 | === 9 | 10 | 11 | ```php 12 | require_once 'vendor/autoload.php'; 13 | 14 | Simplify::run($points, $tolerance, $highestQuality); 15 | 16 | ``` 17 | 18 | Install 19 | ======= 20 | 21 | composer package name is andreychumak/simplify-php 22 | -------------------------------------------------------------------------------- /Simplify.php: -------------------------------------------------------------------------------- 1 | $sqTolerance) { 43 | $newPoints[] = $point; 44 | $prevPoint = $point; 45 | } 46 | } 47 | 48 | if ($prevPoint !== $point) $newPoints[] = $point; 49 | 50 | return $newPoints; 51 | } 52 | 53 | // square distance between 2 points 54 | private static function getSqDist($p1, $p2) { 55 | 56 | $dx = $p1['x'] - $p2['x']; 57 | $dy = $p1['y'] - $p2['y']; 58 | 59 | return $dx * $dx + $dy * $dy; 60 | } 61 | 62 | // simplification using optimized Douglas-Peucker algorithm with recursion elimination 63 | private static function simplifyDouglasPeucker($points, $sqTolerance) { 64 | 65 | $len = count($points); 66 | $markers = array_fill(0, $len-1, null); 67 | $first = 0; 68 | $last = $len - 1; 69 | $stack = array(); 70 | $newPoints = array(); 71 | $index = null; 72 | 73 | $markers[$first] = $markers[$last] = 1; 74 | 75 | while ($last) { 76 | 77 | $maxSqDist = 0; 78 | 79 | for ($i = $first + 1; $i < $last; $i++) { 80 | $sqDist = self::getSqSegDist($points[$i], $points[$first], $points[$last]); 81 | 82 | if ($sqDist > $maxSqDist) { 83 | $index = $i; 84 | $maxSqDist = $sqDist; 85 | } 86 | } 87 | 88 | if ($maxSqDist > $sqTolerance) { 89 | $markers[$index] = 1; 90 | array_push($stack, $first, $index, $index, $last); 91 | } 92 | 93 | $last = array_pop($stack); 94 | $first = array_pop($stack); 95 | } 96 | 97 | //var_dump($markers, $points, $i); 98 | for ($i = 0; $i < $len; $i++) { 99 | if ($markers[$i]) $newPoints[] = $points[$i]; 100 | } 101 | 102 | return $newPoints; 103 | } 104 | 105 | // square distance from a point to a segment 106 | private static function getSqSegDist($p, $p1, $p2) { 107 | $x = $p1['x']; 108 | $y = $p1['y']; 109 | $dx = $p2['x'] - $x; 110 | $dy = $p2['y'] - $y; 111 | 112 | if (intval($dx) !== 0 || intval($dy) !== 0) { 113 | 114 | $t = (($p['x'] - $x) * $dx + ($p['y'] - $y) * $dy) / ($dx * $dx + $dy * $dy); 115 | 116 | if ($t > 1) { 117 | $x = $p2['x']; 118 | $y = $p2['y']; 119 | 120 | } else if ($t > 0) { 121 | $x += $dx * $t; 122 | $y += $dy * $t; 123 | } 124 | } 125 | 126 | $dx = $p['x'] - $x; 127 | $dy = $p['y'] - $y; 128 | 129 | return $dx * $dx + $dy * $dy; 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "andreychumak/simplify-php", 3 | "license": "BSD-2-Clause", 4 | "version": "1.0.2" 5 | } 6 | -------------------------------------------------------------------------------- /test.php: -------------------------------------------------------------------------------- 1 | 224.55,'y'=>250.15),array('x'=>226.91,'y'=>244.19),array('x'=>233.31,'y'=>241.45),array('x'=>234.98,'y'=>236.06), 5 | array('x'=>244.21,'y'=>232.76),array('x'=>262.59,'y'=>215.31),array('x'=>267.76,'y'=>213.81),array('x'=>273.57,'y'=>201.84), 6 | array('x'=>273.12,'y'=>192.16),array('x'=>277.62,'y'=>189.03),array('x'=>280.36,'y'=>181.41),array('x'=>286.51,'y'=>177.74), 7 | array('x'=>292.41,'y'=>159.37),array('x'=>296.91,'y'=>155.64),array('x'=>314.95,'y'=>151.37),array('x'=>319.75,'y'=>145.16), 8 | array('x'=>330.33,'y'=>137.57),array('x'=>341.48,'y'=>139.96),array('x'=>369.98,'y'=>137.89),array('x'=>387.39,'y'=>142.51), 9 | array('x'=>391.28,'y'=>139.39),array('x'=>409.52,'y'=>141.14),array('x'=>414.82,'y'=>139.75),array('x'=>427.72,'y'=>127.30), 10 | array('x'=>439.60,'y'=>119.74),array('x'=>474.93,'y'=>107.87),array('x'=>486.51,'y'=>106.75),array('x'=>489.20,'y'=>109.45), 11 | array('x'=>493.79,'y'=>108.63),array('x'=>504.74,'y'=>119.66),array('x'=>512.96,'y'=>122.35),array('x'=>518.63,'y'=>120.89), 12 | array('x'=>524.09,'y'=>126.88),array('x'=>529.57,'y'=>127.86),array('x'=>534.21,'y'=>140.93),array('x'=>539.27,'y'=>147.24), 13 | array('x'=>567.69,'y'=>148.91),array('x'=>575.25,'y'=>157.26),array('x'=>580.62,'y'=>158.15),array('x'=>601.53,'y'=>156.85), 14 | array('x'=>617.74,'y'=>159.86),array('x'=>622.00,'y'=>167.04),array('x'=>629.55,'y'=>194.60),array('x'=>638.90,'y'=>195.61), 15 | array('x'=>641.26,'y'=>200.81),array('x'=>651.77,'y'=>204.56),array('x'=>671.55,'y'=>222.55),array('x'=>683.68,'y'=>217.45), 16 | array('x'=>695.25,'y'=>219.15),array('x'=>700.64,'y'=>217.98),array('x'=>703.12,'y'=>214.36),array('x'=>712.26,'y'=>215.87), 17 | array('x'=>721.49,'y'=>212.81),array('x'=>727.81,'y'=>213.36),array('x'=>729.98,'y'=>208.73),array('x'=>735.32,'y'=>208.20), 18 | array('x'=>739.94,'y'=>204.77),array('x'=>769.98,'y'=>208.42),array('x'=>779.60,'y'=>216.87),array('x'=>784.20,'y'=>218.16), 19 | array('x'=>800.24,'y'=>214.62),array('x'=>810.53,'y'=>219.73),array('x'=>817.19,'y'=>226.82),array('x'=>820.77,'y'=>236.17), 20 | array('x'=>827.23,'y'=>236.16),array('x'=>829.89,'y'=>239.89),array('x'=>851.00,'y'=>248.94),array('x'=>859.88,'y'=>255.49), 21 | array('x'=>865.21,'y'=>268.53),array('x'=>857.95,'y'=>280.30),array('x'=>865.48,'y'=>291.45),array('x'=>866.81,'y'=>298.66), 22 | array('x'=>864.68,'y'=>302.71),array('x'=>867.79,'y'=>306.17),array('x'=>859.87,'y'=>311.37),array('x'=>860.08,'y'=>314.35), 23 | array('x'=>858.29,'y'=>314.94),array('x'=>858.10,'y'=>327.60),array('x'=>854.54,'y'=>335.40),array('x'=>860.92,'y'=>343.00), 24 | array('x'=>856.43,'y'=>350.15),array('x'=>851.42,'y'=>352.96),array('x'=>849.84,'y'=>359.59),array('x'=>854.56,'y'=>365.53), 25 | array('x'=>849.74,'y'=>370.38),array('x'=>844.09,'y'=>371.89),array('x'=>844.75,'y'=>380.44),array('x'=>841.52,'y'=>383.67), 26 | array('x'=>839.57,'y'=>390.40),array('x'=>845.59,'y'=>399.05),array('x'=>848.40,'y'=>407.55),array('x'=>843.71,'y'=>411.30), 27 | array('x'=>844.09,'y'=>419.88),array('x'=>839.51,'y'=>432.76),array('x'=>841.33,'y'=>441.04),array('x'=>847.62,'y'=>449.22), 28 | array('x'=>847.16,'y'=>458.44),array('x'=>851.38,'y'=>462.79),array('x'=>853.97,'y'=>471.15),array('x'=>866.36,'y'=>480.77) 29 | ); 30 | 31 | $simplified = array( 32 | array('x'=>224.55,'y'=>250.15),array('x'=>267.76,'y'=>213.81),array('x'=>296.91,'y'=>155.64),array('x'=>330.33,'y'=>137.57), 33 | array('x'=>409.52,'y'=>141.14),array('x'=>439.60,'y'=>119.74),array('x'=>486.51,'y'=>106.75),array('x'=>529.57,'y'=>127.86), 34 | array('x'=>539.27,'y'=>147.24),array('x'=>617.74,'y'=>159.86),array('x'=>629.55,'y'=>194.60),array('x'=>671.55,'y'=>222.55), 35 | array('x'=>727.81,'y'=>213.36),array('x'=>739.94,'y'=>204.77),array('x'=>769.98,'y'=>208.42),array('x'=>779.60,'y'=>216.87), 36 | array('x'=>800.24,'y'=>214.62),array('x'=>820.77,'y'=>236.17),array('x'=>859.88,'y'=>255.49),array('x'=>865.21,'y'=>268.53), 37 | array('x'=>857.95,'y'=>280.30),array('x'=>867.79,'y'=>306.17),array('x'=>859.87,'y'=>311.37),array('x'=>854.54,'y'=>335.40), 38 | array('x'=>860.92,'y'=>343.00),array('x'=>849.84,'y'=>359.59),array('x'=>854.56,'y'=>365.53),array('x'=>844.09,'y'=>371.89), 39 | array('x'=>839.57,'y'=>390.40),array('x'=>848.40,'y'=>407.55),array('x'=>839.51,'y'=>432.76),array('x'=>853.97,'y'=>471.15), 40 | array('x'=>866.36,'y'=>480.77) 41 | ); 42 | 43 | require 'Simplify.php'; 44 | 45 | 46 | // TODO PHPUnit 47 | echo serialize($simplified) == serialize(Simplify::run($points, 5)) ? 'pass' : 'fail'; 48 | echo PHP_EOL; 49 | --------------------------------------------------------------------------------