Source: objects/BasePoints.js

import { Utils } from '../common/Utils';
import { MathUtils } from '../math/MathUtils';
import { Object3D } from './Object3D';

/**
 * @class BasePoints
 * The base points object.
 * @memberof THING
 * @extends THING.Object3D
 */
class BasePoints extends Object3D {

	_selfPoints = [];

	_autoAdjustPosition = false;

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

	// #region Private

	_adjustPosition(points) {
		let center = MathUtils.getCenterFromPoints(points);

		this.position = center;
	}

	// #endregion

	// #region Overrides

	onSyncOptions(param) {
		Utils.syncOptions(this, [
			'autoAdjustPosition',
			'selfPoints',
			'points'
		], param, param['extras'] || param['external']);
	}

	onExportExternalData() {
		let external = Object.assign({}, super.onExportExternalData());
		Utils.setAttributeIfExist(external, 'autoAdjustPosition', this);
		Utils.setAttributeIfExist(external, 'selfPoints', this);
		return external;
	}

	onSetupPosition(param) {
		if (param['localPosition'] || param['position']) {
			super.onSetupPosition(param);
		}
		// Auto set center of points as line position
		else if (param['points']) {
			let center = MathUtils.getCenterFromPoints(param['points']);
			this.position = center;
		}
		else {
			super.onSetupPosition(param);
		}
	}

	/**
	 * When get self points callback function.
	 * @param {Array<Array<Number>>} selfPoints The self points.
	 * @returns {Array<Array<Number>>}
	 * @private
	 */
	onGetSelfPoints(selfPoints) {
		return selfPoints;
	}

	/**
	 * Before set body self points.
	 * @param {Array<Array<Number>>} selfPoints The self points.
	 * @private
	 */
	onBeforeSetBodyPoints(selfPoints) {
	}

	/**
	 * After set body self points.
	 * @private
	 */
	onAfterSetBodyPoints() {

	}

	onRefresh() {
		this.onRefreshPoints();
		this.onRefreshAttributes();
	}

	/**
	 * When refresh points.
	 * @returns {Array<Array<Number>>}
	 * @private
	 */
	onRefreshPoints() {
		this.onBeforeSetBodyPoints(this._selfPoints);

		let selfPoints = this.onGetSelfPoints(this._selfPoints);
		this.bodyNode.setPoints(selfPoints);

		this.onAfterSetBodyPoints();

		return selfPoints;
	}

	/**
	 * When refresh attributes.
	 * @private
	 */
	onRefreshAttributes() {

	}

	// #endregion

	/**
	 * Clear all points.
	 * @example
	 * let point = new THING.Points();
	 * let ret = point.points.length == 0;
	 * // @expect(ret == true);
	 * point.addPoint([1,1,1]);
	 * ret = point.points.length == 1;
	 * // @expect(ret == true);
	 * point.clearPoints();
	 * ret = point.points.length == 0;
	 * // @expect(ret == true);
	 */
	clearPoints() {
		this._selfPoints = [];

		this.bodyNode.setPoints([]);
	}

	/**
	 * Insert point by index in world space.
	 * @param {Number} index The index.
	 * @param {Array<Number>} position The world position.
	 *  @example
	 * let point = new THING.Points();
	 * point.addPoint([1,1,1]);
	 * point.insertPoint(0,[10,10,10]);
	 * ret = point.points[0][0] == 10;
	 * // @expect(ret == true);
	 */
	insertPoint(index, position) {
		let selfPoints = this._selfPoints;

		selfPoints._insert(index, this.worldToSelf(position));

		this.onRefreshPoints();
	}

	/**
	 * Add point in world space.
	 * @param {Array<Number>} position The world position.
	 * @example
	 * let point = new THING.Points();
	 * let ret = point.points.length == 0;
	 * // @expect(ret == true);
	 * point.addPoint([1,1,1]);
	 * ret = point.points.length == 1;
	 * // @expect(ret == true);
	 */
	addPoint(position) {
		let selfPoints = this._selfPoints;

		selfPoints.push(this.worldToSelf(position));

		this.onRefreshPoints();
	}

	/**
	 * Add points in world space.
	 * @param {Array<Array<Number>>} points The points in world space.
	 * @example
	 * let point = new THING.Points();
	 * let ret = point.points.length == 0;
	 * // @expect(ret == true);
	 * point.addPoints([[1,1,1],[2,2,2]]);
	 * ret = point.points.length == 2;
	 * // @expect(ret == true);
	 */
	addPoints(points) {
		let selfPoints = this._selfPoints;

		points.forEach(point => {
			selfPoints.push(this.worldToSelf(point));
		});

		this.onRefreshPoints();
	}

	/**
	 * Remove point by index.
	 * @param {Number} index The index.
	 * @example
	 * let point = new THING.Points();
	 * let ret = point.points.length == 0;
	 * // @expect(ret == true);
	 * point.addPoints([[1,1,1],[2,2,2]]);
	 * ret = point.points[0][0] == 1;
	 * // @expect(ret == true);
	 * point.removePoint(0);
	 * ret = point.points[0][0] == 2;
	 * // @expect(ret == true);
	 */
	removePoint(index) {
		let selfPoints = this._selfPoints;

		selfPoints._removeAt(index);

		this.onRefreshPoints();
	}

	/**
	 * Set the point by index in world space.
	 * @param {Number} index The index.
	 * @param {Array<Number>} position The world position.
	 * @example
	 * let point = new THING.Points();
	 * let ret = point.points.length == 0;
	 * // @expect(ret == true);
	 * point.addPoints([[1,1,1],[2,2,2]]);
	 * ret = point.points[0][0] == 1;
	 * // @expect(ret == true);
	 * point.setPoint(0,[10,10,10]);
	 * ret = point.points[0][0] == 10;
	 * // @expect(ret == true);
	 */
	setPoint(index, position) {
		let selfPoints = this._selfPoints;

		selfPoints[index] = this.worldToSelf(position);

		this.onRefreshPoints();
	}

	// #region Accessor

	/**
	 * Get the number of points.
	 * @type {Number}
	 * @example
	 * let point = new THING.Points();
	 * let ret = point.number == 0;
	 * // @expect(ret == true);
	 * point.addPoints([[1,1,1],[2,2,2]]);
	 * ret = point.number == 2;
	 * // @expect(ret == true);
	 */
	get number() {
		return this.points.length;
	}

	/**
	 * Get/Set the auto adjust position flag, default is false.
	 * If the relative position of point(s) is too large, there will be a problem of precision loss (object drawing will shake).
	 * @type {Boolean}
	 * @example
	 * let point = new THING.Points();
	 * let ret = point.autoAdjustPosition == false;
	 * // @expect(ret == true);
	 * point.autoAdjustPosition = true;
	 * ret = point.autoAdjustPosition == true;
	 * // @expect(ret == true);
	 */
	get autoAdjustPosition() {
		return this._autoAdjustPosition;
	}
	set autoAdjustPosition(value) {
		this._autoAdjustPosition = value;
	}

	/**
	 * Get/Set the points in self space.
	 * @type {Array<Array<Number>>}
	 * @example
	 * let point = new THING.Points();
	 * point.selfPoints = [[1,1,1],[2,2,2]];
	 * ret = point.selfPoints[1][1] == 2;
	 * // @expect(ret == true);
	 */
	get selfPoints() {
		return this._selfPoints;
	}
	set selfPoints(value) {
		this._selfPoints = value ? value.slice(0) : [];

		this.onRefreshPoints();
	}

	/**
	 * Get/Set the points in world space.
	 * @type {Array<Array<Number>>}
 	 * @example
	 * let point = new THING.Points();
	 * point.points = [[1,1,1],[2,2,2]];
	 * ret = point.points[1][1] == 2;
	 * // @expect(ret == true);
	 */
	get points() {
		return this._selfPoints.map(point => {
			return this.selfToWorld(point);
		});
	}
	set points(value) {
		if (value) {
			if (this._autoAdjustPosition) {
				this._adjustPosition(value);
			}

			this._selfPoints = value.map(point => {
				return this.worldToSelf(point);
			});

			this.onRefreshPoints();
		}
		else {
			this.clearPoints();
		}
	}

	/**
	 * Get/Set the points in local space.
	 * @type {Array<Array<Number>>}
	 * @example
	 * let point = new THING.Points();
	 * point.localPoints = [[1,1,1],[2,2,2]];
	 * ret = point.localPoints[1][1] == 2;
	 * // @expect(ret == true);
	 */
	get localPoints() {
		return this._selfPoints.map(point => {
			return this.selfToLocal(point);
		});
	}
	set localPoints(value) {
		if (value) {
			this._selfPoints = value.map(point => {
				return this.localToSelf(point);
			});

			this.onRefreshPoints();
		}
		else {
			this.clearPoints();
		}
	}

	/**
	 * Get the length of points.
	 * @type {Number}
	 * @example
	 * let point = new THING.Points();
	 * point.localPoints = [[0,0,0],[3,0,0],[0,4,0]];
	 * ret = point.length == 8;
	 * // @expect(ret == true);
	 */
	get length() {
		return MathUtils.getDistanceFromPoints(this.points);
	}

	// #endregion

}

export { BasePoints }