poly-simplify
Version:
Simplify polyline or polygon vertices in JS
4 lines (3 loc) • 15 kB
JavaScript
;function e(e){return function(e){let t=[{type:"M",values:e[0].values}],a=t[0];for(let n=1,l=e.length;n<l;n++){let l=e[n],{type:r,values:s}=l,i=s.length,h=a.values,p=h.length,[o,u]=[s[i-2],s[i-1]],[f,c]=[h[p-2],h[p-1]];switch(r){case"H":a={type:"L",values:[s[0],c]};break;case"V":a={type:"L",values:[f,s[0]]};break;case"z":case"Z":a={type:"Z",values:[]};break;case"M":a={type:"M",values:[s[0],s[1]]};break;default:a={type:"L",values:[o,u]}}t.push(a)}return t}(a(function(e,t=!0){const a={77:2,109:2,65:7,97:7,67:6,99:6,72:1,104:1,76:2,108:2,81:4,113:4,83:4,115:4,84:2,116:2,86:1,118:1,90:0,122:0};let n=new Set([...Object.keys(a).map(Number)]);const l=new Set([5760,6158,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8239,8287,12288,65279]);function r(e){return 10===e||13===e||8232===e||8233===e||44===e||32===e||9===e||11===e||12===e||160===e||e>=5760&&l.has(e)>=0}function s(e){return n.has(e)}let i,h=0,p=e.length,o="",u=[],f=-1,c="",y=!1,g=!1,x=0,d=0,m=0,v=!1,b=[];const w=()=>{v&&("M"===o?o="L":"m"===o&&(o="l"),u.push({type:o,values:[]}),f++,d=0,v=!1)},M=(e=!1)=>{(e?x>0:""!==c)&&(t&&-1===f&&(i="Pathdata must start with M command",b.push(i),o="M",u.push({type:o,values:[]}),m=2,d=0,f++),"A"===o||"a"===o?(c=A(),u[f].values.push(...c)):(t&&c[1]&&"."!==c[1]&&"0"===c[0]&&(i="Leading zeros not valid: "+c,b.push(i)),u[f].values.push(+c)),d++,c="",x=0,v=d>=m)},A=()=>{let e=c.length,t=!1;return 3===d&&2===e||4===d&&e>1?(c=[+c[0],+c[1]],t=!0,d++):3===d&&e>=3&&(c=[+c[0],+c[1],+c.substring(2)],t=!0,d+=2),t?c:[+c]},k=()=>{if(t){let e=f>0?u[f]:0,t=e?e.values.length:0;(t&&t<m||t&&t>m||("z"===o||"Z"===o)&&t>0)&&(i=`Pathdata commands in "${o}" (segment index: ${f}) don't match allowed number of values: ${m-t}/${m}`,b.push(i))}};for(;h<p;){let t=e[h],n=e.charCodeAt(h);if(s(n)){""!==c&&(u[f].values.push(+c),d++,c=""),k(),o=t,m=a[n];let e="M"===o||"m"===o;f>0&&("z"===u[f].type||"Z"===u[f].type)&&!e&&(u.push({type:"m",values:[0,0]}),f++),u.push({type:o,values:[]}),f++,g=!1,x=0,d=0,v=!1,h++}else if(r(n))M(),g=!0,y=!1,h++;else{if(h===p-1){c+=t,M(),g=!1,y=!1,k();break}if(!y&&!g&&45===n||!y&&46===n){let e=46===n;M(e),w(),e&&x++}else w();c+=t,y=69===n||101===n,g=!1,h++}}if(k(),u[0].type="M",t&&b.length){if(i="Invalid path data:\n"+b.join("\n"),"log"!==t)throw new Error(i);console.warn(i)}return u}(e),!1))}function t(e){let t=[{x:e[0].values[0],y:e[0].values[1]}];for(let a=1,n=e.length;a<n;a++){let{values:n}=e[a];if(n.length){let e=n.slice(-2);t.push({x:e[0],y:e[1]})}}return t}function a(e,t=!1){let a=e[0].values,n=a[0],l=a[1],r=n,s=l;for(let a=1,i=e.length;a<i;a++){let i=e[a],{type:h,values:p}=i,o=t?h.toLowerCase():h.toUpperCase();if(h!==o)switch(h=o,i.type=h,h){case"a":case"A":p[5]=t?p[5]-n:p[5]+n,p[6]=t?p[6]-l:p[6]+l;break;case"v":case"V":p[0]=t?p[0]-l:p[0]+l;break;case"h":case"H":p[0]=t?p[0]-n:p[0]+n;break;case"m":case"M":t?(p[0]-=n,p[1]-=l):(p[0]+=n,p[1]+=l),r=t?p[0]+n:p[0],s=t?p[1]+l:p[1];break;default:if(p.length)for(let e=0;e<p.length;e++)p[e]=t?p[e]-(e%2?l:n):p[e]+(e%2?l:n)}let u=p.length;switch(h){case"z":case"Z":n=r,l=s;break;case"h":case"H":n=t?n+p[0]:p[0];break;case"v":case"V":l=t?l+p[0]:p[0];break;case"m":case"M":r=p[u-2]+(t?n:0),s=p[u-1]+(t?l:0);default:n=p[u-2]+(t?n:0),l=p[u-1]+(t?l:0)}}return e[0].type="M",e=e.map((e=>({type:e.type,values:e.values.map((e=>+e.toFixed(9)))})))}function n(e,t=-1,n=!1,l=!1){return l&&(e=function(e){let t,a=[{type:"M",values:e[0].values}],n=a[0],l={x:e[0].values[0],y:e[0].values[1]},r=.01;for(let s=1,i=e.length;s<i;s++){let i=e[s],{type:h,values:p}=i,o=p.length?p.slice(-2):[];t={x:o[0],y:o[1]};let u=Math.abs(t.x-l.x),f=Math.abs(t.y-l.y),c=(u+f)/2*r;switch(h){case"L":n=0===f||f<c&&u>c?{type:"H",values:[p[0]]}:0===u||f>c&&u<c?{type:"V",values:[p[1]]}:i;break;case"M":case"Z":case"z":n={type:h,values:o};break;default:n={type:"L",values:o}}l={x:o[0],y:o[1]},a.push(n)}return a}(e)),n&&(e=function(e){return a(e,!0)}(e)),t>-1&&(e=e.map((e=>({type:e.type,values:e.values.map((e=>+e.toFixed(t)))})))),e}function l(e,t=1){let a=t>1,n=!a&&0!==t;"l"===(e=JSON.parse(JSON.stringify(e)))[1].type&&n&&(e[0].type="m");let l="";l=a?`${e[0].type} ${e[0].values.join(" ")}\n`:`${e[0].type}${e[0].values.join(" ")}`;for(let t=1,r=e.length;t<r;t++){let r=e[t-1],s=e[t],{type:i,values:h}=s;if(!n||"A"!==i&&"a"!==i||(h=[h[0],h[1],h[2],`${h[3]}${h[4]}${h[5]}`,h[6]]),i=r.type===s.type&&"m"!==s.type.toLowerCase()&&n||("m"===r.type&&"l"===s.type||"M"===r.type&&"l"===s.type||"M"===r.type&&"L"===s.type)&&n?" ":s.type,n){let e="",t=!1;for(let a=0,n=h.length;a<n;a++){let n=h[a],l=n.toString(),r=l.includes(".")&&Math.abs(n)<1;r&&t&&(l=l.replace(/^0\./,".")),!(a>0)||t&&r||(e+=" "),e+=l,t=r}l+=`${i}${e}`}else l+=a?`${i} ${h.join(" ")}\n`:`${i}${h.join(" ")}`}return n&&(l=l.replace(/ 0\./g," .").replace(/ -/g,"-").replace(/-0\./g,"-.").replace(/Z/g,"z")),l}function r(a){if(!a||!a.length)return[];const n=e=>{let t=[];for(let a=1,n=e.length;a<n;a+=2)t.push({x:e[a-1],y:e[a]});return t};let l=a[0].x||!1;if(l&&a.length>0&&"object"==typeof a[0]&&a[0].constructor!==Object&&(e=>{let t=e.length,a=new Array(t);for(let n=0;n<t;n++)a[n]={x:e[n].x,y:e[n].y}})(a),l)return a;let r="string"==typeof a,s=!1;if(!!r&&(a.startsWith("M")||a.startsWith("m"))){0;let n=e(a),l=function(e){let t=[];try{e.map(((e,t)=>"m"===e.type.toLowerCase()?t:-1)).filter((e=>-1!==e))}catch{console.log("catch",e)}let a=e.map(((e,t)=>"m"===e.type.toLowerCase()?t:-1)).filter((e=>-1!==e));return 1===a.length?[e]:(a.forEach(((n,l)=>{t.push(e.slice(n,a[l+1]))})),t)}(n);s=l.length>1;let r=[];return s?l.forEach((e=>{let a=t(e);r.push(a)})):r=t(n),r}if(!!r&&(a.startsWith("{")||a.startsWith("["))){try{a=JSON.parse(a)}catch{a=JSON.parse(a.replace(/([{,])\s*(\w+)\s*:/g,'$1"$2":'))}r=!1}if(r){if((a=a.trim().split(/,| /).filter(Boolean).map(Number)).filter((e=>isNaN(e))).length)return console.warn("input doesn't contain point data – please, check your input structure for syntax errors"),[]}let i=Array.isArray(a),h=i&&2===a[0].length,p=!h&&i&&Array.isArray(a[0]),o=p&&2===a[0][0].length,u=p&&!o&&a[0].length>2&&!a[0][0].hasOwnProperty("x");if(u||o){let e=[];a.forEach((t=>{let a=u?n(t):t.map((e=>({x:e[0],y:e[1]})));e.push(a)})),a=e}else h&&(a=a.map((e=>({x:e[0],y:e[1]}))));
//!Array.isArray(pts[0]
return!Array.isArray(a[0])&&!a[0].hasOwnProperty("x")&&(a=n(a)),a}function s(e,t){return(t.x-e.x)**2+(t.y-e.y)**2}function i(e){let t=s(e[0],e[1]);for(let a=3,n=e.length;a<n;a++){let n=s(e[a-1],e[a]);if(100/t*Math.abs(t-n)>.1)return!1;t=n}return!0}function h(e,t=null){if(0===e.length)return e;if(t=null===t?function(e,t=24){let a=p(e,t),{width:n,height:l}=u(a),r=(Math.max(n,l)/e.length)**2,i=s(e[0],e[e.length-1]);return i<r}(e):t,!t)return e;let a=0;for(let t=1,n=e.length;t<n;t++){let n=e[t],l=e[a];(n.x<l.x||n.x===l.x&&n.y<l.y)&&(a=t)}return e.slice(a).concat(e.slice(0,a))}function p(e,t=48){if(!Array.isArray(e)||e.length<=t)return e;let a=e.length,n=a/t,l=[];for(let a=0;a<t;a++)l.push(e[Math.floor(a*n)]);let r=l.length;return l[r-1]!==e[a-1]&&(l[r-1]=e[a-1]),l}function o(e,t=!0){let a=0;for(let t=0,n=e.length;n&&t<n;t++){a+=e[t].x*e[t===e.length-1?0:t+1].y*.5-e[t===e.length-1?0:t+1].x*e[t].y*.5}return t?Math.abs(a):a}function u(e){let t=e.map((e=>e.x)),a=e.map((e=>e.y)),n=Math.min(...t),l=Math.max(...t),r=Math.min(...a),s=Math.max(...a);return{x:n,left:n,right:l,y:r,top:r,bottom:s,width:l-n,height:s-r}}function f(e,t,a="points",r=!1,i=-1,h=!1,o=!1,f=!1,c=1,y=0,g=0,x=!1,d=0,m=0,v=!1){let b={data:[],ptsArr:[],countOriginal:0,count:0,areaOriginal:0,areaptsSmp:0,areaDiff:0,isPolygon:[]};t=function(e,t=1,a=0,n=0,l=!1,r=0,s=0){if(1===t&&0===r&&0===s&&0===a&&0===n&&!1===l)return e;let i,h,p,o,f=Array.isArray(e[0]),c=f?e:[e],y=f?e.flat():e;return({x:i,y:h,width:p,height:o}=u(y)),c.forEach(((e,u)=>{t=r?r/p:s?s/o:t,s&&o*t>s&&(t=s/o),l&&(a=-i,n=-h);for(let l=0,r=e.length;l<r;l++){let r=e[l];c[u][l]={x:(r.x+a)*t,y:(r.y+n)*t}}})),c}(t,c,y,g,x,d,m);for(let a=0,n=t.length;a<n;a++){let n=e[a],l=t[a],i=n.length;b.countOriginal+=i;let h=l.length;b.count+=h,b.ptsArr.push(l);let o=!1;if(r){let e=p(n,32),{width:t,height:a}=u(e),l=(Math.max(t,a)/n.length)**2;o=s(n[0],n[n.length-1])<l,b.isPolygon.push(o)}}if(i>-1&&i<=3){let e=t.flat(),a=p(e,24),{width:n,height:l}=u(a),r=(n+l)/2;if(r>500)i=0;else{let t=e.length/r,a=r/1e3,n=Math.ceil(1/a).toString().length,l=Math.ceil(t).toString().length,s=Math.ceil((n+l)/2);i=i>-1&&i<s?s:i}}switch(a=a?a.toLowerCase():"points"){case"points":case"pointstring":case"pointsnested":case"json":i>-1&&(b.ptsArr=b.ptsArr.map((e=>e.map((e=>({x:+e.x.toFixed(i),y:+e.y.toFixed(i)})))))),"pointstring"===a?b.data=b.ptsArr.map((e=>e.map((e=>`${e.x} ${e.y}`)).join(" "))):"points"===a?(v||(b.ptsArr=b.ptsArr[0]),b.data=b.ptsArr):"pointsnested"===a?(b.data=b.ptsArr.map((e=>e.map((e=>[e.x,e.y])))),v||(b.data=b.data[0],b.ptsArr=b.ptsArr[0])):"json"===a&&(v||(b.ptsArr=b.ptsArr[0]),b.data=JSON.stringify(b.ptsArr));break;case"pathdata":case"path":let e=[];b.ptsArr.forEach(((t,a)=>{let n=[{type:"M",values:[t[0].x,t[0].y]},...t.slice(1).map((e=>({type:"L",values:[e.x,e.y]})))];b.isPolygon[a]&&n.push({type:"Z",values:[]}),e.push(...n)})),e=n(e,i,h,o),"path"===a?(b.data=[l(e,f?1:0)],v||(b.data=b.data[0])):b.data=e}return b}function c(e){if(e.length<3)return e;let t,a,n=[e[0]],l=e[0],r=e[e.length-1],i=1;for(let r=2,h=e.length;r<h;r++){if(t=e[r-1],a=e[r],t.x===a.x&&t.y===a.y)continue;let p=l.x===t.x,o=l.y===t.y;if(p||o){let e=t.x===a.x,s=t.y===a.y;e&&p||s&&o?r===h-1&&n.push(a):n.push(t),l=t;continue}let u=l.x-a.x,f=l.y-a.y,c=l.x-t.x,y=l.y-t.y,g=Math.abs(u*y-f*c);i=s(t,a)/50,g>i&&(r===h-1?n.push(t,a):n.push(t)),l=t}return l=n[0],l.x===r.x&&l.y===r.y&&e.pop(),n}function y(e,t=.9,a=0,n=0){let l=!1;if("string"==typeof t){l=!0,t=parseFloat(t)}if(e.length<4||(!l&&t)>=1)return e;let r,i=e[0],h=[i],o=t;if(!l){if(o=1-t,!a&&!n){let t=p(e,12);({width:a,height:n}=u(t))}o=(o*((a+n)/2/25))**2,t>.5&&(o/=10)}for(let t=1,a=e.length;t<a;t++){r=e[t],s(i,r)>o&&(h.push(r),i=r)}return i.x!==r.x&&i.y!==r.y&&h.push(r),h}function g(e,t=.9,a=0,n=0){let l=!1;if("string"==typeof t&&(l=!0,t=parseFloat(t)),e.length<4||(!l&&t)>=1)return e;let r=t;if(!l){if(r=1-t,t>.5&&(r/=2),!a&&!n){let t=p(e,12);({width:a,height:n}=u(t))}r=(r*((a+n)/2/100))**2}const s=(e,t,a)=>{let n=t.x,l=t.y,r=a.x-n,s=a.y-l;if(0!==r||0!==s){let t=((e.x-n)*r+(e.y-l)*s)/(r*r+s*s);t>1?(n=a.x,l=a.y):t>0&&(n+=r*t,l+=s*t)}return(e.x-n)**2+(e.y-l)**2};let i=[e[0]],h=[];for(h.push([0,e.length-1]);h.length>0;){let[t,a]=h.pop(),n=r,l=-1;for(let r=t+1;r<a;r++){let i=s(e[r],e[t],e[a]);i>n&&(l=r,n=i)}n>r?(h.push([l,a]),h.push([t,l])):i.push(e[a])}return i}function x(e,t=1,a=0,n=0){let l=!1;if("string"==typeof t&&(l=!0,t=parseFloat(t)),e.length<4||(!l&&t)>=1)return e;let r=function(e){const t=new d;for(let a=0,n=e.length;a<n;a++){let l=0===a?n-1:a-1,r=a,s=a===n-1?0:a+1,i=e[l],h=e[r],p=e[s],u=a>0||a===n-1?h.area?h.area:o([i,h,p]):1/0;h.prev=l,h.index=r,h.next=s,h.area=u,h.heapIndex=a>0?t.push(u,r):0}return t}(e);if(!a&&!n){let t=reducePoints(e,12);({width:a,height:n}=getPolyBBox(t))}let s=t;if(!l){s=((1-t)*((a+n)/2/100))**2}const i=(e,t,a)=>{let n=e[t];if(null===n.prev||null===n.next)return;let l=o([e[n.prev],n,e[n.next]]);n.area=l,void 0!==n.heapIndex?a.update(n.heapIndex,l):n.heapIndex=a.push(l,t)};let h=0,p=e.length;for(;r.size()>0;){const{area:t,index:a}=r.pop(),n=e[a];t&&t<h?n.area=h:h=t,0!==a&&(e[n.prev].next=n.next,i(e,n.prev,r)),a!==p-1&&(e[n.next].prev=n.prev,i(e,n.next,r))}let u=[];for(let t=0,a=e.length;t<a;t++){let n=e[t];(!n.area||0===t||t===a-1||n.area>=s)&&u.push(n)}return u}class d{constructor(){this.heap=[],this.indexMap=new Map}push(e,t){const a={area:e,index:t};this.heap.push(a);const n=this.heap.length-1;return this.indexMap.set(t,n),this.bubbleUp(n),n}pop(){if(0===this.heap.length)return null;const e=this.heap[0],t=this.heap.pop();return this.heap.length>0&&(this.heap[0]=t,this.indexMap.set(t.index,0),this.bubbleDown(0)),this.indexMap.delete(e.index),e}update(e,t){if("number"!=typeof e||e<0||e>=this.heap.length)return;const a=this.heap[e].area;this.heap[e].area=t,t<a?this.bubbleUp(e):this.bubbleDown(e)}size(){return this.heap.length}bubbleUp(e){for(;e>0;){const t=Math.floor((e-1)/2);if(this.heap[t].area<=this.heap[e].area)break;this.swap(e,t),e=t}}bubbleDown(e){for(;;){const t=2*e+1,a=2*e+2;let n=e;if(t<this.heap.length&&this.heap[t].area<this.heap[n].area&&(n=t),a<this.heap.length&&this.heap[a].area<this.heap[n].area&&(n=a),n===e)break;this.swap(e,n),e=n}}swap(e,t){[this.heap[e],this.heap[t]]=[this.heap[t],this.heap[e]],this.indexMap.set(this.heap[e].index,e),this.indexMap.set(this.heap[t].index,t)}}function m(e,t=0){if(e.length<=3||!t||e.length<=t)return e;let a=e,n=Math.ceil(e.length/t),l=a.length;for(let e=0;a.length>t&&e<n;e++){if(a=r(a,t,.5,!0,!1),l===a.length)break;l=a.length}function r(e,t=0,a=.5,n=!0,l=!1){let r,s=function(e){let t=[];for(let a=0;a<e.length;a++){let n=0===a?e.length-1:a-1,l=a,r=a===e.length-1?0:a+1,s=o([e[n],e[l],e[r]]),i=n,h=r;t.push({index:a,area:s,prev:i,next:h})}return t}(e),i=s.length,h=[];if(l){r=s.map((e=>e.area)).reduce(((e,t)=>e+t),0)/i*.25,n=!1}n&&s.sort(((e,t)=>e.area-t.area));let p=e.length-t,u=new Set;p=Math.floor(p*a)+1;let f=0;for(let e=0;e<p;e++){let t=s[e],{index:n,area:i,prev:h,next:p}=t,o=s[h],c=s[p],[y,g]=[o.area,c.area];l?n>0&&i<r&&f<n&&(u.add(n),f=p):(1===a||i<y&&i<g)&&(f=n,n>0&&(u.add(n),f=n))}for(let t=0;t<e.length;t++)u.has(t)||h.push(e[t]);return h}return a.length>t&&(a=r(a,t,1)),a}function v(e,{quality:t=1,RC:a=!0,RDP:n=!0,VW:l=!1,RD:s=!0,overrideQuality:d=!1,optimizeStartingPoint:v=!0,maxPoints:b=0,skipPoints:w=!1,outputFormat:M="points",scale:A=1,alignToZero:k=!1,translateX:$=0,translateY:S=0,scaleToWidth:P=0,scaleToHeight:L=0,meta:z=!1,decimals:O=-1,toRelative:j=!1,toShorthands:D=!1,minifyString:F=!1}={}){try{e=r(e)}catch{return console.warn("invalid input"),e=[{x:0,y:0}]}let C,N=t,Z=!1,I=!1;if("string"==typeof t&&(N=parseFloat(t),C=t.replace(N.toString(),"").trim(),C?"v"===C?(b=N,I=!0,t=.8):Z=!0:t=N<=1?N:.8),b){const a=(e,t,a)=>Math.min(Math.max(e,t),a);let n=+(e.length/b/100).toFixed(1);t=(t=a(t=Math.abs(1-n),.4,.8))>0&&t<1?t:.8}let R=e[0].length>1,E=R?e:[e],W=[],H=[],J=0,V=[];for(let e=0,t=E.length;e<t;e++){let t=p(E[e],64);V.push(u(t));let a=o(t);J+=a,H.push(a)}for(let e=0,r=E.length;e<r;e++){let r=E[e],o=H[e]/J,u=I?Math.ceil(b*o):0;if(!r.length)return[];let f=r;if(r.length<3){W.push(f);continue}if(w&&!n&&!l&&!s&&I){f=p(r,u),W.push(f);continue}if(d||(t>=1&&(n=!1),t>=.75&&(s=!1),t<.5&&(s=!0)),f=v?h(f):f,a){if(!Z&&t>1){W.push(f);continue}f=c(f)}if(a){i(f)&&(l=!1,n=!1)}let{width:M,height:A}=V[e];if(s&&f.length>u&&(f=y(f,t,M,A)),n&&f.length>u&&(f=g(f,t,M,A)),l&&f.length>u&&(f=x(f,t,M,A)),I&&f.length>u){(f.length-u)/f.length>.25&&(f=y(f,t,M,A)),f=m(f,u)}W.push(f)}let T=f(E,W,M,z,O,j,D,F,A,$,S,k,P,L,R);return z?T:T.data}"undefined"!=typeof window&&(window.polySimplify=v,window.normalizePointInput=r),exports.normalizePointInput=r,exports.pathDataToD=l,exports.polySimplify=v,exports.simplifyRC=c,exports.simplifyRD=y,exports.simplifyRDP=g;