1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  * @typechecks
    
  7.  *
    
  8.  * Example usage:
    
  9.  * <Wedge
    
  10.  *   outerRadius={50}
    
  11.  *   startAngle={0}
    
  12.  *   endAngle={360}
    
  13.  *   fill="blue"
    
  14.  * />
    
  15.  *
    
  16.  * Additional optional property:
    
  17.  *   (Int) innerRadius
    
  18.  *
    
  19.  */
    
  20. 
    
  21. 'use strict';
    
  22. 
    
  23. var assign = Object.assign;
    
  24. var PropTypes = require('prop-types');
    
  25. var React = require('react');
    
  26. var ReactART = require('react-art');
    
  27. 
    
  28. var createReactClass = require('create-react-class');
    
  29. 
    
  30. var Shape = ReactART.Shape;
    
  31. var Path = ReactART.Path;
    
  32. 
    
  33. /**
    
  34.  * Wedge is a React component for drawing circles, wedges and arcs. Like other
    
  35.  * ReactART components, it must be used in a <Surface>.
    
  36.  */
    
  37. var Wedge = createReactClass({
    
  38.   displayName: 'Wedge',
    
  39. 
    
  40.   propTypes: {
    
  41.     outerRadius: PropTypes.number.isRequired,
    
  42.     startAngle: PropTypes.number.isRequired,
    
  43.     endAngle: PropTypes.number.isRequired,
    
  44.     innerRadius: PropTypes.number,
    
  45.   },
    
  46. 
    
  47.   circleRadians: Math.PI * 2,
    
  48. 
    
  49.   radiansPerDegree: Math.PI / 180,
    
  50. 
    
  51.   /**
    
  52.    * _degreesToRadians(degrees)
    
  53.    *
    
  54.    * Helper function to convert degrees to radians
    
  55.    *
    
  56.    * @param {number} degrees
    
  57.    * @return {number}
    
  58.    */
    
  59.   _degreesToRadians: function _degreesToRadians(degrees) {
    
  60.     if (degrees !== 0 && degrees % 360 === 0) {
    
  61.       // 360, 720, etc.
    
  62.       return this.circleRadians;
    
  63.     } else {
    
  64.       return (degrees * this.radiansPerDegree) % this.circleRadians;
    
  65.     }
    
  66.   },
    
  67. 
    
  68.   /**
    
  69.    * _createCirclePath(or, ir)
    
  70.    *
    
  71.    * Creates the ReactART Path for a complete circle.
    
  72.    *
    
  73.    * @param {number} or The outer radius of the circle
    
  74.    * @param {number} ir The inner radius, greater than zero for a ring
    
  75.    * @return {object}
    
  76.    */
    
  77.   _createCirclePath: function _createCirclePath(or, ir) {
    
  78.     var path = Path();
    
  79. 
    
  80.     path
    
  81.       .move(0, or)
    
  82.       .arc(or * 2, 0, or)
    
  83.       .arc(-or * 2, 0, or);
    
  84. 
    
  85.     if (ir) {
    
  86.       path
    
  87.         .move(or - ir, 0)
    
  88.         .counterArc(ir * 2, 0, ir)
    
  89.         .counterArc(-ir * 2, 0, ir);
    
  90.     }
    
  91. 
    
  92.     path.close();
    
  93. 
    
  94.     return path;
    
  95.   },
    
  96. 
    
  97.   /**
    
  98.    * _createArcPath(sa, ea, ca, or, ir)
    
  99.    *
    
  100.    * Creates the ReactART Path for an arc or wedge.
    
  101.    *
    
  102.    * @param {number} startAngle The starting degrees relative to 12 o'clock
    
  103.    * @param {number} endAngle The ending degrees relative to 12 o'clock
    
  104.    * @param {number} or The outer radius in pixels
    
  105.    * @param {number} ir The inner radius in pixels, greater than zero for an arc
    
  106.    * @return {object}
    
  107.    */
    
  108.   _createArcPath: function _createArcPath(startAngle, endAngle, or, ir) {
    
  109.     var path = Path();
    
  110. 
    
  111.     // angles in radians
    
  112.     var sa = this._degreesToRadians(startAngle);
    
  113.     var ea = this._degreesToRadians(endAngle);
    
  114. 
    
  115.     // central arc angle in radians
    
  116.     var ca = sa > ea ? this.circleRadians - sa + ea : ea - sa;
    
  117. 
    
  118.     // cached sine and cosine values
    
  119.     var ss = Math.sin(sa);
    
  120.     var es = Math.sin(ea);
    
  121.     var sc = Math.cos(sa);
    
  122.     var ec = Math.cos(ea);
    
  123. 
    
  124.     // cached differences
    
  125.     var ds = es - ss;
    
  126.     var dc = ec - sc;
    
  127.     var dr = ir - or;
    
  128. 
    
  129.     // if the angle is over pi radians (180 degrees)
    
  130.     // we will need to let the drawing method know.
    
  131.     var large = ca > Math.PI;
    
  132. 
    
  133.     // TODO (sema) Please improve theses comments to make the math
    
  134.     // more understandable.
    
  135.     //
    
  136.     // Formula for a point on a circle at a specific angle with a center
    
  137.     // at (0, 0):
    
  138.     // x = radius * Math.sin(radians)
    
  139.     // y = radius * Math.cos(radians)
    
  140.     //
    
  141.     // For our starting point, we offset the formula using the outer
    
  142.     // radius because our origin is at (top, left).
    
  143.     // In typical web layout fashion, we are drawing in quadrant IV
    
  144.     // (a.k.a. Southeast) where x is positive and y is negative.
    
  145.     //
    
  146.     // The arguments for path.arc and path.counterArc used below are:
    
  147.     // (endX, endY, radiusX, radiusY, largeAngle)
    
  148. 
    
  149.     path
    
  150.       .move(or + or * ss, or - or * sc) // move to starting point
    
  151.       .arc(or * ds, or * -dc, or, or, large) // outer arc
    
  152.       .line(dr * es, dr * -ec); // width of arc or wedge
    
  153. 
    
  154.     if (ir) {
    
  155.       path.counterArc(ir * -ds, ir * dc, ir, ir, large); // inner arc
    
  156.     }
    
  157. 
    
  158.     return path;
    
  159.   },
    
  160. 
    
  161.   render: function render() {
    
  162.     // angles are provided in degrees
    
  163.     var startAngle = this.props.startAngle;
    
  164.     var endAngle = this.props.endAngle;
    
  165.     if (startAngle - endAngle === 0) {
    
  166.       return null;
    
  167.     }
    
  168. 
    
  169.     // radii are provided in pixels
    
  170.     var innerRadius = this.props.innerRadius || 0;
    
  171.     var outerRadius = this.props.outerRadius;
    
  172. 
    
  173.     // sorted radii
    
  174.     var ir = Math.min(innerRadius, outerRadius);
    
  175.     var or = Math.max(innerRadius, outerRadius);
    
  176. 
    
  177.     var path;
    
  178.     if (endAngle >= startAngle + 360) {
    
  179.       path = this._createCirclePath(or, ir);
    
  180.     } else {
    
  181.       path = this._createArcPath(startAngle, endAngle, or, ir);
    
  182.     }
    
  183. 
    
  184.     return React.createElement(Shape, assign({}, this.props, {d: path}));
    
  185.   },
    
  186. });
    
  187. 
    
  188. module.exports = Wedge;