2010-07-04 135 views
1

我有以下代码绘制单位圆图形自动缩放

open System 
open Microsoft.FSharp.Collections 
open Microsoft.FSharp.Math 
open System.Drawing 
open System.Windows.Forms 

let make_point (x:float) (y:float) = (fun bit -> if bit = 0.0 then x else y) 
let x_of (point:float->float) = point 0.0 
let y_of (point:float->float) = point 1.0 

let unit_circle (t:float) = 
    make_point (sin <| 2.0 * Math.PI * t) 
       (cos <| 2.0 * Math.PI * t) 
let draw_connected (curve:float->float->float) (values: float list)= 
    let form = new Form(Text = "Curve") 
    let drawCurve (g:Graphics) = 
     for t in values do 
      let p = curve t   
      g.DrawEllipse(Pens.Red, 
          float32 (x_of p * 50.0 + (float)form.ClientSize.Width/2.0), 
          float32 (y_of p * 50.0 + (float)form.ClientSize.Height/2.0), 
          float32 1, 
          float32 1) 
    form.Paint.Add(fun e -> drawCurve e.Graphics)  
    form.Show() 

draw_connected unit_circle ([0.0 .. 0.01 .. 1.0]) 

我并不完全满意,因为我不得不手动“规模”的X和Y坐标50,使圆可见。有没有办法让F#自动执行缩放?

谢谢。

回答

1

我认为代码是一个2D点作为一个函数取3个参数 - 一个标志,x & y。该标志指示要返回哪个x和y。如果国旗是布尔而不是浮动,它会使(略)更有意义。我猜测代码已经从另一种只有浮动的语言转换而来?

这里有一个稍微理解的版本:

open System 
open Microsoft.FSharp.Collections 
open Microsoft.FSharp.Math 
open System.Drawing 
open System.Windows.Forms 
open System.Threading 

type Point = {x : float; y : float} 

let unit_circle (angle : float) = 
    { 
     x = (sin <| 2.0 * Math.PI * angle) 
     y = (cos <| 2.0 * Math.PI * angle) 
    } 

let draw_connected (curve : float -> Point) (radius : float) (angles : float list) = 
    let form = new Form(Text = "Curve") 
    let drawCurve (gfx : Graphics) = 
     for angle in angles do 
      let p = curve angle   
      gfx.DrawEllipse(Pens.Red, 
          float32 (p.x * radius + (float)form.ClientSize.Width/2.0), 
          float32 (p.y * radius + (float)form.ClientSize.Height/2.0), 
          float32 1, 
          float32 1) 
    form.Paint.Add (fun pntEvntArgs -> drawCurve pntEvntArgs.Graphics)  
    form.Show() 
    form 

let form = draw_connected unit_circle 50.0 ([0.0 .. 0.01 .. 1.0]) 

while form.Created do 
    Thread.Sleep (1) 
    Application.DoEvents() 
done 

不知道为什么圈被渲染为1成像素椭圆的集合。

在任何情况下,正如Tomas所说,要么缩放圆或者坐标系。否则,最终会出现1像素的圆圈。

1

我没有完全试着理解你的代码,但也许你可以使用比例转换,它可以指定为Graphics对象。这改变的Graphics的坐标系,因此,所有绘图执行(例如使用DrawEllipse)被自动缩放 - 你可以在某种程度上设置的缩放,使得单位圆显示为圆具有半径50.

  • 要设置转换,请使用ScaleTransfrom方法(请参阅MSDN documentation获取更多信息)Graphics实例(代码中的值g)。
0

由于Tomas说你可以使用缩放转换。如果要使用较小的曲线来绘制一个圆,你可以使用多个DrawCurve电话:)

我已经改变了一下乔恩的代码用于该目的:

  • 用你记录的System.Drawing.Point类型,而不是
  • 修改unit_circle,以便返回代表坐标x和y的元组将角度列表转换为一系列角度序列。这将是有用的,因为我们可以为我们的曲线(基数样条)具有可变数目的结,由常数N
  • 实施了splitRepeatEvery方法,G:

    Seq.splitRepeatEvery 3 { 1 .. 10 }回报 seq [seq [1; 2; 3]; seq [3; 4; 5]; seq [5; 6; 7]; seq [7; 8; 9]; seq [9; 10]]

下面的代码:

module Seq = 
    /// Split a sequence into pieces, each n items long 
    /// repeating elements between start and end of subsequences. 
    let splitRepeatEvery (count : int) (source : seq<'T>) = 
     if not (count > 1) then failwith "count must be superior to 1" 
     seq { use e = source.GetEnumerator() 
       let hasNext = ref (e.MoveNext()) 
       while !hasNext do 
       let (block:option<'T>[]) = Array.create count None 

       for i in 0 .. count - 1 do 
        do block.[i] <- if !hasNext then Some(e.Current) else None 
        if (i <> count - 1) then do hasNext := e.MoveNext() 

       yield seq { yield! block } 
       |> Seq.filter (fun x -> x.IsSome) 
       |> Seq.map (function Some(e) -> e | _ -> failwith "") } 

let unit_circle (angle : float) = 
    (sin <| 2.0 * Math.PI * angle), (cos <| 2.0 * Math.PI * angle) 

let draw_connected curve radius (seqOfAngles : float seq seq) knotsCount = 
    let form = new Form(Text = "Curve") 

    let computeBoundingBox points = 
     let search f acc array = 
      Array.fold (fun (x,y) (p:Point) -> f p.X x, f p.Y y) acc array 
     let minX, minY = search min (form.ClientSize.Width, form.ClientSize.Height) points 
     let maxX, maxY = search max (0,0) points 
     new Rectangle(minX, minY, abs(minX-maxX), abs(minY-maxY)) 

    let drawCurves (gfx : Graphics) = 
     // Create a buffer for storing our knots 
     let buffer = Array.create knotsCount (new Point()) 
     let mutable i = 0 

     for angles in seqOfAngles do 
      for angle in angles do 
       let x, y = curve angle 
       let X = int(x * radius + (float)form.ClientSize.Width/2.0) 
       let Y = int(y * radius + (float)form.ClientSize.Height/2.0) 
       let P = new Point(X, Y) 
       buffer.[i] <- P 
       i <- i + 1 

      let knots = buffer.[0..(i-1)] 
      // Draw spline only if we have one or more knots 
      if knots.Length <> 1 then 
       gfx.DrawCurve(Pens.Red, knots) 
       // For debug: compute BBox of an array of points and draw it 
       let debugRect = computeBoundingBox knots 
       gfx.DrawRectangle(Pens.Black, debugRect) 

      // Don't forget to reset position in buffer between each spline draw call 
      i <- 0 

    form.Paint.Add (fun pntEvntArgs -> drawCurves pntEvntArgs.Graphics)  
    form.Show() 
    form 

// Define constants  
let STEP = 0.050 
let N = 4 
// Define a new sequence of sequence of angles 
let s = {0.0 .. STEP .. 1.0} |> Seq.splitRepeatEvery N 
let form = draw_connected unit_circle 120.0 s N 

// For debug: print sequence of sequence of angles 
s |> Seq.iter (fun s -> Seq.iter (fun x -> printf "%f " x) s; printfn "") 

while form.Created do 
    Thread.Sleep (1) 
    Application.DoEvents() 
done 

您可以用N(数量的结对花键)和STEP不同的值发挥(但要注意STEP应该选择1.0f是STEP的倍数或浮点数,以便最后一个序列的最后一个元素关闭en到1.0f else,最后的样条将不会连接到第一个样条!)。瞧!

alt text http://img818.imageshack.us/img818/9765/circles3.png