UNPKG

@jscad/modeling

Version:

Constructive Solid Geometry (CSG) Library for JSCAD

115 lines (97 loc) 3.98 kB
const mat4 = require('../../maths/mat4') const geom2 = require('../../geometries/geom2') const geom3 = require('../../geometries/geom3') const poly3 = require('../../geometries/poly3') const slice = require('./slice') const repairSlice = require('./slice/repair') const extrudeWalls = require('./extrudeWalls') const defaultCallback = (progress, index, base) => { let baseSlice = null if (geom2.isA(base)) baseSlice = slice.fromSides(geom2.toSides(base)) if (poly3.isA(base)) baseSlice = slice.fromPoints(poly3.toPoints(base)) return progress === 0 || progress === 1 ? slice.transform(mat4.fromTranslation(mat4.create(), [0, 0, progress]), baseSlice) : null } /** * Extrude a solid from the slices as returned by the callback function. * @see slice * * @param {Object} options - options for extrude * @param {Integer} [options.numberOfSlices=2] the number of slices to be generated by the callback * @param {Boolean} [options.capStart=true] the solid should have a cap at the start * @param {Boolean} [options.capEnd=true] the solid should have a cap at the end * @param {Boolean} [options.close=false] the solid should have a closing section between start and end * @param {Boolean} [options.repair=true] - repair gaps in the geometry * @param {Function} [options.callback] the callback function that generates each slice * @param {Object} base - the base object which is used to create slices (see the example for callback information) * @return {geom3} the extruded shape * @alias module:modeling/extrusions.extrudeFromSlices * * @example * // Parameters: * // progress : the percent complete [0..1] * // index : the index of the current slice [0..numberOfSlices - 1] * // base : the base object as given * // Return Value: * // slice or null (to skip) * const callback = (progress, index, base) => { * ... * return slice * } */ const extrudeFromSlices = (options, base) => { const defaults = { numberOfSlices: 2, capStart: true, capEnd: true, close: false, repair: true, callback: defaultCallback } const { numberOfSlices, capStart, capEnd, close, repair, callback: generate } = Object.assign({ }, defaults, options) if (numberOfSlices < 2) throw new Error('numberOfSlices must be 2 or more') // Repair gaps in the base slice if (repair) { // note: base must be a slice, if base is geom2 this doesn't repair base = repairSlice(base) } const sMax = numberOfSlices - 1 let startSlice = null let endSlice = null let prevSlice = null let polygons = [] for (let s = 0; s < numberOfSlices; s++) { // invoke the callback function to get the next slice // NOTE: callback can return null to skip the slice const currentSlice = generate(s / sMax, s, base) if (currentSlice) { if (!slice.isA(currentSlice)) throw new Error('the callback function must return slice objects') const edges = slice.toEdges(currentSlice) if (edges.length === 0) throw new Error('the callback function must return slices with one or more edges') if (prevSlice) { polygons = polygons.concat(extrudeWalls(prevSlice, currentSlice)) } // save start and end slices for caps if necessary if (s === 0) startSlice = currentSlice if (s === (numberOfSlices - 1)) endSlice = currentSlice prevSlice = currentSlice } } if (capEnd) { // create a cap at the end const endPolygons = slice.toPolygons(endSlice) polygons = polygons.concat(endPolygons) } if (capStart) { // create a cap at the start const startPolygons = slice.toPolygons(startSlice).map(poly3.invert) polygons = polygons.concat(startPolygons) } if (!capStart && !capEnd) { // create walls between end and start slices if (close && !slice.equals(endSlice, startSlice)) { polygons = polygons.concat(extrudeWalls(endSlice, startSlice)) } } return geom3.create(polygons) } module.exports = extrudeFromSlices