Skip to content

Commit 2db1496

Browse files
committed
Geom.Polygon.Simplify is a new function that takes a polygon and simplifies the points by running them through a combination of Douglas-Peucker and Radial Distance algorithms, potentially dramatically reducing the number of points while retaining its shape.
1 parent 305ea40 commit 2db1496

2 files changed

Lines changed: 204 additions & 0 deletions

File tree

src/geom/polygon/Simplify.js

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
/**
2+
* @author Richard Davey <rich@photonstorm.com>
3+
* @author Vladimir Agafonkin
4+
* @see Based on Simplify.js mourner.github.io/simplify-js
5+
*/
6+
7+
/**
8+
* Copyright (c) 2017, Vladimir Agafonkin
9+
* All rights reserved.
10+
*
11+
* Redistribution and use in source and binary forms, with or without modification, are
12+
* permitted provided that the following conditions are met:
13+
*
14+
* 1. Redistributions of source code must retain the above copyright notice, this list of
15+
* conditions and the following disclaimer.
16+
*
17+
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
18+
* of conditions and the following disclaimer in the documentation and/or other materials
19+
* provided with the distribution.
20+
*
21+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
22+
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23+
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24+
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
28+
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30+
*/
31+
32+
'use strict';
33+
34+
/**
35+
* @ignore
36+
*/
37+
function getSqDist (p1, p2)
38+
{
39+
var dx = p1.x - p2.x,
40+
dy = p1.y - p2.y;
41+
42+
return dx * dx + dy * dy;
43+
}
44+
45+
/**
46+
* Square distance from a point to a segment
47+
*
48+
* @ignore
49+
*/
50+
function getSqSegDist (p, p1, p2)
51+
{
52+
var x = p1.x,
53+
y = p1.y,
54+
dx = p2.x - x,
55+
dy = p2.y - y;
56+
57+
if (dx !== 0 || dy !== 0)
58+
{
59+
var t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy);
60+
61+
if (t > 1)
62+
{
63+
x = p2.x;
64+
y = p2.y;
65+
}
66+
else if (t > 0)
67+
{
68+
x += dx * t;
69+
y += dy * t;
70+
}
71+
}
72+
73+
dx = p.x - x;
74+
dy = p.y - y;
75+
76+
return dx * dx + dy * dy;
77+
}
78+
79+
/**
80+
* Basic distance-based simplification
81+
*
82+
* @ignore
83+
*/
84+
function simplifyRadialDist (points, sqTolerance)
85+
{
86+
var prevPoint = points[0],
87+
newPoints = [ prevPoint ],
88+
point;
89+
90+
for (var i = 1, len = points.length; i < len; i++)
91+
{
92+
point = points[i];
93+
94+
if (getSqDist(point, prevPoint) > sqTolerance)
95+
{
96+
newPoints.push(point);
97+
prevPoint = point;
98+
}
99+
}
100+
101+
if (prevPoint !== point)
102+
{
103+
newPoints.push(point);
104+
}
105+
106+
return newPoints;
107+
}
108+
109+
/**
110+
* @ignore
111+
*/
112+
function simplifyDPStep (points, first, last, sqTolerance, simplified)
113+
{
114+
var maxSqDist = sqTolerance,
115+
index;
116+
117+
for (var i = first + 1; i < last; i++)
118+
{
119+
var sqDist = getSqSegDist(points[i], points[first], points[last]);
120+
121+
if (sqDist > maxSqDist)
122+
{
123+
index = i;
124+
maxSqDist = sqDist;
125+
}
126+
}
127+
128+
if (maxSqDist > sqTolerance)
129+
{
130+
if (index - first > 1)
131+
{
132+
simplifyDPStep(points, first, index, sqTolerance, simplified);
133+
}
134+
135+
simplified.push(points[index]);
136+
137+
if (last - index > 1)
138+
{
139+
simplifyDPStep(points, index, last, sqTolerance, simplified);
140+
}
141+
}
142+
}
143+
144+
/**
145+
* Simplification using Ramer-Douglas-Peucker algorithm
146+
*
147+
* @ignore
148+
*/
149+
function simplifyDouglasPeucker (points, sqTolerance)
150+
{
151+
var last = points.length - 1;
152+
153+
var simplified = [ points[0] ];
154+
155+
simplifyDPStep(points, 0, last, sqTolerance, simplified);
156+
157+
simplified.push(points[last]);
158+
159+
return simplified;
160+
}
161+
162+
/**
163+
* Takes a Polygon object and simplifies the points by running them through a combination of
164+
* Douglas-Peucker and Radial Distance algorithms. Simplification dramatically reduces the number of
165+
* points in a polygon while retaining its shape, giving a huge performance boost when processing
166+
* it and also reducing visual noise.
167+
*
168+
* @function Phaser.Geom.Polygon.Simplify
169+
* @since 3.50.0
170+
*
171+
* @generic {Phaser.Geom.Polygon} O - [polygon,$return]
172+
*
173+
* @param {Phaser.Geom.Polygon} polygon - The polygon to be simplified. The polygon will be modified in-place and returned.
174+
* @param {number} [tolerance=1] - Affects the amount of simplification (in the same metric as the point coordinates).
175+
* @param {boolean} [highestQuality=false] - Excludes distance-based preprocessing step which leads to highest quality simplification but runs ~10-20 times slower.
176+
*
177+
* @return {Phaser.Geom.Polygon} The input polygon.
178+
*/
179+
var Simplify = function (polygon, tolerance, highestQuality)
180+
{
181+
if (tolerance === undefined) { tolerance = 1; }
182+
if (highestQuality === undefined) { highestQuality = false; }
183+
184+
var points = polygon.points;
185+
186+
if (points.length <= 2)
187+
{
188+
return points;
189+
}
190+
191+
var sqTolerance = tolerance * tolerance;
192+
193+
if (!highestQuality)
194+
{
195+
points = simplifyRadialDist(points, sqTolerance);
196+
}
197+
198+
polygon.setTo(simplifyDouglasPeucker(points, sqTolerance));
199+
200+
return polygon;
201+
};
202+
203+
module.exports = Simplify;

src/geom/polygon/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Polygon.GetNumberArray = require('./GetNumberArray');
1414
Polygon.GetPoints = require('./GetPoints');
1515
Polygon.Perimeter = require('./Perimeter');
1616
Polygon.Reverse = require('./Reverse');
17+
Polygon.Simplify = require('./Simplify');
1718
Polygon.Smooth = require('./Smooth');
1819
Polygon.Translate = require('./Translate');
1920

0 commit comments

Comments
 (0)