modeling/src/primitives/polygon.js

  1. const geom2 = require('../geometries/geom2')
  2. /**
  3. * Construct a polygon in two dimensional space from a list of points, or a list of points and paths.
  4. *
  5. * NOTE: The ordering of points is important, and must define a counter clockwise rotation of points.
  6. *
  7. * @param {Object} options - options for construction
  8. * @param {Array} options.points - points of the polygon : either flat or nested array of 2D points
  9. * @param {Array} [options.paths] - paths of the polygon : either flat or nested array of point indexes
  10. * @param {String} [options.orientation='counterclockwise'] - orientation of points
  11. * @returns {geom2} new 2D geometry
  12. * @alias module:modeling/primitives.polygon
  13. *
  14. * @example
  15. * let roof = [[10,11], [0,11], [5,20]]
  16. * let wall = [[0,0], [10,0], [10,10], [0,10]]
  17. *
  18. * let poly = polygon({ points: roof })
  19. * or
  20. * let poly = polygon({ points: [roof, wall] })
  21. * or
  22. * let poly = polygon({ points: roof, paths: [0, 1, 2] })
  23. * or
  24. * let poly = polygon({ points: [roof, wall], paths: [[0, 1, 2], [3, 4, 5, 6]] })
  25. */
  26. const polygon = (options) => {
  27. const defaults = {
  28. points: [],
  29. paths: [],
  30. orientation: 'counterclockwise'
  31. }
  32. const { points, paths, orientation } = Object.assign({}, defaults, options)
  33. if (!(Array.isArray(points) && Array.isArray(paths))) throw new Error('points and paths must be arrays')
  34. let listofpolys = points
  35. if (Array.isArray(points[0])) {
  36. if (!Array.isArray(points[0][0])) {
  37. // points is an array of something... convert to list
  38. listofpolys = [points]
  39. }
  40. }
  41. listofpolys.forEach((list, i) => {
  42. if (!Array.isArray(list)) throw new Error('list of points ' + i + ' must be an array')
  43. if (list.length < 3) throw new Error('list of points ' + i + ' must contain three or more points')
  44. list.forEach((point, j) => {
  45. if (!Array.isArray(point)) throw new Error('list of points ' + i + ', point ' + j + ' must be an array')
  46. if (point.length < 2) throw new Error('list of points ' + i + ', point ' + j + ' must contain by X and Y values')
  47. })
  48. })
  49. let listofpaths = paths
  50. if (paths.length === 0) {
  51. // create a list of paths based on the points
  52. let count = 0
  53. listofpaths = listofpolys.map((list) => list.map((point) => count++))
  54. }
  55. // flatten the listofpoints for indexed access
  56. const allpoints = []
  57. listofpolys.forEach((list) => list.forEach((point) => allpoints.push(point)))
  58. // convert the list of paths into a list of sides, and accumulate
  59. let sides = []
  60. listofpaths.forEach((path) => {
  61. const setofpoints = path.map((index) => allpoints[index])
  62. const geometry = geom2.fromPoints(setofpoints)
  63. sides = sides.concat(geom2.toSides(geometry))
  64. })
  65. // convert the list of sides into a geometry
  66. let geometry = geom2.create(sides)
  67. if (orientation === 'clockwise') {
  68. geometry = geom2.reverse(geometry)
  69. }
  70. return geometry
  71. }
  72. module.exports = polygon