Hello User
7575Ch08.qxp 4/27/07 1:04 PM Page 197 CHAPTER I USER INTERFACES // create the window object and add event handler // to the button control let window = let temp = createWindow "Window1.xaml" let press = temp.FindName("press") :?> Button let textbox = temp.FindName("input") :?> TextBox let label = temp.FindName("output") :?> Label press.Click.Add (fun _ -> label.Content ignore [] main() To get this program to compile, you must add references to PresentationCore.dll, PresentationFramework.dll, and WindowsBase.dll, which are usually found in the directory C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0 In the other examples in this chapter, you didn’t need to add references, since the libraries were automatically referenced by the compiler The form appears as in Figure 8-11 Figure 8-11 A form created using XAML and F# Introducing Windows Presentation Foundation 3D Another great advantage of WPF is the huge number of controls it offers One control that you’ll dig a little deeper into is Viewport3D, which offers the ability to create impressive 3D graphics, something not readily available with the WinForms library You’ll learn how you can display a 3D plane and then map an equation over it 197 7575Ch08.qxp 198 4/27/07 1:04 PM Page 198 CHAPTER I USER INTERFACES The example (shown later in Listing 8-7) starts with the XAML script Both XAML and 3D graphics are huge topics; my aim is not to cover them in detail but to give you enough of an idea of what they involve and to give you the basis for your own experiments The following XAML script describes a window with one control, a Viewport3D, on it The script is fairly lengthy because there are quite a few elements required to make a 3D scene First you must define a camera so you know which direction you are looking at the scene from You this using the element: The tags inside describe what the scene will look like The tag describes how the scene will be lit, and the tag describes the 3D shape in the scene: Here you could describe all the objects that make up the scene by giving the points that make them up using the tag; however, you don’t describe the points that make up the shape since it is a lot easier to this in F# than in XAML The tag describes what the surface of the shape will look like: The tag describes a transformation that will be applied to the shape, that is, a transformation that will mean the shape will be rotated by a certain angle: 7575Ch08.qxp 4/27/07 1:04 PM Page 199 CHAPTER I USER INTERFACES You this mainly so you can use the tag to define an animation that will alter the angle it is shown at over time: To demonstrate how these various sections hang together, Listing 8-7 shows the complete example Listing 8-7 An XAML Definition of a 3D Scene 199 7575Ch08.qxp 200 4/27/07 1:04 PM Page 200 CHAPTER I USER INTERFACES 7575Ch08.qxp 4/27/07 1:04 PM Page 201 CHAPTER I USER INTERFACES The example continues later in Listing 8-8, with the F# script, which borrows a couple of functions from Listing 8-6; it also assumes that Listing 8-7 is saved in a file called Window2.xaml You use the createWindow function to load the window and use a similar main function to display the window You then use the findMeshes function to find any meshes in the picture (a mesh is a set of points used to describe the 3D plane) You find the meshes by walking the various objects in the Viewport3D and building up a list: // finds all the MeshGeometry3D in a given 3D view port let findMeshes ( viewport : Viewport3D ) = viewport.Children |> Seq.choose (function :? ModelVisual3D as c -> Some(c.Content) | _ -> None) |> Seq.choose (function :? Model3DGroup as mg -> Some(mg.Children) | _ -> None) |> Seq.concat |> Seq.choose (function :? GeometryModel3D as mg -> Some(mg.Geometry) | _ -> None) |> Seq.choose (function :? MeshGeometry3D as mv -> Some(mv) | _ -> None) I kept this function generic so it could work with any Viewport3D It is highly likely that you will want to grab a list of all the meshes in your 3D scene in any 3D work you in XAML and F# because it is likely that you will want to manipulate your meshes in some way in F# Then you use createPlaneItemList, createSquare, createPlanePoints, createIndicesPlane, and addPlaneToMesh to add a flat plane to the mesh object in the scene The function mapPositionsCenter centers the plane so it is in the middle of the scene Finally, a clever little function called changePositions maps the function movingWaves repeatedly across the plane ten times a second The core of this function creates a new Point3DCollection from the Point3D objects contained within the old one using the function movingWaves to decide what the new Z position should be let changePositions () = let dispatcherTimer = new DispatcherTimer() dispatcherTimer.Tick.Add (fun e -> let t = (float_of_int DateTime.Now.Millisecond) / 2000.0 let newPositions = mesh.Positions |> Seq.map (fun position -> let z = movingWaves t position.X position.Y new Point3D(position.X, position.Y, z)) mesh.Positions Window temp.Height None) |> Seq.choose (function :? Model3DGroup as mg -> Some(mg.Children) | _ -> None) |> Seq.concat |> Seq.choose (function :? GeometryModel3D as mg -> Some(mg.Geometry) | _ -> None) |> Seq.choose (function :? MeshGeometry3D as mv -> Some(mv) | _ -> None) // loop function to create all items necessary for a plane let createPlaneItemList f (xRes : int) (yRes : int) = let list = new List() for x = to xRes - for y = to yRes - f list x y list 7575Ch08.qxp 4/27/07 1:04 PM Page 203 CHAPTER I USER INTERFACES // function let point x // function let point3D to initialize a point y = new Point(x, y) to initialize a "d point x y = new Point3D(x, y, 0.0) // create all the points necessary for a square in the plane let createSquare f (xStep : float) (yStep : float) (list : List) (x : int) (y : int) = let x' = Float.of_int x * xStep let y' = Float.of_int y * yStep list.Add(f x' y') list.Add(f (x' + xStep) y') list.Add(f (x' + xStep) (y' + yStep)) list.Add(f (x' + xStep) (y' + yStep)) list.Add(f x' (y' + yStep)) list.Add(f x' y') // create all items in a plane let createPlanePoints f xRes yRes = let xStep = 1.0 / Float.of_int xRes let yStep = 1.0 / Float.of_int yRes createPlaneItemList (createSquare f xStep yStep) xRes yRes // create the 3D positions for a plane, i.e., the thing that says where // the plane will be in 3D space let createPlanePositions xRes yRes = let list = createPlanePoints point3D xRes yRes new Point3DCollection(list) // create the texture mappings for a plane, i.e., the thing that // maps the 2D image to the 3D plane let createPlaneTextures xRes yRes = let list = createPlanePoints point xRes yRes new PointCollection(list) // create indices list for all our triangles let createIndicesPlane width height = let list = new System.Collections.Generic.List() for index = to width * height * list.Add(index) new Int32Collection(list) // center the plane in the field of view let mapPositionsCenter (positions : Point3DCollection) = let newPositions = positions |> Seq.map 203 7575Ch08.qxp 204 4/27/07 1:04 PM Page 204 CHAPTER I USER INTERFACES (fun position -> new Point3D( (position.X - 0.5 ) * -1.0 , (position.Y - 0.5 ) * -1.0, position.Z)) new Point3DCollection(newPositions) // create a plane and add it to the given mesh let addPlaneToMesh (mesh : MeshGeometry3D) xRes yRes = mesh.Positions Seq.map (fun position -> let z = movingWaves t position.X position.Y new Point3D(position.X, position.Y, z)) mesh.Positions 0.0) // function for changing the plane over time let changePositions () = let dispatcherTimer = new DispatcherTimer() dispatcherTimer.Tick.Add (fun e -> let t = (float_of_int DateTime.Now.Millisecond) / 2000.0 let newPositions = mesh.Positions |> Seq.map (fun position -> let z = f t position.X position.Y new Point3D(position.X, position.Y, z)) mesh.Positions