Appendix E: Graphical interaction – the ‘stretchy box’

Previous: Appendix D: Drawing graphics for finite element method (FEM) applications
Next: Appendix F: Useful reading

Appendix E: Graphical interaction – the ‘stretchy box’

Anything approaching an effective demonstration of a complete graphics interaction in a real program would take far too many pages in the book. And this little program is one that I wrote to answer a query that someone raised on the Silverfrost user forum. Basically, it draws an eight-noded rectangular box with four corner nodes and for the mid-sides similar to a second order isoparametric serendipity finite element. It is possible to ‘pick up’ any one of the nodes and move it, as a result of which the box changes shape. It stays rectangular however, and an interesting exercise might be to allow distortions, for example when the corner nodes are moved the adjacent side nodes move too, and that makes for a quadrilateral rather than a rectangle.

Critical elements in this short demonstration program are that the first drawing of the box appears with a start-up format code and that different cursors are selected as the mouse pointer moves over particular nodes. The cursors are taken from a set named ‘smooth cursors’ kindly placed on the Internet by Vlastimil Miler, and their meaning is pretty obvious from their names. A full mouse input is required for this program, and a sensitivity of selection is defined as eight pixels. Finally, GRAPHICS_WRITE_MODE@ (3) is selected. There are four of these modes, and No. 3 has the interesting property that if you draw something with it, and then you draw it again, the second drawing erases the first. It is called XOR mode.

Mouse pointers or cursors are drawn using XOR mode. You may have noticed that ClearWin+ switches to a plus-sign cursor by default when the mouse pointer is moved over an on-screen drawing surface. A further property of XOR drawing is that should this cursor, which is rather simple, be moved over a mid-density grey area it will simply disappear, and that is principally why more sophisticated cursors are usually preferred including those that have a range of colours and effects so that they show up against all backgrounds.

      WINAPP
      OPTIONS (INTL, DREAL)

      PROGRAM WILFRIED
C     ----------------
      COMMON /BOXCORNERS/ iX1, iX2, iY1, iY2, NCURS
      INCLUDE 
      INTEGER, EXTERNAL :: KALLBACK
      INTEGER, EXTERNAL :: iBOXPLOT
      iX1 = 100;  iX2 = 300;  iY1 = 100;  iY2 = 300
      NCURS = 1
      IA=WINIO@('%ca[Test for stretchy box]&')
      IA=WINIO@('%sc&', iBOXPLOT)
      IA=WINIO@('%5cu[arrow][updown][leftright][diag1][diag2]&', NCURS)
      IA=WINIO@('%`^gr[BLACK, full_mouse_input]', 400, 400, iHANDLE, 
     &           KALLBACK)
      STOP;   END


      INTEGER FUNCTION KALLBACK()
C     ---------------------------
      COMMON /BOXCORNERS/ iX1, iX2, iY1, iY2, NCURS
      KALLBACK   = 1
      iSENSITIVE = 8
      iXA = (iX1+iX2)/2
      iYA = (iY1+iY2)/2
      CALL GET_MOUSE_INFO@ (jX, jY, jFLAGS)
      NODE = 0
      IF (ABS(iX1-jX) .LE. iSENSITIVE) THEN
          IF(ABS(iY1-jY) .LE. iSENSITIVE) THEN
             NCURS = 4
             NODE  = 1
          ELSE IF(ABS(iY2-jY) .LE. iSENSITIVE) THEN
             NCURS = 5
             NODE  = 7
          ELSE IF(ABS(iYA-jY) .LE. iSENSITIVE) THEN
             NCURS = 3
             NODE  = 8
          ENDIF
      ELSE IF (ABS(iX2-jX) .LE. iSENSITIVE) THEN
          IF(ABS(iY1-jY) .LE. iSENSITIVE) THEN
             NCURS = 5
             NODE  = 3
          ELSE IF(ABS(iY2-jY) .LE. iSENSITIVE) THEN
             NCURS = 4
             NODE  = 5
          ELSE IF(ABS(iYA-jY) .LE. iSENSITIVE) THEN
             NCURS = 3
             NODE  = 4
          ENDIF
      ELSE IF (ABS(iXA-jX) .LE. iSENSITIVE) THEN
          IF(ABS(iY1-jY) .LE. iSENSITIVE) THEN
             NCURS = 2
             NODE  = 2
          ELSE IF(ABS(iY2-jY) .LE. iSENSITIVE) THEN
             NCURS = 2
             NODE  = 6 
          ENDIF
      ELSE
          NCURS = 1
      ENDIF

      IF (NODE .EQ. 0) RETURN

      IF (jFLAGS .EQ. 1) THEN 
         IK = iBOXPLOT()
         IF (NODE .GE. 1 .AND. NODE .LE. 3) THEN
             iY1 = jY
         ELSE IF (NODE .GE. 5 .AND. NODE .LE. 7) THEN
             iY2 = jY
         ENDIF
         IF (NODE .GE. 3 .AND. NODE .LE. 5) THEN
             iX2 = jX
         ELSE IF (NODE .EQ. 1 .OR. NODE .GE. 7) THEN
             iX1 = jX
         ENDIF
         IK = iBOXPLOT()
         ENDIF     
      RETURN;  END


      INTEGER FUNCTION iBOXPLOT()
C     ---------------------------
      COMMON /BOXCORNERS/ iX1, iX2, iY1, iY2, NCURS
      INCLUDE 
      iBOXPLOT = 2
      CALL GRAPHICS_WRITE_MODE@ (3)
      iXA = (iX1+iX2)/2
      iYA = (iY1+iY2)/2
      CALL DRAW_RECTANGLE@ (iX1, iY1, iX2, iY2, RGB@(0,0,255))
      CALL DRAW_FILLED_ELLIPSE@(iX1, iY1, 4, 4, RGB@(0,0,255))
      CALL DRAW_FILLED_ELLIPSE@(iX1, iY2, 4, 4, RGB@(0,0,255))
      CALL DRAW_FILLED_ELLIPSE@(iX2, iY1, 4, 4, RGB@(0,0,255))
      CALL DRAW_FILLED_ELLIPSE@(iX2, iY2, 4, 4, RGB@(0,0,255))
      CALL DRAW_FILLED_ELLIPSE@(iXA, iY1, 4, 4, RGB@(0,0,255))
      CALL DRAW_FILLED_ELLIPSE@(iXA, iY2, 4, 4, RGB@(0,0,255))
      CALL DRAW_FILLED_ELLIPSE@(iX1, iYA, 4, 4, RGB@(0,0,255))
      CALL DRAW_FILLED_ELLIPSE@(iX2, iYA, 4, 4, RGB@(0,0,255))
      RETURN
      END

      RESOURCES
      1 24 default.manifest
      arrow       CURSOR    "SmoothArrow.cur"
      updown      CURSOR    "SmoothVerticalSize.cur"    
      leftright   CURSOR    "SmoothHorizontalSize.cur"
      diag1       CURSOR    "SmoothDiagonal1.cur"
      diag2       CURSOR    "SmoothDiagonal2.cur"

I have very little doubt that this program might be written in a more compact and certainly more elegant form, but the point is to demonstrate what it does and not to show off a level of skill that I don’t possess. Other useful exercises with this basic framework would be to make the window and the drawing surface resizeable, to give the box a fill colour, to change background colours, or to make the sides into curves, or even to allow a linked series of ‘stretchy boxes’ to be moved into an arbitrary shape.

Two features of this code are noteworthy. One is to set the graphics mode to 3 (XOR), the other is to define cursors from the RESOURCES section thus:

       IA=WINIO@('%5cu[arrow][updown][leftright][diag1][diag2]&', NCURS)

At any point, we can change cursor by simply picking one of the numbers from 1 .. 5 to assign to NCURS, and (obviously requiring NCURS to be still in scope, as I have done it by use of COMMON) and the cursor changes. The format code %cu changes the cursor for the next control in the window, which is what we want to happen in the %gr drawing surface. Another format code, %dc, sets the cursor for the whole application, and otherwise has the same syntax as %cu. If either %cu or %dc is qualified with a grave accent, then it can take the standard cursors (of which there are 11, see section 18.2) and whose names are listed in the documentation. Setting NCURS to 0 restores the default cursor.

Some exercises with this program might be to use colour, or to allow the user to switch between enlarging the rectangle or turning the figure into an arbitrary quadrilateral when a corner node is dragged. As presented above, a node will be dropped if the mouse pointer is moved too rapidly. You could change that if you wished by thinking about just what ‘click’ drops the control point.

Incidentally, the scanner software I use (VueScan from Hamrick software) uses a stretchy box of this type to select the area of a scan to save.

Basket
Empty
 
Copyright © 1999-2024 Silverfrost Limited