Skip to content

Commit b64cd84

Browse files
committed
#38 WIP: add support for grid charts
1 parent 735e6bb commit b64cd84

File tree

9 files changed

+456
-8
lines changed

9 files changed

+456
-8
lines changed

Diff for: docsrc/tools/generate.fsx

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ let rec copyRecursive dir1 dir2 =
3333
copyRecursive subdir1 subdir2
3434
for file in Directory.EnumerateFiles dir1 do
3535
File.Copy(file, file.Replace(dir1, dir2), true)
36+
3637
// Web site location for the generated documentation
3738
let website = "https://muehlhaus.github.io/FSharp.Plotly/"
3839

Diff for: src/FSharp.Plotly.WPF/AssemblyInfo.fs

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ open System.Reflection
55
[<assembly: AssemblyTitleAttribute("FSharp.Plotly.WPF")>]
66
[<assembly: AssemblyProductAttribute("FSharp.Plotly")>]
77
[<assembly: AssemblyDescriptionAttribute("A F# interactive charting library using plotly.js")>]
8-
[<assembly: AssemblyVersionAttribute("1.2.2")>]
9-
[<assembly: AssemblyFileVersionAttribute("1.2.2")>]
8+
[<assembly: AssemblyVersionAttribute("1.2.3")>]
9+
[<assembly: AssemblyFileVersionAttribute("1.2.3")>]
1010
do ()
1111

1212
module internal AssemblyVersionInformation =
1313
let [<Literal>] AssemblyTitle = "FSharp.Plotly.WPF"
1414
let [<Literal>] AssemblyProduct = "FSharp.Plotly"
1515
let [<Literal>] AssemblyDescription = "A F# interactive charting library using plotly.js"
16-
let [<Literal>] AssemblyVersion = "1.2.2"
17-
let [<Literal>] AssemblyFileVersion = "1.2.2"
16+
let [<Literal>] AssemblyVersion = "1.2.3"
17+
let [<Literal>] AssemblyFileVersion = "1.2.3"

Diff for: src/FSharp.Plotly/AssemblyInfo.fs

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ open System.Reflection
55
[<assembly: AssemblyTitleAttribute("FSharp.Plotly")>]
66
[<assembly: AssemblyProductAttribute("FSharp.Plotly")>]
77
[<assembly: AssemblyDescriptionAttribute("A F# interactive charting library using plotly.js")>]
8-
[<assembly: AssemblyVersionAttribute("1.2.2")>]
9-
[<assembly: AssemblyFileVersionAttribute("1.2.2")>]
8+
[<assembly: AssemblyVersionAttribute("1.2.3")>]
9+
[<assembly: AssemblyFileVersionAttribute("1.2.3")>]
1010
do ()
1111

1212
module internal AssemblyVersionInformation =
1313
let [<Literal>] AssemblyTitle = "FSharp.Plotly"
1414
let [<Literal>] AssemblyProduct = "FSharp.Plotly"
1515
let [<Literal>] AssemblyDescription = "A F# interactive charting library using plotly.js"
16-
let [<Literal>] AssemblyVersion = "1.2.2"
17-
let [<Literal>] AssemblyFileVersion = "1.2.2"
16+
let [<Literal>] AssemblyVersion = "1.2.3"
17+
let [<Literal>] AssemblyFileVersion = "1.2.3"

Diff for: src/FSharp.Plotly/ChartExtensions.fs

+86
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,15 @@ module ChartExtensions =
343343
(fun (ch:GenericChart) ->
344344
GenericChart.addLayout layout ch)
345345

346+
// Set the LayoutGrid options of a Chart
347+
[<CompiledName("WithLayoutGrid")>]
348+
static member withLayoutGrid(layoutGrid:LayoutGrid) =
349+
(fun (ch:GenericChart) ->
350+
let layout =
351+
GenericChart.getLayout ch
352+
|> Layout.SetLayoutGrid layoutGrid
353+
GenericChart.setLayout layout ch)
354+
346355
[<CompiledName("WithConfig")>]
347356
static member withConfig (config:Config) =
348357
(fun (ch:GenericChart) ->
@@ -442,6 +451,83 @@ module ChartExtensions =
442451
GenericChart.combine gCharts
443452

444453

454+
[<CompiledName("Grid")>]
455+
static member Grid ((gCharts:seq<#seq<GenericChart>>),
456+
sharedAxes:bool
457+
) =
458+
459+
let nRows = Seq.length gCharts
460+
let rowWidth = 1. / float nRows
461+
462+
let nCols = gCharts |> Seq.maxBy Seq.length |> Seq.length
463+
let colWidth = 1. / float nCols
464+
465+
let pattern = if sharedAxes then StyleParam.LayoutGridPattern.Coupled else StyleParam.LayoutGridPattern.Independent
466+
let grid =
467+
LayoutGrid.init(
468+
Rows=nRows,Columns=nCols,Pattern=pattern
469+
)
470+
gCharts
471+
|> Seq.mapi (fun rowIndex row ->
472+
row |> Seq.mapi (fun colIndex gChart ->
473+
let xdomain = (colWidth * float (colIndex-1), (colWidth * float colIndex))
474+
let ydomain = (1. - ((rowWidth * float rowIndex)),1. - (rowWidth * float (rowIndex-1)))
475+
476+
let newXIndex, newYIndex =
477+
(if sharedAxes then colIndex + 1 else (rowIndex + colIndex + 1)),
478+
(if sharedAxes then rowIndex + 1 else (rowIndex + colIndex + 1))
479+
480+
481+
let xaxis,yaxis,layout =
482+
let layout = GenericChart.getLayout gChart
483+
let xAxisName, yAxisName = StyleParam.AxisId.X 1 |> StyleParam.AxisId.toString, StyleParam.AxisId.Y 1 |> StyleParam.AxisId.toString
484+
485+
let updateXAxis index domain axis =
486+
axis |> Axis.LinearAxis.style(Anchor=StyleParam.AxisAnchorId.X index,Domain=StyleParam.Range.MinMax domain)
487+
488+
let updateYAxis index domain axis =
489+
axis |> Axis.LinearAxis.style(Anchor=StyleParam.AxisAnchorId.Y index,Domain=StyleParam.Range.MinMax domain)
490+
match (layout.TryGetTypedValue<Axis.LinearAxis> xAxisName),(layout.TryGetTypedValue<Axis.LinearAxis> yAxisName) with
491+
| Some x, Some y ->
492+
// remove axis
493+
DynObj.remove layout xAxisName
494+
DynObj.remove layout yAxisName
495+
496+
x |> updateXAxis newXIndex xdomain,
497+
y |> updateYAxis newYIndex ydomain,
498+
layout
499+
500+
| Some x, None ->
501+
// remove x - axis
502+
DynObj.remove layout xAxisName
503+
504+
x |> updateXAxis newXIndex xdomain,
505+
Axis.LinearAxis.init(Anchor=StyleParam.AxisAnchorId.Y newYIndex ,Domain=StyleParam.Range.MinMax ydomain),
506+
layout
507+
508+
| None, Some y ->
509+
// remove y - axis
510+
DynObj.remove layout yAxisName
511+
512+
Axis.LinearAxis.init(Anchor=StyleParam.AxisAnchorId.X newXIndex,Domain=StyleParam.Range.MinMax xdomain),
513+
y |> updateYAxis newYIndex ydomain,
514+
layout
515+
| None, None ->
516+
Axis.LinearAxis.init(Anchor=StyleParam.AxisAnchorId.X newXIndex,Domain=StyleParam.Range.MinMax xdomain),
517+
Axis.LinearAxis.init(Anchor=StyleParam.AxisAnchorId.Y newYIndex,Domain=StyleParam.Range.MinMax ydomain),
518+
layout
519+
520+
gChart
521+
|> GenericChart.setLayout layout
522+
|> Chart.withAxisAnchor(X=newXIndex,Y=newYIndex)
523+
|> Chart.withX_Axis(xaxis,newXIndex)
524+
|> Chart.withY_Axis(yaxis,newYIndex)
525+
)
526+
)
527+
|> Seq.map Chart.Combine
528+
|> Chart.Combine
529+
|> Chart.withLayoutGrid grid
530+
445531
/// Create a combined chart with the given charts merged
446532
[<CompiledName("Stack")>]
447533
static member Stack ( [<Optional;DefaultParameterValue(null)>] ?Columns:int,

Diff for: src/FSharp.Plotly/FSharp.Plotly.fsproj

+2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
<Compile Include="Table.fs" />
5050
<Compile Include="Trace.fs" />
5151
<Compile Include="Trace3d.fs" />
52+
<Compile Include="LayoutGrid.fs" />
5253
<Compile Include="Layout.fs" />
5354
<Compile Include="Config.fs" />
5455
<Compile Include="GenericChart.fs" />
@@ -57,6 +58,7 @@
5758
<Compile Include="CandelstickExtension.fs" />
5859
<Compile Include="SankeyExtension.fs" />
5960
<Compile Include="Templates.fs" />
61+
<None Include="Playground.fsx" />
6062
<None Include="TestScript.fsx" />
6163
<None Include="paket.references" />
6264
<None Include="paket.template" />

Diff for: src/FSharp.Plotly/Layout.fs

+9
Original file line numberDiff line numberDiff line change
@@ -388,4 +388,13 @@ type Layout() =
388388
layout
389389
)
390390

391+
static member SetLayoutGrid
392+
(
393+
grid: LayoutGrid
394+
) =
395+
(fun (layout:Layout) ->
396+
grid |> DynObj.setValue layout "grid"
397+
layout
398+
)
399+
391400

Diff for: src/FSharp.Plotly/LayoutGrid.fs

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
namespace FSharp.Plotly
2+
3+
open System
4+
5+
/// A plot grid that can contain subplots with shared axes.
6+
type LayoutGrid () =
7+
inherit DynamicObj ()
8+
9+
/// Initializes LayoutGrid object
10+
///
11+
///
12+
///SubPlots : Used for freeform grids, where some axes may be shared across subplots but others are not. Each entry should be a cartesian subplot id, like "xy" or "x3y2", or "" to leave that cell empty. You may reuse x axes within the same column, and y axes within the same row. Non-cartesian subplots and traces that support `domain` can place themselves in this grid separately using the `gridcell` attribute.
13+
///
14+
///XAxes : Used with `yaxes` when the x and y axes are shared across columns and rows. Each entry should be an y axis id like "y", "y2", etc., or "" to not put a y axis in that row. Entries other than "" must be unique. Ignored if `subplots` is present. If missing but `xaxes` is present, will generate consecutive IDs.
15+
///
16+
///YAxes : Used with `yaxes` when the x and y axes are shared across columns and rows. Each entry should be an x axis id like "x", "x2", etc., or "" to not put an x axis in that column. Entries other than "" must be unique. Ignored if `subplots` is present. If missing but `yaxes` is present, will generate consecutive IDs.
17+
///
18+
///Rows : The number of rows in the grid. If you provide a 2D `subplots` array or a `yaxes` array, its length is used as the default. But it's also possible to have a different length, if you want to leave a row at the end for non-cartesian subplots.
19+
///
20+
///Columns : The number of columns in the grid. If you provide a 2D `subplots` array, the length of its longest row is used as the default. If you give an `xaxes` array, its length is used as the default. But it's also possible to have a different length, if you want to leave a row at the end for non-cartesian subplots.
21+
///
22+
///RowOrder : Is the first row the top or the bottom? Note that columns are always enumerated from left to right.
23+
///
24+
///Pattern : If no `subplots`, `xaxes`, or `yaxes` are given but we do have `rows` and `columns`, we can generate defaults using consecutive axis IDs, in two ways: "coupled" gives one x axis per column and one y axis per row. "independent" uses a new xy pair for each cell, left-to-right across each row then iterating rows according to `roworder`.
25+
///
26+
///XGap : Horizontal space between grid cells, expressed as a fraction of the total width available to one cell. Defaults to 0.1 for coupled-axes grids and 0.2 for independent grids.
27+
///
28+
///YGap : Vertical space between grid cells, expressed as a fraction of the total height available to one cell. Defaults to 0.1 for coupled-axes grids and 0.3 for independent grids.
29+
///
30+
///Domain : Sets the domains of this grid subplot (in plot fraction). The first and last cells end exactly at the domain edges, with no grout around the edges.
31+
///
32+
///XSide : Sets where the x axis labels and titles go. "bottom" means the very bottom of the grid. "bottom plot" is the lowest plot that each x axis is used in. "top" and "top plot" are similar.
33+
///
34+
///YSide : Sets where the y axis labels and titles go. "left" means the very left edge of the grid. "left plot" is the leftmost plot that each y axis is used in. "right" and "right plot" are similar.
35+
static member init
36+
(
37+
?SubPlots : StyleParam.AxisId [] [],
38+
?XAxes : StyleParam.AxisId [],
39+
?YAxes : StyleParam.AxisId [],
40+
?Rows : int,
41+
?Columns : int,
42+
?RowOrder : StyleParam.LayoutGridRowOrder,
43+
?Pattern : StyleParam.LayoutGridPattern,
44+
?XGap : float,
45+
?YGap : float,
46+
?Domain : Domain,
47+
?XSide : StyleParam.LayoutGridXSide,
48+
?YSide : StyleParam.LayoutGridYSide
49+
) =
50+
LayoutGrid ()
51+
|> LayoutGrid.style
52+
(
53+
?SubPlots = SubPlots,
54+
?XAxes = XAxes ,
55+
?YAxes = YAxes ,
56+
?Rows = Rows ,
57+
?Columns = Columns ,
58+
?RowOrder = RowOrder,
59+
?Pattern = Pattern ,
60+
?XGap = XGap ,
61+
?YGap = YGap ,
62+
?Domain = Domain ,
63+
?XSide = XSide ,
64+
?YSide = YSide
65+
66+
)
67+
68+
// Applies the styles to LayoutGrid()
69+
///
70+
///SubPlots : Used for freeform grids, where some axes may be shared across subplots but others are not. Each entry should be a cartesian subplot id, like "xy" or "x3y2", or "" to leave that cell empty. You may reuse x axes within the same column, and y axes within the same row. Non-cartesian subplots and traces that support `domain` can place themselves in this grid separately using the `gridcell` attribute.
71+
///
72+
///XAxes : Used with `yaxes` when the x and y axes are shared across columns and rows. Each entry should be an y axis id like "y", "y2", etc., or "" to not put a y axis in that row. Entries other than "" must be unique. Ignored if `subplots` is present. If missing but `xaxes` is present, will generate consecutive IDs.
73+
///
74+
///YAxes : Used with `yaxes` when the x and y axes are shared across columns and rows. Each entry should be an x axis id like "x", "x2", etc., or "" to not put an x axis in that column. Entries other than "" must be unique. Ignored if `subplots` is present. If missing but `yaxes` is present, will generate consecutive IDs.
75+
///
76+
///Rows : The number of rows in the grid. If you provide a 2D `subplots` array or a `yaxes` array, its length is used as the default. But it's also possible to have a different length, if you want to leave a row at the end for non-cartesian subplots.
77+
///
78+
///Columns : The number of columns in the grid. If you provide a 2D `subplots` array, the length of its longest row is used as the default. If you give an `xaxes` array, its length is used as the default. But it's also possible to have a different length, if you want to leave a row at the end for non-cartesian subplots.
79+
///
80+
///RowOrder : Is the first row the top or the bottom? Note that columns are always enumerated from left to right.
81+
///
82+
///Pattern : If no `subplots`, `xaxes`, or `yaxes` are given but we do have `rows` and `columns`, we can generate defaults using consecutive axis IDs, in two ways: "coupled" gives one x axis per column and one y axis per row. "independent" uses a new xy pair for each cell, left-to-right across each row then iterating rows according to `roworder`.
83+
///
84+
///XGap : Horizontal space between grid cells, expressed as a fraction of the total width available to one cell. Defaults to 0.1 for coupled-axes grids and 0.2 for independent grids.
85+
///
86+
///YGap : Vertical space between grid cells, expressed as a fraction of the total height available to one cell. Defaults to 0.1 for coupled-axes grids and 0.3 for independent grids.
87+
///
88+
///Domain : Sets the domains of this grid subplot (in plot fraction). The first and last cells end exactly at the domain edges, with no grout around the edges.
89+
///
90+
///XSide : Sets where the x axis labels and titles go. "bottom" means the very bottom of the grid. "bottom plot" is the lowest plot that each x axis is used in. "top" and "top plot" are similar.
91+
///
92+
///YSide : Sets where the y axis labels and titles go. "left" means the very left edge of the grid. "left plot" is the leftmost plot that each y axis is used in. "right" and "right plot" are similar.
93+
static member style
94+
(
95+
?SubPlots : StyleParam.AxisId [] [],
96+
?XAxes : StyleParam.AxisId [],
97+
?YAxes : StyleParam.AxisId [],
98+
?Rows : int,
99+
?Columns : int,
100+
?RowOrder : StyleParam.LayoutGridRowOrder,
101+
?Pattern : StyleParam.LayoutGridPattern,
102+
?XGap : float,
103+
?YGap : float,
104+
?Domain : Domain,
105+
?XSide : StyleParam.LayoutGridXSide,
106+
?YSide : StyleParam.LayoutGridYSide
107+
) =
108+
(fun (layoutGrid: LayoutGrid) ->
109+
SubPlots |> DynObj.setValueOptBy layoutGrid "subplots" (Array.map (Array.map StyleParam.AxisId.toString))
110+
XAxes |> DynObj.setValueOptBy layoutGrid "xaxes" (Array.map StyleParam.AxisId.toString)
111+
YAxes |> DynObj.setValueOptBy layoutGrid "yaxes" (Array.map StyleParam.AxisId.toString)
112+
Rows |> DynObj.setValueOpt layoutGrid "rows"
113+
Columns |> DynObj.setValueOpt layoutGrid "columns"
114+
RowOrder |> DynObj.setValueOptBy layoutGrid "roworder" StyleParam.LayoutGridRowOrder.toString
115+
Pattern |> DynObj.setValueOptBy layoutGrid "pattern" StyleParam.LayoutGridPattern.toString
116+
XGap |> DynObj.setValueOpt layoutGrid "xgap"
117+
YGap |> DynObj.setValueOpt layoutGrid "ygap"
118+
Domain |> DynObj.setValueOpt layoutGrid "domain"
119+
XSide |> DynObj.setValueOptBy layoutGrid "xside" StyleParam.LayoutGridXSide.toString
120+
YSide |> DynObj.setValueOptBy layoutGrid "yside" StyleParam.LayoutGridYSide.toString
121+
122+
layoutGrid
123+
)

0 commit comments

Comments
 (0)