3

I write a program to visulize the electron cloud of hydrogen atom.

import System.Exit import Graphics.UI.GLUT probDensity :: Double -> Double probDensity r = abs $ (1 - r) * exp (-r/2.0) myInit :: IO () myInit = clearColor $= Color4 1 1 1 0 grid :: [(GLint,GLint)] grid = [(x,y) | x <- [-200..200],y <- [-200..200]] density :: [Double] density = map (\(i',j') -> probDensity $ sqrt $ (fromIntegral i' ** 2 + fromIntegral j' ** 2 ) / 324) grid cloud = zip density grid display :: DisplayCallback display = do clear [ColorBuffer] color $ Color4 1 1 1 (0::GLfloat) renderPrimitive Points $ mapM_ (\(c,(x,y)) -> color (Color3 c c 0) >> vertex (Vertex2 x y)) cloud flush idle :: IdleCallback idle = postRedisplay Nothing reshape :: ReshapeCallback reshape (Size _ _) = do viewport $= (Position 0 0, Size 400 400) matrixMode $= Projection loadIdentity ortho2D (-200.0) 200.0 (-200.0) 200.0 matrixMode $= Modelview 0 loadIdentity keyboard :: KeyboardMouseCallback keyboard (Char '\27') Down _ _ = exitSuccess keyboard _ _ _ _ = return () main :: IO () main = do (_, _args) <- getArgsAndInitialize initialDisplayMode $= [ RGBMode ] initialWindowSize $= Size 400 400 initialWindowPosition $= Position 100 100 _ <- createWindow "Cloud" shadeModel $= Smooth myInit displayCallback $= display reshapeCallback $= Just reshape keyboardMouseCallback $= Just keyboard idleCallback $= Just idle mainLoop 

But the result has many lines on the right part of the graph. enter image description here

I checked my code again and again and could not find any faults. Is this a bug of the package?

1 Answer 1

5

I'd guess that this is because floating point error is causing certain columns to be missed during rasterization. You have 401 columns of samples spread over 400 columns of pixels, and your vertex positions are being sent as integers. When the integers get converted into floats in the graphics pipeline, they will not be exact. If you change your viewport and window size to something else, it should look fine:

399x399 :

enter image description here

400x400 :

enter image description here

401x401 (one-to-one pixel to sample):

enter image description here

402x402 :

enter image description here

Note, this also works fine if you increase the number of samples you're taking:

grid = [(x,y) | x <- [-400..400],y <- [-400..400]] density = map (\(i',j') -> probDensity $ sqrt $ (fromIntegral i' ** 2 + fromIntegral j' ** 2 ) / 648) grid renderPrimitive Points $ mapM_ (\(c,(x,y)) -> do color (Color3 c c 0) vertex (Vertex2 (fromIntegral x / 2) (fromIntegral y / 2) :: Vertex2 GLfloat)) cloud 

Another way to fix it is to target the pixel centers with float-valued vertex positions. Change

vertex (Vertex2 x y) 

to

vertex (Vertex2 (fromIntegral x + 0.5) (fromIntegral y + 0.5) :: Vertex2 GLfloat) 
Sign up to request clarification or add additional context in comments.

4 Comments

I just noticed that I really have 1 more sample than the number of columns. However, I think the last sample should just be discarded when it is out of the viewport. How could it cause such strange effects?
It's platform dependent how OpenGL chooses to round the vertex positions for point rendering. For example, on my mac, the code in your example renders fine as is.
A digress. I used a list for the points to be rendered, and the list grid contains more than 160000 elements. I am worried about the performance because as we all know the list will become a monster when its length gets long. But as far as I know the opengl seems to only support list. Does haskell opengl supports array or something else?
Yes it does. What you're using here is what's known as Immediate mode OpenGL which has been deprecated for almost a decade. Haskell has a lot of support for arrays that can be used as pointers in modern OpenGL

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.