Seiten

Donnerstag, 22. Juli 2010

Funktionale Animation. Teil 4.

Teil 1.
Teil 2.
Teil 3.


Es ist soweit. Ich kann endlich eine kleine animierte Statistik generieren.
Nehmen wir an, es gibt eine Firma, die die verschiedene Produkte über Amazon verkauft. Für diese Firma wird z.B eine Statistik über Gewinn, Kosten und Amazon-Rating von einzelnen Produkten im Verlauf der Zeit erstellt.
  • Gewinn - Y-Achse
  • Kosten - X-Achse
  • Rating - Kreisdurchmesser

Die Daten werden einfach von der Random-Funktion generiert und danach weiter interpoliert so, dass letztendlich eine Sequenz folgendes Formates herauskommt.
data: (int * string * float * float * 'color) seq

(Zeitangabe, Produktname, Gewinn, Kosten, Farbe)
oder für die Rating-Werte
(Zeitangabe, Produktname, Rating, der Feld wird ignoriert , Farbe)

Einfachheitshalber werden als Zeitangabe ausschließlich Jahreswerte angegeben, die in fortlaufenden Nummern umgewandelt werden.

[(1,"Produkt A",70.50, 64.00, Brushes.Black );
(1,"Produkt B",80.50, 144.00, Brushes.Red );
(1,"Produkt C",187.50, 54.00, Brushes.Green);
(2,"Produkt A",70.55, 106.05, Brushes.Black );
(2,"Produkt B",80.55, 44.00, Brushes.Red );
(2,"Produkt C",45.50, 54.00, Brushes.Green)]


Der Code für die Datenerstellung befindet sich hier (Data.fsx).
//Behavior.fs
module BehaviorDrawing =
open System.Drawing
open Demo.Drawing
open Demo.DrawingFunctions
open Demo.Data

...

//Erstellt die Daten für XY-Achsen.
let createDataXYAxis (data: (int * string * float * float * 'a) seq) =
behavior{
let! createdData =
filterData data
(fun (_,text,posx,posy,_)->
let fposx,fposy = (float32 posx),(float32 posy)
text,fposx,fposy
)
(fun (text,_,_)->text)
return createdData
}

//Erstellt aus der Daten die Kreise.
let createDataCircle (data: (int * string * float * float * 'a) seq) =
behavior{
let! createdData =
filterData data
(fun (_,text,diameter,_,color)->
let fdiameter = float32 diameter
text, circle color fdiameter fdiameter text
)
(fun (text,_)->text)
return createdData|>Seq.map snd
}

//Alles zusammenfügen.
let dataDrawing dataXY dataCircle=
behavior{
let! xy = dataXY
let! c = dataCircle
match Seq.isEmpty xy,Seq.isEmpty c with
|false,false->
return (
Seq.zip xy c
|>Seq.map (fun ((_,fposx,fposy),circle)->
let moveCircle = translate fposx fposy circle
let textX = drawText (fposy.ToString()) (20.0f) -fposy
let lineX = textX
|>compose (lineDraw 40.f -fposy fposx -fposy)
let textY = drawText (fposx.ToString()) fposx (-20.0f)
let lineY = textY
|>compose (lineDraw fposx -40.f fposx -fposy)
Seq.fold compose moveCircle (lineX::[lineY]))
|>Seq.fold compose emptyDrawing)
|_-> return emptyDrawing
}

//Anzeige der Jahresüberschrift.
let animationYearText duration init =
let year timeStep =
//let td = t % duration
//let o= (td*255/duration)%255
let year = init + (timeStep / duration)
("Jahr " + (year.ToString()))
behavior{
let! t = time
return (drawText (year t) 300.f (-570.f))
}

let animationPart4 duration init=
//Initialisiere die Funktion mit der Dauer und dem Startwert.
let fInterpolation = createInterpolation duration init
//Erstelle die Daten im angegebenen Bereich
//für den Kreisdurchmesser.
let circles = fInterpolation 20 70
//Erstelle die Daten im angegebenen Bereich
//für die X- Y-Achsen.
let xyAxis = fInterpolation 40 500
behavior{
//Erstelle das komplette Bild.
let! all =
dataDrawing (createDataXYAxis xyAxis) (createDataCircle circles)
return all
}
let singelton=
let one duration init = behavior{
let! a = animationPart4 duration init//animation
let! animYear = animationYearText duration init
return (compose a animYear)
}
one

Dequeue.fs
Data.fsx
Drawing.fs
BehaviorPart4.fsx
AnimationPart4.fsx
mainPart4.fs

Keine Kommentare:

Kommentar veröffentlichen