Search code examples
f#system.drawing

Graphic autoscaling


I have the following codes to draw an unit circle

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])

I am not entirely satisfied because I have to manual "scale" the x and y coordinates by 50 to make the circle visible. Is there a way to get F# do the scaling automatically?

Thanks.


Solution

  • I think the code is representing a 2D point as a function taking 3 args - a flag, x & y. The flag indicates which of x and y to return. It would make (slightly) more sense for a start if the flag was a bool rather than a float. I'm guessing the code has been converted from another language which only has floats?

    Here's a slightly more comprehensible version:

    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
    

    Not sure why the circle is rendered as a collection of 1 pixel ellipses.

    In any case, as Tomas says, either the circle has to be scaled or the coordinate system does. Otherwise you'll end up with a 1 pixel circle.