삽질하는플머

'여가생활/산수공부'에 해당되는 글 3건

  1. 책장 넘기는 효과 구현하기 - 3. 6
  2. 책장 넘기는 효과 구현하기 - 2. 2
  3. 책장 넘기는 효과 구현하기 - 1. 2

책장 넘기는 효과 구현하기 - 3.

여가생활/산수공부

1편 - http://oranke.tistory.com/119

2편 - http://oranke.tistory.com/120


어디 제약될 필요 없는 블로그질이라고 해도... 시작한 일은 끝을 봐야 하는 법인데 너무 오래 끌었다. 

지난번 글 이후 초딩이던 큰아들놈은 중학생이 되었고, 신생아였던 둘째가 초딩이 되었으니 세월이 많이 흐르기는 했다. 

블로그 검색로그에 간간히 남아있는 "책장 넘기기 효과"를 보며 어떤 형태로건 마무리를 지어야지 라고 생각하였다. 

(사실... 하드 정리하다가 짱박혀있던 소스를 발견한 이유가 크지만...)


기억을 되살리기 위해 이전 강좌에서 언급했던 몇가지 변수를 되짚어보자. 


  • MvTL - 상단 모서리 이동점
  • MvBL - 하단 모서리 이동점
  • 접힘점1 - 책장이 접힌 위쪽 좌표
  • 접힘점2 - 책장이 접힌 아래쪽 좌표


MvTL값을 임의 위치에 놓고 다른 점들을 비례식으로 계산할 때, MvTL 위치를 적절히 제한해주어야 한다는 것이 지난 글의 마지막이었다. 

그렇다면 어떤 녀석을 구속조건으로 삼으면 좋을까. 


페이지가 이동할 때 접힘점 1과 2, 그리고 이동점간의 관계를 가만히 생각해보자. 


접힘점1을 적당한 위치에 고정하고 이동점 MvTL을 이리저리 움직여보면, 접힘점 2는 책장의 수직부분과 하단부분 내에서 위치하게 된다. 책장이 찢어지지 않는 이상 이 범위를 벗어나지 않게 되므로 0~1 사이의 비율값으로 나타낼 수 있다. 상단, 하단, 수직부분의 비율값을 각각 조절비율 rT, rB, rM 이라고 부르자. 


기준점 MvTL이 주어질 때의 조절비율을 계산하고, 얻어진 비율값을 0~1 사이로 제한한 뒤 다시 기준점을 계산하면, 책장의 모양이 유지되는 적절한 위치값들을 얻을 수 있다. 조금 복잡하게 들릴지 몰라도 어차피 계산은 컴퓨터가 하는 것이니 일단 시켜보자. 


비율변화를 적절히 제한하고 또 구현을 편하게 하기 위해 스크롤바 컨트롤을 사용했다. 

폼 위에 ScrollT, ScrollM, ScrollB 세 개의 TScrollBar를 올리고 Min값은 0, Max값은 10000으로 설정한다. 








마우스가 눌린 Y좌표에 따라 페이지 윗부분을 움직일지, 아니면 아래부분을 움직일지 여부를 결정하기 위해 적절한 타입을 정의하자. 


type

  TRateCalcType = (rctTop, rctBottom);


var
  RateCalcType: TRateCalcType;




이 값은 폼의 MouseDown 이벤트에서 할당한다. 


procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;

  Shift: TShiftState; X, Y: Integer);

begin

  // 화면의 세로 기준으로 절반을 넘어가면 하단을 기준으로 계산.

  if Y > (ClientHeight / 2) then

    RateCalcType := rctBottom

  else

    RateCalcType := rctTop;

......





비율계산타입 RateCalcType과 페이지의 폭, 너비, 그리고 MvTL값을 기준으로 지난번의 비례식을 사용해 rT, rM, rB를 계산해보자. 


CalcPagePosRate





이렇게 계산된 비율값으로부터 다시 상, 하단 이동점들과 접힘점들을 계산하는 함수는 다음과 같다. 

CalcPageInfo





폼의 마우스 이동에서 계산한 비율을 스크롤바에 던지고, OnChange 이벤트에서 화면을 적절히 구성한 결과물은 다음과 같다. 






소스와 실행파일은 여기에. 터보델파이로 빌드되었다. 


BookTest.7z



앞의 코드에서 언급된 TVector2f 및 벡터연산 함수는 여기 포함된 소스를 참고하자. 

위의 설명이 조금 이상하더라도 마우스와 스크롤바를 이리저리 옮겨보면 글쓴놈의 의도가 금방 이해 될 것이다. 




함께 포함된 BookTest2.exe 는 MvTL 위치선정함수(CalcMoveBasePos)의 테스트코드를 풀고 적절한 댐퍼를 추가한 것이다. 

왠지 그럴듯해 보이지 않는가??? 




-----

마우스 위치에 따라 "division by zero" 예외가 발생합니다. 
이전에 다운받았던 분들은 Unit1.pas 의 말미에 initialization 섹션을 추가하고 다음 코드를 넣어주세요. 

......

initialization
  Set8087CW($133F);

end.



책장 넘기는 효과 구현하기 - 2.

여가생활/산수공부

이번에는 앞에서처럼 페이지를 왕창 접지 말고 아래와 같이 약간만 접어놓는다.




좌상단을 원점으로 하는 좌표계를 떠올린 뒤 마음속에 아래와 같은 선을 그어보면 재미난 규칙이 보인다.




좌상단 모서리 이동점이 페이지상 임의 위치 MvTL (x: c, y: b) 에 있을 때, 접힘양 a 는 피타고라스 정의에 의해 다음과 같은 관계를 가진다.



제곱근도 빠진 아주 간단한 1차식으로 위쪽 접힘점의 위치를 계산할 수 있다.

이제 접힘점에서 수직으로 내려진 파란선과 C변의 교점을 K 라고 하자. 또한 MvTL 점을 지나는 수평선과 좌변의 교점을 M 이라고 하자.




접힘점1 - MvTL - K 가 이루는 삼각형은 MvTL - 접힘점2 - M 이 이루는 삼각형과 닮은꼴이 된다.
따라서 다음과 같은 비례식을 세울 수 있다.




접힘점2의 위치는 원점에서 d + b 만큼 떨어진 곳이 된다.
타원으로 풀었던 방식에 비해 뭐가 이래~ 이렇게 쉬워?? 싶다. 역시 구조를 바꾸면 코드도 간편해지는 법.

고민해야 할 상황이 두가지 더 있다.
첫번째는 접힘점2 가 페이지 좌변이 아닌 밑변에 있는 경우, 즉 d + b 값이 페이지의 높이값 ph 보다 클 때이다.



이 경우 O - 접힘점1 - L 이 이루는 삼각형은 N - 접힘점2 - L 이 이루는 삼각형과 닮음이다.
점L 과 점O 의 거리는 위에서 구한 d + b 값이고,
점L 과 점N 의 거리는 d + b 에서 높이 ph 를 뺀 값이다.
따라서 구하고자 하는 점 N 과 접힘점2 사이의 거리를 e 라고 하면 다음과 같은 비례식이 성립한다.




그리고 두번째는 MvTL 이 페이지 위쪽에 있을 때, 지금 좌상단을 기준으로 한 좌표계에서는 b가 음수값이 될 때인데...
반복되는 설명이니 이 부분은 나중에 시간날 때 업데이트 하기로 하고, 결론부터 말해 다음과 같은 식으로 하단 접힘점을 구할 수 있다.




이상의 수식을 가지고 실제 코드를 만들어보면 바로 눈치채겠지만, 페이지상에는 MvTL 이 위치할 수 있는 범위가 정해져있다. 기본적으로 페이지의 우상단 기준으로부터 거리가 pw인 원 이내지만, 이 조건에 맞는 MvTL 값만으로 페이지의 접힘점을 계산하면 때로는 페이지가 찢어진 것 처럼 될수가 있다. 실세계에서 MvTL은 또 다른 움직이는 좌표인 좌하단 모서리 이동값 (이하 MvBL) 에 의해 영향을 받고 있기 때문이다. 따라서 타원에 의한 해법이건 비례식에 의한 해법이건, 유저의 입력에 대해 이 값을 그대로 적용할 수 없고 상호구속적인 두 절점의 적절한 위치를 결정해주어야 한다.

이 방법에는 책장넘김 효과를 구현하는 개개인마다 여러가지 선택이 있겠지만... 아무튼 내가 사용한 방법에 대해서는 3편에서 계속하기로 하자.

책장 넘기는 효과 구현하기 - 1.

여가생활/산수공부

산수공부는 원래 네이버 블로그에 포스팅하는데... 가기 귀찮아서 여기에 남김. 나중에 긁어붙이지 머...

아이패드를 출퇴근시간에 밀린 만화책 보는 용도로 잘 써먹고 있다.
요새 이북 어플들에서는 책장 넘기는 효과가 기본처럼 들어가기 시작하는 듯.
이 효과는 어떻게 만드는건지 갑자기 궁금해져서 삽질을 시작해 봄.

일단 A4용지 하나를 이리저리 접어보며 감을 잡아보자.


왼쪽 페이지를 기준으로 좌상단점을 끌어다 페이지 임의의 점에 올려놓은 형상.
이 이동하는 좌상단점의 벡터를 MvTL 이라고 부르자.
페이지 상단의 접힌점과 페이지의 우상단점, 그리고 MvTL 벡터 사이의 관계에 대해 대해 잠시 고민.




만약 MvTL 의 위치에 따른 적절한 접힌점을 계산할 수 있다면 나머지 좌표들도 계산할 수 있을 것이다.

접힌점은 다음과 같은 방법으로 얻을 수 있다. 먼저 페이지 폭만큼의 실을 준비한 다음


실의 한쪽은 우상단점에, 다른 한쪽은 MvTL 에 올리고


페이지 상단 변을 기준으로 실이 팽팽해지도록 당겨지는 점이 된다.



만약 이 상태에서 페이지의 상단이 아닌 팽팽하게 당겨지는 전체 점의 자취를 구한다면??
뭐 볼것도 없이 타원이 되지.

즉 다시말해 MvTL 점과 우상단점으로부터 거리의 합이 페이지 폭이 되는 타원의 방정식과,
우상단점을 지나고 각도가 Th 인 직선의 방정식을 만족하는 점을 계산하면 그 점이 바로 접힌점이 된다.



C 는 페이지의 우상단점, 그리고 -C는 모서리 이동점 MvTL 이다. 각도 Th 는 우상단점->MvTL 벡터의 ArcTan 로 계산할 수 있다.
타원이 이동하고 회전하면 생각하기 귀찮으니까 원점을 기준으로 고민하자. 어차피 C와 R2 사이의 거리만 구하면 되니까.
이 경우 우상단점에서 MvTL 간의 거리를 구해 원점을 기준으로 나누어 원점 기준의 -C 와 C 값을 결정할 수 있다.

원점을 기준으로 한 타원의 방정식은 다음과 같다.




거리의 합을 pw 라고 할 때 타원 장축의 길이는 다음과 같이 구할 수 있다. (여기서 pw는 페이지의 폭이다.)




타원의 성질에 따라 단축의 길이 b 는 다음과 같다.




촛점 C를 지나며 각도가 th인 직선의 방정식은 다음과 같다.




필요한 값들은 대충 갖춰졌으니 두 식을 연립해 풀어보자. 먼저 y 를 갈아치워 x 에 대해 풀어보면




x 에 대한 2차식이 되었으므로 각 항을 근의 공식에 넣어서 풀어주면 된다.
(어차피 컴퓨터가 계산할 꺼니까 식 정리는 대충대충. 저걸 풀어봤자 나중에 입력하기만 힘들어짐.)

x 를 갈아치워 y 에 대해 풀어본 반대의 경우도 구해둔다.




th 의 범위, 즉 1사분면인지 4사분면인지에 따라 적절한 값을 접힌점으로 선택하고 나머지 좌표들을 계산하면 완료.
테스트 해 본 결과 잘 동작하기는 하는데... 계산이 귀찮고 코드가 지저분하다. 그래도 고민한 과정이 아까워 남겨둠.

접힌 종이가 이루는  삼각형의 비례식을 이용한 좀 더 간단한 해법은... 2편에 계속.


------

덤으로 이건... 공식 테스트 하는 화면. 
 



이건 소스와 실행파일.

test.7z