package alternativa.engine3d.core {
import alternativa.engine3d.*;
import alternativa.engine3d.errors.FaceExistsError;
import alternativa.engine3d.errors.FaceNotFoundError;
import alternativa.engine3d.errors.InvalidIDError;
import alternativa.engine3d.materials.SurfaceMaterial;
import alternativa.types.Set;
use namespace alternativa3d;
/**
* Поверхность — набор граней, объединённых в группу. Поверхности используются для установки материалов,
* визуализирующих грани объекта.
*/
public class Surface {
// Операции
/**
* @private
* Изменение набора граней
*/
alternativa3d var changeFacesOperation:Operation = new Operation("changeFaces", this);
/**
* @private
* Изменение материала
*/
alternativa3d var changeMaterialOperation:Operation = new Operation("changeMaterial", this);
/**
* @private
* Меш
*/
alternativa3d var _mesh:Mesh;
/**
* @private
* Материал
*/
alternativa3d var _material:SurfaceMaterial;
/**
* @private
* Грани
*/
alternativa3d var _faces:Set = new Set();
/**
* Создание экземпляра поверхности.
*/
public function Surface() {}
/**
* Добавление грани в поверхность.
*
* @param face экземпляр класса alternativa.engine3d.core.Face или идентификатор грани полигонального объекта
*
* @throws alternativa.engine3d.errors.FaceNotFoundError грань не найдена в полигональном объекте содержащем поверхность
* @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора
* @throws alternativa.engine3d.errors.FaceExistsError поверхность уже содержит указанную грань
*
* @see Face
*/
public function addFace(face:Object):void {
var byLink:Boolean = face is Face;
// Проверяем на нахождение поверхности в меше
if (_mesh == null) {
throw new FaceNotFoundError(face, this);
}
// Проверяем на null
if (face == null) {
throw new FaceNotFoundError(null, this);
}
// Проверяем наличие грани в меше
if (byLink) {
// Если удаляем по ссылке
if (Face(face)._mesh != _mesh) {
// Если грань не в меше
throw new FaceNotFoundError(face, this);
}
} else {
// Если удаляем по ID
if (_mesh._faces[face] == undefined) {
// Если нет грани с таким ID
throw new FaceNotFoundError(face, this);
} else {
if (!(_mesh._faces[face] is Face)) {
throw new InvalidIDError(face, this);
}
}
}
// Находим грань
var f:Face = byLink ? Face(face) : _mesh._faces[face];
// Проверяем наличие грани в поверхности
if (_faces.has(f)) {
// Если грань уже в поверхности
throw new FaceExistsError(f, this);
}
// Проверяем грань на нахождение в другой поверхности
if (f._surface != null) {
// Удаляем её из той поверхности
f._surface._faces.remove(f);
f.removeFromSurface(f._surface);
}
// Добавляем грань в поверхность
_faces.add(f);
f.addToSurface(this);
// Отправляем операцию изменения набора граней
_mesh.addOperationToScene(changeFacesOperation);
}
/**
* Удаление грани из поверхности.
*
* @param face экземпляр класса alternativa.engine3d.core.Face или идентификатор грани полигонального объекта
*
* @throws alternativa.engine3d.errors.FaceNotFoundError поверхность не содержит указанную грань
* @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора
*
* @see Face
*/
public function removeFace(face:Object):void {
var byLink:Boolean = face is Face;
// Проверяем на нахождение поверхности в меше
if (_mesh == null) {
throw new FaceNotFoundError(face, this);
}
// Проверяем на null
if (face == null) {
throw new FaceNotFoundError(null, this);
}
// Проверяем наличие грани в меше
if (byLink) {
// Если удаляем по ссылке
if (Face(face)._mesh != _mesh) {
// Если грань не в меше
throw new FaceNotFoundError(face, this);
}
} else {
// Если удаляем по ID
if (_mesh._faces[face] == undefined) {
// Если нет грани с таким ID
throw new FaceNotFoundError(face, this);
} else {
if (!(_mesh._faces[face] is Face)) {
throw new InvalidIDError(face, this);
}
}
}
// Находим грань
var f:Face = byLink ? Face(face) : _mesh._faces[face];
// Проверяем наличие грани в поверхности
if (!_faces.has(f)) {
// Если грань не в поверхности
throw new FaceNotFoundError(f, this);
}
// Удаляем грань из поверхности
_faces.remove(f);
f.removeFromSurface(this);
// Отправляем операцию изменения набора граней
_mesh.addOperationToScene(changeFacesOperation);
}
/**
* Материал поверхности. При установке нового значения, устанавливаемый материал будет удалён из старой поверхности.
*/
public function get material():SurfaceMaterial {
return _material;
}
/**
* @private
*/
public function set material(value:SurfaceMaterial):void {
if (_material != value) {
// Если был материал
if (_material != null) {
// Удалить материал из поверхности
_material.removeFromSurface(this);
// Удалить материал из меша
if (_mesh != null) {
_material.removeFromMesh(_mesh);
// Удалить материал из сцены
if (_mesh._scene != null) {
_material.removeFromScene(_mesh._scene);
}
}
}
// Если новый материал
if (value != null) {
// Если материал был в другой поверхности
if (value._surface != null) {
// Удалить его оттуда
value._surface.material = null;
}
// Добавить материал в поверхность
value.addToSurface(this);
// Добавить материал в меш
if (_mesh != null) {
value.addToMesh(_mesh);
// Добавить материал в сцену
if (_mesh._scene != null) {
value.addToScene(_mesh._scene);
}
}
}
// Сохраняем материал
_material = value;
// Отправляем операцию изменения материала
addMaterialChangedOperationToScene();
}
}
/**
* Набор граней поверхности.
*/
public function get faces():Set {
return _faces.clone();
}
/**
* Полигональный объект, которому принадлежит поверхность.
*/
public function get mesh():Mesh {
return _mesh;
}
/**
* Идентификатор поверхности в полигональном объекте. Если поверхность не принадлежит ни одному объекту,
* значение идентификатора равно null.
*/
public function get id():Object {
return (_mesh != null) ? _mesh.getSurfaceId(this) : null;
}
/**
* @private
* Добавление в сцену.
*
* @param scene
*/
alternativa3d function addToScene(scene:Scene3D):void {
// Добавляем на сцену материал
if (_material != null) {
_material.addToScene(scene);
}
}
/**
* @private
* Удаление из сцены.
*
* @param scene
*/
alternativa3d function removeFromScene(scene:Scene3D):void {
// Удаляем все операции из очереди
scene.removeOperation(changeFacesOperation);
scene.removeOperation(changeMaterialOperation);
// Удаляем из сцены материал
if (_material != null) {
_material.removeFromScene(scene);
}
}
/**
* @private
* Добавление к мешу
* @param mesh
*/
alternativa3d function addToMesh(mesh:Mesh):void {
// Подписка на операции меша
// Добавляем в меш материал
if (_material != null) {
_material.addToMesh(mesh);
}
// Сохранить меш
_mesh = mesh;
}
/**
* @private
* Удаление из меша
*
* @param mesh
*/
alternativa3d function removeFromMesh(mesh:Mesh):void {
// Отписка от операций меша
// Удаляем из меша материал
if (_material != null) {
_material.removeFromMesh(mesh);
}
// Удалить ссылку на меш
_mesh = null;
}
/**
* @private
* Удаление граней
*/
alternativa3d function removeFaces():void {
for (var key:* in _faces) {
var face:Face = key;
_faces.remove(face);
face.removeFromSurface(this);
}
}
/**
* @private
* Изменение материала
*/
alternativa3d function addMaterialChangedOperationToScene():void {
if (_mesh != null) {
_mesh.addOperationToScene(changeMaterialOperation);
}
}
/**
* Строковое представление объекта.
*
* @return строковое представление объекта
*/
public function toString():String {
var length:uint = _faces.length;
var res:String = "[Surface ID:" + id + ((length > 0) ? " faces:" : "");
var i:uint = 0;
for (var key:* in _faces) {
var face:Face = key;
res += face.id + ((i < length - 1) ? ", " : "");
i++;
}
res += "]";
return res;
}
}
}