Editing
Graphics development with vplot
(section)
Jump to navigation
Jump to search
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
===High level output routines=== <syntaxhighlight lang="c"> dev.vector(x1, y1, x2, y2, nfat, dashon) int x1, y1, x2, y2, nfat, dashon; { /* These can be declared by including extern.h */ extern float dashsum, dashpos; extern float dashes[/* dashon*2 */]; </syntaxhighlight> Draw a (possibly) fat (possibly) dashed line from device coordinate (x1,y1) to (x2,y2). The fatness of the line is given by nfat. Nfat=0 means the thinnest possible line. Each increase of 1 means to fatten the line by one device pixel WIDTH (the height could be quite different, depending on the value of aspect_ratio). Nfat < 0 should be checked for, and no vector should be drawn at all in that case. If dashon>0 then a dashed line should be drawn. The pattern consists of dashon dash-gap pairs. The pattern is defined in the array dashes[2*dashon]. (ie, dashes[0] is the first dash length, dashes[1] is the first gap length, dashes[2] is the second dash length, etc.) Dovplot will call dev.attributes(NEW_DASH,...) whenever the values in this array are changed. Dashsum gives the total length of the dash-gap pattern (ie, dashsum = summation i=0;i<dashon*2;i++ of dashes[i]). The position in the dash pattern at the start of the vector is given in dashpos (it is the responsibility of dev.vector to update dashpos after each vector is drawn. The only time dovplot ever touches it is to reset it back to zero after "move" commands or when the pattern is reset). Dashpos, dashsum, and dashes are all measured in INCHES. (You should get the same size dash pattern on different devices even if they have different-sized screens!) If you are not sure whether you have supported dashed lines correctly, try using dashvec (described below) to do the dashing and see if the results are compatible. Thus, you can support dashed lines in one of 3 ways: # You can use the generic routine dashvec to do the dashing for you in software, and just ignore all this mess; # You can refer to dashon, dashsum, dashpos, and dashes on every call to dev.vector and use what you find to do the dashing, all the while making sure to keep dashpos up to date; # You can keep track of the current dash pattern via calls to dev.attributes(NEW_DASH,...), and let the dashing take place in hardware. Note that just because a dashed pattern has been put in effect DOES NOT mean it will always be used on all calls to dev.vector. Dev.vector can still be asked to draw continuous lines (ie, those for which dashon=0) WITHOUT WARNING AT ANY TIME. All you are guaranteed is that IF you are asked to draw with a dash pattern, it will be the pattern last sent to dev.attributes(NEW_DASH,...). If the pattern last sent to dev.attributes(NEW_DASH,...) was just a continuous line (ie no dashing at all) then dev.vector is guaranteed to only be called with dashon=0, until dev.attributes(NEW_DASH,...) is called again and a new pattern is set. Just as in the case of dashing, dev.attributes(NEW_FAT,...) warns when the "current fatness" has been changed. But dev.vector can still be asked to draw a line with a different fatness (such as for a polygon border) without warning at any time. If your device does hardware fattening, the best thing to do is to check "nfat" versus the current hardware fatness at the beginning of every call to dev.vector to see if the hardware fatness needs to be reset. (Dev.attributes(NEW_FAT,...) is probably of use only to vppen.) If your device can't do hardware fattening, use the utility routine fatvec described below. Utility routines associated with dev.vector: These routines are meant to be called from a device-dependent dev.vector routine as utility functions. <syntaxhighlight lang="c"> fatvec(x1, y1, x2, y2, nfat, dashon) int x1, y1, x2, y2, nfat, dashon; </syntaxhighlight> Fatvec should ONLY be called if nfat>0. Fatvec will apply Glenn Kroeger's line-fattening algorithm to the given fat vector, replacing it with several non-fat vectors. Fatvec will thus repeatedly call dev.vector with nfat=0. (That's why it is important that your routine only call fatvec if nfat>0!) Fatvec ignores the dashon argument. <syntaxhighlight lang="c"> dashvec(x1, y1, x2, y2, nfat, dashon) int x1, y1, x2, y2, nfat, dashon; </syntaxhighlight> Dashvec should ONLY be called if dashon>0. Dashvec will apply Joe Dellinger and Steve Cole's line-dashing algorithm to the given dashed vector, replacing it with several non-dashed vectors. Dashvec will thus repeatedly call dev.vector with dashon=0. (That's why it is important that your routine only call dashvec if dashon>0!) Dashvec ignores the nfat argument. Note that if both dashvec and fatvec are called, dashvec must be called FIRST. <syntaxhighlight lang="c"> int clip(x1, y1, x2, y2) int *x1, *y1, *x2, *y2; </syntaxhighlight> Clip will clip the vector {(x1,y1),(x2,y2)} to the bounds set by {(xwmin,ywmin),(xwmax,ywmax)}. If the line is completely out of bounds and is clipped away to nothing, clip returns 1. Otherwise, clip returns 0. The standard way of using clip is: <tt> if(clip(&x1,&y1,&x2,&y2)) return; </tt> Generic routines for dev.vector: genvector.c This routine will do all required clipping, fattening, and dashing. It will also keep track of the previous position, and breaks up strings of vectors for you into moves and draws. (It does this in a tricky way, re-ordering the incoming vectors and even discarding moves that can be done with draws. The idea is to make the plot come out as efficiently as possible on the device.) It calls dev.plot(x,y,flag). No other routine calls dev.plot, so if you do not use genvector then dev.plot should be nulldev. <syntaxhighlight lang="c"> dev.marker(npts, mtype, msize, coor) int npts, type, size; int coor[/* npts*2 */]; </syntaxhighlight> Draw npts symbols centered at device coordinates (coor[0],coor[1]), (coor[2],coor[3]),...,(coor[npts*2-2],coor[npts*2-1]). Mtype is an integer from 0 to whatever, defining the type of symbol to draw. Mtype of 0 through 5 is defined as in GKS: * 0,1: Smallest possible dot on the device. * 2: Plus sign * 3: Asterisk * 4: Circle * 5: Cross * 6-19: Reserved by GKS, but currently undefined. These are as used in Vplot: * 20: Square * 21: Triangle (flat side on bottom) * 22: Diamond * 23: 5-pointed Star For values that make sense as ASCII, something that looks like the corresponding ASCII character should be used. For ridiculous values of mtype, at least plot a point. Msize is the vertical height of the symbol in VERTICAL device units. (Ie, msize says how many pixels tall the letter `A' should be.) Generic routines: genmarker.c Genmarker calls dev.point, dev.text, or gentext.c as needed to produce the desired symbols. For ASCII symbols the current font is used, which could either be a device-dependent font or a gentext font depending on the font number. Marker types 2 through 23 are drawn using the MATH and MISC gentext fonts. <syntaxhighlight lang="c"> dev.text(string, pathx, pathy, upx, upy); char *string; float pathx, pathy, upx, upy; { /* These can be defined by including vplot.h and extern.h */ extern int txfont, txprec, txovly; struct txalign {int hor; int ver;}; extern struct txalign txalign; extern int fat; extern int xold, yold; </syntaxhighlight> Print the text in string at the point (xold,yold) in device coordinates. Upon return, xold and yold should be reset to point to the END of the text. (IE, for the normal text justification mode (TH_LEFT, TV_BASE), if the dev.text routine is called twice in a row the two printed strings should fit together nicely, as if produced by one call to dev.text with the two strings concatenated.) The text should have fatness `fat', as defined in the section for dev.vector above. The text should be justified according to the values of txalign.hor (for horizontal alignment) and txalign.ver (for vertical alignment.) See vplot.h and vplottext.mn for descriptions of how text justification should work and to enumerate the various justification modes. (Our modes follow GKS, but with a couple of extensions thrown in to handle symbols and mid-text font and size changes.) The text font value to use is given by txfont. Device-dependent text fonts start at font number NUMGENFONT. For txfont less than this, dovplot will NOT automatically call gentext. Device-dependent text routines, except for certain special cases, should begin by: <tt> if (txfont < NUMGENFONT) { gentext(...pass on all arguments here...); return; } </tt> The text precision value can be used by device-dependent text routines to control the quality of text produced. (You should either follow the GKS conventions or simply produce good text and ignore this parameter.) The three different precisions are enumerated in vplot.h The text overlay value controls whether or not an area is shaded out under the text before it is drawn, and whether or not a box should be drawn around it. The shading should be to the current background color, color 0. The box should be of the same color and fatness as the text. Which value of txovly corresponds to what action is defined in vplot.h. It is up to the device to decide how big a box around the text is appropriate for a given font. (It is probably a good idea to follow gentext's example in this.) Pathx, pathy, upx, and upy define the size, shape, and orientation of the text. Pathx and upx are in HORIZONTAL device units, and pathy and upy are in VERTICAL device units. (This distinction is important if your device does not have aspect_ratio=1..) Note that all 4 values are FLOATS, not ints. The vector (pathx,pathy) defines the text path, and the vector (upx,upy) defines the character up vector. (This is just like in the GKS text notation conventions.) To understand the meaning of these two vectors, consider normal horizontal text with a height of 100 pixels. (We'll assume our device has square pixels for the moment.) For this text, (pathx,pathy) = (100.,0.) and (upx,upy) = (0.,100.). Now the position of any point on this text can be expressed as a linear combination of the path vector and the up vector. This is how dev.text should represent all text internally. In this way, if we linearly transform just the path vector and the up vector, then we do the transform to every point of the text too. If we rotate both the path and up vectors, the text rotates. if we multiply both the path and up vectors by two, the text gets twice as big. If the path and up vectors are not orthogonal, the text gets sheared. To test a device-dependent text routine, try switching between a device-dependent font and one of the gentext fonts (txfont<NUMGENFONT). If your device-dependent test routine is correctly working, the text should always appear the same as far as height, direction, and position in both cases. Generic routines: gentext.c Gentext.c produces text by mixing vectors and filled areas. It calls dev.vector, dev.area, and dev.attributes. It also recognizes many special escape sequences in the text. Gentext is documented in vplottext(9). <syntaxhighlight lang="c"> #include "vertex.h" #include "pat.h" dev.area(npts, verlist) int npts; struct vertex *verlist; { /* These can be defined by including extern.h */ extern int xwmin, xwmax, ywmin, ywmax; extern int overlay; extern int ipat; extern struct pat pat[]; </syntaxhighlight> Dev.area fills a polygon with a user-defined pattern. The even-odd rule should be used to determine whether a point is inside or outside the polygon. (Draw a line from the point in question out to infinity. If it crosses the border of the polygon an odd number of times, it's inside; an even number of times, it's outside.) Npts is the number of vertices in the polygon. The coordinates of the vertices themselves are stored in the doubly-linked list "verlist". You should not change the values of these coordinates, as they may be used later by dovplot to draw a border around the polygon. Note that if you use dev.area, it is up to the device to clip the polygon, REGARDLESS of the setting of smart_clip. The lower-leftmost displayable point is (xwmin,ywmin) and the upper-rightmost displayable point is (xwmax,ywmax). Alternatively, keep track of the current clipping window via dev.attributes(SET_WINDOW,...). The "raster overlay mode" (which is set by the variable "overlay") controls the transparency of pixels of color 0 in polygon fill patterns. Refer to dev.attributes(NEW_OVERLAY,...) and also to dev.raster below. Here is vertex.h, which defines how a vertex is represented: <tt> struct vertex { int x; /* X coordinate of vertex */ int y; /* Y coordinate of vertex */ struct vertex *next; /* pointer to next vertex */ struct vertex *last; /* pointer to last vertex */ struct vertex *soft; /* pointer to some other vertex */ }; </tt> All coordinates are in device pixel units. The pattern to fill with is given in the external pat[ipat], which is a structure of type pat. You can either look in pat[ipat] fresh every time you need it, or use dev.attributes(NEW_PAT,...). Here is pat.h: <tt> struct pat { int ydim; /* Slow dimension */ int xdim; /* Fast dimension */ int ydim_orig; int xdim_orig; int patbits[/*xdim*ydim*/]; /* Array of color numbers */ }; </tt> Pat.patbits is an array of pat.xdim by pat.ydim color values, with one element in the array for each device pixel. The pattern is repeated to fill the polygon. The position of the origin of the pattern does not matter, but should not depend on the particular polygon. (In other words, if two polygons tiled with the same pattern overlap, the pattern should be continuous across the two.) The horizontal dimension of the pattern is length pat.xdim. This is the fast axis. The vertical dimension of the pattern is length pat.ydim. This is the slow axis. The pattern is scanned on the screen TV-style. (In other words, pat.patbits[0] is at the upper-left hand corner, pat.patbits[pat.xdim-1] is the upper-right hand corner, and pat.patbits[pat.ydim*pat.xdim-1] is the lower right hand corner.) If either pat.xdim or pat.ydim is 0 for a given polygon, dev.area is never even called and so no filling is done. If your device has color and cannot fill with an arbitrary pattern, it is OK to fill solidly with the current color instead. The VP_OLDAREA command requires dovplot to generate its own pattern. Pattern number 0 is reserved solely for this purpose. User-defined VP_AREA-style patterns run from 1 up to NPAT. For a VP_OLDAREA polygon on a color device, dovplot will generate a 1 by 1 pattern consisting of a single pixel of the current color, regardless of the requested pattern (this is part of the definition of the VP_OLDAREA command). Xdim_orig and ydim_orig will contain the values that xdim and ydim would have had if the device had been monochrome. This is provided so that no information needed to reconstruct the original vplot command is lost (vppen uses this). For a VP_OLDAREA polygon on a monochrome device, dovplot will generate an xdim by ydim pattern consisting of all zeroes except for the last pixel, which will be of the current color. If your device is monochrome and cannot use loaded patterns but does have grey levels it is permissible to use the proportion of pixels turned on in the pattern as the grey level to shade with. You are especially encouraged to do so for VP_OLDAREA polygons with xdim = ydim. Dovplot will call dev.attributes(NEW_PAT,ipat) whenever the pattern defined by pat[ipat] is loaded or updated by the user. VP_OLDAREA patterns (ipat=0) will NOT be defined in this way. It is also permissible for the user to use a pattern that was never defined. Refer to attrcom.h for more details about these cases. You only need to worry about this if you only keep track of patterns via dev.attributes(NEW_PAT,...). If you look at the contents of the "pat" structure fresh each time, you don't need to worry about whether the user defined the pattern or not, whether it is a VP_AREA or VP_OLDAREA pattern, etc, etc. Generic routines: vecarea.c, genarea.c, genpatarea.c Vecarea fills the polygon with the current color by drawing vertical and horizontal vectors. It shows the size of the pattern by the line spacing. Vecarea calls dev.vector and dev.attributes. Genarea clips the given polygon (if smart_clip=NO), possibly generating more than one polygon out of the pieces. It then calls dev.startpoly, dev.midpoly, and dev.endpoly to do the actual filling of the polygons. These three routines are documented below. Genpatarea fills the polygon with the correct pattern one raster line at a time by calling dev.raster. It uses the "dumb" format for dev.raster, so you cannot use genpatarea if smart_raster=YES. <hr> Dev.raster has 2 formats, depending on the value of smart_raster. If smart_raster=NO, then the raster will be stretched to device coordinates, clipped (regardless of the value of smart_clip), color mapped, dithered (when appropriate), and broken up into individual scan lines. Dev.raster will be called once for each scan line of the raster image. If smart_raster=YES, then the raster will be read in and color mapped (which for monochrome devices using dithering means converted to grey levels, pixc'd, greyc'd, and inverted if necessary), but nothing else. It is up to the device to dither it (depending on the value of the global variable dither), stretch it, and clip it. The entire block of raster will be passed with one call to dev.raster. The "raster overlay mode" (set by the external integer "overlay") controls the transparency of pixels of color 0 in raster. If overlay=NO, then raster (and polygon fill patterns) should completely obscure anything previously plotted underneath. If overlay=YES, then background-colored pixels (color 0) should be considered transparent, allowing things previously plotted underneath to "show through". If your device cannot do this, you can ignore this variable. You can also keep track of the current overlay mode via dev.attributes(NEW_OVERLAY,...). <syntaxhighlight lang="c"> /* Dumb format, smart_raster=NO */ dev.raster(count, out_of, xpos, ypos, rlength, orient, raster, dummy1, dummy2) int count, out_of, xpos, ypos, rlength, orient, dummy1, dummy2; unsigned char raster[/*rlength*/]; { /* Including "extern.h" defines this. */ extern int overlay; </syntaxhighlight> Draw a line of raster, starting at the point (xpos,ypos) and extending for a total of length pixels in a direction determined by orient. Orient is measured in units of 90 degrees clockwise from the device's X (horizontal) axis. Thus for orient=0 you draw the line right, for orient=1 you draw down, for orient=2 left, and for orient=3 up. The line of raster itself is in the array raster, which is of dimension rlength. Each element of the array is a color number which determines the color of one device pixel. Some devices may be able to handle more than one line of raster at a time. The variables count and out_of are provided so that the device can know how many dev.raster calls will be made in a row. Count is zero for the first call, and increases by 1 each time until it reaches out_of-1 on the last call. (Thus out_of is the total number of raster lines.) Scanning is done TV-style. (Thus, for orient=0, ypos decrements by 1 for each call; for orient=1, xpos decrements by 1 for each call; for orient=2, ypos increments by 1 for each call; for orient=3, xpos increments by 1 for each call.) Genraster1 gives you a good example of how to write a routine that builds up blocks of raster over several calls. Dummy1 and dummy2 are not used; they are provided only so that both forms of the command have the same string of arguments. Generic routines, dumb format: genraster.c, genraster1.c Genraster does vector draws along scan lines to produce raster output. It does one raster line at a time. Some fast but stupid devices can actually do raster reasonably fast this way. Genraster calls dev.attributes (to set the colors) and dev.vector. Genraster1 attempts to be a little smarter than genraster. It saves up many lines worth of raster at a time, and sorts the vectors by color and length. It does all the vectors of one color at a time, so as to save on calls to dev.attribute to change the color. It also finds the parts of the image that can be more efficiently drawn via dev.point, and makes a second pass for those. (If the device has a point mode this is considerably more efficient.) Genraster1 calls dev.attributes, dev.vector, and dev.point. <syntaxhighlight lang="c"> /* Smart format, smart_raster=YES */ dev.raster (xpix, ypix, xmin, ymin, xmax, ymax, raster_block, orient, dither_it) int xpix, ypix, xmin, ymin, xmax, ymax, orient, dither_it; unsigned char raster_block[/*xpix*ypix*/]; { /* Including "extern.h" defines these. */ extern int xwmin, xwmax, ywmin, ywmax; extern int overlay; extern int ras_allgrey; </syntaxhighlight> Draw the block of raster in the array raster_block. If dither_it=NO, raster_block is an array of color numbers. If dither_it=YES, raster_block is an array of grey levels. (0 is the background color, 255 is the opposite of that; if you have a device with a white background and black ink this results in a plot that is inverted from what you would get on most screen devices, which have a black background. See the variable "invras" described above on how to have dovplot invert the grey-levels for you so that the final plot matches what you would get on screen devices.) If possible, you should refer to the global variable "dither" and dither the grey levels by the same method the utility routine "dithline" would use. The external variable "ras_allgrey" is available for color devices to check in smart-format dev.raster. If nonzero, the device can safely assume that this raster block is entirely grey-scale. This may allow the filter to sometimes use a more efficient special-case device command that draws grey-scale-only raster. Raster_block has dimensions xpix times ypix, with xpix the fast axis. If orient=0, the raster is painted on the screen TV-style. The first array value is the upper-leftmost point. Each line of raster (xpix long) fills from left to right. Each new line of raster (ypix lines in all) is below the previous one. If orient=1, we rotate the raster 90 degrees clockwise. Thus the first value is the upper rightmost point, and the raster fills in top to bottom and then right to left. For orient=2 we rotate another 90 degrees and fill in right to left and then bottom to top. For orient=3 we fill the raster in bottom to top and then left to right. The lower-leftmost pixel in the raster image should appear on the screen at device coordinate (xmin, ymin) (assuming it isn't clipped, of course). The raster image should be (xmax-xmin) horizontal device units wide and (ymax-ymin) vertical device units tall. Thus, the pixel (xmax,ymax) is not the upper-rightmost pixel in the raster image, but is the first pixel above and to the right of the one that is. (I know this sounds strange. But when you actually code this up you'll see that it makes sense to do it this way, and that this is probably what you actually would have done even if I had told you to make sure that the point (xmax,ymax) was in the image.) Finally, the resulting image must be clipped so that (xwmin,ywmin) is the lower-leftmost pixel that can appear, and (xwmax,ywmax) is the upper-rightmost pixel that can appear. (Note that this time the corner IS in the image. Just trying to be confusing.) Generic routines: NONE <syntaxhighlight lang="c"> dev.point(x1, y1) int x1, y1; </syntaxhighlight> Change the pixel at device coordinate (x1,y1) to the current color. Generic routines: genpoint.c Genpoint calls dev.vector with zero length. Somethings wrong with the dev.vector for your device if it doesn't do anything in this case. <syntaxhighlight lang="c"> int dev.attributes(command, value, v1, v2, v3) int command, value, v1, v2, v3; </syntaxhighlight> Set various attributes (color table, current color, clipping window, etc) using device-specific routines. Commands are defined and DOCUMENTED in the include file attrcom.h (So GO PRINT IT OUT RIGHT NOW!). Value and v[1-3] are used to pass parameters as needed. Sometimes some of the 4 will be dummy arguments. This routine must return with an integer return value, which is normally zero. Comments about color: The device tells dovplot how many colors it has by setting num_col and mono. Num_col is the number of settable colors. The color for color table numbers num_col and up will never attempt to be defined by dovplot. If num_col is zero, the device has no settable colors, and dev.attributes(SET_COLOR_TABLE,...) will never be called at all. Devices with no settable colors can still have color. If mono=NO, dovplot assumes that colors 0 through 7 correspond to the standard Vplot colors (as listed in vplot.h). Dovplot will not attempt to set the current color outside of the range 0 through 7 if num_col=0. (There is no provision for devices with more than 8 colors but no settable colors.) Rather than forcing people to change the colors on their terminal to have vplot's colors come out right, you should map the color numbers dovplot asks for onto the correct device color numbers so that the factory default setting gives the correct colors. (Don't forget to similarly map the color table numbers too.) Calls to dev.attributes(SET_COLOR,...) by dovplot will only set colors in the range 0 through MAX(7,num_col-1). If mono=YES, dovplot will force num_col=0 and will only ever try to set the current color to be either 0 (meaning background) or 7 (meaning NOT background). Calls to device routines should NEVER upset the current color. If for some reason they should change it, they should always put it back again when they are done. (Dovplot keeps track of the current color, and only calls dev.attributes(SET_COLOR,...) when necessary.) Calls to generic routines are guaranteed to never (permanently) change the current color. Color tables should usually be set to factory default settings at the beginning of every plot, if it is possible to do this in such a way that the former settings can be restored again afterwards. Every device should AT LEAST keep track of whether or not the current drawing color is color 0 or not. Color 0 always defines the background color. If the device cannot draw (or rather undraw) in the background color, then it SHOULDN'T DRAW AT ALL when the current color is 0. Generic routines: NONE -------------------------------------------------------------
Summary:
Please note that all contributions to Madagascar are considered to be released under the GNU Free Documentation License 1.3 or later (see
My wiki:Copyrights
for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource.
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Navigation menu
Personal tools
English
Not logged in
Talk
Contributions
Create account
Log in
Namespaces
Page
Discussion
English
Views
Read
Edit
View history
More
Search
Getting Madagascar
download
Installation
GitHub repository
SEGTeX
Introduction
Package overview
Tutorial
Hands-on tour
Reproducible documents
Hall of Fame
User Documentation
List of programs
Common programs
Popular programs
The RSF file format
Reproducibility with SCons
Developer documentation
Adding programs
Contributing programs
API demo: clipping data
API demo: explicit finite differences
Community
Conferences
User mailing list
Developer mailing list
GitHub organization
LinkedIn group
Development blog
Twitter
Slack
Tools
What links here
Related changes
Special pages
Page information