Contents
Next

First Steps: Drawing on the Screen


Smalltalk is a nice tool do draw. You can draw immediately on the screen. For the following examples you need a workspace. All examples will draw into the top left part of your Squeak window. For best results, you should place the workspace in the bottom right part of your window.

To try an example, you copy the text into a workspace, select it, bring up the menu and select the option 'do it'. You will do this very often and we call this process 'evaluation of a statement'.

Now try to evaluate this statement:

Display fill: (10 @ 10 extent: 100 @ 100)
        rule: Form over
        fillColor: (Color red)

This draws a red quadrangle into the top left corner of your Squeak window.

First, a word about the coordinates:

The coordinates on the horizontal axis increase from left to the right, the coordinates on the vertical axis increase from top to down. The to left corner of the Squeak window has the coordinates (0, 0), written in Smalltalk: 0 @ 0.

The expression (10 @ 10 extent: 100 @ 100) creates a Rectangle with origin 10 @ 10 and extent 100 @ 100. To create such a rectangle, you send the message extent: 100 @ 100 to the Point 10 @ 10. Alternatively, you can create a rectangle with this expression
(Rectangle origin:10 @ 10 extent: 100 @ 100)

Display is a global name, it references your display device. Evaluate

Display inspect

to find out what a Display is: It is an instance of DisplayScreen.

In the inspector view you see the word 'self' in the first line of the view on the left. Select this line, bring up the menu and select 'browse hierarchy'

A browser opens that show you the class together with all superclasses.

DisplayScreen is a subclass of Form and a Form is a structure that stores an image. You can best compare it with a painting canvas.

Forms are certainly worth a tutorial of its own, for now I mention only that the method fill:rule:fillColor: is defined in the instance protocol of Form and inherited by DisplayScreen.

To remove a drawing from the screen, bring up the desktop menu and select the option: restore display

Now try this:

  | pen |

 pen := Pen new.
 pen defaultNib: 2;
     color: (Color red);
     place: 30 @ 30;
     goto: 130@130.

This is a short program. Copy it into a workspace and evaluate it. You will see a red line drawn on the screen.

Let us examine this example line by line:

That's it.

Now let us do something more fanciful:

   | points radius centralAngle n pen |
radius := 100.
n := 5.
centralAngle := 360.0/n.
points := OrderedCollection new: n.
0 to: n - 1 do:
   [:idx | | angle |
     angle := centralAngle * idx.
     points add: ((angle degreeSin @ angle degreeCos) * radius) rounded
                     + radius + 10
]. Display fill: (0 @ 0 extent: radius @ radius + 10 * 2) fillColor: Color white. pen := Pen new defaultNib: 2; place: points last. points do: [:pt | pen goto: pt].

This is now really a small program. You can copy it into a workspace and evaluate it. It draws a polygon with 5 vertices. Let us discuss the code line by line:

Just for fun we can try this with different values for n.

A Pen that is created with Pen new draws on the screen and is normally allowed to draw on the entire screen. This is not always desired. It is possible to restrict the area a pen can draw into with a clipping rectangle:

   | points radius centralAngle n pen |
  radius := 100.
  n := 5.
  centralAngle := 360.0/n.
  points := OrderedCollection new: n.
  0 to: n - 1 do:
      [:idx | | angle |
        angle := centralAngle * idx.
        points add: ((angle degreeSin @ angle degreeCos) * radius) rounded
                       + radius + 10
]. Display fill: (0 @ 0 extent: radius @ radius + 10 * 2) fillColor: Color white. pen := Pen new defaultNib: 2; clipRect: (0 @ 0 extent: radius @ radius + 10); place: points last. points do: [:pt | pen goto: pt].

A Pen can be moved outside its clipping rectangle, but it draws only that part of a line that falls within its clipping rectangle.

For additional beauty we can add the following code after the last statement of our program (to see the entire drawing, you should remove the line that adds the clipping rectangle!):

  pen roundNib: 9;
      color: Color red.
  points do: [:pt | pen place: pt; goto: pt].

These two statements mark the vertices with red points. The method place: moves the pen to a given position, but it does not draw a line. The method goto: draws a line as the pen moves from the old position to the new one, the currently installed brush shape is used to draw the line. When the new pen position is equal to the old one, the brush shape is displayed at that position.

An even more beautiful coloring of the vertices is only slightly more complicated, but gives very beautiful drawings for larger values of n:

  pen roundNib: 9.
  1 to: n do:
      [:idx | | angle pt |
        angle := centralAngle * (idx - 1).
        pt := points at: idx.
        pen color: (Color h: angle s: 0.5 v: 0.9).
        pen place: pt; goto: pt].

The method h:s:v: is a class method of class Color, it creates a color that is defined in terms of hue, saturation and brightness. Note that saturation 0 creates gray values of different brightness - the hue does not influence gray values.

Note that in the above code fragment, the iteration with the method do: is replaced with a counted repetition. This is necessary to compute a value that depends on the collection index. In Squeak we can also use the method withIndexDo: to accomplish this:

  pen roundNib: 9.
  points withIndexDo:
    [ :pt :idx | | angle |
        angle := centralAngle * (idx - 1).
        pen color: (Color h: angle s: 0.5 v: 0.9).
        pen place: pt; goto: pt]

The next example is a refinement of the previous one. Again we compute the vertex points of a polygon. This time, however, we do not draw from one point to the next. The variable delta is used to define the draw policy. A value of 2 says that we move from a point to the second next point, a value of three says that we move from a point to the third next point. The OrderedCollection 'pointIndices' contains the computed indices of the vertex points in their drawing order.

  | points radius centralAngle n pen
     pointIndices delta cnt current |
  radius := 100.

  n := 7.      " number of vertices "
  delta := 2.  " increment to the next vertex "
   " for best results, (n gcd: delta) should be 1 "

  centralAngle := 360.0/n.
  points := OrderedCollection new: n.

  0 to: n - 1 do:
      [:idx | | angle |
       angle := centralAngle * idx.
       points add: ((angle degreeSin @ angle degreeCos) * radius) rounded
                   + radius + 10
      ].

  Display fill: (0 @ 0 extent: radius @ radius + 10 * 2)
          fillColor: Color white.
  pen := Pen new
       defaultNib: 2;
       place: points last.
  pointIndices := OrderedCollection new: n.
  cnt := 0.
  current := 0.
  [cnt < n]
     whileTrue:
        [current := current + delta \\ n.
         current = 0 ifTrue: [current := n].
         pointIndices add: current.
         cnt := cnt + 1
        ].
  pointIndices do: [:idx | pen goto: (points at: idx)].

This is a nice game to play, but it would be much more attractive to put the drawing into a window. This is what we will do next.

What we have learned:


Contents
Next