Source: columns.js

"use strict"

/**
 * Builds columns. Input 2D profiles must be centred at (0, 0, 0)
 * @namespace builders.columns
 */

const columnBuilder = ({ lib, swLib }) => {
  const { cuboid, roundedCylinder, cylinder } = lib.primitives
  const { align } = lib.transforms
  const { subtract, union } = lib.booleans
  const { extrudeLinear } = lib.extrusions
  const { measureBoundingBox } = lib.measurements

  /**
   * Simple cuboid
   * @param {Object} opts
   * @param {number} opts.height - element height
   * @param {number} opts.radius - element radius
   */
  const colCuboid = (opts) => {

    return cuboid({ size: [opts.radius * 2, opts.radius * 2, opts.height] });
  }

  /**
   * Simple cylinder
   * @param {Object} opts
   * @param {number} opts.height - element height
   * @param {number} opts.radius - element radius
   */
  const colCylinder = (opts) => {


    return cylinder({ radius: opts.radius, height: opts.height });
  }

  /**
   * Simple round cylinder
   * @param {Object} opts
   * @param {number} opts.height - element height
   * @param {number} opts.radius - element radius
   * @param {number} opts.roundRadius - radius of cylinder edge
   */
  const capRdCylinder = (opts) => {
    const rdRadius = opts.roundRadius || 0.75;
    const baseShape = roundedCylinder({ radius: opts.radius, height: opts.height * 2, roundRadius: rdRadius });
    const cutBlock = align(
      { modes: ['none', 'none', 'min'] },
      cuboid({ size: [opts.radius * 2.5, opts.radius * 2.5, opts.height * 2] })
    );

    return subtract(baseShape, cutBlock);
  }

  /**
   * Base round cylinder
   * @param {Object} opts
   * @param {number} opts.height - element height
   * @param {number} opts.radius - element radius
   * @param {number} opts.roundRadius - radius of cylinder edge
   */
  const baseRdCylinder = (opts) => {
    const rdRadius = opts.roundRadius || 1;
    const baseShape = roundedCylinder({ radius: opts.radius, height: opts.height * 2, roundRadius: rdRadius });
    const cutBlock = align(
      { modes: ['none', 'none', 'max'] },
      cuboid({ size: [opts.radius * 2.5, opts.radius * 2.5, opts.height * 2] })
    );

    return subtract(baseShape, cutBlock);
  }

  /**
   * Simple extrude
   * @param {Object} opts
   * @param {number} opts.height - element height
   * @param {geom2.Geom2} geomProfile - 2D cross-section profile
   */
  const colExtrude = (opts, geomProfile) => {
    return extrudeLinear({ height: opts.height }, geomProfile);
  }


  /**
   * Defines the construction of column sub-components (base, shaft, capital).
   * All functions follow the function signature of base.cuboid().
   * @access private
   */
  const columnPartBuilder = {
    base: {
      cuboid: colCuboid,
      cylinder: colCylinder,
      roundCylinder: baseRdCylinder,
      extrude: colExtrude,
    },
    shaft: {
      cuboid: colCuboid,
      cylinder: colCylinder,
      extrude: colExtrude,
    },
    capital: {
      cuboid: colCuboid,
      cylinder: colCylinder,
      roundCylinder: capRdCylinder,
      extrude: colExtrude,
    },
  }

  return {
    columnPartBuilder,
    /**
     * Builds a three-part column using the specified dimensions and styles.
     * @memberof builders.columns
     * @instance
     * @param {Object} opts
     * @param {Array<string|number|geom2.Geom2|null>} opts.base - specs for column base (style, height, radius, geomProfile)
     * @param {Array<string|number|geom2.Geom2|null>} opts.shaft - specs for column shaft (style, radius, geomProfile)
     * @param {Array<string|number|geom2.Geom2|null>} opts.capital - specs for column capital (style, height, radius, geomProfile)
     * @param {number} opts.height - total height of column
     */
    threePtColumn: (opts) => {
      const baseStyle = opts.base[0];
      const shaftStyle = opts.shaft[0];
      const capitalStyle = opts.capital[0];

      const base = columnPartBuilder.base[baseStyle]({
        height: opts.base[1],
        radius: opts.base[2],
      }, opts.base[3]);

      const shaft = columnPartBuilder.shaft[shaftStyle]({
        height: opts.height,
        radius: opts.shaft[1],
      }, opts.shaft[2]);

      const capital = columnPartBuilder.capital[capitalStyle]({
        height: opts.capital[1],
        radius: opts.capital[2],
      }, opts.capital[3]);

      const shaftBbox = measureBoundingBox(shaft);
      const [shaftMin, shaftMax] = [shaftBbox[0][2], shaftBbox[1][2]];

      const alignedBase = align({ modes: ['center', 'center', 'min'], relativeTo: [0, 0, shaftMin] }, base)
      const alignedCap = align({ modes: ['center', 'center', 'max'], relativeTo: [0, 0, shaftMax] }, capital)

      return align({ modes: ['center', 'center', 'min'] }, union(alignedBase, shaft, alignedCap))
    }
  }
}

module.exports = { init: columnBuilder };