UNPKG

plotboilerplate

Version:

A simple javascript plotting boilerplate for 2d stuff.

158 lines (140 loc) 6.2 kB
/** * @author mbostock, extended and ported to TypeScript by Ikaros Kappler * @date 2020-05-19 * @modified 2021-02-08 Fixed a lot of es2015 compatibility issues. * @version 1.0.2 * @file contextPolygonIncircle * @public */ import { Circle } from "../../Circle"; import { geomutils } from "../../geomutils"; import { Line } from "../../Line"; import { Polygon } from "../../Polygon"; import { Triangle } from "../../Triangle"; import { Vertex } from "../../Vertex"; /** * For circle-polygon-intersection-count detection we need an epsilon to * eliminate smaller precision errors. */ const EPS : number = 0.000001; export interface PolygonIncircle { circle : Circle; triangle : Triangle; }; /** * Compute the max sized inlying circle in the given convex (!) polygon - also called the * convex-polygon incircle. * * The function will return an object with either: the circle, and the triangle that defines * the three tangent points where the circle touches the polygon. * * Inspired by * https://observablehq.com/@mbostock/convex-polygon-incircle * https://observablehq.com/@mbostock/circle-tangent-to-three-lines * * * @requires Circle * @requires Line * @requires Vertex * @requires Triangle * @requires nsectAngle * @requires geomutils * * @global * @name convexPolygonIncircle * @param {Polygon} convexHull - The actual convex polygon. * @return {PolygonIncircle} A pair of a circle (the incircle) and a triangle (the three points where the circle is touching the polygon). */ export const convexPolygonIncircle = ( convexHull:Polygon ) : PolygonIncircle => { var n : number = convexHull.vertices.length; var bestCircle : Circle|undefined = undefined; var bestTriangle : Triangle|undefined = undefined; for( var a = 0; a < n; a++ ) { for( var b = a+1; b < n; b++ ) { for( var c = b+1; c < n; c++ ) { // As these lines are part of the convex hull, we know that // * line a preceeds line b and // * line b preceeds line c :) let lineA : Line = new Line( convexHull.vertices[a], convexHull.vertices[(a+1)%n] ); let lineB : Line = new Line( convexHull.vertices[b], convexHull.vertices[(b+1)%n] ); let lineC : Line = new Line( convexHull.vertices[c], convexHull.vertices[(c+1)%n] ); // Find intersections by expanding the lines let vertB : Vertex = lineA.intersection(lineB); let vertC : Vertex = lineB.intersection(lineC); // An object: { center: Vertex, radius: number } let triangle : Triangle = getTangentTriangle4( lineA.a, vertB, vertC, lineC.b ); // Workaround. There will be a future version where the 'getCircumCircle()' functions // returns a real Circle instance. let _circle : Circle = triangle.getCircumcircle(); let circle : Circle = new Circle( _circle.center, _circle.radius ); // Count the number of intersections with the convex hull: // If there are exactly three, we have found an in-lying circle. // * Check if this one is better (bigger) than the old one. // * Also check if the circle is located inside the polygon; // The construction can, in some cases, produce an out-lying circle. if( !convexHull.containsVert(circle.center) ) { continue; } var circleIntersections : Array<number> = findCircleIntersections( convexHull, circle ); if( circleIntersections.length == 3 && (bestCircle == undefined || bestCircle.radius < circle.radius) ) { bestCircle = circle; bestTriangle = triangle; } } } } return { circle : bestCircle, triangle : bestTriangle }; }; /** * This function computes the three points for the inner maximum circle * lying tangential to the three subsequential lines (given by four points). * * Compute the circle from that triangle by using Triangle.getCircumcircle(). * * Not all three lines should be parallel, otherwise the circle might have infinite radius. * * LineA := [vertA, vertB] * LineB := [vertB, vertC] * LineC := [vertC, vertD] * * @param {Vertex} vertA - The first point of the three connected lines. * @param {Vertex} vertB - The second point of the three connected lines. * @param {Vertex} vertC - The third point of the three connected lines. * @param {Vertex} vertD - The fourth point of the three connected lines. * @return {Triangle} The triangle of the circular tangential points with the given lines (3 or 4 of them). */ const getTangentTriangle4 = ( vertA : Vertex, vertB : Vertex, vertC : Vertex, vertD : Vertex ) : Triangle => { const lineA : Line = new Line(vertA,vertB); const lineB : Line = new Line(vertB,vertC); const lineC : Line = new Line(vertC,vertD); const bisector1 : Line = geomutils.nsectAngle( vertB, vertA, vertC, 2 )[0]; // bisector of first triangle const bisector2 : Line = geomutils.nsectAngle( vertC, vertB, vertD, 2 )[0]; // bisector of second triangle const intersection : Vertex = bisector1.intersection( bisector2 ); // Find the closest points on one of the polygon lines (all have same distance by construction) const circleIntersA : Vertex = lineA.getClosestPoint( intersection ); const circleIntersB : Vertex = lineB.getClosestPoint( intersection ); const circleIntersC : Vertex = lineC.getClosestPoint( intersection ); return new Triangle(circleIntersA, circleIntersB, circleIntersC); }; /** * Find all intersecting lines (indices) on the polyon that intersect the circle. * * @param {Polygon} convexHull - The polygon to detect the intersections on (here this is the convex hull given). * @param {Circle} circle - The circle to detect the intersections with. * @return {Array<number>} The indices of those lines that intersect (or touch) the circle. **/ const findCircleIntersections = ( convexHull:Polygon, circle:Circle ) : Array<number> => { var result = []; for( var i = 0; i < convexHull.vertices.length; i++ ) { var line : Line = new Line( convexHull.vertices[i], convexHull.vertices[(i+1)%convexHull.vertices.length] ); // Use an epsilon here because circle coordinates can be kind of unprecise in the detail. if( circle.lineDistance(line) < EPS ) { result.push( i ); } } return result; };