Source: objects/BasePlanePoints.js

import { Utils } from '../common/Utils'
import { MathUtils } from '../math/MathUtils';
import { PlanePointsHelperComponent } from '../components/PlanePointsHelperComponent';
import { BaseDynamicPoints } from './BaseDynamicPoints';

const registerComponentParam = { isResident: true };

/**
 * @class BasePlanePoints
 * The base plane points object.
 * @memberof THING
 * @extends THING.BaseDynamicPoints
 */
class BasePlanePoints extends BaseDynamicPoints {

	_selfHoles = [];

	/**
	 * The base points object in plane.
	 * @param {Object} param The initial parameters.
	 */
	constructor(param = {}) {
		super(param);
	}

	// #region Private Functions

	/**
	 * Refresh holes.
	 * @private
	 */
	_refreshHoles() {
		let selfPlaneHoles = this.selfPlaneHoles;

		this.bodyNode.setHoles(selfPlaneHoles);

		if (this.hasComponent('helper')) {
			this.helper.refresh();
		}
	}

	/**
	 * Get the outline points in self space in plane.
	 * @returns {Array<Array<Number>>}
	 * @private
	 */
	_getOutlinePoints() {
		let planePoints = this.onGetSelfPoints(this.selfPoints);

		return MathUtils.getOutlinePoints(planePoints);
	}

	// #endregion

	// #region Overrides

	onSyncOptions(param) {
		super.onSyncOptions(param);

		Utils.syncOptions(this, [
			'selfPlanePoints',
			'selfPlaneHoles',
			'selfHoles',
			'holes'
		], param, param['extras'] || param['external']);
	}

	onExportExternalData() {
		let external = Object.assign({}, super.onExportExternalData());
		Utils.setAttributeIfExist(external, 'selfPlanePoints', this);
		Utils.setAttributeIfExist(external, 'selfPlaneHoles', this);
		Utils.setAttributeIfExist(external, 'selfHoles', this);
		return external;
	}

	onSetupComponent(param) {
		super.onSetupComponent(param);

		this.addComponent(PlanePointsHelperComponent, 'helper', registerComponentParam);
	}

	onSetupTransform(param) {
		// Auto calculate the position when it do not provide any positions
		let position = param['position'] || param['localPosition'];
		if (!position) {
			let points = param['points'];
			if (points && points.length) {
				// Get the height
				let height = points.reduce((value, point) => {
					return value + point[1];
				}, 0) / points.length;

				// Make label position as default position
				param['position'] = MathUtils.getLabelPosition(points, param['holes'], height);
			}
		}

		super.onSetupTransform(param);
	}

	onGetSelfPoints(selfPoints) {
		return selfPoints.map(point => {
			return [point[0], point[2]];
		});
	}

	onBeforeSetBodyPoints(selfPoints) {
	}

	onRefresh() {
		this._refreshHoles();

		super.onRefresh();
	}

	// #endregion

	// #region Accessor

	/**
	 * Get/Set the plane holes in self space.
	 * @type {Array<Array<Array<Number>>>}
	 * @private
	 */
	get selfPlaneHoles() {
		return this._selfHoles.map(hole => {
			return hole.map(_hole => {
				return [_hole[0], _hole[2]];
			});
		});
	}
	set selfPlaneHoles(value) {
		this._selfHoles = value.map(hole => {
			return hole.map(_hole => {
				return [_hole[0], 0, _hole[1]];
			});
		});

		this._refreshHoles();
	}

	/**
	 * Get/Set the holes in self space.
	 * @type {Array<Array<Array<Number>>>}
	 * @private
	 */
	get selfHoles() {
		return this._selfHoles;
	}
	set selfHoles(value = []) {
		this._selfHoles = value.slice(0);

		this._refreshHoles();
	}

	/**
	 * Get/Set the holes in world space.
	 * @type {Array<Array<Array<Number>>>}
	 */
	get holes() {
		return this._selfHoles.map(hole => {
			return hole.map(_hole => {
				return this.selfToWorld(_hole);
			});
		});
	}
	set holes(value = []) {
		this._selfHoles = value.map(hole => {
			return hole.map(_hole => {
				return this.worldToSelf(_hole);
			});
		});

		this._refreshHoles();
	}

	/**
	 * Get/Set the plane points in self space.
	 * @type {Array<Array<Array<Number>>>}
	 * @private
	 */
	get selfPlanePoints() {
		return this.points.map(point => {
			let selfPoint = this.worldToSelf(point);

			return [selfPoint[0], selfPoint[2]];
		});
	}
	set selfPlanePoints(value = []) {
		this.points = MathUtils.makeClockWisePoints(value).map(point => {
			return this.selfToWorld([point[0], 0, point[1]]);
		});
	}

	/**
	 * Get the area.
	 * @type {Number}
	 */
	get area() {
		let outlinePoints = this._getOutlinePoints();

		let area = Math.abs(MathUtils.getArea(outlinePoints));

		// Sub holes area
		this.selfPlaneHoles.forEach(holes => {
			let holeArea = MathUtils.getArea(holes);
			area -= holeArea;
		});

		return area;
	}

	/**
	 * Get the perimeter.
	 * @type {Number}
	 */
	get perimeter() {
		let outlinePoints = this.outlinePoints;

		return MathUtils.getDistanceFromPoints(outlinePoints, true);
	}

	/**
	 * Get the label position in world space.
	 * @type {Array<Number>}
	 */
	get labelPosition() {
		let outlinePoints = this._getOutlinePoints();

		let labelPos = MathUtils.getLabelPosition(outlinePoints);
		return this.selfToWorld(labelPos);
	}

	/**
	 * Get the outline points in plane.
	 * @type {Array<Array<Number>>}
	 */
	get outlinePoints() {
		let planePoints = this._getOutlinePoints();

		return planePoints.map(point => {
			return this.selfToWorld([point[0], 0, point[1]]);
		});
	}

	// #endregion

}

export { BasePlanePoints }