There are many code samples in the Pan# distribution, including a number of examples taken from high school mathematics presentations. There is also a wealth of code in the Prelude. In this section, we will look at the code in one of these examples in detail.
This program demonstrates the idea of the sine and cosine function. It is intended to show students that have not had any trigonometry the intuition behind these functions. This example uses a timer to animate a point moving around the unit circle. As it moves, the x and y coordinates are highlighted. On the right side of the screen graphs of the sine and cosine functions are constructed from motion around the circle. This program is found in the demos/matrices directory as 06-sine.pan.
The program starts with an overall zoom control. The graphs are sized
to the unit circle; a zoom of 100 results in a circle 200 pixels
wide.
zoom :: Number
zoom <- slider "Zoom" (20, 300) 100
The width of the line used to draw the unit circle is not expressed in
pixel coordinates (although it could be). This defines the width in
the user coordinate space:
lineWidth :: Number
lineWidth = 0.02
The unit circle is expressed as the exclusive or of two circles
centered at the origin:
theCircle :: Region
theCircle = circle (0,0) (1+lineWidth) `xorR` circle (0,0) (1-lineWidth)
The x and y axis are also defined as a region.
The line width is really the line radius.
theAxis :: Region
theAxis (x,y) = abs(x) < lineWidth || abs(y) < lineWidth
The colorRegion function is used to paint black ink on the unit
circle and axis. This produces an image which is transparant except
for the black ink. The unionR function combines the unit
circle and the x and y axis into a single region.
background :: ImageC
background = colorRegion black (theCircle `unionR` theAxis)
This timer is used to drive the animation. Timers move at a somewhat
unspecified rate but t is more or less time in seconds. The
4*pi is the maximum value of the timer and the true
causes the timer to resume from 0 when it hits this value. This
number is chosen to generate two full cycles in the graphs and to move
the position of the point on the unit circle smoothly as the timer
wraps around.
t :: Number
t <- timer "Distance" (4*pi) true
This function highlights a point p by drawing a dot of color
c over it.
hilight :: Point2 -> Color -> ImageC
hilight p c = colorRegion c $ circle p (3*lineWidth)
The left side of the screen contains three moving dots, one on the
circle and two on the axis.
picture1 :: ImageC
picture1 = hilight (cos t, sin t) green `over`
hilight (cos t, 0) blue `over`
hilight (0, sin t) red) `over`
background
The right side of the screen is a pair of graphs for sine and cosine.
We zoom out by a factor of 2 to make room for both of them.
picture2 = zoomOut 2 (graphOf sin red t `above` graphOf cos blue t)
For the graphs, we want the origin near the left side of the frame
instead of in the center. This pushes the origin to -pi/4.
graphOf :: (Number -> Number) -> Color -> Number -> Framed ImageC
graphOf fn color t =
let ((xlow, xhigh), (ylow, yhigh)) <- localBounds
dx = xlow + pi/4 -- margin on the right is pi/4
in
translateFrame (-dx, 0) $ graphOf1 fn color t
Since Pan doesn't really do curved lines we create the graph by
shading the region under the curve. We also add the axis. The
interval on the grid, 10, is high enough that you only see the axis.
The use of overFrame instead of over is essential since
the grid function has to run within a frame.
graphOf1 :: (Number -> Number) -> Color -> Number -> Framed ImageC
graphOf1 fn color t =
let g1 :: ImageC
g1 (x,y) = if x >= 0 && x <= t &&
((fn x >= 0 && y >= 0 && y <= fn x) ||
(fn x < 0 && y <= 0 && y >= fn x))
then color else invisible
in
overFrame g1 (grid (grey 0.5) 1 10)
Finally, this assembles the full image and adds the zoom control.
picture :: Framed ImageC
picture = zoomIn zoom $ beside picture1 picture2