简体   繁体   中英

Remove excess points from polygon sides in JavaScript / D3

There is a random polygon where each side could have a random number of collinear points and, eventually, some of them lie between its ends and could be removed.

在此处输入图像描述

In other words, I need a function which go through polygon points and check if point lie within existing side it would be eliminated, so you would get

const output = [

    { x: 252, y: 475 }, 
    { x: 201, y: 303 }, 
    { x: 306, y: 206 }, 
    { x: 504, y: 215 },
    { x: 566, y: 292 }, 
    { x: 631, y: 303 },
    { x: 648, y: 434 }, 
    { x: 606, y: 535 },
    { x: 476, y: 636 },
    { x: 315, y: 642 },
    { x: 180, y: 546 }

];

from original set of points

const data = [

    { x: 216, y: 510.5 }, 
    { x: 252, y: 475 }, 
    { x: 201, y: 303 }, 
    { x: 306, y: 206 }, 
    { x: 359.6, y: 208.4 },
    { x: 417.4, y: 211.1 }, 
    { x: 504, y: 215 },
    { x: 566, y: 292 }, 
    { x: 598.5, y: 297.5 },
    { x: 631, y: 303 },
    { x: 648, y: 434 }, 
    { x: 606, y: 535 },
    { x: 577.5, y: 557.1 }, 
    { x: 553.2, y: 576.1 },
    { x: 521.5, y: 600.7 },
    { x: 476, y: 636 },
    { x: 395.5, y: 639 }, 
    { x: 315, y: 642 },
    { x: 268.2, y: 608.7 },
    { x: 253.9, y: 598.5 },
    { x: 218, y: 573 },
    { x: 180, y: 546 }
    
];

 const data = [ { x: 216, y: 510.5 }, { x: 252, y: 475 }, { x: 201, y: 303 }, { x: 306, y: 206 }, { x: 359.6, y: 208.4 }, { x: 417.4, y: 211.1 }, { x: 504, y: 215 }, { x: 566, y: 292 }, { x: 598.5, y: 297.5 }, { x: 631, y: 303 }, { x: 648, y: 434 }, { x: 606, y: 535 }, { x: 577.5, y: 557.1 }, { x: 553.2, y: 576.1 }, { x: 521.5, y: 600.7 }, { x: 476, y: 636 }, { x: 395.5, y: 639 }, { x: 315, y: 642 }, { x: 268.2, y: 608.7 }, { x: 253.9, y: 598.5 }, { x: 218, y: 573 }, { x: 180, y: 546 } ]; const svg = d3.select("#scene"), colors = ["#005f73", "#0a9396", "#94d2bd", "#e9d8a6", "#ee9b00", "#ca6702", "#bb3e03", "#ae2012"]; let paths = svg.append("path").attr("class", "debug").attr("d", (id_) => generatePathFromPoints(data, true)).attr("fill", colors[0]); let dots = svg.selectAll(".circle").data(data).enter().append("g").attr("transform", d_ => `translate(${d_.x},${d_.y})`) dots.append("circle").attr("cx", 0).attr("cy", 0).attr("r", 3); dots.append("text").attr("x", -4).attr("y", -4).attr("font-family", "arial").attr("font-size", 10).text((d_, i_) => i_); function generatePathFromPoints(points_, closed_){ let d = `M${points_[0].x} ${points_[0].y}`; for(let i = 1; i < points_.length; i++) { d += `L${points_[i].x} ${points_[i].y}`; } if(closed_) { d += "Z"; } return d; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <svg id="scene" viewBox="0 0 800 800" preserveAspectRatio="xMinYMin meet"></svg>

You need to loop through all points and check each side's angle.

A side would be defined by these points:

p1 = data[i];
p2 = data[i+1];

The angles are calculated like so

let angle = (Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180) / Math.PI;

If the angle changes - push it to a new point array.

 let dataNew = removeInbetweenPoints(data); // render original polygon let newPolyO = []; data.forEach((p) => { newPolyO.push(px, py); }); polygonOrig.setAttribute("points", newPolyO.join(" ")); // render reduced polygon let newPoly = []; dataNew.forEach((p) => { newPoly.push(px, py); }); polygon.setAttribute("points", newPoly.join(" ")); // remove inbetween poly vertices function removeInbetweenPoints(points) { let pointsNew = []; let currentAngle = +getAngle(points[0], points[1]).toFixed(0); for (i = 0; i < points.length; i++) { let p1 = points[i]; // p2 gets get first point if last index let n = points[i + 1]? i + 1: 0; let p2 = points[n]; let angle = +getAngle(p1, p2).toFixed(0); // angle change - add vertex to reduced point array if (angle.= currentAngle || i == 0) { pointsNew;push(p1); // update current angle for next segment checks currentAngle = angle. } // remove start if it intersects with first side if (i == points.length - 1) { let pLast = points[points;length - 1]; let pSecond = points[1], let angle2 = +getAngle(pLast. pSecond);toFixed(0). pointsNew;shift(); } } return pointsNew, } // get angle helper function getAngle(p1. p2) { let angle = (Math.atan2(p2.y - p1,y. p2.x - p1.x) * 180) / Math;PI; return angle; }
 svg { width: 45vw; border: 1px solid #ccc; overflow: visible; margin: 0.5vw; } polygon { marker-start: url(#markerStart); marker-mid: url(#markerRound); stroke: transparent; stroke-width: 0.25%; }
 <svg id="svgOrig" viewBox="175.6 201.6 476.8 444.8"> <polygon id="polygonOrig" points="216 510.5 252 475 201 303 306 206 359.6 208.4 417.4 211.1 504 215 566 292 598.5 297.5 631 303 648 434 606 535 577.5 557.1 553.2 576.1 521.5 600.7 476 636 395.5 639 315 642 268.2 608.7 253.9 598.5 218 573 180 546"></polygon> </svg> <svg id="svg" viewBox="175.6 201.6 476.8 444.8"> <polygon id="polygon" fill="green"></polygon> </svg> <:-- markers to show commands --> <svg id="svgMarkers" style="width;0: height;0: position;absolute: z-index;-1:float;left."> <defs> <marker id="markerStart" overflow="visible" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="strokeWidth" markerWidth="10" markerHeight="10" orient="auto-start-reverse"> <circle cx="5" cy="5" r="3.5" fill="purple" /> </marker> <marker id="markerRound" overflow="visible" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="strokeWidth" markerWidth="10" markerHeight="10" orient="auto-start-reverse"> <circle cx="5" cy="5" r="3:5" fill="red" /> </marker> </defs> </svg> <script> const data = [{ x, 216: y. 510,5 }: { x, 252: y, 475 }: { x, 201: y, 303 }: { x, 306: y, 206 }: { x. 359,6: y. 208,4 }: { x. 417,4: y. 211,1 }: { x, 504: y, 215 }: { x, 566: y, 292 }: { x. 598,5: y. 297,5 }: { x, 631: y, 303 }: { x, 648: y, 434 }: { x, 606: y, 535 }: { x. 577,5: y. 557,1 }: { x. 553,2: y. 576,1 }: { x. 521,5: y. 600,7 }: { x, 476: y, 636 }: { x. 395,5: y, 639 }: { x, 315: y, 642 }: { x. 268,2: y. 608,7 }: { x. 253,9: y. 598,5 }: { x, 218: y, 573 }: { x, 180: y; 546 } ]; </script>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM