UNPKG

@jscad/modeling

Version:

Constructive Solid Geometry (CSG) Library for JSCAD

100 lines (87 loc) 4.51 kB
const { TAU } = require('../maths/constants') const vec3 = require('../maths/vec3') const geom3 = require('../geometries/geom3') const poly3 = require('../geometries/poly3') const { sin, cos } = require('../maths/utils/trigonometry') const { isGTE, isNumberArray } = require('./commonChecks') /** * Construct an axis-aligned ellipsoid in three dimensional space. * @param {Object} [options] - options for construction * @param {Array} [options.center=[0,0,0]] - center of ellipsoid * @param {Array} [options.radius=[1,1,1]] - radius of ellipsoid, along X, Y and Z * @param {Number} [options.segments=32] - number of segments to create per full rotation * @param {Array} [options.axes] - an array with three vectors for the x, y and z base vectors * @returns {geom3} new 3D geometry * @alias module:modeling/primitives.ellipsoid * * @example * let myshape = ellipsoid({radius: [5, 10, 20]}) */ const ellipsoid = (options) => { const defaults = { center: [0, 0, 0], radius: [1, 1, 1], segments: 32, axes: [[1, 0, 0], [0, -1, 0], [0, 0, 1]] } const { center, radius, segments, axes } = Object.assign({}, defaults, options) if (!isNumberArray(center, 3)) throw new Error('center must be an array of X, Y and Z values') if (!isNumberArray(radius, 3)) throw new Error('radius must be an array of X, Y and Z values') if (!radius.every((n) => n >= 0)) throw new Error('radius values must be positive') if (!isGTE(segments, 4)) throw new Error('segments must be four or more') // if any radius is zero return empty geometry if (radius[0] === 0 || radius[1] === 0 || radius[2] === 0) return geom3.create() const xvector = vec3.scale(vec3.create(), vec3.normalize(vec3.create(), axes[0]), radius[0]) const yvector = vec3.scale(vec3.create(), vec3.normalize(vec3.create(), axes[1]), radius[1]) const zvector = vec3.scale(vec3.create(), vec3.normalize(vec3.create(), axes[2]), radius[2]) const qsegments = Math.round(segments / 4) let prevcylinderpoint const polygons = [] const p1 = vec3.create() const p2 = vec3.create() for (let slice1 = 0; slice1 <= segments; slice1++) { const angle = TAU * slice1 / segments const cylinderpoint = vec3.add(vec3.create(), vec3.scale(p1, xvector, cos(angle)), vec3.scale(p2, yvector, sin(angle))) if (slice1 > 0) { let prevcospitch, prevsinpitch for (let slice2 = 0; slice2 <= qsegments; slice2++) { const pitch = TAU / 4 * slice2 / qsegments const cospitch = cos(pitch) const sinpitch = sin(pitch) if (slice2 > 0) { let points = [] let point point = vec3.subtract(vec3.create(), vec3.scale(p1, prevcylinderpoint, prevcospitch), vec3.scale(p2, zvector, prevsinpitch)) points.push(vec3.add(point, point, center)) point = vec3.subtract(vec3.create(), vec3.scale(p1, cylinderpoint, prevcospitch), vec3.scale(p2, zvector, prevsinpitch)) points.push(vec3.add(point, point, center)) if (slice2 < qsegments) { point = vec3.subtract(vec3.create(), vec3.scale(p1, cylinderpoint, cospitch), vec3.scale(p2, zvector, sinpitch)) points.push(vec3.add(point, point, center)) } point = vec3.subtract(vec3.create(), vec3.scale(p1, prevcylinderpoint, cospitch), vec3.scale(p2, zvector, sinpitch)) points.push(vec3.add(point, point, center)) polygons.push(poly3.create(points)) points = [] point = vec3.add(vec3.create(), vec3.scale(p1, prevcylinderpoint, prevcospitch), vec3.scale(p2, zvector, prevsinpitch)) points.push(vec3.add(vec3.create(), center, point)) point = vec3.add(point, vec3.scale(p1, cylinderpoint, prevcospitch), vec3.scale(p2, zvector, prevsinpitch)) points.push(vec3.add(vec3.create(), center, point)) if (slice2 < qsegments) { point = vec3.add(point, vec3.scale(p1, cylinderpoint, cospitch), vec3.scale(p2, zvector, sinpitch)) points.push(vec3.add(vec3.create(), center, point)) } point = vec3.add(point, vec3.scale(p1, prevcylinderpoint, cospitch), vec3.scale(p2, zvector, sinpitch)) points.push(vec3.add(vec3.create(), center, point)) points.reverse() polygons.push(poly3.create(points)) } prevcospitch = cospitch prevsinpitch = sinpitch } } prevcylinderpoint = cylinderpoint } return geom3.create(polygons) } module.exports = ellipsoid