Previous: Appendix D: Drawing graphics for finite element method (FEM) applications
Next: Appendix F: Useful reading
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 INCLUDEINTEGER, 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.
FORTRAN and the ART of Windows Programming, Copyright © Eddie Bromhead, 2023.