UNPKG

@jscad/modeling

Version:

Constructive Solid Geometry (CSG) Library for JSCAD

87 lines (74 loc) 3.21 kB
const { EPS, TAU } = require('../maths/constants') const vec2 = require('../maths/vec2') const path2 = require('../geometries/path2') const { isGT, isGTE, isNumberArray } = require('./commonChecks') /** * Construct an arc in two dimensional space where all points are at the same distance from the center. * @param {Object} [options] - options for construction * @param {Array} [options.center=[0,0]] - center of arc * @param {Number} [options.radius=1] - radius of arc * @param {Number} [options.startAngle=0] - starting angle of the arc, in radians * @param {Number} [options.endAngle=TAU] - ending angle of the arc, in radians * @param {Number} [options.segments=32] - number of segments to create per full rotation * @param {Boolean} [options.makeTangent=false] - adds line segments at both ends of the arc to ensure that the gradients at the edges are tangent * @returns {path2} new 2D path * @alias module:modeling/primitives.arc * @example * let myshape = arc({ center: [-1, -1], radius: 2, endAngle: (TAU / 4)}) */ const arc = (options) => { const defaults = { center: [0, 0], radius: 1, startAngle: 0, endAngle: TAU, makeTangent: false, segments: 32 } let { center, radius, startAngle, endAngle, makeTangent, segments } = Object.assign({}, defaults, options) if (!isNumberArray(center, 2)) throw new Error('center must be an array of X and Y values') if (!isGT(radius, 0)) throw new Error('radius must be greater than zero') if (!isGTE(startAngle, 0)) throw new Error('startAngle must be positive') if (!isGTE(endAngle, 0)) throw new Error('endAngle must be positive') if (!isGTE(segments, 4)) throw new Error('segments must be four or more') startAngle = startAngle % TAU endAngle = endAngle % TAU let rotation = TAU if (startAngle < endAngle) { rotation = endAngle - startAngle } if (startAngle > endAngle) { rotation = endAngle + (TAU - startAngle) } const minangle = Math.acos(((radius * radius) + (radius * radius) - (EPS * EPS)) / (2 * radius * radius)) const centerv = vec2.clone(center) let point const pointArray = [] if (rotation < minangle) { // there is no rotation, just a single point point = vec2.fromAngleRadians(vec2.create(), startAngle) vec2.scale(point, point, radius) vec2.add(point, point, centerv) pointArray.push(point) } else { const numsteps = Math.floor(segments * (Math.abs(rotation) / TAU)) let edgestepsize = numsteps * 0.5 / rotation // step size for half a degree if (edgestepsize > 0.25) edgestepsize = 0.25 const totalsteps = makeTangent ? (numsteps + 2) : numsteps for (let i = 0; i <= totalsteps; i++) { let step = i if (makeTangent) { step = (i - 1) * (numsteps - 2 * edgestepsize) / numsteps + edgestepsize if (step < 0) step = 0 if (step > numsteps) step = numsteps } const angle = startAngle + (step * (rotation / numsteps)) point = vec2.fromAngleRadians(vec2.create(), angle) vec2.scale(point, point, radius) vec2.add(point, point, centerv) pointArray.push(point) } } return path2.fromPoints({ closed: false }, pointArray) } module.exports = arc