음악, 삶, 개발

Juce 로 만드는 GUI 파트6 - Point 클래스는 x, y 좌표 본문

개발 공부/Juce 공부방

Juce 로 만드는 GUI 파트6 - Point 클래스는 x, y 좌표

Lee_____ 2020. 10. 31. 00:58

< GUI 를 그릴때 가장 중요한건? : 좌표 (Coordinate) >

 

무언가를 그리기위해서 가장 중요한건 무엇일까?

Juce 코드에 세계에서는 다름아닌 "좌표 (coordinate)" 이다.

Illustrator 에서는 이러한 좌표를 입력하지않고 마우스로 바로 바로 그려낼수있지만,

코딩의 세계에서는 마우스를 대체하는것이 좌표다. 

우리가 원하는 도형을 만들기위해서는, 

Juce 가 제공하는 여러 함수들의 인자로 좌표를 넘겨주어야한다.

이 좌표가 나타내는것은 직선일수도있고, 곡선일수도, 원일수도, 직사가형, 정사각형, 아니면 다각형일수도있다.


< Juce 에서 제공하는 여러 좌표 클래스들 : Path 클래스의 인자가 될 녀석들>

 

우리의 수고를 조금이나마 덜어주기위해 

Juce 에서는 다양한 도형을 나타낼수있는 좌표 클래스들을 제공한다.

우리는 이 좌표 클래스들을 매우 잘 숙지하여야한다.

이 좌표 클래스의 객체들이 다음 포스트에서 배울 Path 클래스의 인자로 사용되기때문이다.

이 포스팅에서는 Point 클래스를 배우고 순차적으로 포스팅마다 좌표 클래스들을 하나씩 배워나갈것이다.


< Point 클래스 >

 

Point 클래스는 2개의 x, y 값을 나타내는 클래스이다.

 

Point 클래스는 template 클래스로써, x, y 값의 type 을 생성자에서 결정해줘야한다.

template<typename ValueType>
class Point<ValueType>

주의할점은 Point 클래스의 ValueType 이 될수있는것은 int, float, double 뿐이다.

그외의 Type 은 사용할수없다.


< 공식 문서 >

 

Point<ValueType> Class Template Reference

 

A pair of (x, y) coordinates.

The ValueType template should be a primitive type such as int, float, double, rather than a class.

 

See also Line, Path, AffineTransform

/* Constructor */
constexpr Point ()=default
constexpr Point (const Point& )=default
constexpr Point (ValueType initialX, ValueType initialY) noexcept
/* Operator */
constexpr   bool 	operator==  (Point other)               const noexcept
constexpr   bool 	operator!=  (Point other)               const noexcept
constexpr   Point 	operator+   (Point other)               const noexcept
constexpr   Point 	operator-   (Point other)               const noexcept
constexpr   Point 	operator*   (Point<OtherType> other)    const noexcept
constexpr   Point 	operator/   (Point<OtherType> other)    const noexcept
constexpr   Point 	operator*   (OtherType multiplier)      const noexcept
constexpr   Point 	operator/   (OtherType divisor)         const noexcept
constexpr   Point 	operator-   ()                          const noexcept
            Point& 	operator=   (const Point& )             =default
            Point& 	operator+=  (Point other)               noexcept
            Point& 	operator-=  (Point other)               noexcept
            Point& 	operator*=  (Point<OtherType> other)    noexcept
            Point& 	operator/=  (Point<OtherType> other)    noexcept
            Point& 	operator*=  (FloatType multiplier)      noexcept
            Point& 	operator/=  (FloatType divisor)         noexcept
/* Get */
constexpr   bool        isOrigin                        ()              const noexcept
constexpr   bool        isFinite                        ()              const noexcept
constexpr   ValueType   getX                            ()              const noexcept
constexpr   ValueType 	getY                            ()              const noexcept
            FloatType 	getAngleToPoint                 (Point other)   const noexcept
constexpr   FloatType 	getDotProduct                   (Point other)   const noexcept
constexpr   ValueType 	getDistanceSquaredFromOrigin    ()              const noexcept
constexpr   ValueType 	getDistanceSquaredFrom          (Point other)   const noexcept
            ValueType 	getDistanceFromOrigin           ()              const noexcept
            ValueType 	getDistanceFrom                 (Point other)   const noexcept
/* Set */
void setX           (ValueType newX)                        noexcept
void setY           (ValueType newY)                        noexcept
void setXY          (ValueType newX, ValueType newY)        noexcept
void addXY          (ValueType xToAdd, ValueType yToAdd)    noexcept
void applyTransform (const AffineTransform& transform)      noexcept
/* Create */
constexpr Point             withX                   (ValueType newX)                            const noexcept
constexpr Point             withY                   (ValueType newY)                            const noexcept
constexpr Point<int> 	    toInt                   ()                                          const noexcept
constexpr Point<float> 	    toFloat                 ()                                          const noexcept
constexpr Point<double>     toDouble                ()                                          const noexcept
constexpr Point<int> 	    roundToInt              ()                                          const noexcept
constexpr Point             translated              (ValueType deltaX, ValueType deltaY)        const noexcept
          Point             transformedBy           (const AffineTransform& transform)          const noexcept
          Point             rotatedAboutOrigin      (ValueType angleRadians)                    const noexcept
          Point<FloatType>  getPointOnCircumference (float radius, float angle)                 const noexcept
          Point<FloatType>  getPointOnCircumference (float radiusX, float radiusY, float angle) const noexcept
          String            toString ()                                                         const

 

In function argument :

 

AffineTransform


< Point 객체 만들기 >

 

template 클래스이므로, 값의 Type 을 < > 안에 넣어주어야한다.

생성자의 첫번째 인자는 x, 두번째 인자는 y 이다.

constexpr Point (ValueType initialX, ValueType initialY) noexcept

위의 생성자를 사용하여 아래와 같이 객체들을 생성할수있다.

juce::Point<int>    rectInt     {0, 0};         // x, y is int.
juce::Point<float>  rectFloat   {0.0f ,0.0f};   // x, y is float.
juce::Point<double> rectDouble  {0.0 ,0.0};     // x, y is double.

< 근데 Point 객체는 어디에 사용되는가 ? : Path 클래스의 set 함수의 인자로. >

 

Point 객체는 추후 다룰 Path 클래스의 멤버 함수의 인자로 매우 자주 사용된다.

왜냐면 결국 Path 객체를 만들려면 좌표들이 필요한데, 이때 좌표를 나타내는 수단중 하나가 Point 객체이기대문이다.

Point 객체를 인자로 사용하는 Path 의 함수는 매우 많은데, 그중 하나를 살펴보자.

/* Path 클래스 */
void addTriangle (Point<float> point1, Point<float> point2, Point<float> point3)

함수명 addTriangle을 보면 알수있듯이 

삼각형은 Path 를 만들어내기위한 3개의 Point 객체를 인자로 받는 Set 함수이다.

이때 각 Point 가 삼각형의 세 꼭지점의 좌표를 나타내는것이다.

 

그럼 이렇게 만든 Path 객체는 다시 어디로 넘겨지는가?

/* Graphics 클래스 */
void fillPath (const Path& path) const

 

결국 Graphics 의 fillPath 함수의 인자로 넘겨지며, 우리가 만든 Path 를 최종적으로 그리게된다.


< Point 클래스를 사용하여 그림 그리기 : "포패그" 패턴 >

 

위에서 우리는 Point 객체는 Path 객체의 멤버 함수에 사용되고,

다시 이 Path 객체는 Graphics 객체의 멤버 함수에 사용된다는것을 알았다.

우리는 이를 패턴화해 볼수있다.

객체의 이동이 Point -> Path -> Graphics 로 향하는데

앞으로 이를 "포패그" 패턴이라 명명하겠다.

 

1.  포인트     : Point 객체를 만든다.

2. 패스        : Point 객체를 사용하여 Path 객체를 만든다.

3. 그래픽스  : 위 Path 객체를 사용하여 Graphics 객체를 만든다.

 

Juce 에서는 매우 다양한 함수들의 조합으로 그림을 그릴수있지만,

이 "포패그" 패턴은 매우 자주 쓰이는 패턴중에 하나이다.

 

포패그 패턴을 사용하여 삼각형을 그려보자.

void paint(juce::Graphics& g) override {
    
    /* < 삼각형을 그려봅시다 > */
    
    // 1. 포
    juce::Point<float> triPoint1 {0, 500};
    juce::Point<float> triPoint2 {500, 500};
    juce::Point<float> triPoint3 {250, 0};
    
    // 2. 페
    juce::Path triPath;
    triPath.addTriangle(triPoint1, triPoint2, triPoint3);
    
    // 3. 그
    g.setColour(juce::Colours::yellow);
    g.fillPath(triPath);
    
}

위의 코드를 컴파일하면,

위와 같이 그려진다.

Path 클래스에서는 이와 같이, 삼각형같은 다양한 모양의 Path 를 만들도록

많은 함수가 준비되어있다. 이때 인자로 Point 객체들이 사용되는것이다.

Path 클래스는 추후 별도의 포스팅에서 깊이 있게 공부할것이다.


< Point 클래스의 크기 : 걱정 No! Pass-By-Value 합시다! >

DBG(sizeof(juce::Point<int>));      // 8
DBG(sizeof(juce::Point<float>));    // 8
DBG(sizeof(juce::Point<double>));   // 16

위와 같이, 매우 작은 사이즈이다.

창조자는 역시 복사가 가능하게끔 해놓았다.

constexpr Point  (const Point& )=default // Copy Constructor
Point& operator= (const Point& )=default // Copy Assignment

위에서 사용했던 Path 클래스의 AddRectangle 함수의 인자를 보자.

void addTriangle (Point<float> point1, Point<float> point2, Point<float> point3)

이처럼 Point 는 매우 가볍기때문에 Pass-By-Value 로 넘기는것을 볼수있다.

 

추후 우리만의 함수들을 만들때 인자로 Juce 의 클래스 객체를 사용할일이 매우 자주있는데,

이때는 Juce 가 작성한 함수들이 인자로 각 객체들을 넘기는 방식을 참고하여 따르면 된다.


< Point 클래스의 Get 함수들 >

/* Get */
constexpr   bool        isOrigin                        ()              const noexcept
constexpr   bool        isFinite                        ()              const noexcept
constexpr   ValueType   getX                            ()              const noexcept
constexpr   ValueType 	getY                            ()              const noexcept
            FloatType 	getAngleToPoint                 (Point other)   const noexcept
constexpr   FloatType 	getDotProduct                   (Point other)   const noexcept
constexpr   ValueType 	getDistanceSquaredFromOrigin    ()              const noexcept
constexpr   ValueType 	getDistanceSquaredFrom          (Point other)   const noexcept
            ValueType 	getDistanceFromOrigin           ()              const noexcept
            ValueType 	getDistanceFrom                 (Point other)   const noexcept

위의 Point 클래스의 get 함수들은 객체의 상태를 가져오는것뿐만 아니라,

다른 Point 객체를 인자로 받아 두 Point 객체간에 거리등을 계산하는 함수 또한 제공한다.

각 함수들을 코드로 정리해보았다.

코멘트에 설명이 담겨있다.

/* isOrigin */
// 설명 : 해당 Point 객체의 x, y 가 0, 0 이면 true 아니면 false 를 return 한다.

juce::Point<int> point1 {0, 0};
juce::Point<int> point2 {10, 10};

DBG((point1.isOrigin() ? "true" : "false")); // true
DBG((point2.isOrigin() ? "true" : "false")); // false
/* isFinite */
// 설명 by Peter : 
// Some math operations can lead to +inf or -inf. (for example, tan)
// So this is a way of making sure values are in bounds.
/* getX, getY */
// 설명 : 해당 객체의 type 에 따른 x, y 값을 return 한다.

juce::Point<int>   point1 {10, 20};
juce::Point<float> point2 {10.0f, 20.0f};

DBG(point1.getX()); // x is 10,    type is int.
DBG(point1.getY()); // y is 20,    type is int.
DBG(point2.getX()); // x is 10.0f, type is float.
DBG(point2.getY()); // y is 20.0f, type is float.
/* getAngleToPoint */
// 설명 : 이 함수를 호출하는 객체가 정원의 중심점이 되고,
// 이 함수의 인자가 가리키는 지점까지 "시계방향" 으로 각도를 계산하여
// float type 인 "radian" 값으로 return 한다.

juce::Point<int> point1 {0, 0};
juce::Point<int> point2 {100, 0};
juce::Point<int> point3 {0, 100};
juce::Point<int> point4 {100, -100};

DBG(point1.getAngleToPoint(point2)); // 1.5708   radian : 90 deg
DBG(point1.getAngleToPoint(point3)); // 3.14159  radian : 180 deg
DBG(point1.getAngleToPoint(point4)); // 0.785387 radian : 45 deg
/* getDotProduct */
// 설명 : 두 Point 객체의 x, y 값을 각각 곱한
// (x1 * x2 + y1 * y2) 값을 float 으로 return 한다.

juce::Point<int> point1 {2, 3};
juce::Point<int> point2 {10, 100};

DBG(point1.getDotProduct(point2)); // (2 * 10) + (3 * 100) = 320
/* getDistanceSquaredFromOrigin */
// 설명 : 모르겠음
/* getDistanceSquaredFrom */
// 설명 : 모르겠음
/* getDistanceFromOrigin */
// 설명 : Origin 인 0, 0 에서 해당 객체의 x, y 까지의
// 직선거리를 ValueType 으로 return 함.

juce::Point<int> point {100, 100};

DBG(point.getDistanceFromOrigin()); // 200
/* getDistanceFrom */
// 설명 : 두 Point 객체간의 직선거리를 ValueType 으로 return 함.

juce::Point<int> p1 {0, 0};
juce::Point<int> p2 {10, 0};

DBG(p1.getDistanceFrom(p2)); // 10

< Point 클래스의 Set 함수들 >

void setX           (ValueType newX)                        noexcept
void setY           (ValueType newY)                        noexcept
void setXY          (ValueType newX, ValueType newY)        noexcept
void addXY          (ValueType xToAdd, ValueType yToAdd)    noexcept
void applyTransform (const AffineTransform& transform)      noexcept

setX, setY 는 각 x, y 멤버 변수를 set 하고

setXY 는 한번에 x, y 멤버 변수를 둘다 set 할수있고

addXY 는 기존값에 새로운 x, y 값을 더하는것이다.

applyTransform 은 현재 AffineTransform 클래스를 배우지않아서 추후 설명하겠다.


< Point 클래스의 Create 함수들 >

/* Create */
constexpr Point             withX                   (ValueType newX)                            const noexcept
constexpr Point             withY                   (ValueType newY)                            const noexcept
constexpr Point<int> 	    toInt                   ()                                          const noexcept
constexpr Point<float> 	    toFloat                 ()                                          const noexcept
constexpr Point<double>     toDouble                ()                                          const noexcept
constexpr Point<int> 	    roundToInt              ()                                          const noexcept
constexpr Point             translated              (ValueType deltaX, ValueType deltaY)        const noexcept
          Point             transformedBy           (const AffineTransform& transform)          const noexcept
          Point             rotatedAboutOrigin      (ValueType angleRadians)                    const noexcept
          Point<FloatType>  getPointOnCircumference (float radius, float angle)                 const noexcept
          Point<FloatType>  getPointOnCircumference (float radiusX, float radiusY, float angle) const noexcept
          String            toString ()                                                         const

사실 Create 함수라는것은 없다. 

이렇게 명명한 이유는, 이 Create 함수들이 해당 Point 객체를 변화시키지않고,

변화된 값이 적용된 복사본을 return 하기때문이다.

get 함수처럼, 인자 리스트들 뒤에 const 가 붙어있는것을 알수있다.

따라서 중요한건 이 함수들을 호출한 Point 객체 자체의 상태는 전혀 변하지않는다.

/* withX */
// 새로운 x 값이 반영된 Point 객체를 return 한다.

juce::Point<int> p1 {10, 10};

auto p2 = p1.withX(20); // p2 is 20, 10
/* withY */
// 새로운 y 값이 반영된 Point 객체를 return 한다.

juce::Point<int> p1 {10, 10};

auto p2 = p1.withY(20); // p2 is 10, 20
/* toInt */
// float 이나 double type 의 Point 객체를,
// int 값을 가진 객체로 변환하여 return 한다.
// 각 x, y 값의 소수점이 사라지고 정수가 된다.

juce::Point<float> p1 {3.14, 3.14};

auto p2 = p1.toInt(); // p2 is coverted to 3, 3 (int from float)
/* toFloat, toDouble */
// int 였다면 float 이나 double 값을 가진 Point 객체로 변환후 return 한다.

juce::Point<float> p1 {0, 0};

auto p2 = p1.toFloat();  // p2 is 0.0f, 0.0f (float)
auto p3 = p1.toDouble(); // p3 is 0.0, 0.0   (double)
/* roundToInt */
// 해당 객체의 각 값을 반올림한 객체를 return 한다.

juce::Point<float> p1 {3.7, 3.14};

auto p2 = p1.roundToInt(); // p2 is 4, 3 (rounded from 3.7, 3.14)
/* translated */
// trnaslate 를 호출하는 객체에 x, y 값을 더한
// Point 객체를 return 한다.

juce::Point<int> p1 {1, 1};

auto p2 = p1.translated(1, 1); // p2 is (2, 2), p1 is still 1, 1
Point transformedBy (const AffineTransform& transform)
// 설명 : AffineTransform 클래스를 배운후에 채워넣겠음.
Point rotatedAboutOrigin (ValueType angleRadians) 
// 설명 : {0, 0} 을 중심으로 함수를 호출하는 Point 객체에서, 
// angleRadians 값만믐 "시계방향"으로 회전한 Point 객체를 return 함.
// Origin 은 0, 0
juce::Point<int> p1 {100, 0};
auto p2 = p1.rotatedAboutOrigin(2); // p2 is -41, 90
Point<FloatType> getPointOnCircumference (float radius, float angle)                 const noexcept
Point<FloatType> getPointOnCircumference (float radiusX, float radiusY, float angle) const noexcept
// 원의 반지름과 angle 값을 이용해 이 함수를 호출하는 
// Point 객체를 회전한 새 Point 객체를 return 함.
// 주의할점 : angle 은 radian 임. (180도, 이렇게 적으면 됨)
String toString ()
// 설명 : Point 객체의 x,y 값을 juce::String 으로 변환.
// 따라서 DBG 할때 유용.

juce::Point<float> p {1.5, 2.53};

DBG(p.toString()); // 1.5, 2.53