plotboilerplate
Version:
A simple javascript plotting boilerplate for 2d stuff.
185 lines (184 loc) • 7.68 kB
TypeScript
/**
* 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;
}