SqueakByExample:11.4

From 흡혈양파의 번역工房
Revision as of 12:43, 17 August 2012 by Onionmixer (talk | contribs) (SBE 당신만의모프를만들고그리기 페이지 추가)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

당신만의 모프(morphs)를 만들고 그리기

Morphs를 구성하여 많은 흥미롭고 유용한 그래픽 표현을 만드는 것이 가능한 반면에, 때때로, 여러분은 완전히 다른 무엇을 만들 필요가 있으실 것입니다. 이 작업을 수행하기 위해, 여러분은 Morph의 서브클래스를 정의하고, 그것의 외관을 변경하기 위해 drawOn:을 재정의(override)해야 합니다.

Morphic 프레임워크는 화면에 morph를 다시 디스플레이 할 필요가 있을 때, 메시지 drawOn:을 morph에 보냅니다. drawOn:에 대한 파라미터는 Canvas의 종류이며, 기대되는 동작은 morph는 그것의 경계를 이루는 내부의 canvas위에 morph 그 자체를 그리게 될 것이라는 것입니다.


Squeak comment.png클래스 브라우저(the class browser)를 사용하여 Morph로부터 상속받은 새로운 클래스 CrossMorph를 정의합니다:


클래스 11.2: CrossMorph를 정의하기

Morph subclass: #CrossMorph
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''
  category: 'SBE--Morphic'


우리는 drawOn: 메소드를 이렇게 정의할 수 있습니다:


메소드 11.3: CrossMorph 그리기.

drawOn: aCanvas
  | crossHeight crossWidth horizontalBar verticalBar |
  crossHeight := self height / 3.0 .
  crossWidth := self width / 3.0 .
  horizontalBar := self bounds insetBy: 0 @ crossHeight.
  verticalBar := self bounds insetBy: crossWidth @ 0.
  aCanvas fillRectangle: horizontalBar color: self color.
  aCanvas fillRectangle: verticalBar color: self color


Morph에 bound 메시지를 발송하는 것은 그것의 경계 상자(bounding box)를 답변으로 내놓으며, 그 답변은 직사각형(Ractangle)의 인스턴스 입니다. 직사각형들은(Rectangles) 관련된 지오메트리의 다른 직사각형들을 만드는 많은 메시지들을 이해합니다. 여기서 우리는, 줄어든 높이(height)로 첫번째 직사각형을 만들고, 줄어든 너비(width)로 다른 직사각형을 만들기 위해, 메시지의 인수로서의 점(point)과 함께 insetBy: 메시지를 사용합니다.


그림 11.3: 그 자체의 halo와 함께 있는 CrossMorph. 여러분은 원하는 만큼 CrossMorph 크기를 다시 조정할 수 있습니다.


Squeak comment.png새로운 morph를 테스트 하기 위해 CrossMorph new openInWorld를 실행합니다.


결과는 그림 11.3과 같이 보여야만 합니다. 그럼에도 불구하고 여러분은 민감한 영역(the sensitive zone)-모프(morph)를 쥐기 취해 클릭을 할 수 있는 영역-이 여전히 전체 경계 상자(the whole bounding box)라는 것을 알 수 있으실 것입니다. 이것을 수정해 봅시다.

Morphic 프레임워크가 어떤 morph가 커서 아래에 놓여 있다는 것을 알아낼 필요가 있을 때, 그 프레임워크는 메시지 containsPoint:를 마우스 포인터 아래에morphs들의 경계상자(bounding boxes)가 놓여 있는 모든 morph에 발송합니다. 그러므로, 십자가 문양(the cross shape)에 morph의 민감한 영역(the sensitive zone)을 제한하기 위해, 우리는 containsPoint: 메소드를 재정의할 필요가 있습니다.


Squeak comment.png클래스 CrossMorph에서 다음 메소드를 정의합니다:


메소드11.4: CrossMorph의 민감한 영역(sensitive zone)을 조성하기

containsPoint: aPoint
  | crossHeight crossWidth horizontalBar verticalBar |
  crossHeight := self height / 3.0.
  crossWidth := self width / 3.0.
  horizontalBar := self bounds insetBy: 0 @ crossHeight.
  verticalBar := self bounds insetBy: crossWidth @ 0.
   (horizontalBar containsPoint: aPoint)
    or: [verticalBar containsPoint: aPoint]


이 메소드는 drawOn:과 동일한 논리(logic)를 사용하므로, 우리는 containsPoint:가 true로 답변하는 점들(points)이 drawOn에 의해 색칠될 점들(points)과 동일하다는 것을 확신할 수 있습니다. 어려운 작업을 수행하기 위해 클래스 Rectangle에서 containsPoint: 메소드를 어떻게 레버리지 하는지를 주의하여 학습하십시오.

메소드 11.3과 11.4에 있는 코드에 2 가지 문제가 있습니다. 가장 분명한 문제점은 우리가 중복된 코드를 갖고 있다는 것입니다. 이것은 심각한 에러입니다: 만약 우리가 horizonatalBar 또는 verticalBar가 계산된 방식을 변경할 필요성을 발견하였다면, 우리는 두 개의 occurrences중의 하나를 변경하는 작업을 잊어버릴 가능성이 큽니다. 이 문제에 대한 솔루션은, 우리가 개별적 프로토콜(private protocol)에 집어넣은 2 개의 새로운 메소드 속으로 이 계산들을 뽑아내는(factor out) 것입니다.


메소드 11.5: horizontalBar.

horizontalBar
  | crossHeight |
  crossHeight := self height / 3.0.
   self bounds insetBy: 0 @ crossHeight


메소드 11.6: verticalBar.

verticalBar
  | crossWidth |
  crossWidth := self width / 3.0.
   self bounds insetBy: crossWidth @ 0


우리는 이 메소드들을 사용하여 drawOn: 와 containsPoint: 모두를 정의할 수 있습니다.


메소드 11.7: 리펙토링된(refectored) CrossMorph»drawOn:.

drawOn: aCanvas
  aCanvas fillRectangle: self horizontalBar color: self color.
  aCanvas fillRectangle: self verticalBar color: self color


메소드 11.8: 리펙토링된(refectored) CrossMorph»containsPoint:.

containsPoint: aPoint
   (self horizontalBar containsPoint: aPoint)
     or: [self verticalBar containsPoint: aPoint]


이 코드는 광범위하게 이해하기가 훨씬 쉽습니다. 그 이유는 우리는 개별적 메소드(the private method)에 의미있는 이름들을 부여했기 때문입니다. 여러분이 두 번째 두 번째 문제를 인식하신 내용은 매우 단순한 형태의 문제입니다: 십자가의 중앙에 있는 영역은, 모두 수평 수직 막대 아래에 있으며, 두 번 그리기 되었습니다. 이 사실은 우리가 불투명한 색상으로 십자가를 채울 때 문제가 되지 않지만, 그림 11.4에 보이는 것 처럼, 만약 우리가 반 투명 십자가를 그릴 때에는, 즉시 분명한 버그가 생깁니다.


Squeak comment.png다음 코드를 워크스페이스에서 한 라인씩 실행합니다.


이 수정은 3 조각 안에 수직 막대를 제공하며, 오직 상단과 하단을 채웁니다 다시 한번 우리는, 우리를 위해 어려운 일을 수행하는 클래스 Rectangle에서 메소드를 발견합니다: r1 areasOutside: r2는 r1의 부분들과 r2 외부의 부분으로 구성된 직사각형의 배열로 답변합니다. 여기에 개정된 코드가 있습니다:


OverdrawBug.png HairlineBug.png
그림 11.4: colour로 2번 채워진 십자가의 중심십자 그림 11.5: 채워지지 않은 픽셀의 열을 보여주는 십자가 모양의 Morph


메소드 11.9: 개정된 drawOn: 메소드, 이 메소드는 십자가의 중심을 한번 채웁니다.

drawOn: aCanvas
  | topAndBottom |
  aCanvas fillRectangle: self horizontalBar color: self color.
  topAndBottom := self verticalBar areasOutside: self horizontalBar.
  topAndBottom do: [ :each | aCanvas fillRectangle: each color: self color]


이 코드는 작동하는 것 처럼 보이지만, 여러분이 몇몇 십자가 위에서 이 코드를 시도해 보고, 크기를 재조정하면, 그림 11.5에서 보이는 것 처럼, 몇몇 크기에서 1 픽셀의 넓은 라인이 reminder로부터 십자가의 하단을 구분시킨다는 것을 알게 되실 것입니다. 이러한 현상은 rounding(회전) 때문입니다: 채워지지 않은 픽셀들의 열을 남겨둠으로써, fillRectangle: color:는 직사각형(the rectangle)의 크기가 채워질 때, 정수가(integer)아닌, 불규칙하게 회전 하는 듯 합니다. 우리는 막대들의 크기를 계산할 때, 명백하게 회전(rounding)시킴으로써 예비수단(차선책)을 실행할 수 있습니다.


메소드 11.10: 명백한 rounding로 CrossMorph»horizontalBar

horizontalBar
  | crossHeight |
  crossHeight := (self height / 3.0) rounded.
   self bounds insetBy: 0 @ crossHeight


메소드 11.11: 명백한 rounding으로 CrossMorph»verticalBar 실행

verticalBar
  | crossWidth |
  crossWidth := (self width / 3.0) rounded.
   self bounds insetBy: crossWidth @ 0


Notes