Writing Code for Surface Plots?
MySchizoBuddy asks: "In what ways can you code plots of surface charts using a z(x,y) function or a cloud of points? I'm looking for a tutorial that explains this that doesn't use DirectX or OpenGL libraries (the language I'm using cannot use either framework anyway). How is the 3D mess generated and how can the 2D contour plots be generated as well? I'm assuming once I know that I can also use it to make torus plots as well. Remember, I'm asking for the explanation of the underlying math and an example code that does that. The GNUPlot gallery has some examples that I find helpful, but are there similar examples out there? (Remember, I am writing the plotting code as well)? Can anyone help or point me in the right direction?"
first plot
...
hum
You could plot graphics that represent levels as colours. I think I invented a kind of R^2->R function plot that represents a Z value as the degree of grey (0 being black, 255 being white) corresponding to Z mod 255. The result is that the more frequent "cuts" (sudden changes from white to black) are, the highest is the derivative.
Looky here, here and here.
I looked at 3D rendering quite a while back (about 15 years ago). Believe me -- you probably don't really want to built this up from scratch.
It sounds like you just need some sample code that doesn't rely on OpenGL or DirectX. To that end, the following (open-source) projects may be useful:
Gan Family Homepage
just a thought, but why not use MATLAB. You can sometimes use it to make stand-alone programs and maybe even DLLs. then add it to your code. I help develop code for a Cray X1 and all visualization (for my project) is done locally using MATLAB. It's pretty beefy, but it'll make a contour plot.
887321 = 337*2633
Good 2D contour plotting is not easy (I know!!!).
If you are not afraid of coding, then the best reference that I know of is:
by Albrecht Preusser, ACM TOMS V10#4
The code (in FORTRAN, unfortunately) is also in TOMS as:
Algorithm 626 TRICP: A Contour Plot Program Triangular Meshes
You can get ACM TOMS (Transaction on Mathematical Software) from any reasonable college library.
This might be a bit of overkill, as it is designed for irregular data sets (versus regular datasets - grids). However, the code can be separated into a separate Delaunay Triangulation step. If you have a grid (or in your case, a function f(x,y)), it is easy to generate fixed triangles.
If anybody knows of a better algorithm, I'm all ears!!!
I know this doesn't help you, but I'm still curious -- under what circumstances can you not use OpenGL for this? Under what circumstances would it be easier to implement it yourself than to find/create a decent GL binding? Or maybe you could just read the software implementation from Mesa?
Don't thank God, thank a doctor!
Check out the R-project at www.r-project.org. It is very good for that sort of thing.
My advice would be to pick up a decent intro book on graphics. The Foley and Van Dam is a classic, but nearly any good book on the topic tends to have a review of the basic math involved in 3d: linear algebra with 3- or 4-value vectors and 3x3 or 4x4 matrices, the perspective divide, etc. Knowing the basics will help you have a lot better idea of what to do. But for anyone curious about the most basic 3d, here's a simple way to do it that should work decently for simple wireframe plots.
" > /> <!-- First line to draw --> /> <!-- Second line to draw --> /> <!-- Third line to draw, etc... -->
1) Take each 3d point of interest and apply some set of rotations around the X, Y, and Z axis to get the view angle right. You'll probably want to just do one rotation around each axis. e.g.:
x2 = x * cos( z_angle ) + y * sin( z_angle );
y2 = x * -sin( z_angle ) + y * cos( z_angle );
x3 = x2 * cos( y_angle ) + z * sin( y_angle );
z2 = x2 * -sin( y_angle ) + z * cos( y_angle );
y3 = y2 * cos( x_angle ) + z2 * sin( x_angle );
z3 = y2 * -sin( x_angle ) + z2 * cos( x_angle );
At this point, you'll have rotated your original coordinate (x,y,z) by some angle around some axis to get (x3,y3,z3)
(To my fellow graphics geeks: yes, yes, I know full well that matrices and quaternions are better -- I'm trying keep this simple.)
2) Maybe add some distance to the z value, to move ("translate") it away from the "camera" at the origin. You'll need to add enough to make sure that all of your z-values are positive and non-zero before step 3:
z4 = z3 + z_translate
3) Do what's called the "perspective divide" to find the two dimensional coordinate of this point. e.g.:
px = x3 / z4 * scale + center_x;
py = y3 / z4 * scale + center_y;
4) Either plot a point at the (px,py) that you just got, or compute steps 1 and 2 twice for the end points of lines and then use your system's graphics primitives to draw lines between (px1,py1) and (px2,py2). If you're not using a language or a library that can do simple 2d graphics, then I'd suggest writing out a file with the lines in SVG format (and then rasterize to bitmap with a program like Batik, Inkscape, Illustrator, etc.) e.g.:
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd
<svg width="4cm" height="4cm" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg" version="1.1">
<path d="M px1 py1 L px2 py2" fill="none" stroke="black" stroke-width="1"
<path d="M px1 py1 L px2 py2" fill="none" stroke="black" stroke-width="1"
<path d="M px1 py1 L px2 py2" fill="none" stroke="black" stroke-width="1"
</svg>
5) If you want more than wireframe, things will be a lot more complicated. At the very least, you'll need to be able to fill polygons and sort back to front by the z-value after the rotation so that everything gets drawn in the correct order. Z-buffers would be even better.
Well, I hope all that helps. Again, if anyone's really interested, I'd suggest getting a real book on the topic. But I hope this's enough to get the curious started.
dim ymax(screenwidth),ymin(screenwidth)
For xtemp = xlower to xupper step xincr
for ytemp = ylower to yupper step yincr
compute z=f(xtemp,ytemp)
(x,y)=transform(xtemp,ytemp,z)
if yymax(x) then plot x,y : ymax(x) = y
next ytemp, xtemp
Adjust your viewpoint for your 3D to 2D matrix so it plots from front to back.
Algorithm comes from an old book on doing 3D on an Apple ][ - it wasn't too hard to convert it to work on a TRS-80 Model I or a CoCo.
I just looked at the GNUPlot page, and regardless of what anyone else says, I don't think it should be too difficult to code at all, at least the grid stuff like near the top of that page. The reason - it's all drawn without perspective, which means a little vector math tells you where to put each point. You might be a bit foolish to try to hand-code something substantial with perspective, but orthogonal projections are straightforward.
Here's what you need to do. First, figure out what a unit in the X, Y, and Z axes corresponds to in screen coordinates. In the examples on the GNUPlot page, I'd estimate that a unit in the X direction is about 10 pixels to the right and 2 pixels down in screen coordinates, or (10, -2). Similarly, a unit in the Y direction is about (3, 3), and a unit in the Z direction is (0, 1). Now, to find the screen location of a point given in 3D, say (x,y,z), we start at the screen coordinate of the origin (wherever you want to put (0,0,0)). To this, we add x times the vector (10, -2), y times the vector (3, 3), and z times the vector (0, 1). For example, the point (10, 3, 6), with an origin at (100, 100), would show up at (100, 100) + (100, -20) + (9, 9) + (0, 6) = (209, 95).
In other words, if we call the unit vectors in the X, Y and Z directions (given in screen coordinates) Xhat, Yhat, and Zhat, then the point (x,y,z) should be plotted at (origin + x*Xhat+y*Yhat+z*Zhat).
I presume that from here you should be able to figure out how to draw the grid lines - just sample a bunch of points, and draw straight lines between neighbors. If you want to fill in the tiles (like the pictures near the bottom of the GNUPlot page), you'll need to be clever about the order you fill them in so that the ones in front are filled last. For this, you can sort the tiles by the appropriate combination of x and y coordinates (in "real" space, not screen space) - in our example, Xhat - 3.333Yhat points straight down, so you would sort by the combination x - 3.333y and draw in that order.
This should at least get you on your way. I'm sure there are subtleties to consider, but hey - it's your project, not mine, so I'll leave it at that!
Hate to disappoint you, but you're probably not going to find a tutorial and code that conveniently meets your requirements. For starters, eliminating OpenGL and D3D is going to remove a large number of tutorials, simply because it doesn't make sense for most people to roll their own rendering code.
Your best bet would be to look through "Computer Graphics: Principles and Practice" by Foley, van Dam, Feiner and Hughes. It covers a large number of topics, in enough detail to get a good idea of how to get started and where to look for more information. Also "Real-Time Rendering" by Tomas Möller and Eric Haines. Despite the name, a large portion of the book is applicable to almost all computer graphics, not just real-time. Also, a book on linear algebra would probably be helpful.
Maybe not
If its a very small part of a larger program, then look for some good examples and try to trudge through getting it to work. Otherwise, start writing code and examine the visual output of the screen. Look for algorithms and methods for producing the output you want. When you begin to understand them, you can integrate the concepts into your solution.
Unfortunately, even if you have an API, you still have to understand what it does and how to interact with it. Many people believe that modular programming simply means using code alrealy written. This is a false premise as there is no substitute for some degree of understanding.
In the end, you may wish to use a public library because it is highly accurate, optimized, well researched, or portable, but by that time you will have the knowledge to understand why you are selecting the particular library.
You can use the marching cubes algorithm. You can go from a cloud of points to a 3d object that you can transform like you want. I think they use that in MRI imaging. I used the 2d version (marching squares) to optimize a metablob routine a long time ago.
see: http://www.exaflop.org/docs/marchcubes/index.html
Generic Mapping Tool, will do everything you want, and a lot you have not even thought of yet. GMT produces postscript and encapsuleated postscript, so if you have a postscript viewer your good to go, runs on unix/linux, is GPL'd so there source code to analyse. There are lots of examples with generating scripts such as time-series collected along a track or a 3D perspective, artificialy illuminated, greyscale image; just reading and understanding the accompanying ducumentation should be good for college credit in cartography.
Apocalypse Cancelled, Sorry, No Ticket Refunds