폴리곤의 구성 좌표가 시계방향인지 반시계방향인지 검사하기

폴리곤이 오목한지 볼록한지에 대한 각각의 경에 대해서, 좌표가 시계방향으로 되어 있는지, 반시계 방향으로 되어 있는지를 검사하기 위한 방법입니다. GIS에서 폴리곤 도형에 대해 시계방향인지 반시계방향인지에 따라 폴리곤에 대한 구멍(Hole)인지의 여부를 결정짓는 경우가 있습니다. 이외에 폴리곤의 앞면인지 뒷면인지의 여부도 시계방향인지의 여부에 따라 결정됩니다.

먼저 폴리곤의 구성하는 좌표의 개수가 N개라고 하고, 구성 좌표를 아래처럼 표기합니다.

사용자 삽입 이미지

볼록한 폴리곤에 대해서 좌표의 방향에 대한 간단한 검사는 인접한 좌표들이 이루는 선분(벡터)에 대한 외적(Cross Product)을 통해 가능합니다. 만약에 외적이 양수이면 수직벡터가 폴리곤이 이루는 평면(z축이 평면의 위쪽으로 향한다고 가정) 윗 방향이고, 음수이면 평면의 안쪽 즉 평면에 대해 아래 방향으로 향합니다.

사용자 삽입 이미지
오목한 폴리곤의 경우는 폴리곤의 면적을 계산함으로써 알 수가 있습니다. 면적을 계산하는 공식은 다음과 같습니다. (왜 이렇게 되는지는 블로그의 다른 글을 통해 곧 소개 하겠습니다)

사용자 삽입 이미지
위의 값이 양수이면 폴리곤의 구성 좌표의 구성 순서가 시계 반대 방향이고, 음수이면 시계 방향임을 나타냅니다.

이제 그렇다면, 뒤집어서…. 폴리곤이 오목한지 볼록한지는 어떻게 알 수 있을까요? 그것은 볼록한 폴리곤을 이루는 인접한 선분 벡터들은 모두 같은 부호이지만, 오목한 폴리곤의 경우는 부호가 섞여 있다는 것으로 알 수 있습니다.

평면상의 세 점으로부터 평면의 방정식 구하기

평면의 방정식은 아래처럼 기술할 수 있으며, (A, B, C)는 평면에 대한 법선(수직) 백터이고.. D는 이 법선 백터의 길이(크기)입니다. (x,y,z)는 평면상의 임이의 점입니다.

사용자 삽입 이미지
평면은 최소한 점 3개가 정해지면 평면의 방정식이 명확히 정의됩니다. 즉, 평면 상의 (x1,y1,z1)과 (x2, y2, z2) 그리고 (x3, y3, z3)가 정해지면 위의 공식에서 A, B, C, D의 값이 정해진다는 의미입니다. 각 A,B,C,D는 정해진 점들에 대해서 다음과 같은 행렬식으로 정의되며…

사용자 삽입 이미지
위의 행렬식은 다음과 같이 다시 한번 전개가 됩니다.

사용자 삽입 이미지
결국 이렇게 구한 A,B,C,D로부터 평면의 방정식이 결정되게 됩니다.

평면과 선의 교차점 구하기

하나의 평면에 대해서 2가지의 평면공식이 도출되는데, 이 2가지 평면의 공식에 대해서.. 선과 평면에 대한 교차점을 구하는 방법에 대해서 논의해 보겠습니다. 이 자료는 1991년에 작성된 Paul Bourke(http://local.wasp.uwa.edu.au)님의 글을 좀더 알기 쉽게 풀어 쓴 글입니다.
사용자 삽입 이미지방법 1.

점 P는 평면 위의 임이의 점이고, N은 법선벡터이며 P3는 이미 알고 있는 평면상의 점이라고 하면 평면의 공식은 우리가 고등학교때 배운 평면의 방정식의 형태인, 다음처럼 기술됩니다.

사용자 삽입 이미지
평면을 수학적으로 기술했으니, 이제는 선에 대한 공식을 알아볼 차례입니다. 이제 앞에서 언급한 점 P를 평면과 선과의 교차점이라고 하면, 점 P는 선에 대한 점입니다. 그리고 선이 지나는 이미 알고 있는 2개의 점을 각각 P1, P2라고 하면 선에 대한 공식은 수학적으로 다음과 같습니다.

사용자 삽입 이미지
위에서 u는 선에 대한 기울기 값이 되겠지요. 이제.. 평면의 공식에서 P에 선의 공식을 대입할 수 있는 형태입니다. 대입해 보면 아래와 같습니다.

사용자 삽입 이미지
위의 식을 u에 대해서 전개해 보면 아래와 같은 공식이 됩니다.

사용자 삽입 이미지

이제 u값을 구했으니.. 이 u값을 선에 대한 공식에 대입하여 교차점 P를 구할 수 있습니다. 다음은 주의할 점입니다.

  • 만약 u에 대한 식에서 분모가 0이면, 주어진 선과 평면의 법선은 수직이라는 의미입니다. 즉, 이말은 평면과 선은 서로 만나지 않는다는 의미입니다.
  • 교점 P가 P1과 P2 사이에 있는지 검사해야한다면, u값이 0~1사이의 값인지 확인해 보면 됩니다.

방법 2.

이제 평면에 대한 공식으로, 고등학교때 배운 또 다른 형태는 다음과 같습니다.

사용자 삽입 이미지
(x,y,z)는 평면상의 점이고 (A,B,C)는 평면에 대한 법선벡터이며 D는 이 법선벡터의 길이값입니다. 이 형태의 평면방정식은 임이의 점에 대해서 위의 공식에 대입하여 그 값이 양수인경우 평면을 경계로 법선벡터가 향하는 부분에 존재하는 것이고, 음수인경우는 그 반대방향에 존재하는 것을 간단히 판단할 수 있습니다.

P1(x1,y1,z1)과 P2(x2,y2,z2)를 지나는 선에 대한 공식을 방법1에서의 선에 대한 공식… 다시 언급해 보면 아래와 같습니다.

사용자 삽입 이미지
위의 선 공식을 방법2에서의 평면의 공식에 대입해 보면 다음과 같은 형태로 전개됩니다.

사용자 삽입 이미지
위의 식을 u에 대해서 정리해 보면…

사용자 삽입 이미지
이제 u를 구했으니, 방법1과 마찬가지로 교점을 구할 수 있습니다. 이 방법에 대해 주의할 점은 방법 1과 동일합니다.

[C++] Template Summary – 3/3

클래스 템플릿도 특수화가 가능하며, 특수화를 위해서는 특수화 대상이 되는 템플릿 클래스가 필요하다. 아래는 대상이 되는 템플릿 클래스와 이를 특정 타입(double)에 대해 특수화 시킨 예이다.

template class BASE {
public:
	T func() {};
};

template<> class BASE {
public:
	int FuNc(float v) {};
};

주지할 점은  기본 대상이 되는 클래스 템플릿인 BASE와 double 타입에 대해 특수화된 클래스 템플릿 BASE는 완전히 별개의 클래스라는 점으로, 위처럼 서로 가지고 있는 맴버에 공통점이 없다.

템플릿 클래스 자체를 특수화하는 경우 뿐만 아니라, 가지고 있는 맴버 함수만을 콕… 지정해서 특수화 하는 것도 가능하다. 아래는 그 예이다.

template class BASE {
public:
    T func() {};
};

template<> int BASE::func()
{
	// another implementation for int type
}

위의 예는 BASE 함수의 func 함수에 대해 T가 int일때에 대한 특수화의 예이다.

템플릿 인자열 변형을 통한 특수화, 즉 클래스 템플릿 부분 특수화의 예

template class CLASS_NAME {}; //
template class CLASS_NAME<T*, T, i> {};     //
template class CLASS_NAME<char, T, 5> {};          //
template class CLASS_NAME<T*, T, 0> {};            //

이 특수화 대상이 되는 원래 클래스 템플릿이며, , , 가 클래스 템플릿 부분 특수화의 경우이다.

클래스 템플릿이 사용되는 경우는 아래와 같다.

CLASS_NAME<int, double, 10> a;

, , 에 대한 실제 사용 예는 아래와 같으며 순서대로 각 , , 에 일치한다.

CLASS_NAME<int*, int, 20> b;
CLASS_NAME<char, float, 5> c;
CLASS_NAME<char*, char, 3> d;

주목할 점은 클래스 템플릿 부분 특화의 경우 특수화 대상이 되는 원래 클래스 템플릿과 템플릿 인자의 개수가 정확히 일치해야한다. 위의 경우 3개로써 <type, type, int value>이다. 또한 , , , 의 템플릿 클래스는 서로 완전이 다른 클래스 라는 점이며, 단지 템플릿을 통한 관계를 유일한 공통점으로 가지고 있다.

[C++] Template Summary – 2/3

기본적인 클래스 탬플릿(Class Template)의 정의

template  class Stack_Tpl
{
private:
	int size_;
	int top_;
	T *pMem_;

public:
	Stack_Tpl(int size) : size_(size), top_(-1) {
		pMem_ = new T [size];
	}

	~Stack_Tpl()
	{
		delete [] pMem_;
	}

	void push(T v) 
	{
		pMem_[++top_] = v;
	}

	T pop()
	{
		return pMem_[top_--];
	}
};

위처럼 클래스의 정의와 선언을 동시에 하는 경우도 있지만, 선언과 정의를 분리할 경우에 맴버 함수의 정의는 다음과 같다.

template void Stack_Tpl::push(T v) 
{
	pMem_[++top_] = v;
}

위에서 정의된 클래스 템플릿을 사용하는 방법, 즉 클래스 템플릿을 인스턴스화 하여 템플릿 클래스로 만드는 방법은 아래와 같다.

Stack_Tpl s(10);

참고로, 위처럼 템플릿을 사용하지 않으면 해당 타입의 클래스 템플릿 코드가 만들어지지 않는다. 이때 사용하지는 않지만 해당 타입에 대한 코드를 명시적으로 만들도록 하는 방법은 아래와 같다.

template class Stack_Tpl;

클래스 템플릿의 경우 템플릿의 인자에 타입 이외에 값도 들어갈 수 있는데, 그 경우의 예는 아래와 같다. (비록 예의 기능이 의미가 없음에도 그 문법 자체에 염두해 두길 바란다)

template  class someClass
{
public:
	someClass();
};

template someClass<T, N>::someClass()
{
	T v = N;
}

또한  템플릿의 인자는 기본값을 가질 수 있다는 점을 알아 두어 코드 작성에 융통성을 발휘하길 바란다. 예를 들어 위의 someClass 클래스의 경우를 약간 변형해보면..

template  class someClass
{ ...

기본 템플릿 인자의 경우 함수 템플릿에서는 적용할 수 없다는 점을 염두해 두길 바란다.