Графическое автомасштабирование

Для скриптов Python 3.2 единственным выбором является Cxfreeze. Создайте его из источников, иначе он не сработает.

Для python 2.x я предлагаю pyinstaller, поскольку он может упаковать программу python в один исполняемый файл, в отличие от CxFreeze, который выводит также библиотеки.

1
задан Stringer 5 July 2010 в 00:29
поделиться

3 ответа

Я думаю, что код представляет двухмерную точку как функцию, принимающую 3 аргумента - флаг, x и y. Флаг указывает, какой из x и y вернуть. Для начала было бы (немного) больше смысла, если бы флаг был bool, а не float. Я предполагаю, что код был преобразован из другого языка, в котором есть только числа с плавающей запятой?

Вот немного более понятная версия:

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 пиксель.

В любом случае, как говорит Томас, масштаб нужно либо масштабировать, либо масштабировать систему координат. В противном случае вы получите круг в 1 пиксель.

1
ответ дан 2 September 2019 в 23:18
поделиться

Я не до конца пытался понять ваш код, но, возможно, вы могли бы использовать масштабное преобразование, которое можно указать объекту Graphics. Оно изменяет систему координат Graphics, поэтому все рисунки, которые вы выполняете (например, используя DrawEllipse), автоматически масштабируются - вы можете установить масштабирование таким образом, что единичный круг будет выглядеть как круг радиусом 50.

  • Чтобы задать преобразование, используйте метод ScaleTransfrom (см. MSDN documentation для получения дополнительной информации) экземпляра Graphics (значение g в вашем коде).
1
ответ дан 2 September 2019 в 23:18
поделиться

Как сказал Томаш, вы можете использовать масштабные преобразования. Если вы хотите нарисовать круг, используя маленькие кривые, вы можете использовать несколько вызовов DrawCurve :)

Для этого я немного изменил код Джона:

  • used System.Drawing. Point вместо вашей записи
  • модифицировал unit_circle так, чтобы он возвращал кортеж, представляющий координаты x и y
  • превратил ваш список углов в последовательность последовательностей углов. Это будет полезно, поскольку мы можем иметь переменное количество узлов для нашей кривой (кардинального сплайна), представленное константой N
  • реализовали метод splitRepeatEvery, например:

    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 или числу с плавающей запятой, чтобы последний элемент последней последовательности был достаточно близок к 1.0f, иначе последний сплайн не соединится с первым!) И вуаля!

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

0
ответ дан 2 September 2019 в 23:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: