Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 60 additions & 11 deletions src/FSharp.Charting.Gtk.fs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ namespace FSharp.Charting

module ChartTypes =

/// An implementation of a histogram bin.
type Bin = { LowerBound: float; UpperBound: float; Count: int}

/// An implementation type for items on a chart. This type should not be used directly.
type public LineChartItem(X: key, Y: value) =
member __.X = X
Expand Down Expand Up @@ -616,6 +619,14 @@ namespace FSharp.Charting
// ----------------------------------------------------------------------------------
// Data operations

let internal binData data lowerBound upperBound intervals =
seq{lowerBound .. (upperBound - lowerBound)/intervals .. upperBound }
|> Seq.pairwise
|> Seq.map (fun (l,u) ->
let cnt = data |> Seq.filter (fun e -> e >= l && e < u) |> Seq.length
{LowerBound = l; UpperBound = u; Count = cnt}
)

let internal allowNull x = match x with None -> null | Some v -> v

let internal listen data = NotifySeq.notifyOrOnce data
Expand Down Expand Up @@ -694,7 +705,7 @@ namespace FSharp.Charting
type internal Helpers() =

/// Use a DateTime axis if the input key data is DateTime
static member ApplyStaticAxis(xty, pos) = (fun (ch:('T :> GenericChart)) ->
static member ApplyStaticAxis(xty, pos, ?gap) = (fun (ch:('T :> GenericChart)) ->
let model = ch.Model
match model.DefaultXAxis with
| null ->
Expand All @@ -703,7 +714,12 @@ namespace FSharp.Charting
if xty = typeof<System.TimeSpan> then
model.Axes.Add (Axes.TimeSpanAxis(Position=pos))
if xty = typeof<string> then
model.Axes.Add (Axes.CategoryAxis(Position=pos))
match gap with
| Some g ->
let a = Axes.CategoryAxis(Position=pos)
a.GapWidth <- g
model.Axes.Add (a)
| _ -> model.Axes.Add (Axes.CategoryAxis(Position=pos))
| _ -> ()
ch)

Expand Down Expand Up @@ -731,6 +747,7 @@ namespace FSharp.Charting
AxisYTitle |> Option.iter (fun t -> ensureDefaultYAxis().Title <- t)
ch)


/// Provides a set of static methods for creating charts.
type Chart =
/// Register a function that is used to automatically transform X values (keys)
Expand Down Expand Up @@ -912,9 +929,13 @@ namespace FSharp.Charting
/// <param name="Color">The color for the data.</param>
/// <param name="XTitle">The title of the X-axis.</param>
/// <param name="YTitle">The title of the Y-axis.</param>
static member Column(data:seq<('key :> key)*#value>,?Name,?Title,?Labels, ?Color,?XTitle,?YTitle) =
/// <param name="ColumnWidth">The width of columns versus whitespace as a percentage.</param>
static member Column(data:seq<('key :> key)*#value>,?Name,?Title,?Labels, ?Color,?XTitle,?YTitle,?ColumnWidth:float) =
let gap = match ColumnWidth with
| Some columnWidth -> Some (1.0 - columnWidth)
| _ -> None
GenericChart.Create(data |> listen |> mergeLabels Labels |> makeItems (fun ((k,v),labelOpt) -> ColumnItem(valueToDouble v)), ColumnSeries(ValueField="Value"))
|> Helpers.ApplyStaticAxis(typeof<'key>, Axes.AxisPosition.Bottom)
|> Helpers.ApplyStaticAxis(typeof<'key>, Axes.AxisPosition.Bottom, ?gap = gap)
|> Helpers.ApplyStyles(?Name=Name,?Title=Title,?Color=Color,?AxisXTitle=XTitle,?AxisYTitle=YTitle)

/// <summary>Uses a sequence of columns to compare values across categories.</summary>
Expand All @@ -925,8 +946,35 @@ namespace FSharp.Charting
/// <param name="Color">The color for the data.</param>
/// <param name="XTitle">The title of the X-axis.</param>
/// <param name="YTitle">The title of the Y-axis.</param>
static member Column(data:seq<#value>,?Name,?Title,?Labels, ?Color,?XTitle,?YTitle) =
Chart.Column(indexData data,?Name=Name,?Title=Title,?Labels=Labels, ?Color=Color,?XTitle=XTitle,?YTitle=YTitle)
/// <param name="ColumnWidth">The width of columns versus whitespace as a percentage.</param>
static member Column(data:seq<#value>,?Name,?Title,?Labels, ?Color,?XTitle,?YTitle, ?ColumnWidth) =
Chart.Column(indexData data,?Name=Name,?Title=Title,?Labels=Labels, ?Color=Color,?XTitle=XTitle,?YTitle=YTitle, ?ColumnWidth=ColumnWidth)


/// <summary>Generates a Histogram with reasonable defaults.</summary>
/// <param name="data">The data for the chart.</param>
/// <param name="Name">The name of the data set.</param>
/// <param name="Title">The title of the chart.</param>
/// <param name="Color">The color for the data.</param>
/// <param name="XTitle">The title of the X-axis.</param>
/// <param name="YTitle">The title of the Y-axis.</param>
/// <param name="LowerBound">The lower bound of the histogram.</param>
/// <param name="UpperBound">The upper bound of the histogram.</param>
/// <param name="Intervals">The number of intervals in the histogram.</param>
static member Histogram(data:seq<#value>,?Name,?Title,?Color,?XTitle,?YTitle, ?LowerBound, ?UpperBound, ?Intervals) =
let data' = data |> Seq.map valueToDouble
let lowerBound = match LowerBound with
| Some lowerBound -> lowerBound
| _ -> Seq.min data
let upperBound = match UpperBound with
| Some upperBound -> upperBound
| _ -> Seq.max data
let intervals = match Intervals with
| Some intervals -> intervals
| _ -> 30. // corresponds to what ggplot does
let bins = binData data' lowerBound upperBound intervals
let data'' = bins |> Seq.map (fun b -> ( sprintf "%.2f" b.LowerBound), b.Count)
Chart.Column(data'',?Name=Name,?Title=Title,?Color=Color,?XTitle=XTitle,?YTitle=YTitle, ?ColumnWidth=Some 0.95)

#if INCOMPLETE_API
/// <summary>Similar to the Pie chart type, except that it has a hole in the center.</summary>
Expand Down Expand Up @@ -1785,8 +1833,9 @@ namespace FSharp.Charting
/// <param name="Color">The color for the data.</param>
/// <param name="XTitle">The title of the X-axis.</param>
/// <param name="YTitle">The title of the Y-axis.</param>
static member Column(data:IObservable<#seq<#key * #value>>,?Name,?Title,(* ?Labels, *) ?Color,?XTitle,?YTitle) =
Chart.Column(NotifySeq.ofObservableReplacing data,?Name=Name,?Title=Title(* ,?Labels=Labels *),?Color=Color,?XTitle=XTitle,?YTitle=YTitle)
/// <param name="ColumnWidth">The width of columns versus whitespace as a percentage.</param>
static member Column(data:IObservable<#seq<#key * #value>>,?Name,?Title,(* ?Labels, *) ?Color,?XTitle,?YTitle,?ColumnWidth) =
Chart.Column(NotifySeq.ofObservableReplacing data,?Name=Name,?Title=Title(* ,?Labels=Labels *),?Color=Color,?XTitle=XTitle,?YTitle=YTitle,?ColumnWidth=ColumnWidth)

/// <summary>Uses a sequence of columns to compare values across categories.</summary>
/// <param name="data">The data for the chart. Each observation adds a data element to the chart.</param>
Expand All @@ -1796,8 +1845,9 @@ namespace FSharp.Charting
/// <param name="Color">The color for the data.</param>
/// <param name="XTitle">The title of the X-axis.</param>
/// <param name="YTitle">The title of the Y-axis.</param>
static member ColumnIncremental(data:IObservable<#key * #value>,?Name,?Title,(* ?Labels, *) ?Color,?XTitle,?YTitle) =
Chart.Column(NotifySeq.ofObservableIncremental data,?Name=Name,?Title=Title(* ,?Labels=Labels *),?Color=Color,?XTitle=XTitle,?YTitle=YTitle)
/// <param name="ColumnWidth">The width of columns versus whitespace as a percentage.</param>
static member ColumnIncremental(data:IObservable<#key * #value>,?Name,?Title,(* ?Labels, *) ?Color,?XTitle,?YTitle,?ColumnWidth) =
Chart.Column(NotifySeq.ofObservableIncremental data,?Name=Name,?Title=Title(* ,?Labels=Labels *),?Color=Color,?XTitle=XTitle,?YTitle=YTitle, ?ColumnWidth=ColumnWidth)

#if INCOMPLETE_API
/// <summary>Similar to the Pie chart type, except that it has a hole in the center.</summary>
Expand Down Expand Up @@ -2578,4 +2628,3 @@ namespace FSharp.Charting


#endif

74 changes: 62 additions & 12 deletions src/FSharp.Charting.fs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@ namespace FSharp.Charting
do registerConvertor (fun (dto:DateTimeOffset) -> dto.DateTime :> key)

module ChartTypes =

/// An implementation of a histogram bin.
type Bin = { LowerBound: float; UpperBound: float; Count: int}

/// An implementation type for labelled points on chart. This type should not be used directly.
type public DataPoint(X: key, Y: value, Label: string) =
member __.Label = Label
Expand Down Expand Up @@ -830,6 +834,13 @@ namespace FSharp.Charting
// stacked values
| StackedXYValues of seq<IEnumerable * IEnumerable>

let internal binData data lowerBound upperBound intervals =
seq{lowerBound .. (upperBound - lowerBound)/intervals .. upperBound }
|> Seq.pairwise
|> Seq.map (fun (l,u) ->
let cnt = data |> Seq.filter (fun e -> e >= l && e < u) |> Seq.length
{LowerBound = l; UpperBound = u; Count = cnt}
)

// ----------------------------------------------------------------------------------
// Utilities for working with enumerable and tuples
Expand All @@ -847,6 +858,8 @@ namespace FSharp.Charting
// Converts Y value of a chart (defines the type too)
let culture = System.Globalization.CultureInfo.InvariantCulture

let valueToDouble (x:value) = x.ToDouble(culture)

open System.Collections.Specialized

let private convertKeys (selector:_ -> key) (transform:(key -> key) -> _) data =
Expand Down Expand Up @@ -2395,6 +2408,9 @@ namespace FSharp.Charting
GenericChart.Create(mergeDataAndLabelsForY4 data Labels, fun() -> CandlestickChart() )
|> Helpers.ApplyStyles(?Name=Name,?Title=Title,?Color=Color,?AxisXTitle=XTitle,?AxisYTitle=YTitle)

static member internal ConfigureColumn(c:GenericChart, property, vPointWidth) =
vPointWidth |> Option.iter (fun v -> c.SetCustomProperty<float>(property, v))

/// <summary>Uses a sequence of columns to compare values across categories.</summary>
/// <param name="data">The data for the chart.</param>
/// <param name="Name">The name of the data set.</param>
Expand All @@ -2403,10 +2419,14 @@ namespace FSharp.Charting
/// <param name="Color">The color for the data.</param>
/// <param name="XTitle">The title of the X-axis.</param>
/// <param name="YTitle">The title of the Y-axis.</param>
static member Column(data,?Name,?Title,?Labels, ?Color,?XTitle,?YTitle) =
GenericChart.Create(mergeDataAndLabelsForXY data Labels, fun () -> GenericChart(SeriesChartType.Column))
|> Helpers.ApplyStyles(?Name=Name,?Title=Title,?Color=Color,?AxisXTitle=XTitle,?AxisYTitle=YTitle)

/// <param name="ColumnWidth">The width of columns versus whitespace as a percentage.</param>
static member Column(data,?Name,?Title,?Labels, ?Color,?XTitle,?YTitle,?ColumnWidth) =
let c =
GenericChart.Create(mergeDataAndLabelsForXY data Labels, fun () -> GenericChart(SeriesChartType.Column))
|> Helpers.ApplyStyles(?Name=Name,?Title=Title,?Color=Color,?AxisXTitle=XTitle,?AxisYTitle=YTitle)
Chart.ConfigureColumn(c, "PointWidth", ColumnWidth)
c

/// <summary>Uses a sequence of columns to compare values across categories.</summary>
/// <param name="data">The data for the chart.</param>
/// <param name="Name">The name of the data set.</param>
Expand All @@ -2415,10 +2435,13 @@ namespace FSharp.Charting
/// <param name="Color">The color for the data.</param>
/// <param name="XTitle">The title of the X-axis.</param>
/// <param name="YTitle">The title of the Y-axis.</param>
static member Column(data,?Name,?Title,?Labels, ?Color,?XTitle,?YTitle) =
GenericChart.Create(mergeDataAndLabelsForY data Labels, fun () -> GenericChart(SeriesChartType.Column))
|> Helpers.ApplyStyles(?Name=Name,?Title=Title,?Color=Color,?AxisXTitle=XTitle,?AxisYTitle=YTitle)

/// <param name="ColumnWidth">The width of columns versus whitespace as a percentage.</param>
static member Column(data,?Name,?Title,?Labels,?Color,?XTitle,?YTitle,?ColumnWidth) =
let c =
GenericChart.Create(mergeDataAndLabelsForY data Labels, fun () -> GenericChart(SeriesChartType.Column))
|> Helpers.ApplyStyles(?Name=Name,?Title=Title,?Color=Color,?AxisXTitle=XTitle,?AxisYTitle=YTitle)
Chart.ConfigureColumn(c, "PointWidth", ColumnWidth)
c

/// <summary>Similar to the Pie chart type, except that it has a hole in the center.</summary>
/// <param name="data">The data for the chart.</param>
Expand Down Expand Up @@ -2529,6 +2552,31 @@ namespace FSharp.Charting
GenericChart.Create(mergeDataAndLabelsForY data Labels, fun () -> FunnelChart () )
|> Helpers.ApplyStyles(?Name=Name,?Title=Title,?Color=Color,?AxisXTitle=XTitle,?AxisYTitle=YTitle)

/// <summary>Generates a Histogram with reasonable defaults.</summary>
/// <param name="data">The data for the chart.</param>
/// <param name="Name">The name of the data set.</param>
/// <param name="Title">The title of the chart.</param>
/// <param name="Color">The color for the data.</param>
/// <param name="XTitle">The title of the X-axis.</param>
/// <param name="YTitle">The title of the Y-axis.</param>
/// <param name="LowerBound">The lower bound of the histogram.</param>
/// <param name="UpperBound">The upper bound of the histogram.</param>
/// <param name="Intervals">The number of intervals in the histogram.</param>
static member Histogram(data:seq<#value>,?Name,?Title,?Color,?XTitle,?YTitle, ?LowerBound, ?UpperBound, ?Intervals) =
let data' = data |> Seq.map valueToDouble
let lowerBound = match LowerBound with
| Some lowerBound -> lowerBound
| _ -> Seq.min data
let upperBound = match UpperBound with
| Some upperBound -> upperBound
| _ -> Seq.max data
let intervals = match Intervals with
| Some intervals -> intervals
| _ -> 30. // corresponds to what ggplot does
let bins = binData data' lowerBound upperBound intervals
let data'' = bins |> Seq.map (fun b -> ( sprintf "%.2f" b.LowerBound), b.Count)
Chart.Column(data'',?Name=Name,?Title=Title,?Color=Color,?XTitle=XTitle,?YTitle=YTitle,?ColumnWidth=Some 0.95)

/// <summary>Displays a series of connecting vertical lines where the thickness and direction of the lines are dependent on the action of the price value.</summary>
/// <param name="data">The data for the chart.</param>
/// <param name="Name">The name of the data set.</param>
Expand Down Expand Up @@ -3292,8 +3340,9 @@ namespace FSharp.Charting
/// <param name="Color">The color for the data.</param>
/// <param name="XTitle">The title of the X-axis.</param>
/// <param name="YTitle">The title of the Y-axis.</param>
static member Column(data:IObservable<#seq<#key * #value>>,?Name,?Title,(* ?Labels, *) ?Color,?XTitle,?YTitle) =
Chart.Column(NotifySeq.ofObservableReplacing data,?Name=Name,?Title=Title(* ,?Labels=Labels *),?Color=Color,?XTitle=XTitle,?YTitle=YTitle)
/// <param name="ColumnWidth">The width of columns versus whitespace as a percentage.</param>
static member Column(data:IObservable<#seq<#key * #value>>,?Name,?Title,(* ?Labels, *) ?Color,?XTitle,?YTitle,?ColumnWidth) =
Chart.Column(NotifySeq.ofObservableReplacing data,?Name=Name,?Title=Title(* ,?Labels=Labels *),?Color=Color,?XTitle=XTitle,?YTitle=YTitle,?ColumnWidth=ColumnWidth)

/// <summary>Uses a sequence of columns to compare values across categories.</summary>
/// <param name="data">The data for the chart. Each observation adds a data element to the chart.</param>
Expand All @@ -3303,8 +3352,9 @@ namespace FSharp.Charting
/// <param name="Color">The color for the data.</param>
/// <param name="XTitle">The title of the X-axis.</param>
/// <param name="YTitle">The title of the Y-axis.</param>
static member ColumnIncremental(data:IObservable<#key * #value>,?Name,?Title,(* ?Labels, *) ?Color,?XTitle,?YTitle) =
Chart.Column(NotifySeq.ofObservableIncremental data,?Name=Name,?Title=Title(* ,?Labels=Labels *),?Color=Color,?XTitle=XTitle,?YTitle=YTitle)
/// <param name="ColumnWidth">The width of columns versus whitespace as a percentage.</param>
static member ColumnIncremental(data:IObservable<#key * #value>,?Name,?Title,(* ?Labels, *) ?Color,?XTitle,?YTitle,?ColumnWidth) =
Chart.Column(NotifySeq.ofObservableIncremental data,?Name=Name,?Title=Title(* ,?Labels=Labels *),?Color=Color,?XTitle=XTitle,?YTitle=YTitle,?ColumnWidth=ColumnWidth)

/// <summary>Similar to the Pie chart type, except that it has a hole in the center.</summary>
/// <param name="data">The data for the chart. Each observation replaces the entire data on the chart.</param>
Expand Down