11.8: A Complete Example
- Page ID
- 39211
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)
( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\id}{\mathrm{id}}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\kernel}{\mathrm{null}\,}\)
\( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\)
\( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\)
\( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)
\( \newcommand{\vectorA}[1]{\vec{#1}} % arrow\)
\( \newcommand{\vectorAt}[1]{\vec{\text{#1}}} % arrow\)
\( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vectorC}[1]{\textbf{#1}} \)
\( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)
\( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)
\( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)Let’s design a morph to roll a die1. Clicking on it will display the values of all sides of the die in a quick loop, and another click will stop the animation.

\(\bigstar\) Define the die as a subclass of BorderedMorph instead of Morph, because we will make use of the border.
Code \(\PageIndex{1}\) (Squeak): Defining the Die Morph
BorderedMorph subclass: #DieMorph instanceVariableNames: 'faces dieValue isStopped' classVariableNames: '' poolDictionaries: '' category: 'SBE-Morphic'
The instance variable faces
records the number of faces on the die; we allow dice with up to 9 faces! dieValue
records the value of the face that is currently displayed, and isStopped
is true if the die animation has stopped running. To create a die instance, we define the faces: n
method on the class side of DieMorph
to create a new die with n
faces.
Code \(\PageIndex{2}\) (Squeak): Creating a New Die with the Number of Faces We Like
DieMorph class»faces: aNumber ↑ self new faces: aNumber
The initialize
method is defined on the instance side in the usual way; remember that new
sends initialize
to the newly-created instance.
Code \(\PageIndex{3}\) (Squeak): Initializing Instances of DieMorph
DieMorph»initialize super initialize. self extent: 50 @ 50. self useGradientFill; borderWidth: 2; useRoundedCorners. self setBorderStyle: #complexRaised. self fillStyle direction: self extent. self color: Color green. dieValue := 1. faces := 6. isStopped := false
We use a few methods of BorderedMorph
to give a nice appearance to the die: a thick border with a raised effect, rounded corners, and a color gradient on the visible face. We define the instance method faces:
to check for a valid parameter as follows:
Code \(\PageIndex{4}\) (Squeak): Setting the Number of Faces of the Die
DieMorph»faces: aNumber "Set the number of faces" (aNumber isInteger and: [aNumber > 0] and: [aNumber <= 9]) ifTrue: [faces := aNumber]
It may be good to review the order in which the messages are sent when a die is created. For instance, if we start by evaluating DieMorph faces: 9:
- The class method DieMorph class»faces: sends
new
toDieMorph
class. - The method for
new
(inherited byDieMorph
class fromBehavior
) creates the new instance and sends it theinitialize
message. - The
initialize
method inDieMorph
setsfaces
to an initial value of 6. - DieMorph class»new returns to the class method DieMorph class»faces:, which then sends the message
faces: 9
to the new instance. - The instance method DieMorph»faces: now executes, setting the
faces
instance variable to 9.
Before defining drawOn:
, we need a few methods to place the dots on the displayed face:
Code \(\PageIndex{5}\) (Squeak): Nine Methods for Placing Points on the Faces of the Die
DieMorph»face1 ↑{0.5@0.5} DieMorph»face2 ↑{0.25@0.25 . 0.75@0.75} DieMorph»face3 ↑{0.25@0.25 . 0.75@0.75 . 0.5@0.5} DieMorph»face4 ↑{0.25@0.25 . 0.75@0.25 . 0.75@0.75 . 0.25@0.75} DieMorph»face5 ↑{0.25@0.25 . 0.75@0.25 . 0.75@0.75 . 0.25@0.75 . 0.5@0.5} DieMorph»face6 ↑{0.25@0.25 . 0.75@0.25 . 0.75@0.75 . 0.25@0.75 . 0.25@0.5 . 0.75@0.5} DieMorph»face7 ↑{0.25@0.25 . 0.75@0.25 . 0.75@0.75 . 0.25@0.75 . 0.25@0.5 . 0.75@0.5 . 0.5@0.5} DieMorph »face8 ↑{0.25@0.25 . 0.75@0.25 . 0.75@0.75 . 0.25@0.75 . 0.25@0.5 . 0.75@0.5 . 0.5@0.5 . 0.5@0.25} DieMorph »face9 ↑{0.25@0.25 . 0.75@0.25 . 0.75@0.75 . 0.25@0.75 . 0.25@0.5 . 0.75@0.5 . 0.5@0.5 . 0.5@0.25 . 0.5@0.75}
These methods define collections of the coordinates of dots for each face. The coordinates are in a square of size 1 \(\times\) 1; we will simply need to scale them to place the actual dots.
The drawOn:
method does two things: it draws the die background with the super-send, and then draws the dots.
Code \(\PageIndex{6}\) (Squeak): Drawing the Die Morph
DieMorph»drawOn: aCanvas super drawOn: aCanvas. (self perform: ('face' , dieValue asString) asSymbol) do: [:aPoint | self drawDotOn: aCanvas at: aPoint]
The second part of this method uses the reflective capacities of Smalltalk. Drawing the dots of a face is a simple matter of iterating over the collection given by the faceX
method for that face, sending the drawDotOn:at:
message for each coordinate. To call the correct faceX
method, we use the perform:
method which sends a message built from a string, here ('face', dieValue asString) asSymbol
. You will encounter this use of perform:
quite regularly.
Code \(\PageIndex{7}\) (Squeak): Drawing a Single Dot on a Face
DieMorph»drawDotOn: aCanvas at: aPoint aCanvas fillOval: (Rectangle center: self position + (self extent * aPoint) extent: self extent / 6) color: Color black
Since the coordinates are normalized to the [0:1] interval, we scale them to the dimensions of our die: self extent * aPoint
.
\(\bigstar\) We can already create a die instance from a workspace:
(DieMorph faces: 6) openInWorld.
To change the displayed face, we create an accessor that we can use as myDie dieValue: 4
:
Code \(\PageIndex{8}\) (Squeak): Setting the Current Value of the Die
DieMorph»dieValue: aNumber (aNumber isInteger and: [aNumber > 0] and: [aNumber <= faces]) ifTrue: [dieValue := aNumber. self changed]
Now we will use the animation system to show quickly all the faces:
Code \(\PageIndex{9}\) (Squeak): Animating the Die
DieMorph»stepTime ↑ 100 DieMorph»step isStopped ifFalse: [self dieValue: (1 to: faces) atRandom]
Now the die is rolling!
To start or stop the animation by clicking, we will use what we learned previously about mouse events. First, activate the reception of mouse events:
Code \(\PageIndex{10}\) (Squeak): Handling Mouse Clicks to Start and Stop the Animation
DieMorph»handlesMouseDown: anEvent ↑ true DieMorph»mouseDown: anEvent anEvent redButtonPressed ifTrue: [isStopped := isStopped not]
Now the die will roll or stop rolling when we click on it.
- NB: One die, two dice.