123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- /* *
- *
- * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
- *
- * */
- 'use strict';
- import Geometry from './Geometry.js';
- var getAngleBetweenPoints = Geometry.getAngleBetweenPoints, getCenterOfPoints = Geometry.getCenterOfPoints, getDistanceBetweenPoints = Geometry.getDistanceBetweenPoints;
- /**
- * @private
- * @param {number} x
- * Number to round
- * @param {number} decimals
- * Number of decimals to round to
- * @return {number}
- * Rounded number
- */
- function round(x, decimals) {
- var a = Math.pow(10, decimals);
- return Math.round(x * a) / a;
- }
- /**
- * Calculates the area of a circle based on its radius.
- * @private
- * @param {number} r
- * The radius of the circle.
- * @return {number}
- * Returns the area of the circle.
- */
- function getAreaOfCircle(r) {
- if (r <= 0) {
- throw new Error('radius of circle must be a positive number.');
- }
- return Math.PI * r * r;
- }
- /**
- * Calculates the area of a circular segment based on the radius of the circle
- * and the height of the segment.
- * See http://mathworld.wolfram.com/CircularSegment.html
- * @private
- * @param {number} r
- * The radius of the circle.
- * @param {number} h
- * The height of the circular segment.
- * @return {number}
- * Returns the area of the circular segment.
- */
- function getCircularSegmentArea(r, h) {
- return r * r * Math.acos(1 - h / r) - (r - h) * Math.sqrt(h * (2 * r - h));
- }
- /**
- * Calculates the area of overlap between two circles based on their radiuses
- * and the distance between them.
- * See http://mathworld.wolfram.com/Circle-CircleIntersection.html
- * @private
- * @param {number} r1
- * Radius of the first circle.
- * @param {number} r2
- * Radius of the second circle.
- * @param {number} d
- * The distance between the two circles.
- * @return {number}
- * Returns the area of overlap between the two circles.
- */
- function getOverlapBetweenCircles(r1, r2, d) {
- var overlap = 0;
- // If the distance is larger than the sum of the radiuses then the circles
- // does not overlap.
- if (d < r1 + r2) {
- if (d <= Math.abs(r2 - r1)) {
- // If the circles are completely overlapping, then the overlap
- // equals the area of the smallest circle.
- overlap = getAreaOfCircle(r1 < r2 ? r1 : r2);
- }
- else {
- // Height of first triangle segment.
- var d1 = (r1 * r1 - r2 * r2 + d * d) / (2 * d),
- // Height of second triangle segment.
- d2 = d - d1;
- overlap = (getCircularSegmentArea(r1, r1 - d1) +
- getCircularSegmentArea(r2, r2 - d2));
- }
- // Round the result to two decimals.
- overlap = round(overlap, 14);
- }
- return overlap;
- }
- /**
- * Calculates the intersection points of two circles.
- *
- * NOTE: does not handle floating errors well.
- * @private
- * @param {Highcharts.CircleObject} c1
- * The first circle.
- * @param {Highcharts.CircleObject} c2
- * The second sircle.
- * @return {Array<Highcharts.PositionObject>}
- * Returns the resulting intersection points.
- */
- function getCircleCircleIntersection(c1, c2) {
- var d = getDistanceBetweenPoints(c1, c2), r1 = c1.r, r2 = c2.r;
- var points = [];
- if (d < r1 + r2 && d > Math.abs(r1 - r2)) {
- // If the circles are overlapping, but not completely overlapping, then
- // it exists intersecting points.
- var r1Square = r1 * r1, r2Square = r2 * r2,
- // d^2 - r^2 + R^2 / 2d
- x = (r1Square - r2Square + d * d) / (2 * d),
- // y^2 = R^2 - x^2
- y = Math.sqrt(r1Square - x * x), x1 = c1.x, x2 = c2.x, y1 = c1.y, y2 = c2.y, x0 = x1 + x * (x2 - x1) / d, y0 = y1 + x * (y2 - y1) / d, rx = -(y2 - y1) * (y / d), ry = -(x2 - x1) * (y / d);
- points = [
- { x: round(x0 + rx, 14), y: round(y0 - ry, 14) },
- { x: round(x0 - rx, 14), y: round(y0 + ry, 14) }
- ];
- }
- return points;
- }
- /**
- * Calculates all the intersection points for between a list of circles.
- * @private
- * @param {Array<Highcharts.CircleObject>} circles
- * The circles to calculate the points from.
- * @return {Array<Highcharts.GeometryObject>}
- * Returns a list of intersection points.
- */
- function getCirclesIntersectionPoints(circles) {
- return circles.reduce(function (points, c1, i, arr) {
- var additional = arr.slice(i + 1)
- .reduce(function (points, c2, j) {
- var indexes = [i, j + i + 1];
- return points.concat(getCircleCircleIntersection(c1, c2)
- .map(function (p) {
- p.indexes = indexes;
- return p;
- }));
- }, []);
- return points.concat(additional);
- }, []);
- }
- /**
- * Tests wether the first circle is completely overlapping the second circle.
- *
- * @private
- * @param {Highcharts.CircleObject} circle1 The first circle.
- * @param {Highcharts.CircleObject} circle2 The The second circle.
- * @return {boolean} Returns true if circle1 is completely overlapping circle2,
- * false if not.
- */
- function isCircle1CompletelyOverlappingCircle2(circle1, circle2) {
- return getDistanceBetweenPoints(circle1, circle2) + circle2.r <
- circle1.r + 1e-10;
- }
- /**
- * Tests wether a point lies within a given circle.
- * @private
- * @param {Highcharts.PositionObject} point
- * The point to test for.
- * @param {Highcharts.CircleObject} circle
- * The circle to test if the point is within.
- * @return {boolean}
- * Returns true if the point is inside, false if outside.
- */
- function isPointInsideCircle(point, circle) {
- return getDistanceBetweenPoints(point, circle) <= circle.r + 1e-10;
- }
- /**
- * Tests wether a point lies within a set of circles.
- * @private
- * @param {Highcharts.PositionObject} point
- * The point to test.
- * @param {Array<Highcharts.CircleObject>} circles
- * The list of circles to test against.
- * @return {boolean}
- * Returns true if the point is inside all the circles, false if not.
- */
- function isPointInsideAllCircles(point, circles) {
- return !circles.some(function (circle) {
- return !isPointInsideCircle(point, circle);
- });
- }
- /**
- * Tests wether a point lies outside a set of circles.
- *
- * TODO: add unit tests.
- * @private
- * @param {Highcharts.PositionObject} point
- * The point to test.
- * @param {Array<Highcharts.CircleObject>} circles
- * The list of circles to test against.
- * @return {boolean}
- * Returns true if the point is outside all the circles, false if not.
- */
- function isPointOutsideAllCircles(point, circles) {
- return !circles.some(function (circle) {
- return isPointInsideCircle(point, circle);
- });
- }
- /**
- * Calculates the points for the polygon of the intersection area between a set
- * of circles.
- *
- * @private
- * @param {Array<Highcharts.CircleObject>} circles
- * List of circles to calculate polygon of.
- * @return {Array<Highcharts.GeometryObject>} Return list of points in the
- * intersection polygon.
- */
- function getCirclesIntersectionPolygon(circles) {
- return getCirclesIntersectionPoints(circles)
- .filter(function (p) {
- return isPointInsideAllCircles(p, circles);
- });
- }
- /**
- * Calculate the path for the area of overlap between a set of circles.
- * @todo handle cases with only 1 or 0 arcs.
- * @private
- * @param {Array<Highcharts.CircleObject>} circles
- * List of circles to calculate area of.
- * @return {Highcharts.GeometryIntersectionObject|undefined}
- * Returns the path for the area of overlap. Returns an empty string if
- * there are no intersection between all the circles.
- */
- function getAreaOfIntersectionBetweenCircles(circles) {
- var intersectionPoints = getCirclesIntersectionPolygon(circles), result;
- if (intersectionPoints.length > 1) {
- // Calculate the center of the intersection points.
- var center_1 = getCenterOfPoints(intersectionPoints);
- intersectionPoints = intersectionPoints
- // Calculate the angle between the center and the points.
- .map(function (p) {
- p.angle = getAngleBetweenPoints(center_1, p);
- return p;
- })
- // Sort the points by the angle to the center.
- .sort(function (a, b) {
- return b.angle - a.angle;
- });
- var startPoint = intersectionPoints[intersectionPoints.length - 1];
- var arcs = intersectionPoints
- .reduce(function (data, p1) {
- var startPoint = data.startPoint, midPoint = getCenterOfPoints([startPoint, p1]);
- // Calculate the arc from the intersection points and their
- // circles.
- var arc = p1.indexes
- // Filter out circles that are not included in both
- // intersection points.
- .filter(function (index) {
- return startPoint.indexes.indexOf(index) > -1;
- })
- // Iterate the circles of the intersection points and
- // calculate arcs.
- .reduce(function (arc, index) {
- var circle = circles[index], angle1 = getAngleBetweenPoints(circle, p1), angle2 = getAngleBetweenPoints(circle, startPoint), angleDiff = angle2 - angle1 +
- (angle2 < angle1 ? 2 * Math.PI : 0), angle = angle2 - angleDiff / 2;
- var width = getDistanceBetweenPoints(midPoint, {
- x: circle.x + circle.r * Math.sin(angle),
- y: circle.y + circle.r * Math.cos(angle)
- });
- var r = circle.r;
- // Width can sometimes become to large due to floating
- // point errors
- if (width > r * 2) {
- width = r * 2;
- }
- // Get the arc with the smallest width.
- if (!arc || arc.width > width) {
- arc = {
- r: r,
- largeArc: width > r ? 1 : 0,
- width: width,
- x: p1.x,
- y: p1.y
- };
- }
- // Return the chosen arc.
- return arc;
- }, null);
- // If we find an arc then add it to the list and update p2.
- if (arc) {
- var r = arc.r;
- data.arcs.push(['A', r, r, 0, arc.largeArc, 1, arc.x, arc.y]);
- data.startPoint = p1;
- }
- return data;
- }, {
- startPoint: startPoint,
- arcs: []
- }).arcs;
- if (arcs.length === 0) {
- // empty
- }
- else if (arcs.length === 1) {
- // empty
- }
- else {
- arcs.unshift(['M', startPoint.x, startPoint.y]);
- result = {
- center: center_1,
- d: arcs
- };
- }
- }
- return result;
- }
- var geometryCircles = {
- getAreaOfCircle: getAreaOfCircle,
- getAreaOfIntersectionBetweenCircles: getAreaOfIntersectionBetweenCircles,
- getCircleCircleIntersection: getCircleCircleIntersection,
- getCirclesIntersectionPoints: getCirclesIntersectionPoints,
- getCirclesIntersectionPolygon: getCirclesIntersectionPolygon,
- getCircularSegmentArea: getCircularSegmentArea,
- getOverlapBetweenCircles: getOverlapBetweenCircles,
- isCircle1CompletelyOverlappingCircle2: isCircle1CompletelyOverlappingCircle2,
- isPointInsideCircle: isPointInsideCircle,
- isPointInsideAllCircles: isPointInsideAllCircles,
- isPointOutsideAllCircles: isPointOutsideAllCircles,
- round: round
- };
- export default geometryCircles;
|