4 Making Stations

[1kb 30x30 GIF] The next step is to create stations on top of the map in the window. First a Station pattern is declared. One way to present the stations is by a filled circle with a letter centered inside, denoting the station, as shown to the right. Since the graphical object representing a station thus contains several other graphical objects, it is declared as a Picture in subway3.bet:

Program 2: subway3.bet

Station: Picture
  (# name: @text;
     label: @GraphicText;
     position: @point;
     Circle: Ellipse
       (# radius: @
            (# r: @integer
            enter (# 
                  enter r 
                  do r->horizontalradius->verticalradius;
                  #)
            exit r
            #);
       #);
     
     filledcircle, circleoutline: @Circle;

     init:: 
       (# ch: @char;
          r: @rectangle;
          c: @point;
       enter (position, ch)
       do (* Initialize filledcircle *)
          filledcircle.init;
          10->filledcircle.radius;
          position->filledcircle.center;
          fill[]->filledcircle.setPaint;
          
          (* Initialize circleoutline *)
          circleoutline.init;
          11->circleoutline.radius;
          position->circleoutline.center;
          true->circleoutline.theshape.stroked;
          2->circleoutline.theshape.strokewidth;
          color[]->circleoutline.setPaint;
          
          (* Center the label within the circles *)
          ch->name.put;
          label.init;
          (position, Times, Bold, 20, false, name[])
            -> label.inittext;
          label.getbounds->r;
          (r.x+(r.width) div 2, r.y-(r.height+1) div 2)->c;
          (circleoutline.center,c)->subpoints->label.move;
          color[]->label.setPaint;

          (* Add circles and label to THIS(Picture) *)
          filledcircle[]->add;
          circleoutline[]->add;
          label[]->add;
        #);
     #);

In a Station, init enters the position to display the station, and a character to display as a label in it. The Picture is actually made up of two circles, a stroked one displaying the outline of the Station and a filled one for the background. To simplify matters a Circle pattern is defined; all it does is to extend the Ellipse pattern with a radius attribute for setting horizontalradius and verticalradius to the same value. Notice the use of a pattern in the enter part of radius. This is to assure that horizontalradius and verticalradius are only changed when a value is entered to radius, not when it is only used to exit its value. The filled Circle uses a Paint called fill, and the stroked one uses a Paint called color. These are both SolidColors

color: @SolidColor;
  (* The color used for stations and rails *)
fill: @SolidColor; 
  (* The color used to fill station backgrounds *)

and are initialized in open in theWindow, just before the Canvas is opened:

open:: 
  (# 
  do (* Initialize colors for Stations and Rails *)
     color.init; IndianRed->color.name;
     fill.init;  PaleGreen->fill.name;
     (* Initialize and open the BifrostCanvas *)
     myCanvas.open
  #)

To display the label inside the circles, a GraphicText is used. This Predefined Graphical Object has an attribute for the text to display. Thus the first thing to do is to put the char ch into a text name. Then the GraphicText is initialized, its attributes are set using inittext, and it is centered within the circles by moving it a certain distance. This distance is calculated using the bounding box of the GraphicText. The subpoints pattern simply exits the coordinatewise difference between the two points entered. The GraphicText is given the same Paint as the outline of the Station.

Finally, the three graphical objects just defined and initialized are added to the Picture, and the Station is ready to be displayed.

To create a Station and display it when the mouse is pressed in the window, the onMouseDown virtual is extended:

onMouseDown:: 
  (# 
  do (* Transform mousepos to BifrostCanvas coordinates *)
     mousepos->devicetocanvas->mousepos;
     mousepos->makeStation;
  #);

Local to onMouseDown is a Point called mousepos, which is the position of the mouse in the window at the time of the button press. This position is reported in device coordinates, i.e., screen coordinates, so in this case, the first thing to do is to transform mousepos to Canvas coordinates using the devicetocanvas attribute of the Canvas pattern. Once this is done, an item called makeStation is invoked to create the Station.

makeStation: @
   (# pos: @point;
      aStation: ^station;
      ch: @char;
   enter pos
   do &station[] -> aStation[];
      (pos, ch) -> aStation.init;
      ch+1 -> ch;
      aStation[] -> draw;
   #);

makeStation is a static item because it must remember the ch attribute, which is used as the label of the station being created. All it does is to instantiate a Station, increment ch by one (such that the next Station created will be labelled with the next letter in the alphabet), and finally draw the Station just created in myCanvas. As mentioned in the introduction, this means that the Station will now be the front most graphical object in the Picture used by the Canvas to 'remember' its Graphical Objects. To make the first Station appear with the right label, makeStation.ch must be initialized. An appropriate place to do this is in open of myCanvas, which then becomes

open:: 
  (# 
  do (* Make THIS(BifrostCanvas) the size of the map *)
     map.init;
     (map.width, map.height)->Size->theWindow.size;
     (* The first Station will have label 'A' *)
     'A'->makeStation.ch;
  #);

Exercise: The above solution for makeStation limits the number of Stations to the number of characters in the alphabeth. Describe a solution without this limitation.


The Bifrost Graphics System - Tutorial
© 1991-2004 Mjølner Informatics
[Modified: Friday April 6th 2001 at 12:43]