#ifndef GAME_Plane3D_H
#define GAME_Plane3D_H
#include "stdafx.h"
#include "vertex3d.h"
// Plane3D: Шаблон класса "плоскость" в трехмерной декартовой системе координат.
// Поддерживает примитивные операции над плоскостями и векторами в пространстве.
// Основан на коде Алексея В. Борескова (http://s...content-available-to-author-only...d.ru)
// В общем случае плоскость в трехмерном пространстве описывается уравнением:
// A * x + B * y + C * z + D = 0,
// где x, y и z - "плавающие" координаты любой точки на плоскости;
// A, B, C - координаты нормали - вектора, перпендикулярного плоскости;
// D - коэффициент "отступа" от начала координат. В случае если
// нормаль имеет единичную длину -D будет тем расстоянием, которое надо
// отступить от начала координат вдоль нормали, чтобы найти ближайшую
// к началу координат точку на плоскости.
// Коэффициенты A, B, C, D уникальны для каждой плоскости в пространстве вплоть
// до общего множителя. В классе Plane3D для упрощения вычислений предполагается
// что длина нормали плоскости должна быть равна 1 (т.е. A * A + B * B + C * C = 1).
template < class T >
class Plane3D
{
protected:
// f1_sgn: функция, вычисляющая знак числа.
// Возвращает 1, если число больше 0;
// -1, если число меньше 0;
// 0, если число равно 0.
static int f1_sgn( const T k )
{
if( k > 0 ) return 1;
if( k < 0 ) return -1;
return 0;
};
public:
// EPS: статический член данных класса, содержащий предельную точность
// вычислений с плавающей запятой.
// По умолчанию принимается равным 1e-3f, но может быть изменен.
// Методы, использующие EPS помечены в описании фразой "Использует EPS.".
static T EPS;
Vertex3D< T > n; // Нормаль плоскости, т.е. вектор, перпендикулярный ей.
// Координаты x, y, z этой точки соответствуют коэффициентам
// A, B, C в уравнении плоскости.
T dist; // Коэффициент D в уравнении плоскости.
// Конструктор по умолчанию создаёт "нулевую" плоскость.
Plane3D(): n( 0, 0, 0 ), dist( 0 ) {};
// Конструктор плоскости из нормали и дистанции D.
// Внимание! Параметры уже должны быть нормализованы до поступления в конструктор!
Plane3D( const Vertex3D< T > &normal, T d ): n( normal ), dist( d ) {};
// Конструктор плоскости из координат нормали и дистанции D.
// Внимание! Параметры уже должны быть нормализованы до поступления в конструктор!
Plane3D( T nx, T ny, T nz, T d ): n( nx, ny, nz ), dist( d ) {};
// Конструктор плоскости из нормали и любой точки на плоскости.
// Параметры автоматически нормализуются.
Plane3D( const Vertex3D< T > &normal, const Vertex3D< T > &point ): n( normal )
{
n.Normalize();
Vertex3D< T > temp = point;
dist = -(temp & n);
};
// Конструктор плоскости из трех точек, лежащих на ней, но не на одной прямой.
// Параметры автоматически нормализуются.
Plane3D( const Vertex3D< T > &p1, const Vertex3D< T > &p2, const Vertex3D< T > &p3 )
{
n = (p2 - p1) ^ (p3 - p1);
n.Normalize();
dist = -(p1 & n);
};
// Конструктор копирования из другой плоскости.
// Внимание! Параметры уже должны быть нормализованы до поступления в конструктор!
Plane3D( const Plane3D< T > &plane ): n ( plane.n ), dist( plane.dist ) {};
// SignedDistanceTo: возвращает минимальное расстояние от точки v до плоскости.
// Значение положительно, если точка лежит на стороне в которую направлен вектор нормали,
// и отрицательно в обратном случае.
T SignedDistanceTo( const Vertex3D< T > &v ) const
{
return (v & n) + dist;
};
// DistanceTo: возвращает минимальное расстояние от точки v до плоскости.
// В отличие от signedDistanceTo возвращаемое начение всегда положительно.
T DistanceTo( const Vertex3D< T > &v ) const
{
return (T) fabs( SignedDistanceTo( v ) );
};
// Point: возвращает ближайшую к началу координат точку, лежащую на плоскости.
Vertex3D< T > Point() const
{
return (-dist) * n;
}
// Classify: "классифицирует" переданную в качестве параметра точку, вовзращая:
// 1, если точка лежит в полупространстве в которое направлен вектор нормали,
// 0, если точка лежит в самой плоскости (с точностью до EPS!)
// -1, если точка лежит в полупространстве от которой направлен вектор нормали.
// Использует EPS!
int Classify( const Vertex3D< T > &p ) const
{
int ret = 0;
T v = SignedDistanceTo( p );
if ( v > EPS )
ret = 1;
else if ( v < -EPS )
ret = -1;
return ret;
};
// ReflectPos: "отражает" позицию точки v относительно плоскости в другое
// полупространство. Другими словами смещает позицию точки вдоль перпендикуляра
// опущенного из точки на плоскость на расстояние, равное удвоенному
// начальному расстоянию от точки до плоскости в направлении к плоскости.
void ReflectPos( Vertex3D< T > *v ) const
{
*v -= (2 * ((*v & n) + dist)) * n;
};
// ReflectDir: "отражает" вектор направления v относительно плоскости под тем же
// углом в обратную сторону (угол падения равен углу отражения).
// В отличие от reflectPos отражение происходит не относительно
// самой плоскости, но относительно начала координат.
void ReflectDir( Vertex3D< T > *v ) const
{
*v -= (2 * (*v & n)) * n;
};
// ReflectPlane: отражает плоскость plane относительно текущей плоскости.
void ReflectPlane( Plane3D< T > *plane ) const
{
Vertex3D< T > p( -plane->dist * plane->n );
ReflectDir( plane->n );
ReflectPos( p );
plane->dist = -(p & plane->n);
};
// Flip: меняет местами лицевую и заднюю стороны плоскости.
void Flip()
{
n = -n;
dist = -dist;
};
// ClosestPoint: сохраняет в параметр res ближайшую точку на текущей
// плоскости к указанной точке p.
// Возвращает минимальное расстояние от плоскости до точки p (со знаком).
T ClosestPoint( const Vertex3D< T > &p, Vertex3D< T > *res ) const
{
T distanceToPlane3D = -dist - (p & n);
*res = p + distanceToPlane3D * n;
return distanceToPlane3D;
};
// IntersectByRay: определяет пересекает ли луч, испущенный из точки org
// в направлении dir текущую поверхность, и если да, то возвращает в
// параметре t расстояние до точки пересечения.
// Возвращает: true, если луч пересекает плоскость, false иначе.
// В случае если возвращает true в параметр t сохраняется расстояние
// до точки пересечения от точки org в единицах, равных длине вектора dir.
bool IntersectByRay( const Vertex3D< T > &org, const Vertex3D< T > &dir, T *t ) const
{
T denom = dir & n;
if ( fabs( denom ) < EPS )
return false;
T numer = -(dist + (org & n));
*t = numer / denom;
return true;
};
// IntersectByRay: определяет пересекает ли луч, испущенный из точки org
// в направлении dir текущую поверхность, и если да, то возвращает в
// параметре res координаты точки пересечения.
// Возвращает: true, если луч пересекает плоскость, false иначе.
// В случае если возвращает true в параметр res сохраняются координаты пересечения.
bool IntersectByRay( const Vertex3D< T > &org, const Vertex3D< T > &dir, Vertex3D< T > *res ) const
{
T t;
if ( !IntersectByRay( org, dir, &t ) )
return false;
res = org + t * dir;
return true;
};
// SplitPolygon: рассекает полигон poly с количеством вершин polyCount
// на два полигона, лежащих целиком во передней и задней полупространствах
// от плоскости соответственно. Результат (точки полигонов) возвращается в
// буфера точек front и back. Параметры fontCount и backCount принимают в себя
// количество точек, записанных в буфер front и back соответственно.
// Внимание! максимальное число выходных точек определяется исходя из геометрии
// полигона и должно быть рассчитано до вызова этой функции! Результирующие
// полигоны могут иметь 0, 1 или 2 точки (т.е. не являтся полигонами)!
void SplitPolygon( const Vertex3D< T > *poly, int polyCount,
Vertex3D< T > *front, int *frontCount,
Vertex3D< T > *back, int *backCount ) const;
};
template < class T >
T Plane3D< T >::EPS = 1e-3f;
/*
template < class T >
void SplitPolygon( const Vertex3D< T > *poly, int polyCount,
Vertex3D< T > *front, int *frontCount,
Vertex3D< T > *back, int *backCount )
{
T EPS = 0.0001;
Vertex3D< T > *prevP = poly + polyCount - 1;
T prevF = SignedDistanceTo( *prevP );
*frontCount = 0;
*backCount = 0;
for ( int i = 0; i < polyCount; i++ )
{
Vertex3D< T > *curP = poly + i;
T curF = poly.SignedDistanceTo ( *curP );
if ( curF > EPS )
{
if ( prevF < -EPS )
{
T t = - curF / ( prevF - curF );
Vertex3D< T > sp = *curP + t * ( prevP - curP );
*front = sp;
fontCount++;
*back = sp;
backCount++;
}
*front = *curP;
frontCount++;
}
else
if ( curF < -EPS )
{
if ( prevF > -EPS )
{
T t = - curF / ( prevF - curF );
Vector3D< T > sp = *curP + t * ( prevP - curP );
*front = sp;
fontCount++;
*back = sp;
backCount++;
}
*back = *curP;
backCount++;
}
else
{
*front = *curP;
fontCount++;
*back = *curP;
backCount++;
}
prevP = curP;
prevF = curF;
};
};
*/
typedef Plane3D< float > Plane3Df;
typedef Plane3D< double > Plane3Dd;
#endif