SqueakByExample:11.4: Difference between revisions

From 흡혈양파의 번역工房
Jump to navigation Jump to search
(SBE 당신만의모프를만들고그리기 페이지 추가)
 
(번역수정)
 
(3 intermediate revisions by the same user not shown)
Line 1: Line 1:
==당신만의 모프(morphs)만들고 그리기 ==
==나만의 morph 생성하고 그리기==


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


Morphic 프레임워크는 화면에 morph를 다시 디스플레이 할 필요가 있을 때, 메시지 '''drawOn:'''을 morph에 보냅니다. drawOn:에 대한 파라미터는 Canvas의 종류이며, 기대되는 동작은 morph는 그것의 경계를 이루는 내부의 canvas위에 morph 그 자체를 그리게 될 것이라는 것입니다.
Morph 의 합성을 이용해서 많은 종류의 편리하고, 재미있는 그래픽 표현을 만들 수 있지만, 가끔은 완전히 다른 뭔가를 만들고 싶을때가 있겠죠. 그런 작업을 하기 위해서는, Morph 의 서브클래스를 만들어서 drawOn: 메서드를 오버라이드<sup>override</sup> 해서 morph 의 모양새를 바꿀 수 있습니다.


Morphic 프레임워크는 화면에 morph 를 다시 표시해야하는 경우, 메시지 '''drawOn:''' 을 morph 에 보냅니다. drawOn: 메시지의 인자는, Canvas 클래스(또는 서브클래스)의 인스턴스로서, morph 가 스스로를 canvas 의 bounds<ref name="역자주1">이 경우는 그릴 morph 의 bounds 가 아니라 그림판이 될 대상 canvas 의 bounds 를 의미하는것 같습니다</ref> 내에 표시할거라는걸 예상할 수 있겠죠.


{{CommentSqueak|클래스 브라우저(the class browser)를 사용하여 {{Template:HighlightBold|Morph}}로부터 상속받은 새로운 클래스 {{Template:HighlightBold|CrossMorph}}를 정의합니다:}}
 
{{CommentSqueak|클래스 브라우저를 사용하여 {{Template:HighlightBold|Morph}} 로부터 상속받은 새로운 클래스 {{Template:HighlightBold|CrossMorph}}를 정의하십시오:}}




클래스 11.2: CrossMorph를 정의하기
클래스 11.2: CrossMorph를 정의하기
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Morph subclass: #CrossMorph
Morph subclass: #CrossMorph
Line 19: Line 21:




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


메서드 11.3: CrossMorph 그리기.


메소드 11.3: CrossMorph 그리기.
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
drawOn: aCanvas
drawOn: aCanvas
Line 35: Line 38:




Morph에 bound 메시지를 발송하는 것은 그것의 경계 상자(bounding box)를 답변으로 내놓으며, 그 답변은 직사각형(Ractangle)의 인스턴스 입니다. 직사각형들은(Rectangles) 관련된 지오메트리의 다른 직사각형들을 만드는 많은 메시지들을 이해합니다. 여기서 우리는, 줄어든 높이(height)로 첫번째 직사각형을 만들고, 줄어든 너비(width)로 다른 직사각형을 만들기 위해, 메시지의 인수로서의 점(point)과 함께 insetBy: 메시지를 사용합니다.
[[image:NewCross.png|none|172px|thumb|그림 11.3: CrossMorph 가 할로를 표시한 모습. 원하는 만큼 CrossMorph 크기를 변경할 수 있습니다.]]




[[image:NewCross.png|none|172px|thumb|그림 11.3: 그 자체의 halo와 함께 있는 CrossMorph. 여러분은 원하는 만큼 CrossMorph 크기를 다시 조정할 수 있습니다.]]
Morph 에 bound 메시지를 전송하면, Rectangle 클래스의 인스턴스인 bounding box 가 반환되며, 이것은 morph 가 Rectangle 의 인스턴스 라는 의미가 됩니다. Rectangle 은 geometry 와 관련된 다른 사각형을 행성하는등의 많은 메시지를 사용할 수 있습니다; 위의 예제에서는, 인수로 점<sup>point</sup> 을 사용하는 insetBy: 메시지를 사용해서, 줄어든 높이로 첫번째 직사각형을 만들고, 줄어든 너비로 다른 직사각형을 만들고 있습니다.




{{CommentSqueak|새로운 morph를 테스트 하기 위해 CrossMorph new openInWorld를 실행합니다.}}
{{CommentSqueak|새로운 morph 를 테스트 하기 위해 CrossMorph new openInWorld 를 실행하십시오.}}




결과는 그림 11.3과 같이 보여야만 합니다. 그럼에도 불구하고 여러분은 민감한 영역(the sensitive zone)-모프(morph)쥐기 취해 클릭을 할 수 있는 영역-이 여전히 전체 경계 상자(the whole bounding box)라는 것을 알 수 있으실 것입니다. 이것을 수정해 봅시다.  
실행하면 그림 11.3 과 같은 결과를 얻을 수 있습니다. 그렇지만 마우스에 반응하는장소-morph 를 잡기위해 클릭할 수 있는 영역-bounding box 전체가 된다는걸 알아차렸을거라고 생각합니다. 이걸 고쳐보도록 하죠.


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




{{CommentSqueak|클래스 CrossMorph에서 다음 메소드를 정의합니다:}}
{{CommentSqueak|클래스 CrossMorph 에서 다음 메서드를 정의하십시오:}}




메소드11.4: CrossMorph의 민감한 영역(sensitive zone)을 조성하기
메서드11.4: CrossMorph 의 반응 영역<sup>sensitive zone</sup> 설정하기
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
containsPoint: aPoint
containsPoint: aPoint
Line 65: Line 69:




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


메소드 11.3과 11.4에 있는 코드에 2 가지 문제가 있습니다. 가장 분명한 문제점은 우리가 중복된 코드를 갖고 있다는 것입니다. 이것은 심각한 에러입니다: 만약 우리가 horizonatalBar 또는 verticalBar가 계산된 방식을 변경할 필요성을 발견하였다면, 우리는 두 개의 occurrences중의 하나를 변경하는 작업을 잊어버릴 가능성이 큽니다. 이 문제에 대한 솔루션은, 우리가 개별적 프로토콜(private protocol)에 집어넣은 2 개의 새로운 메소드 속으로 이 계산들을 뽑아내는(factor out) 것입니다.  
메서드 11.3 과 11.4 에 있는 코드는 2 가지 문제를 가지고 있습니다. 눈에 띄는 문제점으로는 코드가 중복되고 있다는거죠. 이건 매우 큰 잘못입니다: 왜냐하면 horizonatalBar 또는 verticalBar 가 계산된 방식을 변경할 필요가 있을때, 2 개 지점중 한쪽의 코드를 변경하는 작업을 쉽게 잊어버리기 때문입니다. 해결하려면 새로운 2 개 메서드에서 계산에 관련된 부분을 분리해낸 후, private 프로토콜에 넣으면 됩니다.




메소드 11.5: horizontalBar.
메서드 11.5: horizontalBar.
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
horizontalBar
horizontalBar
Line 79: Line 85:
   
   


메소드 11.6: verticalBar.
메서드 11.6: verticalBar.
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
verticalBar
verticalBar
Line 88: Line 95:




우리는 이 메소드들을 사용하여 drawOn: 와 containsPoint: 모두를 정의할 수 있습니다.  
이렇게 만든 메서드를 이용해서 drawOn: 와 containsPoint: 양쪽을 다 정의하면 됩니다.  
 


메서드 11.7: 리팩토링한 CrossMorph>>drawOn:.


메소드 11.7: 리펙토링된(refectored) CrossMorph»drawOn:.
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
drawOn: aCanvas
drawOn: aCanvas
Line 99: Line 107:
   
   


메소드 11.8: 리펙토링된(refectored) CrossMorph»containsPoint:.
메서드 11.8: 리팩토링한 CrossMorph>>containsPoint:.
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
containsPoint: aPoint
containsPoint: aPoint
Line 107: Line 116:




이 코드는 광범위하게 이해하기가 훨씬 쉽습니다. 그 이유는 우리는 개별적 메소드(the private method)에 의미있는 이름들을 부여했기 때문입니다. 여러분이 두 번째 두 번째 문제를 인식하신 내용은 매우 단순한 형태의 문제입니다: 십자가의 중앙에 있는 영역은, 모두 수평 수직 막대 아래에 있으며, 두 그리기 되었습니다.  이 사실은 우리가 불투명한 색상으로 십자가를 채울 때 문제가 되지 않지만, 그림 11.4에 보이는 것 처럼, 만약 우리가 반 투명 십자가를 그릴 때에는, 즉시 분명한 버그가 생깁니다.
{| style="border: 1px solid white;"
|- style="color: white;"
|[[image:overdrawBug.png]]||[[image:hairlineBug.png]]
|- style="text-align: center;"
|그림 11.4: colour 로 2 채워진 십자의 중심부분||그림 11.5: 채워지지 않은 픽셀의 열을 보여주는 십자 모양의 Morph
|}




{{CommentSqueak|다음 코드를 워크스페이스에서 한 라인씩 실행합니다.}}
private 메서드에 의미있는 이름을 붙인 덕분에, 이 코드는 보다 이해가 쉬워졌습니다. 이제 간단히 두번째의 문제를 알 수 있게 됐죠: 십자의 중앙부분이 horizontalBar 와 verticalBar 의 양쪽에 걸쳐져 있기때문에 2 번 그려지는 문제입니다. 이런 버그는 투명하지 않는 생상으로 십자를 그린다면 별 문제가 아닐겁니다만, 그림 11.4 에서 확인할 수 있듯이 반투명 십자를 그리는 순간, 확실히 버그가 드러날겁니다.


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


                                         
{{CommentSqueak|다음 코드를 워크스페이스에서 한 라인씩 실행하십시오.}}
{| style="border: 1px solid white;"
 
|- style="color: white;"
<syntaxhighlight lang="smalltalk">
|[[image:overdrawBug.png]]||[[image:hairlineBug.png]]
m := CrossMorph new bounds: (0@0 corner: 300@300).
|- style="text-align: center;"
m openInWorld.
|그림 11.4: colour로 2번 채워진 십자가의 중심십자||그림 11.5: 채워지지 않은 픽셀의 열을 보여주는 십자가 모양의 Morph
m color: (Color blue alpha: 0.3).
|}   
</syntaxhighlight>
 
 
이 문제를 해결하려면 verticalBar 를 세부분으로 나눠서, 위쪽과 아래쪽만 칠하면 됩니다. Rectangle 클래스에서 이런 여려운 일을 해주는 메서드를 다시 발견할 수 있습니다: r1 areasOutside: r2 는 r2 의 바깥쪽으로 삐져나온 부분에 해당되는 r1 을 구성하는 사각형의 배열을 반환합니다. 아래쪽에 수정된 코드가 있습니다:
 


메서드 11.9: 개량된 drawOn: 메서드, 이 메서드는 십자의 중심부분을 한번만 채웁니다.


메소드 11.9: 개정된 drawOn: 메소드, 이 메소드는 십자가의 중심을 한번 채웁니다.
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
drawOn: aCanvas
drawOn: aCanvas
Line 134: Line 150:




코드는 작동하는 것 처럼 보이지만, 여러분이 몇몇 십자가 위에서 이 코드를 시도해 보고, 크기를 재조정하면, 그림 11.5에서 보이는 것 처럼, 몇몇 크기에서 1 픽셀의 넓은 라인이 reminder로부터 십자가의 하단을 구분시킨다는 것을 알게 되실 것입니다. 이러한 현상은 rounding(회전) 때문입니다: 채워지지 않은 픽셀들의 열을 남겨둠으로써, fillRectangle: color:는 직사각형(the rectangle)의 크기가 채워질 때, 정수가(integer)아닌, 불규칙하게 회전 하는 듯 합니다. 우리는 막대들의 크기를 계산할 때, 명백하게 회전(rounding)시킴으로써 예비수단(차선책)을 실행할 수 있습니다.
위의 코드는 잘 작동하는걸로 보입니다만, 몇번정도 십자의 크기를 변경하려고 하는경우, 그림 11.5 처럼 특정 크기에서 1 pixel 만큼의 선이 십자의 아래부분과 위쪽을 나눠버리는것을 확인할 수 있습니다. 이 문제는 어림수(짐작하는수)처리<sup>rounding</sup> 때문에 일어납니다: 색을 채우려고 하는 사각형의 크기가 정수<sup>integer</sup>가 아닌경우, fillRectangle: color: 는 적당히 수치를 조절하기때문에 1 pixel 만큼 문제가 생기게 됩니다. 이 문제는, 사각형의 크기를 계산할때 명시적으로 값을 대입하는것으로 해결할 수 있습니다.
 


메서드 11.10: 정확한 CrossMorph>>horizontalBar rounding 처리


메소드 11.10: 명백한 rounding로  CrossMorph»horizontalBar
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
horizontalBar
horizontalBar
Line 146: Line 163:




메소드 11.11: 명백한 rounding으로 CrossMorph»verticalBar 실행
메서드 11.11: 정확한 CrossMorph>>verticalBar rounding 처리
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
verticalBar
verticalBar
Line 153: Line 171:
   ↑ self bounds insetBy: crossWidth @ 0
   ↑ self bounds insetBy: crossWidth @ 0
</syntaxhighlight>
</syntaxhighlight>





Latest revision as of 19:05, 16 September 2013

나만의 morph 를 생성하고 그리기

Morph 의 합성을 이용해서 많은 종류의 편리하고, 재미있는 그래픽 표현을 만들 수 있지만, 가끔은 완전히 다른 뭔가를 만들고 싶을때가 있겠죠. 그런 작업을 하기 위해서는, Morph 의 서브클래스를 만들어서 drawOn: 메서드를 오버라이드override 해서 morph 의 모양새를 바꿀 수 있습니다.

Morphic 프레임워크는 화면에 morph 를 다시 표시해야하는 경우, 메시지 drawOn: 을 morph 에 보냅니다. drawOn: 메시지의 인자는, Canvas 클래스(또는 서브클래스)의 인스턴스로서, morph 가 스스로를 canvas 의 bounds[1] 내에 표시할거라는걸 예상할 수 있겠죠.


Squeak comment.png클래스 브라우저를 사용하여 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


그림 11.3: CrossMorph 가 할로를 표시한 모습. 원하는 만큼 CrossMorph 크기를 변경할 수 있습니다.


Morph 에 bound 메시지를 전송하면, Rectangle 클래스의 인스턴스인 bounding box 가 반환되며, 이것은 morph 가 Rectangle 의 인스턴스 라는 의미가 됩니다. Rectangle 은 geometry 와 관련된 다른 사각형을 행성하는등의 많은 메시지를 사용할 수 있습니다; 위의 예제에서는, 인수로 점point 을 사용하는 insetBy: 메시지를 사용해서, 줄어든 높이로 첫번째 직사각형을 만들고, 줄어든 너비로 다른 직사각형을 만들고 있습니다.


Squeak comment.png새로운 morph 를 테스트 하기 위해 CrossMorph new openInWorld 를 실행하십시오.


실행하면 그림 11.3 과 같은 결과를 얻을 수 있습니다. 그렇지만 마우스에 반응하는장소-morph 를 잡기위해 클릭할 수 있는 영역-는 bounding box 전체가 된다는걸 알아차렸을거라고 생각합니다. 이걸 고쳐보도록 하죠.

Morphic 프레임워크는, 어떤 morph 가 마우스의 커서 아래에 있는지 알아내야할 필요가 있을때, 마우스의 포인터 아래에 bounding box 가 있는 모든 morph 에 대해서 containsPoint: 메시지를 보냅니다. 즉 마우스에 반응하는 영역을 morph 의 십자 부분으로 제한하고 싶다면, 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: 과 동일한 로직을 사용하기 때문에, containsPoint: 에서 true 를 반환하는 영역이, drawOn 에 의해 색칠이 가능한 영역과 일치한다는 것을 확신할 수 있죠. Rectangle 클래스의 containsPoint: 메서드를 활용하면, 이런 번거로운 작업을 해낼 수 있습니다.


메서드 11.3 과 11.4 에 있는 코드는 2 가지 문제를 가지고 있습니다. 눈에 띄는 문제점으로는 코드가 중복되고 있다는거죠. 이건 매우 큰 잘못입니다: 왜냐하면 horizonatalBar 또는 verticalBar 가 계산된 방식을 변경할 필요가 있을때, 2 개 지점중 한쪽의 코드를 변경하는 작업을 쉽게 잊어버리기 때문입니다. 해결하려면 새로운 2 개 메서드에서 계산에 관련된 부분을 분리해낸 후, private 프로토콜에 넣으면 됩니다.


메서드 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: 리팩토링한 CrossMorph>>drawOn:.

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


메서드 11.8: 리팩토링한 CrossMorph>>containsPoint:.

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


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


private 메서드에 의미있는 이름을 붙인 덕분에, 이 코드는 보다 이해가 쉬워졌습니다. 이제 간단히 두번째의 문제를 알 수 있게 됐죠: 십자의 중앙부분이 horizontalBar 와 verticalBar 의 양쪽에 걸쳐져 있기때문에 2 번 그려지는 문제입니다. 이런 버그는 투명하지 않는 생상으로 십자를 그린다면 별 문제가 아닐겁니다만, 그림 11.4 에서 확인할 수 있듯이 반투명 십자를 그리는 순간, 확실히 버그가 드러날겁니다.


Squeak comment.png다음 코드를 워크스페이스에서 한 라인씩 실행하십시오.

m := CrossMorph new bounds: (0@0 corner: 300@300).
m openInWorld.
m color: (Color blue alpha: 0.3).


이 문제를 해결하려면 verticalBar 를 세부분으로 나눠서, 위쪽과 아래쪽만 칠하면 됩니다. Rectangle 클래스에서 이런 여려운 일을 해주는 메서드를 다시 발견할 수 있습니다: r1 areasOutside: r2 는 r2 의 바깥쪽으로 삐져나온 부분에 해당되는 r1 을 구성하는 사각형의 배열을 반환합니다. 아래쪽에 수정된 코드가 있습니다:


메서드 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 pixel 만큼의 선이 십자의 아래부분과 위쪽을 나눠버리는것을 확인할 수 있습니다. 이 문제는 어림수(짐작하는수)처리rounding 때문에 일어납니다: 색을 채우려고 하는 사각형의 크기가 정수integer가 아닌경우, fillRectangle: color: 는 적당히 수치를 조절하기때문에 1 pixel 만큼 문제가 생기게 됩니다. 이 문제는, 사각형의 크기를 계산할때 명시적으로 값을 대입하는것으로 해결할 수 있습니다.


메서드 11.10: 정확한 CrossMorph>>horizontalBar rounding 처리

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


메서드 11.11: 정확한 CrossMorph>>verticalBar rounding 처리

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


Notes

  1. 이 경우는 그릴 morph 의 bounds 가 아니라 그림판이 될 대상 canvas 의 bounds 를 의미하는것 같습니다