UNPKG

plotboilerplate

Version:

A simple javascript plotting boilerplate for 2d stuff.

125 lines (108 loc) 4.23 kB
/** * @requires Line * @requires Vertex * * @author Ikaros Kappler * @date 2020-12-04 * @modified 2020-12-07 Ported from vanilla JS to typescript. * @modified 2024-11-22 Added the `insideBoundsOnly` param to the `splitPolygonToNonIntersecting` algorithm. * @version 1.1.0 */ import { Line } from "../../Line"; import { Vertex } from "../../Vertex"; /** * Splitting a polygon at a self intersection results in two new polygons. */ type ISplit = [Array<Vertex>, Array<Vertex>]; /** * This function cuts a given self-intersecting polygon into non-self-intersecting * sub polygons. * * The algorithm only works for non-self-overlapping polygons: * Please note that the union set of the sub polygons themselves will _not_ be disjunct if * the input polyon overlaps with itself! * * See this illustration for details: * ./demos/27-polygon-intersection-greinerhormann/splitting-polygon-types.png * * @name splitPolygonToNonIntersecting * @param {Array<Vertex>} vertices - The polygon as an array of vertices. * @param {number=10} maxDepth - Number of max recursive steps (default is 10). * @return {Array<Array<Vertex>>} A sequence of non-self-intersecting sub polygons. */ export const splitPolygonToNonIntersecting = (() => { /** * @param {Array<Vertex>} vertices * @param {number=10} maxDepth */ const splitPolygonToNonIntersecting = ( vertices: Array<Vertex>, maxDepth?: number, insideBoundsOnly?: boolean ): Array<Array<Vertex>> => { if (typeof maxDepth === "undefined") maxDepth = 10; return _splitPolygonToNonIntersecting(vertices, maxDepth, insideBoundsOnly); }; const _splitPolygonToNonIntersecting = ( vertices: Array<Vertex>, maxDepth: number, insideBoundsOnly?: boolean ): Array<Array<Vertex>> => { if (maxDepth <= 0) { // aborting at max depth return [vertices]; } if (vertices.length <= 3) { // No intersections possible return [vertices]; } const n: number = vertices.length; const lineA: Line = new Line(new Vertex(), new Vertex()); const lineB: Line = new Line(new Vertex(), new Vertex()); for (var a = 0; a < vertices.length; a++) { lineA.a.set(vertices[a]); lineA.b.set(vertices[(a + 1) % n]); for (var b = 0; b < vertices.length; b++) { // Equal edges or neighbour edges intersect by definition. // We ignore them. if (a == b || a + 1 == b || a == b + 1 || (a == 0 && b + 1 == vertices.length) || (b == 0 && a + 1 == vertices.length)) continue; lineB.a.set(vertices[b]); lineB.b.set(vertices[(b + 1) % n]); const intersectionPoint: Vertex | null = lineA.intersection(lineB); if ( intersectionPoint && lineA.hasPoint(intersectionPoint, insideBoundsOnly) && lineB.hasPoint(intersectionPoint, insideBoundsOnly) ) { // Cut polygon into two here const split: ISplit = splitPolygonAt(vertices, a, b, intersectionPoint); // Split has 2 elements. const leftCleaned: Array<Array<Vertex>> = _splitPolygonToNonIntersecting(split[0], maxDepth - 1, insideBoundsOnly); const rightCleaned: Array<Array<Vertex>> = _splitPolygonToNonIntersecting(split[1], maxDepth - 1, insideBoundsOnly); return leftCleaned.concat(rightCleaned); } } } // No intersection found: // just return a list with the original // polygon as its only element. return [vertices]; }; // Pre: edgeIndexA < edgeIndexB && vertices.length >= 4 const splitPolygonAt = (vertices: Array<Vertex>, edgeIndexA: number, edgeIndexB: number, intersectionPoint: Vertex): ISplit => { const first: Array<Vertex> = []; var second: Array<Vertex> = []; for (var i = 0; i < vertices.length; i++) { if (i <= edgeIndexA) first.push(vertices[i]); if (i == edgeIndexA) { first.push(intersectionPoint); second.push(intersectionPoint); // clone??? } if (i > edgeIndexA && i <= edgeIndexB) second.push(vertices[i]); if (i > edgeIndexB) first.push(vertices[i]); } return [first, second]; }; return splitPolygonToNonIntersecting; })();