diff --git a/docsrc/tools/generate.fsx b/docsrc/tools/generate.fsx index 256a30e9c..334849f67 100644 --- a/docsrc/tools/generate.fsx +++ b/docsrc/tools/generate.fsx @@ -33,6 +33,7 @@ let rec copyRecursive dir1 dir2 = copyRecursive subdir1 subdir2 for file in Directory.EnumerateFiles dir1 do File.Copy(file, file.Replace(dir1, dir2), true) + // Web site location for the generated documentation let website = "https://muehlhaus.github.io/FSharp.Plotly/" diff --git a/src/FSharp.Plotly.WPF/AssemblyInfo.fs b/src/FSharp.Plotly.WPF/AssemblyInfo.fs index 84e091890..464e44d0d 100644 --- a/src/FSharp.Plotly.WPF/AssemblyInfo.fs +++ b/src/FSharp.Plotly.WPF/AssemblyInfo.fs @@ -5,13 +5,13 @@ open System.Reflection [] [] [] -[] -[] +[] +[] do () module internal AssemblyVersionInformation = let [] AssemblyTitle = "FSharp.Plotly.WPF" let [] AssemblyProduct = "FSharp.Plotly" let [] AssemblyDescription = "A F# interactive charting library using plotly.js" - let [] AssemblyVersion = "1.2.2" - let [] AssemblyFileVersion = "1.2.2" + let [] AssemblyVersion = "1.2.3" + let [] AssemblyFileVersion = "1.2.3" diff --git a/src/FSharp.Plotly/AssemblyInfo.fs b/src/FSharp.Plotly/AssemblyInfo.fs index d69454d22..12d669962 100644 --- a/src/FSharp.Plotly/AssemblyInfo.fs +++ b/src/FSharp.Plotly/AssemblyInfo.fs @@ -5,13 +5,13 @@ open System.Reflection [] [] [] -[] -[] +[] +[] do () module internal AssemblyVersionInformation = let [] AssemblyTitle = "FSharp.Plotly" let [] AssemblyProduct = "FSharp.Plotly" let [] AssemblyDescription = "A F# interactive charting library using plotly.js" - let [] AssemblyVersion = "1.2.2" - let [] AssemblyFileVersion = "1.2.2" + let [] AssemblyVersion = "1.2.3" + let [] AssemblyFileVersion = "1.2.3" diff --git a/src/FSharp.Plotly/ChartExtensions.fs b/src/FSharp.Plotly/ChartExtensions.fs index 8c06dfc4c..7e62cf26c 100644 --- a/src/FSharp.Plotly/ChartExtensions.fs +++ b/src/FSharp.Plotly/ChartExtensions.fs @@ -343,6 +343,55 @@ module ChartExtensions = (fun (ch:GenericChart) -> GenericChart.addLayout layout ch) + // Set the LayoutGrid options of a Chart + [] + static member withLayoutGrid(layoutGrid:LayoutGrid) = + (fun (ch:GenericChart) -> + let layout = + GenericChart.getLayout ch + |> Layout.SetLayoutGrid layoutGrid + GenericChart.setLayout layout ch) + + // Set the LayoutGrid options of a Chart + [] + static member withLayoutGridStyle([]?SubPlots : StyleParam.AxisId [] [], + []?XAxes : StyleParam.AxisId [], + []?YAxes : StyleParam.AxisId [], + []?Rows : int, + []?Columns : int, + []?RowOrder : StyleParam.LayoutGridRowOrder, + []?Pattern : StyleParam.LayoutGridPattern, + []?XGap : float, + []?YGap : float, + []?Domain : Domain, + []?XSide : StyleParam.LayoutGridXSide, + []?YSide : StyleParam.LayoutGridYSide + ) = + (fun (ch:GenericChart) -> + let layout = GenericChart.getLayout ch + let updatedGrid = + let currentGrid = + match layout.TryGetTypedValue "grid" with + | Some grid -> grid + | None -> LayoutGrid() + currentGrid + |> LayoutGrid.style( + ?SubPlots = SubPlots, + ?XAxes = XAxes , + ?YAxes = YAxes , + ?Rows = Rows , + ?Columns = Columns , + ?RowOrder = RowOrder, + ?Pattern = Pattern , + ?XGap = XGap , + ?YGap = YGap , + ?Domain = Domain , + ?XSide = XSide , + ?YSide = YSide + ) + let updatedLayout = layout |> Layout.SetLayoutGrid updatedGrid + GenericChart.setLayout updatedLayout ch) + [] static member withConfig (config:Config) = (fun (ch:GenericChart) -> @@ -441,8 +490,136 @@ module ChartExtensions = static member Combine(gCharts:seq) = GenericChart.combine gCharts + ///Creates a Grid containing the given plots as subplots with the dimensions of the input (amount of columns equal to the largest inner sequence). + /// + ///Parameters: + /// + ///sharedAxes : Wether the subplots share one xAxis per column and one yAxis per row or not. (default:TopToBottom) + /// + ///rowOrder : the order in which the rows of the grid will be rendered (default:false) + /// + ///xGap : The space between columns of the grid relative to the x dimension of the grid + /// + ///yGap : The space between rows of the grid relative to the y dimension of the grid + /// + ///Use Chart.withLayoutGridStyle to further style the grid object contained in the returned chart. + [] + static member Grid ((gCharts:seq<#seq>), + []?sharedAxes:bool, + []?rowOrder:StyleParam.LayoutGridRowOrder, + [] ?xGap, + [] ?yGap + ) = + + let sharedAxes = defaultArg sharedAxes false + let rowOrder = defaultArg rowOrder StyleParam.LayoutGridRowOrder.TopToBottom + let xGap = defaultArg xGap 0.05 + let yGap = defaultArg yGap 0.05 + + let nRows = Seq.length gCharts + let nCols = gCharts |> Seq.maxBy Seq.length |> Seq.length + let pattern = if sharedAxes then StyleParam.LayoutGridPattern.Coupled else StyleParam.LayoutGridPattern.Independent + + let generateDomainRanges (count:int) (gap:float) = + [|0. .. (1. / (float count)) .. 1.|] + |> fun doms -> + doms + |> Array.windowed 2 + |> Array.mapi (fun i x -> + if i = 0 then + x.[0], (x.[1] - (gap / 2.)) + elif i = (doms.Length - 1) then + (x.[0] + (gap / 2.)),x.[1] + else + (x.[0] + (gap / 2.)) , (x.[1] - (gap / 2.)) + ) + + let yDomains = generateDomainRanges nRows yGap + let xDomains = generateDomainRanges nCols xGap + + gCharts + |> Seq.mapi (fun rowIndex row -> + row |> Seq.mapi (fun colIndex gChart -> + let xdomain = xDomains.[colIndex] + let ydomain = yDomains.[rowIndex] + + let newXIndex, newYIndex = + (if sharedAxes then colIndex + 1 else ((nRows * rowIndex) + (colIndex + 1))), + (if sharedAxes then rowIndex + 1 else ((nRows * rowIndex) + (colIndex + 1))) + + + let xaxis,yaxis,layout = + let layout = GenericChart.getLayout gChart + let xAxisName, yAxisName = StyleParam.AxisId.X 1 |> StyleParam.AxisId.toString, StyleParam.AxisId.Y 1 |> StyleParam.AxisId.toString + + let updateXAxis index domain axis = + axis |> Axis.LinearAxis.style(Anchor=StyleParam.AxisAnchorId.X index,Domain=StyleParam.Range.MinMax domain) + + let updateYAxis index domain axis = + axis |> Axis.LinearAxis.style(Anchor=StyleParam.AxisAnchorId.Y index,Domain=StyleParam.Range.MinMax domain) + match (layout.TryGetTypedValue xAxisName),(layout.TryGetTypedValue yAxisName) with + | Some x, Some y -> + // remove axis + DynObj.remove layout xAxisName + DynObj.remove layout yAxisName + + x |> updateXAxis newXIndex xdomain, + y |> updateYAxis newYIndex ydomain, + layout + + | Some x, None -> + // remove x - axis + DynObj.remove layout xAxisName + + x |> updateXAxis newXIndex xdomain, + Axis.LinearAxis.init(Anchor=StyleParam.AxisAnchorId.Y newYIndex ,Domain=StyleParam.Range.MinMax ydomain), + layout + + | None, Some y -> + // remove y - axis + DynObj.remove layout yAxisName + + Axis.LinearAxis.init(Anchor=StyleParam.AxisAnchorId.X newXIndex,Domain=StyleParam.Range.MinMax xdomain), + y |> updateYAxis newYIndex ydomain, + layout + | None, None -> + Axis.LinearAxis.init(Anchor=StyleParam.AxisAnchorId.X newXIndex,Domain=StyleParam.Range.MinMax xdomain), + Axis.LinearAxis.init(Anchor=StyleParam.AxisAnchorId.Y newYIndex,Domain=StyleParam.Range.MinMax ydomain), + layout + + gChart + |> GenericChart.setLayout layout + |> Chart.withAxisAnchor(X=newXIndex,Y=newYIndex) + |> Chart.withX_Axis(xaxis,newXIndex) + |> Chart.withY_Axis(yaxis,newYIndex) + ) + ) + |> Seq.map Chart.Combine + |> Chart.Combine + |> Chart.withLayoutGrid( + LayoutGrid.init( + Rows=nRows,Columns=nCols,XGap= xGap,YGap= yGap,Pattern=pattern,RowOrder=rowOrder + ) + ) + + ///Creates a chart stack from the input charts by stacking them on top of each other starting from the first chart. + /// + ///Parameters: + /// + ///sharedAxis : wether the stack has a shared x axis (default:true) + [] + static member SingleStack (charts:#seq, + [] ?sharedXAxis:bool) = + + let sharedAxis = defaultArg sharedXAxis true + let singleCol = seq { + for i = 0 to ((Seq.length charts) - 1) do + yield seq {Seq.item i charts} + } + Chart.Grid(gCharts = singleCol, sharedAxes = sharedAxis, rowOrder = StyleParam.LayoutGridRowOrder.BottomToTop) /// Create a combined chart with the given charts merged + [] [] static member Stack ( [] ?Columns:int, [] ?Space) = diff --git a/src/FSharp.Plotly/FSharp.Plotly.fsproj b/src/FSharp.Plotly/FSharp.Plotly.fsproj index 792e5ab1b..1813ee51f 100644 --- a/src/FSharp.Plotly/FSharp.Plotly.fsproj +++ b/src/FSharp.Plotly/FSharp.Plotly.fsproj @@ -49,6 +49,7 @@ + @@ -57,6 +58,7 @@ + diff --git a/src/FSharp.Plotly/Layout.fs b/src/FSharp.Plotly/Layout.fs index 1785db525..9b29a9b8e 100644 --- a/src/FSharp.Plotly/Layout.fs +++ b/src/FSharp.Plotly/Layout.fs @@ -388,4 +388,23 @@ type Layout() = layout ) + static member SetLayoutGrid + ( + grid: LayoutGrid + ) = + (fun (layout:Layout) -> + grid |> DynObj.setValue layout "grid" + layout + ) + + + static member GetLayoutGrid + ( + grid: LayoutGrid + ) = + (fun (layout:Layout) -> + grid |> DynObj.setValue layout "grid" + layout + ) + diff --git a/src/FSharp.Plotly/LayoutGrid.fs b/src/FSharp.Plotly/LayoutGrid.fs new file mode 100644 index 000000000..22b8f4a74 --- /dev/null +++ b/src/FSharp.Plotly/LayoutGrid.fs @@ -0,0 +1,123 @@ +namespace FSharp.Plotly + +open System + +/// A plot grid that can contain subplots with shared axes. +type LayoutGrid () = + inherit DynamicObj () + + /// Initializes LayoutGrid object + /// + /// + ///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. + /// + ///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. + /// + ///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. + /// + ///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. + /// + ///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. + /// + ///RowOrder : Is the first row the top or the bottom? Note that columns are always enumerated from left to right. + /// + ///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`. + /// + ///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. + /// + ///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. + /// + ///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. + /// + ///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. + /// + ///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. + static member init + ( + ?SubPlots : StyleParam.AxisId [] [], + ?XAxes : StyleParam.AxisId [], + ?YAxes : StyleParam.AxisId [], + ?Rows : int, + ?Columns : int, + ?RowOrder : StyleParam.LayoutGridRowOrder, + ?Pattern : StyleParam.LayoutGridPattern, + ?XGap : float, + ?YGap : float, + ?Domain : Domain, + ?XSide : StyleParam.LayoutGridXSide, + ?YSide : StyleParam.LayoutGridYSide + ) = + LayoutGrid () + |> LayoutGrid.style + ( + ?SubPlots = SubPlots, + ?XAxes = XAxes , + ?YAxes = YAxes , + ?Rows = Rows , + ?Columns = Columns , + ?RowOrder = RowOrder, + ?Pattern = Pattern , + ?XGap = XGap , + ?YGap = YGap , + ?Domain = Domain , + ?XSide = XSide , + ?YSide = YSide + + ) + + // Applies the styles to LayoutGrid() + /// + ///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. + /// + ///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. + /// + ///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. + /// + ///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. + /// + ///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. + /// + ///RowOrder : Is the first row the top or the bottom? Note that columns are always enumerated from left to right. + /// + ///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`. + /// + ///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. + /// + ///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. + /// + ///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. + /// + ///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. + /// + ///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. + static member style + ( + ?SubPlots : StyleParam.AxisId [] [], + ?XAxes : StyleParam.AxisId [], + ?YAxes : StyleParam.AxisId [], + ?Rows : int, + ?Columns : int, + ?RowOrder : StyleParam.LayoutGridRowOrder, + ?Pattern : StyleParam.LayoutGridPattern, + ?XGap : float, + ?YGap : float, + ?Domain : Domain, + ?XSide : StyleParam.LayoutGridXSide, + ?YSide : StyleParam.LayoutGridYSide + ) = + (fun (layoutGrid: LayoutGrid) -> + SubPlots |> DynObj.setValueOptBy layoutGrid "subplots" (Array.map (Array.map StyleParam.AxisId.toString)) + XAxes |> DynObj.setValueOptBy layoutGrid "xaxes" (Array.map StyleParam.AxisId.toString) + YAxes |> DynObj.setValueOptBy layoutGrid "yaxes" (Array.map StyleParam.AxisId.toString) + Rows |> DynObj.setValueOpt layoutGrid "rows" + Columns |> DynObj.setValueOpt layoutGrid "columns" + RowOrder |> DynObj.setValueOptBy layoutGrid "roworder" StyleParam.LayoutGridRowOrder.toString + Pattern |> DynObj.setValueOptBy layoutGrid "pattern" StyleParam.LayoutGridPattern.toString + XGap |> DynObj.setValueOpt layoutGrid "xgap" + YGap |> DynObj.setValueOpt layoutGrid "ygap" + Domain |> DynObj.setValueOpt layoutGrid "domain" + XSide |> DynObj.setValueOptBy layoutGrid "xside" StyleParam.LayoutGridXSide.toString + YSide |> DynObj.setValueOptBy layoutGrid "yside" StyleParam.LayoutGridYSide.toString + + layoutGrid + ) diff --git a/src/FSharp.Plotly/Playground.fsx b/src/FSharp.Plotly/Playground.fsx new file mode 100644 index 000000000..b83cf7eb4 --- /dev/null +++ b/src/FSharp.Plotly/Playground.fsx @@ -0,0 +1,204 @@ +#load "StyleParams.fs" +#load "DynamicObj.fs" +#load "Colors.fs" +#load "Colorbar.fs" +#load "RangeSlider.fs" +#load "Light.fs" +#load "Contours.fs" +#load "Dimensions.fs" +#load "Domain.fs" +#load "Line.fs" +#load "Box.fs" +#load "Meanline.fs" +#load "Marker.fs" +#load "Font.fs" +#load "Hoverlabel.fs" +#load "Axis.fs" +#load "Bins.fs" +#load "Cumulative.fs" +#load "Scene.fs" +#load "Selected.fs" +#load "Shape.fs" +#load "Error.fs" +#load "Table.fs" +#load "Trace.fs" +#load "Trace3d.fs" +#load "LayoutGrid.fs" +#load "Layout.fs" +#load "Config.fs" +#r @"..\..\packages\Newtonsoft.Json\lib\netstandard2.0\Newtonsoft.Json.dll" +#load "GenericChart.fs" +#load "Chart.fs" +#load "ChartExtensions.fs" +#load "CandelstickExtension.fs" +#load "SankeyExtension.fs" +#load "Templates.fs" + +open FSharp.Plotly +open GenericChart + + +let grid ((gCharts:seq<#seq>),sharedAxes:bool,xGap,yGap) = + + let nRows = Seq.length gCharts + let nCols = gCharts |> Seq.maxBy Seq.length |> Seq.length + let pattern = if sharedAxes then StyleParam.LayoutGridPattern.Coupled else StyleParam.LayoutGridPattern.Independent + + let grid = + LayoutGrid.init( + Rows=nRows,Columns=nCols,XGap= xGap,YGap= yGap,Pattern=pattern + ) + + let generateDomainRanges (count:int) (gap:float) = + [|0. .. (1. / (float count)) .. 1.|] + |> fun doms -> + doms + |> Array.windowed 2 + |> Array.mapi (fun i x -> + if i = 0 then + x.[0], (x.[1] - (gap / 2.)) + elif i = (doms.Length - 1) then + (x.[0] + (gap / 2.)),x.[1] + else + (x.[0] + (gap / 2.)) , (x.[1] - (gap / 2.)) + ) + + let yDomains = generateDomainRanges nRows yGap + let xDomains = generateDomainRanges nCols xGap + + gCharts + |> Seq.mapi (fun rowIndex row -> + row |> Seq.mapi (fun colIndex gChart -> + let xdomain = xDomains.[colIndex] + let ydomain = yDomains.[rowIndex] + + let newXIndex, newYIndex = + (if sharedAxes then colIndex + 1 else ((nRows * rowIndex) + (colIndex + 1))), + (if sharedAxes then rowIndex + 1 else ((nRows * rowIndex) + (colIndex + 1))) + + + let xaxis,yaxis,layout = + let layout = GenericChart.getLayout gChart + let xAxisName, yAxisName = StyleParam.AxisId.X 1 |> StyleParam.AxisId.toString, StyleParam.AxisId.Y 1 |> StyleParam.AxisId.toString + + let updateXAxis index domain axis = + axis |> Axis.LinearAxis.style(Anchor=StyleParam.AxisAnchorId.X index,Domain=StyleParam.Range.MinMax domain) + + let updateYAxis index domain axis = + axis |> Axis.LinearAxis.style(Anchor=StyleParam.AxisAnchorId.Y index,Domain=StyleParam.Range.MinMax domain) + match (layout.TryGetTypedValue xAxisName),(layout.TryGetTypedValue yAxisName) with + | Some x, Some y -> + // remove axis + DynObj.remove layout xAxisName + DynObj.remove layout yAxisName + + x |> updateXAxis newXIndex xdomain, + y |> updateYAxis newYIndex ydomain, + layout + + | Some x, None -> + // remove x - axis + DynObj.remove layout xAxisName + + x |> updateXAxis newXIndex xdomain, + Axis.LinearAxis.init(Anchor=StyleParam.AxisAnchorId.Y newYIndex ,Domain=StyleParam.Range.MinMax ydomain), + layout + + | None, Some y -> + // remove y - axis + DynObj.remove layout yAxisName + + Axis.LinearAxis.init(Anchor=StyleParam.AxisAnchorId.X newXIndex,Domain=StyleParam.Range.MinMax xdomain), + y |> updateYAxis newYIndex ydomain, + layout + | None, None -> + Axis.LinearAxis.init(Anchor=StyleParam.AxisAnchorId.X newXIndex,Domain=StyleParam.Range.MinMax xdomain), + Axis.LinearAxis.init(Anchor=StyleParam.AxisAnchorId.Y newYIndex,Domain=StyleParam.Range.MinMax ydomain), + layout + + gChart + |> GenericChart.setLayout layout + |> Chart.withAxisAnchor(X=newXIndex,Y=newYIndex) + |> Chart.withX_Axis(xaxis,newXIndex) + |> Chart.withY_Axis(yaxis,newYIndex) + ) + ) + |> Seq.map Chart.Combine + |> Chart.Combine + |> Chart.withLayoutGrid grid + + +grid ([ + [Chart.Point([(0,1)]);Chart.Point([(0,1)]);Chart.Point([(0,1)]);] + [Chart.Point([(0,1)]);Chart.Point([(0,1)]);Chart.Point([(0,1)]);] + [Chart.Point([(0,1)]);Chart.Point([(0,1)]);Chart.Point([(0,1)]);] +],true, 0.05,0.05) +|> Chart.Show + +let stack ( columns:int, space) = + (fun (charts:#seq) -> + + let col = columns + let len = charts |> Seq.length + let colWidth = 1. / float col + let rowWidth = + let tmp = float len / float col |> ceil + 1. / tmp + let space = + let s = defaultArg space 0.05 + if s < 0. || s > 1. then + printfn "Space should be between 0.0 - 1.0. Automaticaly set to default (0.05)" + 0.05 + else + s + + let contains3d ch = + ch + |> existsTrace (fun t -> + match t with + | :? Trace3d -> true + | _ -> false) + + charts + |> Seq.mapi (fun i ch -> + let colI,rowI,index = (i%col+1), (i/col+1),(i+1) + let xdomain = (colWidth * float (colI-1), (colWidth * float colI) - space ) + let ydomain = (1. - ((rowWidth * float rowI) - space ),1. - (rowWidth * float (rowI-1))) + xdomain) + ) + +let a = + stack (2, None) [Chart.Point([0,1]);Chart.Point([0,1])] + |> Array.ofSeq + +let generateDomainRanges nRows nCols = + + if nCols > 0 && nRows > 0 then + + [0. .. (1. / (float nRows)) .. 1.] + |> List.windowed 2 + |> List.map (fun x -> x.[0], x.[1]) + , + [0. .. (1. / (float nCols)) .. 1.] + |> List.windowed 2 + |> List.map (fun x -> x.[0], x.[1]) + + else failwith "negative amount of rows or columns is stupid." + +generateDomainRanges 8 1 + + + + +[ + Chart.Point([(0,1)]) |> Chart.withY_AxisStyle("This title") + Chart.Point([(0,1)]) + |> Chart.withY_AxisStyle("Must be set",Zeroline=false) + Chart.Point([(0,1)]) + |> Chart.withY_AxisStyle("on the respective charts",Zeroline=false) +] +|> Chart.SingleStack +|> Chart.withLayoutGridStyle(XSide=StyleParam.LayoutGridXSide.Bottom) +|> Chart.withTitle("Hi i am the new SingleStackChart") +|> Chart.withX_AxisStyle("im the shared xAxis") +|> Chart.Show \ No newline at end of file diff --git a/src/FSharp.Plotly/StyleParams.fs b/src/FSharp.Plotly/StyleParams.fs index be9e3f90c..4847bedf3 100644 --- a/src/FSharp.Plotly/StyleParams.fs +++ b/src/FSharp.Plotly/StyleParams.fs @@ -485,6 +485,70 @@ module StyleParam = static member convert = LocationFormat.toString >> box + + /// Determines wether the rows of a LayoutGrid are enumerated from the top or the bottom. + [] + type LayoutGridRowOrder = + |TopToBottom + |BottomToTop + + static member toString = function + |TopToBottom -> "top to bottom" + |BottomToTop -> "bottom to top" + + static member convert = + LayoutGridRowOrder.toString >> box + + /// Pattern to use for autogenerating Axis Ids when not specifically specifying subplot axes IDs in LayoutGrids + [] + type LayoutGridPattern = + /// Uses a new xy pair for each cell, left-to-right across each row then iterating rows according to `roworder` + | Independent + /// Gives one x axis per column and one y axis per row + | Coupled + + static member toString = function + | Independent -> "independent" + | Coupled -> "coupled" + + static member convert = + LayoutGridPattern.toString >> box + + + /// Sets where the x axis labels and titles go on a layout grid. + [] + type LayoutGridXSide = + | Bottom + | BottomPlot + | Top + | TopPlot + + static member toString = function + | Bottom -> "bottom" + | BottomPlot -> "bottom plot" + | Top -> "top" + | TopPlot -> "top plot" + + static member convert = LayoutGridXSide.toString >> box + + /// Sets where the y axis labels and titles go on a layout grid. + [] + type LayoutGridYSide = + | Left + | LeftPlot + | Right + | RightPlot + + static member toString = function + | Left -> "left" + | LeftPlot -> "left plot" + | Right -> "right" + | RightPlot -> "right plot" + + static member convert = LayoutGridYSide.toString >> box + + + //-------------------------- // #M# //-------------------------- @@ -627,6 +691,8 @@ module StyleParam = | MinMax (min,max) -> box [|min;max|] | Values arr -> box arr + + //-------------------------- // #S# //--------------------------