Source: components/SpaceHelperComponent.js

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

const __ = {
	private: Symbol('private'),
}

/**
 * @class SpaceHelperComponent
 * The space helper component.
 * @memberof THING
 * @extends THING.BaseComponent
 * @public
 */
class SpaceHelperComponent extends BaseComponent {

	/**
	 * The space relation component of object.
	 */
	constructor(param) {
		super(param);

		this[__.private] = {};
		let _private = this[__.private];

		_private.lines = null;
		_private.box = null;

		_private.linesColor = [1, 1, 1];
		_private.boxColor = [0, 1, 0];
		_private.boxOpacity = 0.3;

		_private.points = [];
		_private.colors = [];

		_private.initHalfSize = [];
		_private.initOffset = [];
	}

	onRemove() {
		let _private = this[__.private];
		if (_private.box) {
			_private.box.dispose();
			_private.box = null;
			_private.lines = null;
		}

		super.onRemove();
	}

	/**
	 * show bounding
	 * @param {Boolean} value The value
	 * @example
	 * 	space.showBounding();
	 * @public
	 */
	showBounding(value = true) {
		let _private = this[__.private];

		if (_private.box) {
			_private.box.setVisible(value);
		}
		else if (value) {
			this._initBoundingBox();
		}
	}

	async _initBoundingBox() {
		await this.object.loaded;

		this._initOrientedBoxData()
		this._begin();

		let _private = this[__.private];

		const size = _private.initHalfSize;
		const boxPoints = {
			// TOP
			tlt: this._getRealPoint([-size[0], size[1], -size[2]]), // 0
			trt: this._getRealPoint([size[0], size[1], -size[2]]), // 1
			trb: this._getRealPoint([size[0], size[1], size[2]]), // 2
			tlb: this._getRealPoint([-size[0], size[1], size[2]]), // 3
			// BOTTOM
			blt: this._getRealPoint([-size[0], -size[1], -size[2]]), // 4
			brt: this._getRealPoint([size[0], -size[1], -size[2]]), // 5
			brb: this._getRealPoint([size[0], -size[1], size[2]]), // 6
			blb: this._getRealPoint([-size[0], -size[1], size[2]]), // 7
		};

		// Insert line
		this._insertLine(boxPoints.tlt, boxPoints.trt);
		this._insertLine(boxPoints.trt, boxPoints.trb);
		this._insertLine(boxPoints.trb, boxPoints.tlb);
		this._insertLine(boxPoints.tlb, boxPoints.tlt);

		this._insertLine(boxPoints.blt, boxPoints.brt);
		this._insertLine(boxPoints.brt, boxPoints.brb);
		this._insertLine(boxPoints.brb, boxPoints.blb);
		this._insertLine(boxPoints.blb, boxPoints.blt);

		this._insertLine(boxPoints.tlt, boxPoints.blt);
		this._insertLine(boxPoints.trt, boxPoints.brt);
		this._insertLine(boxPoints.trb, boxPoints.brb);
		this._insertLine(boxPoints.tlb, boxPoints.blb);

		this._end();
	}

	_initOrientedBoxData() {
		let _private = this[__.private];

		const object = this.object;
		const orientedBox = object.getOBB(false);

		_private.initHalfSize = MathUtils.divideVector(MathUtils.divideVector(orientedBox.size, object.scale), 2);

		_private.initOffset = MathUtils.divideVector(
			MathUtils.vec3ApplyQuat(MathUtils.subVector(orientedBox.center, object.position),
				MathUtils.quat.invert([], object.quaternion)), object.scale);
	}

	_begin() {
		let _private = this[__.private];

		// Line
		let lines = Utils.createObject('LineSegments');
		lines.setAttribute('Pickable', false);
		_private.lines = lines;

		// Box
		let box = this.object.app.resourceManager.parseModel('Box');
		box.setAttribute('Pickable', false);
		box.setScale(MathUtils.scaleVector(_private.initHalfSize, 2));
		let style = box.getStyle();
		style.setColorOp(() => { return _private.boxColor; });
		style.setOpacityOp(() => { return _private.boxOpacity; });

		_private.box = box;
	}

	_end() {
		let _private = this[__.private];

		// Update lines
		_private.lines.setSize(_private.points.length);

		// Let line's position is center of points
		let center = MathUtils.getCenterFromPoints(_private.points);
		// Make points to relative position
		let relativePoints = _private.points.map(point => {
			return MathUtils.subVector(point, center);
		});

		_private.lines.setPoints(relativePoints);
		_private.lines.setColors(_private.colors);

		_private.box.add(_private.lines);

		this.object.body.createRootNode();
		this.object.node.add(_private.box);

		_private.lines.setWorldQuaternion([0, 0, 0, 1]);
		_private.lines.setWorldScale([1, 1, 1]);
	}

	_getRealPoint(point) {
		let _private = this[__.private];

		return MathUtils.addVector(_private.initOffset, this.object.selfToWorld(point));
	}

	_insertLine(p0, p1) {
		let _private = this[__.private];

		_private.points.push(p0);
		_private.colors.push(_private.linesColor);

		_private.points.push(p1);
		_private.colors.push(_private.linesColor);
	}

	/**
	 * @private
	 */
	setColor(val) {
		let _private = this[__.private];

		if (_private.lines) {
			let style = _private.lines.getStyle();
			style.setColorOp(() => { return val || _private.linesColor; });
		}
	}

}

export { SpaceHelperComponent }