modeling/src/primitives/polygon.js

const geom2 = require('../geometries/geom2')

/**
 * Construct a polygon in two dimensional space from a list of points, or a list of points and paths.
 *
 * NOTE: The ordering of points is important, and must define a counter clockwise rotation of points.
 *
 * @param {Object} options - options for construction
 * @param {Array} options.points - points of the polygon : either flat or nested array of 2D points
 * @param {Array} [options.paths] - paths of the polygon : either flat or nested array of point indexes
 * @param {String} [options.orientation='counterclockwise'] - orientation of points
 * @returns {geom2} new 2D geometry
 * @alias module:modeling/primitives.polygon
 *
 * @example
 * let roof = [[10,11], [0,11], [5,20]]
 * let wall = [[0,0], [10,0], [10,10], [0,10]]
 *
 * let poly = polygon({ points: roof })
 * or
 * let poly = polygon({ points: [roof, wall] })
 * or
 * let poly = polygon({ points: roof, paths: [0, 1, 2] })
 * or
 * let poly = polygon({ points: [roof, wall], paths: [[0, 1, 2], [3, 4, 5, 6]] })
 */
const polygon = (options) => {
  const defaults = {
    points: [],
    paths: [],
    orientation: 'counterclockwise'
  }
  const { points, paths, orientation } = Object.assign({}, defaults, options)

  if (!(Array.isArray(points) && Array.isArray(paths))) throw new Error('points and paths must be arrays')

  let listofpolys = points
  if (Array.isArray(points[0])) {
    if (!Array.isArray(points[0][0])) {
      // points is an array of something... convert to list
      listofpolys = [points]
    }
  }

  listofpolys.forEach((list, i) => {
    if (!Array.isArray(list)) throw new Error('list of points ' + i + ' must be an array')
    if (list.length < 3) throw new Error('list of points ' + i + ' must contain three or more points')
    list.forEach((point, j) => {
      if (!Array.isArray(point)) throw new Error('list of points ' + i + ', point ' + j + ' must be an array')
      if (point.length < 2) throw new Error('list of points ' + i + ', point ' + j + ' must contain by X and Y values')
    })
  })

  let listofpaths = paths
  if (paths.length === 0) {
    // create a list of paths based on the points
    let count = 0
    listofpaths = listofpolys.map((list) => list.map((point) => count++))
  }

  // flatten the listofpoints for indexed access
  const allpoints = []
  listofpolys.forEach((list) => list.forEach((point) => allpoints.push(point)))

  // convert the list of paths into a list of sides, and accumulate
  let sides = []
  listofpaths.forEach((path) => {
    const setofpoints = path.map((index) => allpoints[index])
    const geometry = geom2.fromPoints(setofpoints)
    sides = sides.concat(geom2.toSides(geometry))
  })

  // convert the list of sides into a geometry
  let geometry =  geom2.create(sides)
  if (orientation == "clockwise") {
    geometry = geom2.reverse(geometry)
  }
  return geometry
}

module.exports = polygon