M_draw(3fm) - [M_draw::INTRO] The M_draw graphics library (LICENSE:PD)
Description
Library Function Descriptions
Examples
Bugs
M_draw is a portable public-domain device-independent graphics library intended for being called from Fortran that is based on VOGLE (from the University of Melbourne) that is loosely based on the Silicon Graphics Iris GL library. It was also partly inspired by the DIGS library developed at the U.S. Naval Academy under the guidance of Prof David Rogers.
Many output devices are available:
o FrameMaker MIF 3.0 (Maker Interchange File) driver. o Adobe PDF driver. o HTML5 Canvas driver. o SVG driver. o A PCL5/HPGL2 driver that supports prefsize() calls. o Monochrome PBM (Poskanzer bitmap P1 and P4 formats) and X11 bitmap driver. o Color PBM (Poskanzer pixmap P3 and P6 formats). If you have the pbmplus package you can use it to make M_draw appear to write any format pbmplus writes (assuming your system supports the popen(3c) function). o A clear-text CGM (Computer Graphics Metafile) driver. o A different (color) PostScript driver. o A driver for Microsoft VML (Vector Markup Language) M_draw is intended to produce simple graphics composed of line drawings and polygon fills in two and three dimensions. It handles circles, curves, arcs, patches, polygons, and software text in a device independent fashion. Simple hidden line removal is also available via polygon backfacing. Access to hardware text and double buffering of drawings depends on the driver.
M_draw is callable from C and Fortran, and Pascal; but M_draw is only supported in Fortran (the C components are being converted to Fortran).
The original VOGLE sources ownership statement
This software is public domain and may be used for any purpose commercial or otherwise. It is offered without any guarantee as to its suitability for any purpose or as to the sanity of its writers. The authors do ask that the source is passed on to anyone that requests a copy, and that people who get copies dont go round claiming they wrote it. Use at your own risk.
vinit(device) Initialise device vexit() Reset window/terminal (must be last routine called) voutput(path) Redirect output from *next* vinit to file vnewdev(device) Reinitialize to use new device without changing vgetdev(device) Get name of current device pushdev(device) push current device onto a stack popdev(device) pop device from stack created by pushdev. getdepth() Return number of bit planes (color planes)
Some devices are basically window oriented - like sunview and X11. You can give M_draw some information on the window that it will use with these routines. These can make your code very device independent. Both routines take arguments which are in device space. (0, 0) is the top left hand corner in device space. To have any effect these routines must be called before vinit. For the X11 device, an entry may be made in your .Xdefaults file or loaded in with the xrdb(1) command:
prefposition(x, y) Specify preferred position of window prefsize(width, height) Specify preferred width and height of window
xrdb <<\end_of_file ! X11 Windows fonts to use for "small" and "large" fonts M_draw*smallfont: fixed M_draw*largefont: 9x15 ! title on decoration bar for the window M_draw*title: My M_draw program ! window geometry and position, ! overridden by prefsize(3c) and prefposition(3c) M_draw.Geometry: =500x500-10+20 end_of_file(where you specify your geometry as you please).
clipping(onoff) Turn clipping on or off
clear() Clears screen to current color color(col) Set current color mapcolor(indx, red, green, blue) Set color map index
getkey() Return ASCII ordinal of next key typed checkkey() Returns zero if no key is pressed or ASCII ordinal getstring(bcol, string) Read in a string, echoing it in current font locator(xaddr, yaddr) Find out where cursor is slocator(xaddr, yaddr) Find out where cursor is in screen coordinates
There are two user routines that can be used to control flushing.
vsetflush(yesno) Set global flushing status vflush() Call device flush or syncronisation routine On some devices (particularly X11) considerable speedups in display can be achieved by not flushing each graphics primitive call to the actual display until necessary. M_draw automatically delays flushing in the following cases:
o Within a callobj() call. o Within curves and patches. o Within Hershey software text. o When double buffering (the flush is only done within swapbuffers).
Viewpoint routines alter the current transformation matrix.
viewport(left, right, bottom, top) Specify which part of screen to draw in pushviewport() Save current viewport popviewport() Retrieve last viewport getviewport(left, right, bottom,top) Returns limits of current viewport in screen coordinates expandviewport() use the entire device viewport unexpandviewport() undo expandviewport(3f)
Often the screen is not perfectly square and it would be nice to use the extra space without having to turn clipping off. The following routines are provided to get the values needed to adjust the calls to viewport, etc as needed.
getaspect() Returns the ratio height over width of the display device. getfactors(wfact, hfact) Returns width over min(width of device, height of device) and height over min(width of device, height of device). getdisplaysize(w, h) Returns width and height of device in device units
The attribute stack contains details such as current color, filling, hatching, centered, fixedwidth, text height, text width, and the current font. If you need to prevent object calls from changing these, use pushattributes before the call and popattributes after.
pushattributes() Save the current attributes on the attribute stack. popattributes() Restore attributes to what they were at last pushattributes().
ortho(left, right, bottom, top,near,far) Define x,y,z clipping planes. ortho2(left, right, bottom, top) Define x and y clipping planes. perspective(fov, aspect, near, far) Specify perspective viewing pyramid window(left, right, bot, top, near,far) Specify a perspective viewing pyramid
All the projection routines define a new transformation matrix, and consequently the world units. Parallel projections are defined by ortho or ortho2. Perspective projections can be defined by perspective and window.
subroutine page (left, right, bottom, top) create a window of the specified size and then set the viewport to the largest viewport with that aspect so that output acts much like a page of paper of the specified size without distortion.
pushmatrix() Save the current transformation matrix on the matrix stack. popmatrix() Reinstall the last matrix pushed
polarview(dist, azim, inc, twist) Specify the viewers position in polar coordinates up(x, y, z) Specify the world up. lookat(vx, vy, vz, px, py, pz,twist) Specify the viewers position
move(x, y, z) Move current graphics position to (x, y, z) rmove(deltax, deltay, deltaz) Relative move move2(x, y) Move graphics position to point (x, y) rmove2(deltax, deltay) Relative move in world units. smove2(x, y) Move current graphics position in screen coordinates (-1.0 to 1.0). rsmove2(deltax, deltay) Relative move in screen units (-1.0 to 1.0).
Linestyles are specified by giving a nominal length of a single dash and a character string consisting of 1s and 0s (zeros) that specify when to draw a dash and when not to draw a dash. Linestyles will follow curves and "go around" corners. If a linestyle is set or reset, the accumulated information as to where on a curve (or line) a dash is to be draw is also reset.
linewidth() set line width in rasters dashcode() set dash pattern length linestyle() set the line dash pattern For EXAMPLE, with a nominal view of -1 to 1, setting the dash length to 0.5, and the linestyle to 11010 would draw a line(or curve) with a 1.0 unit solid part, followed by a 0.5 unit blank part followed by a 0.5 unit solid part followed by a 0.5 unit blank part. The linestyle would then repeat itself.
The dash sizes are affected by the current viewport/transformation scaling factors, meaning that in perspective, the dashes look smaller the farther away they are.
draw(x, y, z) Draw from current graphics position to (x, y, z) rdraw(deltax, deltay, deltaz) Relative draw draw2(x, y) Draw from current graphics position to point (x, y) rdraw2(deltax,deltay) Relative draw sdraw2(x, y) Draw in screen coordinates (-1.0 to 1.0). rsdraw2(deltax, deltay) Relative draw in screen units (-1.0 to 1.0).
When creating arcs and sectors note that angles are measured in degrees; where zero(0) is the positive X axis in a right-handed Cartesian coordinate system and positive angles sweep counterclockwise. If filling sectors or circles (As described in the section on polygons) hatch pitch is measured in world coordinates and is initially set to 0.1. The initial hatch angle is zero(0).
circleprecision(nsegs) Set number of line segments in a circle. Default is 32. arc(x, y, radius, startang, endang) Draw an arc in world units. sector(x, y, radius, startang,endang) Draw a sector. Note: sectors are polygons. circle(x, y, radius) Draw a circle. Note: circles are polygons.
curvebasis(basis) Define a basis matrix for a curve. curveprecision(nsegs) Define number of line segments used to draw a curve. rcurve(geom) Draw a rational curve. curve(geom) Draw a curve. curven(n, geom) Draw n - 3 overlapping curve segments. Note: n must be at least 4.
rect(x1, y1, x2, y2) Draw a rectangle. polyfill(onoff) Set the polygon fill flag polyhatch(onoff) Set the polygon hatch flag hatchang(angle) Set the angle of the hatch lines. hatchpitch(pitch) Set the distance between hatch lines. poly2(n, points) Construct an (x, y) polygon from an array of points poly(n, points) Construct a polygon from an array of points makepoly() opens polygon constructed by a series of move-draws and closed by closepoly closepoly() Terminates a polygon opened by makepoly. backface(onoff) Turns on culling of backfacing polygons. backfacedir(clockwise) Sets backfacing direction to clockwise or anti-clockwise A polygon is composed of a number of coplanar line segments connected end to end to form a closed shape. In M_draw curves are estimated by a series of line segments, and thus may be included easily into polygons. Regular A polygon with all sides and interior angles the same. Regular polygons are always convex. See Regular Polygons Irregular Each side may a different length, each angle may be a different measure. The opposite of a regular polygon. See Irregular Polygons Convex All interior angles less than 180 ,and all vertices point outwards away from the interior. The opposite of concave. Regular polygons are always convex. See Convex Polygons Concave One or more interior angles greater than 180 . Some vertices push inwards towards the interior of the polygon. The opposite of convex. Self-intersecting or Crossed A polygon where one or more sides crosses back over another side, creating multiple smaller polygons. Most of the properties and theorems concerning polygons do not apply to this shape. It is best considered as several separate polygons. A polygon that in not self-intersecting in this way is called a simple polygon.
M_draw supports hardware and software fonts. The software fonts are based on the character set digitized by Dr Allen V. Hershey while working at the U. S. National Bureau of Standards. Exactly what hardware fonts are supported depends on the device, but it is guaranteed that the names "large" and "small" will result in something readable. For X11 displays the default large and small fonts used by the program can be overridden by placing the following defaults in the ~/.Xdefaults file:
font(fontname) set the current font numchars() return number of characters in the current SOFTWARE font. textsize(width, height) set maximum size of a character in the current SOFTWARE font. textang(ang) set the SOFTWARE text angle. fixedwidth(onoff) turns fixedwidth mode on or off for SOFTWARE fonts. getcharsize(c, width, height) get the width and height of a character. getfontdec() return size of maximum font descender getfontsize(width, height) get maximum width and height of a character in a font. drawchar(c) draw the character c and update current position. drawstr(str) draw the text in string at the current position. strlength(str) return the length of the string s boxtext(x, y, l, h, s) stretch and draw the SOFTWARE string s so that it fits in the imaginary box boxfit(x, y, l, h, s) resize the SOFTWARE text size so it fits in a box textjustify(val) general text justification leftjustify() left justify text rightjustify() right justify text topjustify() top justify text bottomjustify() bottom justify text centertext(onoff) turns centertext mode on or off for SOFTWARE fonts. xcentertext() center text in the X direction ycentertext() center text in the Y direction textslant() defines the obliqueness of the fonts. textweight() defines the weight of the fonts.
draw.smallfont: X11-font-name draw.largefont: X11-font-nameIt is noted here that hardware text is always assumed to be drawn parallel to the (x, y) plane, using whatever the current z coordinate is. The following software fonts are supported:
astrology cursive cyrillic futura.l futura.m gothic.eng gothic.ger gothic.ita greek markers math.low math.upp meteorology music script symbolic times.g times.i times.ib times.r times.rb japaneseA markers font "markers" is also provided for doing markers - you need to have centertext mode on for this to give sensible results when placing the markers.
If the environment variable "M_DRAW_FONTPATH" is set M_draw looks for the software fonts in the directory given by this value.
the default font is futura.l
All transformations are cumulative, so if you rotate something and then do a translate you are translating relative to the rotated axes. If you need to preserve the current transformation matrix use pushmatrix(), do the drawing, and then call popmatrix() to get back where you were before.
translate(x, y, z) Set up a translation. scale(x, y, z) Set up scaling factors in x, y, and z axis. rotate(angle, axis) Set up a rotation in axis axis where axis is one of x,y, or z. When doing transformations, ensure your objects remain in the viewing volume or they will be clipped. See routines such as ortho(3) for more information.
patchbasis(tbasis, ubasis) Define the t and u basis matrices of a patch. patchprecision(tseg, useg) Set minimum number of line segments making up curves in a patch. patchcurves(nt, nu) Set the number of curves making up a patch. rpatch(gx, gy, gz, gw) Draws a rational patch in the current basis, according to the geometry matrices gx, gy, gz, and gw. patch(gx, gy, gz) Draws a patch in the current basis, according to the geometry matrices gx, gy, and gz.
points are drawn with the current color and linewidth. Points are currently device-specific and may appear as circles, squares, or not at all; as they are generated by a zero-length vector using the hardware line style.
point(x, y, z) Draw a point at x, y, z point2(x, y) Draw a point at x, y.
Objects are graphical entities created by the drawing routines called between makeobj and closeobj. Objects may be called from within other objects. When an object is created most of the calculations required by the drawing routines called within it are done up to where the calculations involve the current transformation matrix. So if you need to draw the same thing several times on the screen but in different places it is faster to use objects than to call the appropriate drawing routines each time. Objects also have the advantage of being savable to a file, from where they can be reloaded for later reuse. Routines which draw or move in screen coordinates, or change device, cannot be included in objects.
makeobj(n) Commence the object number n. closeobj() Close the current object. genobj() Returns a unique object identifier. getopenobj() Return the number of the current object. callobj(n) Draw object number n. isobj(n) Returns non-zero if there is an object of number n. delobj(n) Delete the object number n. loadobj(n, filename) Load the object in the file filename as object number n. saveobj(n, filename) Save object number n into file filename. Does NOT save objects called inside object n. invokeobj(xt,yt,zt,xs,ys,zs,xr,yr,zr,iobject) push environment and do a transformation and then pop environment
Where possible M_draw allows for front and back buffers to enable things like animation and smooth updating of the screen. The routine backbuffer is used to initialise double buffering.
backbuffer() Draw in the backbuffer. Returns -1 if the device is not up to it. frontbuffer() Draw in the front buffer. This will always work. swapbuffers() Swap the front and back buffers.
getgp(x, y, z) Gets the current graphics position getgpt(x, y, z, w) Gets the current transformed graphics position in world coords. getgp2(x, y) Gets the current graphics position sgetgp2(x, y) Gets the current screen graphics position in screen coords (-1 to 1)
subroutine pop() pop subroutine push() push
Sample program:
program demo_M_draw use M_draw use M_draw, only : D_BLACK, D_WHITE use M_draw, only : D_RED, D_GREEN, D_BLUE use M_draw, only : D_YELLOW, D_MAGENTA, D_CYAN implicit none integer :: ipaws real :: x1, y1 integer :: icolor integer :: i,j! initialize image call prefsize(400,400) ! set size before starting call vinit( ) ! start graphics using device $M_DRAW_DEVICE call textsize(10.0,10.0) call mapcolor( 0, 255,255,255 ) !white call mapcolor( 1, 255, 0, 0 ) !red call mapcolor( 2, 0,255, 0 ) !green call mapcolor( 3, 255,255, 0 ) !yellow call mapcolor( 4, 0, 0,255 ) !blue call mapcolor( 5, 255, 0,255 ) !magenta call mapcolor( 6, 0,255,255 ) !cyan call mapcolor( 7, 0, 0, 0 ) !black call mapcolor( 8, 0,155, 0 ) call mapcolor( 9, 155,155,155 ) call mapcolor(10, 155,255,255 ) call mapcolor(11, 155,155, 0 ) call mapcolor(12, 0, 0,155 ) call mapcolor(13, 155, 0,155 ) call mapcolor(14, 0,155,155 ) call mapcolor(15, 100,100,100 ) call mapcolor(16, 155,100,100 ) call color(D_BLACK) call clear() ! clear to color 0 call color(D_WHITE)
! map area of virtual world to specified device area ! notice Y-axis for viewport is zero at TOP ! define the virtual world area we want to work in call page(0.0, 400.0, 0.0, 400.0) ! the drawing routines use these world units
! put some colored boxes into pixmap by address ! so show how the pixel map can be edited easily with ! other routines that can manipulate a pixel array. ! The P_pixel array was created when vinit(3f) was called call polyfill(.true.) icolor=1 do i=0,3 do j=0,3 x1=j*100.0 y1=i*100.0 icolor=icolor+1 call color(icolor) call rect(x1,y1,x1+100.0,y1+100.0) enddo enddo call polyfill(.false.)
! draw polar grids call linewidth(100)
call linewidth(100) call color(14) call target(200.0,200.0,200.0)
call linewidth(75) call color(0) call target(100.0,200.0,50.0)
! draw some lines call color(D_RED) call linewidth(200) call line(1.0,1.0,400.0,400.0)
call color(D_BLUE) call linewidth(250) call line(350.0,200.0,350.0,300.0)
! print some text call color(1) call linewidth(125) call font(futura.l) call hershey(40.0, 40.0,35.0,Hello World,0.0) call color(7) call linewidth(25) call hershey(40.0, 80.0,35.0,Hello World,0.0) call linewidth(100) call hershey(40.0,120.0,35.0,Hello World,30.0)
call hershey( 40.0,350.0,35.0,Hello World,0.0) call font(futura.m) call hershey( 40.0,310.0,35.0,Hello World,0.0) call font(times.r) call hershey( 350.0,400.0,35.0,Hello World,90.0) call linewidth(50) call font(times.i) call hershey(200.0,120.0,15.0,Hello World,20.0)
ipaws=getkey() call vexit()
contains
subroutine target(xc,yc,rc) real :: xc,yc,rc integer :: i real :: x,y do i=0,360,10 x=rc*cosd(real(i)) y=rc*sind(real(i)) call line(xc,yc,xc+x,yc+y) enddo do i=1,int(rc),10 call circle(xc,yc,real(i)) enddo end subroutine target
subroutine line(x1,y1,x2,y2) real,intent(in) :: x1,y1,x2,y2 call move2(x1,y1) call draw2(x2,y2) end subroutine line
subroutine hershey(x,y,height,itext,theta) real,intent(in) :: x,y real,intent(in) :: height character(len=*),intent(in) :: itext real,intent(in) :: theta call move2(x,y) call textang(theta) call textsize(height,height) call drawstr(itext) end subroutine hershey end program demo_M_draw
Polygon hatching will give unexpected results unless the polygon is initially defined in the X-Y plane.
Double buffering isnt supported on all devices.
We dont recommend the use of the smove/sdraw routines.
The yobbarays may be turned on or they may be turned off.
When creating an object, current position and text size are not actually changed so almost any query routine to get position or font size or whatever will not work properly.
If call vexit(3f) and then call vinit(3f) everything should probably be reset to initial values at program startup, such as linewidth, current font, and color. It is currently left up to the output device initialization routine. It should not be. To minimize the issue, call all the push* routines after a vinit(3f) and call all the pop* routines before vexit(3f).
Exactly what attributes should and should not be reset with a vnewdev(3f) is questionable.
Nemo Release 3.1 | M_draw (3) | February 23, 2025 |