189 lines
4.8 KiB
JavaScript
189 lines
4.8 KiB
JavaScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
* @typechecks
|
|
*
|
|
* Example usage:
|
|
* <Wedge
|
|
* outerRadius={50}
|
|
* startAngle={0}
|
|
* endAngle={360}
|
|
* fill="blue"
|
|
* />
|
|
*
|
|
* Additional optional property:
|
|
* (Int) innerRadius
|
|
*
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var assign = Object.assign;
|
|
var PropTypes = require('prop-types');
|
|
var React = require('react');
|
|
var ReactART = require('react-art');
|
|
|
|
var createReactClass = require('create-react-class');
|
|
|
|
var Shape = ReactART.Shape;
|
|
var Path = ReactART.Path;
|
|
|
|
/**
|
|
* Wedge is a React component for drawing circles, wedges and arcs. Like other
|
|
* ReactART components, it must be used in a <Surface>.
|
|
*/
|
|
var Wedge = createReactClass({
|
|
displayName: 'Wedge',
|
|
|
|
propTypes: {
|
|
outerRadius: PropTypes.number.isRequired,
|
|
startAngle: PropTypes.number.isRequired,
|
|
endAngle: PropTypes.number.isRequired,
|
|
innerRadius: PropTypes.number,
|
|
},
|
|
|
|
circleRadians: Math.PI * 2,
|
|
|
|
radiansPerDegree: Math.PI / 180,
|
|
|
|
/**
|
|
* _degreesToRadians(degrees)
|
|
*
|
|
* Helper function to convert degrees to radians
|
|
*
|
|
* @param {number} degrees
|
|
* @return {number}
|
|
*/
|
|
_degreesToRadians: function _degreesToRadians(degrees) {
|
|
if (degrees !== 0 && degrees % 360 === 0) {
|
|
// 360, 720, etc.
|
|
return this.circleRadians;
|
|
} else {
|
|
return (degrees * this.radiansPerDegree) % this.circleRadians;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* _createCirclePath(or, ir)
|
|
*
|
|
* Creates the ReactART Path for a complete circle.
|
|
*
|
|
* @param {number} or The outer radius of the circle
|
|
* @param {number} ir The inner radius, greater than zero for a ring
|
|
* @return {object}
|
|
*/
|
|
_createCirclePath: function _createCirclePath(or, ir) {
|
|
var path = Path();
|
|
|
|
path
|
|
.move(0, or)
|
|
.arc(or * 2, 0, or)
|
|
.arc(-or * 2, 0, or);
|
|
|
|
if (ir) {
|
|
path
|
|
.move(or - ir, 0)
|
|
.counterArc(ir * 2, 0, ir)
|
|
.counterArc(-ir * 2, 0, ir);
|
|
}
|
|
|
|
path.close();
|
|
|
|
return path;
|
|
},
|
|
|
|
/**
|
|
* _createArcPath(sa, ea, ca, or, ir)
|
|
*
|
|
* Creates the ReactART Path for an arc or wedge.
|
|
*
|
|
* @param {number} startAngle The starting degrees relative to 12 o'clock
|
|
* @param {number} endAngle The ending degrees relative to 12 o'clock
|
|
* @param {number} or The outer radius in pixels
|
|
* @param {number} ir The inner radius in pixels, greater than zero for an arc
|
|
* @return {object}
|
|
*/
|
|
_createArcPath: function _createArcPath(startAngle, endAngle, or, ir) {
|
|
var path = Path();
|
|
|
|
// angles in radians
|
|
var sa = this._degreesToRadians(startAngle);
|
|
var ea = this._degreesToRadians(endAngle);
|
|
|
|
// central arc angle in radians
|
|
var ca = sa > ea ? this.circleRadians - sa + ea : ea - sa;
|
|
|
|
// cached sine and cosine values
|
|
var ss = Math.sin(sa);
|
|
var es = Math.sin(ea);
|
|
var sc = Math.cos(sa);
|
|
var ec = Math.cos(ea);
|
|
|
|
// cached differences
|
|
var ds = es - ss;
|
|
var dc = ec - sc;
|
|
var dr = ir - or;
|
|
|
|
// if the angle is over pi radians (180 degrees)
|
|
// we will need to let the drawing method know.
|
|
var large = ca > Math.PI;
|
|
|
|
// TODO (sema) Please improve theses comments to make the math
|
|
// more understandable.
|
|
//
|
|
// Formula for a point on a circle at a specific angle with a center
|
|
// at (0, 0):
|
|
// x = radius * Math.sin(radians)
|
|
// y = radius * Math.cos(radians)
|
|
//
|
|
// For our starting point, we offset the formula using the outer
|
|
// radius because our origin is at (top, left).
|
|
// In typical web layout fashion, we are drawing in quadrant IV
|
|
// (a.k.a. Southeast) where x is positive and y is negative.
|
|
//
|
|
// The arguments for path.arc and path.counterArc used below are:
|
|
// (endX, endY, radiusX, radiusY, largeAngle)
|
|
|
|
path
|
|
.move(or + or * ss, or - or * sc) // move to starting point
|
|
.arc(or * ds, or * -dc, or, or, large) // outer arc
|
|
.line(dr * es, dr * -ec); // width of arc or wedge
|
|
|
|
if (ir) {
|
|
path.counterArc(ir * -ds, ir * dc, ir, ir, large); // inner arc
|
|
}
|
|
|
|
return path;
|
|
},
|
|
|
|
render: function render() {
|
|
// angles are provided in degrees
|
|
var startAngle = this.props.startAngle;
|
|
var endAngle = this.props.endAngle;
|
|
if (startAngle - endAngle === 0) {
|
|
return null;
|
|
}
|
|
|
|
// radii are provided in pixels
|
|
var innerRadius = this.props.innerRadius || 0;
|
|
var outerRadius = this.props.outerRadius;
|
|
|
|
// sorted radii
|
|
var ir = Math.min(innerRadius, outerRadius);
|
|
var or = Math.max(innerRadius, outerRadius);
|
|
|
|
var path;
|
|
if (endAngle >= startAngle + 360) {
|
|
path = this._createCirclePath(or, ir);
|
|
} else {
|
|
path = this._createArcPath(startAngle, endAngle, or, ir);
|
|
}
|
|
|
|
return React.createElement(Shape, assign({}, this.props, {d: path}));
|
|
},
|
|
});
|
|
|
|
module.exports = Wedge;
|