use better polyline simplification

This commit is contained in:
Skyler Lehmkuhl 2024-11-16 05:39:23 -05:00
parent caa6aa8cee
commit 277aec918c
3 changed files with 140 additions and 19 deletions

View File

@ -7,7 +7,7 @@
<script src="coloris.js"></script> <script src="coloris.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tauri App</title> <title>Tauri App</title>
<!-- <script type="module" src="/fit-curve.js"></script> --> <script type="module" src="/simplify.js"></script>
<script type="module" src="/main.js" defer></script> <script type="module" src="/main.js" defer></script>
</head> </head>

View File

@ -1,6 +1,8 @@
const { invoke } = window.__TAURI__.core; const { invoke } = window.__TAURI__.core;
import * as fitCurve from '/fit-curve.js'; import * as fitCurve from '/fit-curve.js';
let simplifyPolyline = simplify
let greetInputEl; let greetInputEl;
let greetMsgEl; let greetMsgEl;
let rootPane; let rootPane;
@ -96,27 +98,23 @@ class Shape {
addCurve(curve) { addCurve(curve) {
this.curves.push(curve) this.curves.push(curve)
} }
simplify(mode="smooth") { simplify(mode="corners") {
// Mode can be corners, smooth or auto // Mode can be corners, smooth or auto
if (mode=="corners") { if (mode=="corners") {
let angles; let points = [{x: this.startx, y: this.starty}]
while (this.curves.length > 3) { points = points.concat(this.curves)
angles = [2*Math.PI] let newpoints = simplifyPolyline(points, 10, false)
for (let i=1; i<this.curves.length-1; i++) { console.log(points.length)
let P1 = this.curves[i] console.log(newpoints.length)
let P2 = this.curves[i-1] this.curves = []
let P3 = this.curves[i+1] let lastpoint = newpoints.shift()
let angle = Math.atan2(P3.y - P1.y, P3.x - P1.x) - let midpoint
Math.atan2(P2.y - P1.y, P2.x - P1.x); for (let point of newpoints) {
angles[i] = Math.abs(Math.PI - Math.abs(angle)) midpoint = {x: (lastpoint.x+point.x)/2, y: (lastpoint.y+point.y)/2}
} this.curves.push(new Curve(midpoint.x, midpoint.y,midpoint.x,midpoint.y,point.x,point.y))
let smallestAngle = Math.min(...angles) lastpoint = point
if (smallestAngle < maxSmoothAngle) {
this.curves.splice(angles.indexOf(smallestAngle), 1)
} else {
break;
}
} }
console.log(this.curves)
} else if (mode=="smooth") { } else if (mode=="smooth") {
let error = 30; let error = 30;
let points = [[this.startx, this.starty]] let points = [[this.startx, this.starty]]

123
src/simplify.js Normal file
View File

@ -0,0 +1,123 @@
/*
(c) 2017, Vladimir Agafonkin
Simplify.js, a high-performance JS polyline simplification library
mourner.github.io/simplify-js
*/
(function () { 'use strict';
// to suit your point format, run search/replace for '.x' and '.y';
// for 3D version, see 3d branch (configurability would draw significant performance overhead)
// square distance between 2 points
function getSqDist(p1, p2) {
var dx = p1.x - p2.x,
dy = p1.y - p2.y;
return dx * dx + dy * dy;
}
// square distance from a point to a segment
function getSqSegDist(p, p1, p2) {
var x = p1.x,
y = p1.y,
dx = p2.x - x,
dy = p2.y - y;
if (dx !== 0 || dy !== 0) {
var t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy);
if (t > 1) {
x = p2.x;
y = p2.y;
} else if (t > 0) {
x += dx * t;
y += dy * t;
}
}
dx = p.x - x;
dy = p.y - y;
return dx * dx + dy * dy;
}
// rest of the code doesn't care about point format
// basic distance-based simplification
function simplifyRadialDist(points, sqTolerance) {
var prevPoint = points[0],
newPoints = [prevPoint],
point;
for (var i = 1, len = points.length; i < len; i++) {
point = points[i];
if (getSqDist(point, prevPoint) > sqTolerance) {
newPoints.push(point);
prevPoint = point;
}
}
if (prevPoint !== point) newPoints.push(point);
return newPoints;
}
function simplifyDPStep(points, first, last, sqTolerance, simplified) {
var maxSqDist = sqTolerance,
index;
for (var i = first + 1; i < last; i++) {
var sqDist = getSqSegDist(points[i], points[first], points[last]);
if (sqDist > maxSqDist) {
index = i;
maxSqDist = sqDist;
}
}
if (maxSqDist > sqTolerance) {
if (index - first > 1) simplifyDPStep(points, first, index, sqTolerance, simplified);
simplified.push(points[index]);
if (last - index > 1) simplifyDPStep(points, index, last, sqTolerance, simplified);
}
}
// simplification using Ramer-Douglas-Peucker algorithm
function simplifyDouglasPeucker(points, sqTolerance) {
var last = points.length - 1;
var simplified = [points[0]];
simplifyDPStep(points, 0, last, sqTolerance, simplified);
simplified.push(points[last]);
return simplified;
}
// both algorithms combined for awesome performance
function simplify(points, tolerance, highestQuality) {
if (points.length <= 2) return points;
var sqTolerance = tolerance !== undefined ? tolerance * tolerance : 1;
points = highestQuality ? points : simplifyRadialDist(points, sqTolerance);
points = simplifyDouglasPeucker(points, sqTolerance);
return points;
}
// export as AMD module / Node module / browser or worker variable
if (typeof define === 'function' && define.amd) define(function() { return simplify; });
else if (typeof module !== 'undefined') {
module.exports = simplify;
module.exports.default = simplify;
} else if (typeof self !== 'undefined') self.simplify = simplify;
else window.simplify = simplify;
})();