UNPKG

plotboilerplate

Version:

A simple javascript plotting boilerplate for 2d stuff.

185 lines (184 loc) 7.68 kB
/** * Calculate the inset of any non-self-overlapping polygon. * * The polygon may be * - convex or non-convex * - self-intersecting * * The algorithms will * - first construct a general offset polygon which may be corrupt due to ouf-of-bounds or in illegal-area problems. * - then dissect the possibly self-intersecting inset polygon into separate simple polygons. * - then detect for each if it's out of bounds or in some illegal area. * - keeping only the valid ones. * * @required sutherlandHodgman * @required splitPolygonToNonIntersecting * * @author Ikaros Kappler * @date 2024-11-04 Ported the script to a class. * @modified 2024-12-02 Ported to Typescript. */ import { Line } from "../../Line"; import { Polygon } from "../../Polygon"; import { Vertex } from "../../Vertex"; export interface IPolygonInsetOptions { innerPolygonOffset: number; maxPolygonSplitDepth?: number; intersectionEpsilon?: number; removeEars?: boolean; } export declare class PolygonInset { /** * The polygon to work with. */ readonly polygon: Polygon; /** * The input polygon, but without ear edges. */ optimizedPolygon: Polygon; /** * The original polygon as a sequence of line objects. This is easier to use than a list of points. */ originalPolygonLines: Array<Line>; /** * The simple inset lines, directly generated by offsetting the original polygon lines by the given amount. */ insetLines: Array<Line>; /** * The cropped inset lines that makes up the first direct iteration of the inset polygon. * This one will probably be self-intersecting. */ insetPolygonLines: Array<Line>; /** * The inset polygon lines resembling an actual polygon instance (not just a sequence of lines). */ insetPolygon: Polygon; /** * Each polygon line and it's offsetted inset-line resemble a rectangular polygon. * Array<Polygon> with 4 vertices each **/ insetRectanglePolygons: Array<Polygon>; /** * The inset polygon split up into simple non-self-intersecting polygons. * Represented as a list of vertex-lists. Each sub-list represents a single polygon. */ splitPolygons: Array<Array<Vertex>>; /** * The final result: all valid split-polygons. These are making up the actual polygon inset. * Note: list may be empty. Depending on the offset amount there is not a guaranteed offset polygon existing. */ filteredSplitPolygons: Array<Array<Vertex>>; /** * Constructs a new PolygonInset instance with the underlying given polygon to work with. * * Please note that the algorithm will reverse the vertex order if the given polygon * is not clockwise. * * @param {Polygon} polygon - The polygon to calculate the offset for. */ constructor(polygon: Polygon); /** * This is the main method. * * Call this with the required option params to compute your desired offset polygon(s). * * @param {number} options.innerPolygonOffset - The offset to use. Should be a positive number. Correct result for negative numbers are not guaranteed. * @param {number?} options.maxPolygonSplitDepth - (default is the number of polygon.vertices) The maximum amount of recursive stepts to simplify the complex first iteration of the offset polygon. * @param {number?} options.intersectionEpsilon - (default is 1.0) The epsilon to use for detecting overlapping polygons. This indicates a tolerance value, because directly adjecent polygons may have a little arithmetic intersection area. * @returns {Array<Array<Vertex>>} A list of vertex-lists, each one representing a simple polygon from the offset-polygons. */ computeOutputPolygons(options: IPolygonInsetOptions): Array<Array<Vertex>>; /** * This method transforms each polygon line into a new line * by moving it to the inside direction of the polygon (by the given `insetAmount`). * * @param {Array<Line>} polygonLines * @param {number} insetAmount * @return {Array<Line>} The transformed lines. The result array has the same length and order as the input array. */ private _collectInsetLines; /** * For a sequence of inset polygon lines get the inset polygon by detecting * useful intersections (by cropping or extending them). * * The returned lines resemble a new polygon. * * Please note that the returned polygon can be self-intersecting! * * @param {Array<Line>} insetLines * @returns {Array<Line>} The cropped or exented inset polygon lines. */ private _collectInsetPolygonLines; /** * Converts two lists (same length) of original polygon lines and inset lines (interpreted as * pairs) to a list of rectangular polyons. * * @static * @param {Array<Line>} originalPolygonLines * @param {Array<Line>} insetLines * @returns {Array<Polygon>} A list of rectangular polygons; each returned polyon has exactly four vertices. */ private _collectRectangularPolygonInsets; /** * Converts a sequence of (hopefully adjacent) lines to a polygon by using all second line vertices `b`. * * @static * @param insetPolygonLines * @returns */ private static convertToBasicInsetPolygon; /** * Optimize the inset polygon by removing critical `ear` edges. * Such an is identified by: the edge itself is completely located inside its neighbours inset rectangle. * * In this process multiple edges from the ear might be dropped and two adjacent edges get a new * common intersection point. * * Note: this method is not working as expected in all cases and quite experimental. * * @param insetPolygon * @param insetRectangles */ private static _optimizeInsetPolygon; private static _locateExcessiveEarEdge; private static _rectangleFullyContainsLine; /** * Filter split polygons: only keep those whose vertices are all contained inside the original polygon. * Reason: scaling too much will result in excessive translation beyond the opposite bounds of the polygon (like more than 200% of possible insetting). * * @param {Array<Array<Vertex>>} splitPolygonsVertices * @param {Polygon} originalPolygon * @return {Array<Array<Vertex>>} The filtered polygon list. */ private static _filterInnerSplitPolygonsByOriginalBounds; /** * Filter split polygons: only keep those that do not (signifiantly) interset with any rectangles. * * @static * @param {Array<Array<Vertex>>} splitPolygonsVertices * @param {Array<Polygon>} insetRectanglePolygons * @param {number?=1.0} intersectionEpsilon - (optional, default is 1.0) A epsislon to define a tolerance for checking if two polygons intersect. */ private static _filterInnerSplitPolygonsByCoverage; /** * This private method will reverse each polygon's vertex order that's not clockwise. * * @param {Array<Vertex[]>} polygons */ private static _assertAllPolygonsAreClockwise; /** * This private method will revert the vertex order if the polygon is not clockwise. * * @param {Vertex[]} polygonVerts */ private static _assertPolygonIsClockwise; /** * For simplification I'm using a callback generator function here. * * @param splitPolyVerts * @param eps * @param _splitPolyIndex * @returns {Function} The callback for an `Array.some` parameter. */ private static _hasIntersectionCallback; }