├── Icon.png ├── tests ├── GenApi.Plotly.Tests │ ├── Program.fs │ ├── GenApi.Plotly.Tests.fsproj │ └── Tests.fs ├── IPlot.HighCharts.Tests │ ├── Program.fs │ ├── IPlot.HighCharts.Tests.fsproj │ └── ChartProp.Tests.fs ├── GenApi.HighCharts.Tests │ ├── Program.fs │ ├── GenApi.HighCharts.Tests.fsproj │ └── Tests.fs ├── IPlot.Plotly.Tests │ ├── Program.fs │ ├── IPlot.Plotly.Tests.fsproj │ └── ChartProp.Tests.fs └── NotebookTest.ipynb ├── src ├── IPlot │ ├── Common.fs │ ├── IPlot.fsproj │ └── HighCharts.Chart.fs ├── IPlot.HighCharts │ ├── IArrayProp.cs │ ├── IPlot.HighCharts.csproj │ ├── ModuleLoading.cs │ ├── BaseElements │ │ └── Trace.cs │ ├── ChartElement.cs │ ├── ChartProp.cs │ ├── ReadMe.md │ ├── Html.cs │ └── HighChartsChart.cs ├── IPlot.Plotly │ ├── BaseProps │ │ ├── IArrayProp.cs │ │ ├── Chart_IProp.cs │ │ ├── Traces_IArrayProp.cs │ │ ├── ChartProp.cs │ │ └── Trace_IProp.cs │ ├── BaseElements │ │ └── Trace.cs │ ├── IPlot.Plotly.csproj │ ├── ChartElement.cs │ ├── Html.cs │ ├── ReadMe.md │ └── PlotlyChart.cs └── IPlot.Interactive │ ├── IPlot.Interactive.fsproj │ └── Extension.fs ├── tools ├── GenApi │ ├── Program.fs │ └── GenApi.fsproj ├── GenApi.Common │ ├── GenApi.Common.fsproj │ └── Utils.fs ├── GenApi.Plotly │ ├── GenApi.Plotly.fsproj │ ├── GenTrace.fsx │ ├── Property.fs │ ├── ElementType.fs │ ├── Hacks.fs │ ├── Gen.fs │ └── Templates.fs └── GenApi.HighCharts │ ├── GenApi.HighCharts.fsproj │ ├── Hacks.fs │ ├── DictUtils.fs │ ├── GenTrace.fsx │ ├── Property.fs │ └── Templates.fs ├── gensrc.cmd ├── runtests.cmd ├── .gitignore ├── pack.cmd ├── LICENSE └── ReadMe.md /Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malisimo/IPlot/HEAD/Icon.png -------------------------------------------------------------------------------- /tests/GenApi.Plotly.Tests/Program.fs: -------------------------------------------------------------------------------- 1 | module Program = let [] main _ = 0 2 | -------------------------------------------------------------------------------- /src/IPlot/Common.fs: -------------------------------------------------------------------------------- 1 | namespace IPlot 2 | 3 | module internal Common = 4 | let (!<) v = 5 | System.Nullable(v) 6 | -------------------------------------------------------------------------------- /tools/GenApi/Program.fs: -------------------------------------------------------------------------------- 1 | [] 2 | let main argv = 3 | GenApi.Plotly.Gen.go() 4 | GenApi.HighCharts.Gen.go() 5 | 0 6 | -------------------------------------------------------------------------------- /tests/IPlot.HighCharts.Tests/Program.fs: -------------------------------------------------------------------------------- 1 | module Program 2 | 3 | [] 4 | let main _ = 5 | IPlot.HighCharts.Tests.``Saving images``.``Save Basic Line Plot``() 6 | 0 7 | -------------------------------------------------------------------------------- /tests/GenApi.HighCharts.Tests/Program.fs: -------------------------------------------------------------------------------- 1 | module Program 2 | 3 | [] 4 | let main _ = 5 | Tests.``Implementation Tests``.``Gathers all parent plotOption properties``() 6 | 0 7 | -------------------------------------------------------------------------------- /tests/IPlot.Plotly.Tests/Program.fs: -------------------------------------------------------------------------------- 1 | module Program 2 | 3 | [] 4 | let main _ = 5 | IPlot.Plotly.Tests.``Scatter properties``.``Multiple Time Series Line Plot (DateTimes)``() 6 | 0 7 | -------------------------------------------------------------------------------- /src/IPlot.HighCharts/IArrayProp.cs: -------------------------------------------------------------------------------- 1 | namespace IPlot.HighCharts 2 | { 3 | /// Interface for array chart elements 4 | public interface IArrayProp 5 | { 6 | /// Last accessed array index 7 | int Index { get; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/IPlot.Plotly/BaseProps/IArrayProp.cs: -------------------------------------------------------------------------------- 1 | namespace IPlot.Plotly 2 | { 3 | /// Interface for array chart elements 4 | public interface IArrayProp 5 | { 6 | /// Last accessed array index 7 | int Index { get; } 8 | } 9 | } -------------------------------------------------------------------------------- /gensrc.cmd: -------------------------------------------------------------------------------- 1 | dotnet build .\tools\GenApi.Common\GenApi.Common.fsproj 2 | dotnet build .\tools\GenApi.Plotly\GenApi.Plotly.fsproj 3 | dotnet build .\tools\GenApi.HighCharts\GenApi.HighCharts.fsproj 4 | dotnet build .\tools\GenApi\GenApi.fsproj 5 | 6 | dotnet run --project .\tools\GenApi\GenApi.fsproj -------------------------------------------------------------------------------- /tools/GenApi.Common/GenApi.Common.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /runtests.cmd: -------------------------------------------------------------------------------- 1 | dotnet build .\tests\IPlot.Plotly.Tests\IPlot.Plotly.Tests.fsproj 2 | dotnet build .\tests\IPlot.HighCharts.Tests\IPlot.HighCharts.Tests.fsproj 3 | 4 | dotnet test .\tests\IPlot.Plotly.Tests\IPlot.Plotly.Tests.fsproj 5 | dotnet test .\tests\IPlot.HighCharts.Tests\IPlot.HighCharts.Tests.fsproj -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | lib/ 4 | .ionide/ 5 | .vscode/ 6 | 7 | *.nupkg 8 | 9 | src/IPlot.Plotly/Elements/ 10 | src/IPlot.Plotly/Props/ 11 | src/IPlot.HighCharts/Elements/ 12 | src/IPlot.HighCharts/Props/ 13 | 14 | tools/GenApi.Plotly/Plotly.Doc.json 15 | tools/GenApi.Plotly/Plotly.Layout.json 16 | tools/GenApi.HighCharts/HighCharts.Doc.json 17 | 18 | push.cmd -------------------------------------------------------------------------------- /tools/GenApi/GenApi.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /pack.cmd: -------------------------------------------------------------------------------- 1 | dotnet build .\src\IPlot.Plotly\IPlot.Plotly.csproj -c Release 2 | dotnet build .\src\IPlot.HighCharts\IPlot.HighCharts.csproj -c Release 3 | dotnet build .\src\IPlot\IPlot.fsproj -c Release 4 | dotnet build .\src\IPlot.Interactive\IPlot.Interactive.fsproj -c Release 5 | 6 | del .\pkg\*.nupkg 7 | 8 | dotnet pack .\src\IPlot.Plotly\IPlot.Plotly.csproj -c Release -o pkg 9 | dotnet pack .\src\IPlot.HighCharts\IPlot.HighCharts.csproj -c Release -o pkg 10 | dotnet pack .\src\IPlot\IPlot.fsproj -c Release -o pkg 11 | dotnet pack .\src\IPlot.Interactive\IPlot.Interactive.fsproj -c Release -o pkg -------------------------------------------------------------------------------- /src/IPlot.Plotly/BaseProps/Chart_IProp.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace IPlot.Plotly 4 | { 5 | /// Property helper for the base Chart object 6 | public class Chart_IProp : ChartProp 7 | { 8 | /// Array of traces associated with the chart (each can be a different Trace type) 9 | public Traces_IArrayProp traces 10 | { 11 | get => new Traces_IArrayProp() { _parent = this }; 12 | } 13 | 14 | /// The visual layout of the chart 15 | public Layout_IProp layout 16 | { 17 | get => new Layout_IProp() { _parent = this }; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /tools/GenApi.Plotly/GenApi.Plotly.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /tools/GenApi.HighCharts/GenApi.HighCharts.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /tools/GenApi.HighCharts/Hacks.fs: -------------------------------------------------------------------------------- 1 | module Hacks 2 | 3 | open Property 4 | 5 | let filterTraceChildProperties (props:Property list) = 6 | props 7 | |> List.filter (fun p -> p.name <> "id") 8 | |> List.filter (fun p -> p.name <> "index") 9 | |> List.filter (fun p -> p.name <> "name") 10 | |> List.filter (fun p -> p.name <> "mapData") 11 | |> List.filter (fun p -> p.name <> "legendIndex") 12 | |> List.filter (fun p -> p.name <> "stack") 13 | |> List.filter (fun p -> p.name <> "xAxis") 14 | |> List.filter (fun p -> p.name <> "yAxis") 15 | |> List.filter (fun p -> p.name <> "zIndex") 16 | |> List.map (fun p -> if p.name = "data" then { p with name = "data_obj" } else p) -------------------------------------------------------------------------------- /src/IPlot.Plotly/BaseElements/Trace.cs: -------------------------------------------------------------------------------- 1 | namespace IPlot.Plotly 2 | { 3 | 4 | /// Root Trace class, for all trace types 5 | public class Trace : ChartElement 6 | { 7 | 8 | /// The name of the series (appears in legend, tooltip, etc) 9 | public string name { get; set; } 10 | 11 | /// The specific type of series. 12 | public virtual string type_iplot { get; set; } 13 | 14 | /// Deep copy of chart element and all properties 15 | public static void DeepCopy(Trace src, Trace dest) 16 | { 17 | dest.name = src.name; 18 | dest.type_iplot = src.type_iplot; 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/IPlot.Plotly/BaseProps/Traces_IArrayProp.cs: -------------------------------------------------------------------------------- 1 | namespace IPlot.Plotly 2 | { 3 | /// 4 | /// Array of chart traces associated with a single chart 5 | /// (each can be a different Trace type). 6 | /// 7 | public class Traces_IArrayProp : ChartProp, IArrayProp 8 | { 9 | /// Last accessed array index 10 | private int _index; 11 | 12 | /// Last accessed array index 13 | public int Index 14 | { 15 | get => _index; 16 | } 17 | 18 | /// Access specific element in this array 19 | public Trace_IProp this[int i] 20 | { 21 | get 22 | { 23 | _index = i; 24 | return new Trace_IProp() { _parent = this }; 25 | } 26 | set {} 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /tests/GenApi.Plotly.Tests/GenApi.Plotly.Tests.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | false 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/GenApi.HighCharts.Tests/GenApi.HighCharts.Tests.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | false 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/IPlot.Plotly.Tests/IPlot.Plotly.Tests.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | false 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /tests/IPlot.HighCharts.Tests/IPlot.HighCharts.Tests.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | false 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 matt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tools/GenApi.Common/Utils.fs: -------------------------------------------------------------------------------- 1 | module Utils 2 | 3 | let firstCharToLower (str:string) = 4 | match str.Length with 5 | | 0 -> "" 6 | | 1 -> str.ToLower() 7 | | _ -> 8 | System.Char.ToLower str.[0] 9 | |> fun x -> string x + str.Substring 1 10 | 11 | let firstCharToUpper (str:string) = 12 | match str.Length with 13 | | 0 -> "" 14 | | 1 -> str.ToUpper() 15 | | _ -> 16 | System.Char.ToUpper str.[0] 17 | |> fun x -> string x + str.Substring 1 18 | 19 | let makeSafeTypeName name = 20 | let unHyphen (str:string) = 21 | str.Replace("-","_") 22 | 23 | match name with 24 | | "type" -> "type_iplot" 25 | | "end" -> "end_iplot" 26 | | "base" -> "base_iplot" 27 | | "default" -> "default_iplot" 28 | | "params" -> "params_iplot" 29 | | "operator" -> "operator_iplot" 30 | | name -> name 31 | |> unHyphen 32 | 33 | let pathToPropName (path: string list) = 34 | path 35 | |> List.map firstCharToUpper 36 | |> (fun l -> "IProp"::l) 37 | |> List.rev 38 | |> String.concat "_" 39 | 40 | let repStr n str = 41 | seq { for _ in 1..n -> str } 42 | 43 | let surrStr strBefore strAfter n (str:string) = 44 | let surr s = 45 | strBefore + s + strAfter 46 | 47 | [1..n] 48 | |> List.fold (fun s _ -> surr s) str 49 | -------------------------------------------------------------------------------- /tests/IPlot.Plotly.Tests/ChartProp.Tests.fs: -------------------------------------------------------------------------------- 1 | namespace IPlot.Plotly.Tests 2 | 3 | open System 4 | open Xunit 5 | open IPlot.Plotly 6 | 7 | module ``Type coercion`` = 8 | type PropertyHolder() = 9 | member val PropBool:Nullable = Nullable() with get, set 10 | member val PropInt:Nullable = Nullable() with get, set 11 | member val PropFloat:Nullable = Nullable() with get, set 12 | member val PropString:String = Unchecked.defaultof with get, set 13 | 14 | [] 15 | let ``Zero to bool`` () = 16 | let p = PropertyHolder() 17 | 18 | p.PropBool <- ChartProp.SafeConvert(p.PropBool, 0) 19 | Assert.Equal(false, p.PropBool.Value) 20 | 21 | [] 22 | let ``One to bool`` () = 23 | let p = PropertyHolder() 24 | 25 | p.PropBool <- ChartProp.SafeConvert(p.PropBool, 1) 26 | Assert.Equal(true, p.PropBool.Value) 27 | 28 | [] 29 | let ``False to int`` () = 30 | let p = PropertyHolder() 31 | 32 | p.PropInt <- ChartProp.SafeConvert(p.PropInt, false) 33 | Assert.Equal(0, p.PropInt.Value) 34 | 35 | [] 36 | let ``True to int`` () = 37 | let p = PropertyHolder() 38 | 39 | p.PropInt <- ChartProp.SafeConvert(p.PropInt, true) 40 | Assert.Equal(1, p.PropInt.Value) 41 | -------------------------------------------------------------------------------- /tests/IPlot.HighCharts.Tests/ChartProp.Tests.fs: -------------------------------------------------------------------------------- 1 | namespace IPlot.HighCharts.Tests 2 | 3 | open System 4 | open Xunit 5 | open IPlot.HighCharts 6 | 7 | module ``Type coercion`` = 8 | type PropertyHolder() = 9 | member val PropBool:Nullable = Nullable() with get, set 10 | member val PropInt:Nullable = Nullable() with get, set 11 | member val PropFloat:Nullable = Nullable() with get, set 12 | member val PropString:String = Unchecked.defaultof with get, set 13 | 14 | [] 15 | let ``Zero to bool`` () = 16 | let p = PropertyHolder() 17 | 18 | p.PropBool <- ChartProp.SafeConvert(p.PropBool, 0) 19 | Assert.Equal(false, p.PropBool.Value) 20 | 21 | [] 22 | let ``One to bool`` () = 23 | let p = PropertyHolder() 24 | 25 | p.PropBool <- ChartProp.SafeConvert(p.PropBool, 1) 26 | Assert.Equal(true, p.PropBool.Value) 27 | 28 | [] 29 | let ``False to int`` () = 30 | let p = PropertyHolder() 31 | 32 | p.PropInt <- ChartProp.SafeConvert(p.PropInt, false) 33 | Assert.Equal(0, p.PropInt.Value) 34 | 35 | [] 36 | let ``True to int`` () = 37 | let p = PropertyHolder() 38 | 39 | p.PropInt <- ChartProp.SafeConvert(p.PropInt, true) 40 | Assert.Equal(1, p.PropInt.Value) 41 | -------------------------------------------------------------------------------- /src/IPlot.Plotly/IPlot.Plotly.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | IPlot.Plotly 5 | Matt Jones 6 | netstandard2.0 7 | 1.2.0 8 | true 9 | IPlot.Plotly 10 | Component used in IPlot to render Plotly charts 11 | Component used in IPlot to render Plotly charts 12 | false 13 | https://github.com/malisimo/IPlot 14 | Icon.png 15 | Icon.png 16 | iplot;plot;chart;interactive;highcharts;plotly 17 | https://github.com/malisimo/IPlot.git 18 | git 19 | master 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | true 30 | Icon.png 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/IPlot.HighCharts/IPlot.HighCharts.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | IPlot.HighCharts 5 | Matt Jones 6 | netstandard2.0 7 | 1.3.0 8 | true 9 | IPlot.HighCharts 10 | Component used in IPlot to render charts using HighCharts 11 | Component used in IPlot to render charts using HighCharts 12 | false 13 | https://github.com/malisimo/IPlot 14 | Icon.png 15 | Icon.png 16 | iplot;plot;chart;interactive;highcharts;plotly 17 | https://github.com/malisimo/IPlot.git 18 | git 19 | master 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | true 31 | Icon.png 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /tests/GenApi.Plotly.Tests/Tests.fs: -------------------------------------------------------------------------------- 1 | module Tests 2 | 3 | open Xunit 4 | open ElementType 5 | 6 | module ``ElementType Tests`` = 7 | [] 8 | let ``ElementBool from bool`` () = 9 | Assert.Equal(ElementBool, ElementType.FromPropType "boolean" "" None) 10 | 11 | [] 12 | let ``ElementString from sceneid`` () = 13 | Assert.Equal(ElementString, ElementType.FromPropType "sceneid" "" None) 14 | 15 | [] 16 | let ``ElementArray of ElementInt from enumerated`` () = 17 | Assert.Equal(ElementArray(ElementInt), ElementType.FromPropType "enumerated" "" None) 18 | 19 | [] 20 | let ``ElementArray of ElementString from colorlist`` () = 21 | Assert.Equal(ElementArray(ElementString), ElementType.FromPropType "colorlist" "" None) 22 | 23 | [] 24 | let ``ElementArray of ElementFloat from data_array`` () = 25 | Assert.Equal(ElementArray(ElementFloat), ElementType.FromPropType "data_array" "" None) 26 | 27 | [] 28 | let ``Line from line`` () = 29 | Assert.Equal(ElementOther("Line"), ElementType.FromPropType "line" "" None) 30 | 31 | 32 | [] 33 | let ``ToString called on ElementArray of ElementArray of ElementInt`` () = 34 | Assert.Equal("int?[][]", ElementArray(ElementArray(ElementInt)).ToString()) 35 | 36 | [] 37 | let ``ToString called on ElementArray of ElementArray of ElementArray of ElementFloat`` () = 38 | Assert.Equal("float?[][][]", ElementArray(ElementArray(ElementArray(ElementFloat))).ToString()) 39 | -------------------------------------------------------------------------------- /tools/GenApi.Plotly/GenTrace.fsx: -------------------------------------------------------------------------------- 1 | // Script to generate Trace_IProp.cs with all relevent plot types 2 | // NOTE: Run GenApi first, as it requires all Element types to have been generated already! 3 | 4 | let templateClass = @" 5 | namespace IPlot.Plotly 6 | { 7 | /// Base class for all Series types 8 | public class Trace_IProp : ChartProp 9 | {##TRACES## 10 | } 11 | } 12 | " 13 | 14 | let templateProp = @" 15 | /// ##DESCRIPTION## 16 | public ##TRACE##_IProp as##TRACE## 17 | { 18 | get { return new ##TRACE##_IProp() { _parent = _parent }; } 19 | }" 20 | 21 | open System.IO 22 | 23 | let elPath = Path.Combine(__SOURCE_DIRECTORY__,"../../src/IPlot.Plotly/Elements") 24 | let outFile = Path.Combine(__SOURCE_DIRECTORY__,"../../src/IPlot.Plotly/BaseProps/Trace_IProp.cs") 25 | 26 | let traceTypes = 27 | Directory.GetFiles elPath 28 | |> Seq.map (fun f -> (f, File.ReadAllText f)) 29 | |> Seq.filter (fun (_,txt) -> txt.Contains(": Trace")) 30 | |> Seq.map (fun (f,_) -> Path.GetFileNameWithoutExtension f) 31 | 32 | printfn "Found %i trace types" (Seq.length traceTypes) 33 | 34 | let makeDesc t = 35 | sprintf "Cast trace to %s type for setting specific parameters" t 36 | 37 | let propStr = 38 | traceTypes 39 | |> Seq.map (fun t -> 40 | templateProp 41 | .Replace("##TRACE##", t) 42 | .Replace("##DESCRIPTION##", makeDesc t)) 43 | |> String.concat "\n" 44 | 45 | let classStr = 46 | templateClass.Replace("##TRACES##", propStr) 47 | 48 | File.WriteAllText(outFile, classStr) 49 | 50 | printfn "Written %i chars to %s" classStr.Length outFile -------------------------------------------------------------------------------- /src/IPlot/IPlot.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | false 6 | IPlot 7 | 0.0.1 8 | Matt Jones 9 | IPlot 10 | Charting library for generating charts with HighCharts or Plotly 11 | Charting library for generating charts with HighCharts or Plotly 12 | false 13 | https://github.com/malisimo/IPlot 14 | Icon.png 15 | Icon.png 16 | iplot;plot;chart;interactive;highcharts;plotly 17 | https://github.com/malisimo/IPlot.git 18 | git 19 | master 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | true 31 | Icon.png 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | MIT 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /tests/GenApi.HighCharts.Tests/Tests.fs: -------------------------------------------------------------------------------- 1 | module Tests 2 | 3 | open Xunit 4 | open GenApi.HighCharts.Gen.Implementation 5 | open Property 6 | 7 | module ``Implementation Tests`` = 8 | let rec makeProperty name extendsFrom childPropNames = 9 | { 10 | fullType = name 11 | name = name 12 | childProps = childPropNames |> List.map (fun n -> makeProperty n None []) 13 | types = [] 14 | description = "" 15 | elementType = "elType" 16 | isObjectArray = false 17 | baseType = "baseType" 18 | extends = extendsFrom 19 | isRoot = false 20 | isChartSeries = false 21 | } 22 | 23 | [] 24 | let ``Gathers all parent plotOption properties`` () = 25 | let getParentFromPropName (s:string) = 26 | match s.IndexOf('_') with 27 | | i when i > 0 -> 28 | s.Substring(0,i) 29 | | _ -> 30 | "" 31 | 32 | let baseProp = makeProperty "base" None ["base_prop1";"base_prop2"] 33 | let childProp1 = makeProperty "child1" (Some("base")) ["child1_prop1"] 34 | let childProp2 = makeProperty "child2" (Some("child1")) ["child2_prop1"] 35 | let plotOptions = { 36 | makeProperty "plotOptions" None [] with childProps = [baseProp; childProp1; childProp2] 37 | } 38 | 39 | let allProps = getInheritedProperties [] plotOptions None "#" "~" "child2" (Some("child2")) 40 | let allPropNames = allProps |> List.map (fun p -> p.name) 41 | printfn "allPropNames: %A" allPropNames 42 | 43 | // Check that each plot option child name appears somewhere in allProps 44 | Assert.False(List.exists (fun c -> List.contains c.name (List.map getParentFromPropName allPropNames) |> not) plotOptions.childProps) 45 | -------------------------------------------------------------------------------- /src/IPlot.Plotly/ChartElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | 5 | namespace IPlot.Plotly 6 | { 7 | /// Root ChartElement class, from which all chart elements (axes, titles, etc) derive 8 | public class ChartElement 9 | { 10 | /// Use reflection to get a property from a property name 11 | public static ChartElement GetElement(string prop, ChartElement el) 12 | { 13 | ChartElement propEl = null; 14 | 15 | if (prop.Contains("#")) 16 | { 17 | int i = prop.IndexOf('#'); 18 | 19 | if (int.TryParse(prop.Substring(i + 1), out int index) && index >= 0) 20 | { 21 | var p = el.GetType().GetProperty(prop.Substring(0, i)); 22 | var arr = p.GetValue(el) as IEnumerable; 23 | 24 | if ((arr != null) && arr.Count() > index) 25 | return arr.ElementAt(index); 26 | } 27 | } 28 | else 29 | { 30 | var p = el.GetType().GetProperty(prop); 31 | 32 | if (p != null) 33 | { 34 | // Create a new instance on el if proprty value is null 35 | if (p.GetValue(el) == null) 36 | { 37 | var newEl = (ChartElement)Activator.CreateInstance(p.PropertyType); 38 | p.SetValue(el, newEl); 39 | } 40 | 41 | propEl = p.GetValue(el) as ChartElement; 42 | } 43 | } 44 | 45 | return propEl; 46 | } 47 | 48 | /// Base implementation of deep clone for this element and all properties 49 | public virtual ChartElement DeepClone() 50 | { 51 | return new ChartElement(); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /tools/GenApi.HighCharts/DictUtils.fs: -------------------------------------------------------------------------------- 1 | module DictUtils 2 | 3 | open System.Collections.Generic 4 | open Newtonsoft.Json 5 | open Newtonsoft.Json.Linq 6 | 7 | 8 | let tryDeserialiseDict str = 9 | if System.Text.RegularExpressions.Regex.Match(str, @"^\s{+\s*}+\s$").Success then 10 | printfn "Found empty class - empty dict" 11 | new Dictionary() 12 | else 13 | try 14 | JsonConvert.DeserializeObject(str, typeof>) 15 | :?> Dictionary 16 | with _ -> 17 | printfn "Exception deserialising dict - empty dict" 18 | new Dictionary() 19 | 20 | let getObjectPropFromDict propName (propertyDict: IDictionary) = 21 | if propertyDict.ContainsKey propName then 22 | match propertyDict.[propName] with 23 | | :? JObject as o -> 24 | o.ToObject>() |> Some 25 | | _ -> 26 | None 27 | else 28 | None 29 | 30 | let getArrayPropFromDict propName (propertyDict: IDictionary) = 31 | if propertyDict.ContainsKey propName then 32 | match propertyDict.[propName] with 33 | | :? JArray as a -> 34 | a.Children() |> Seq.map (fun el -> el :> obj) |> Some 35 | | _ -> 36 | None 37 | else 38 | None 39 | 40 | let getBooleanPropFromDict propName (propertyDict: IDictionary) = 41 | if propertyDict.ContainsKey propName then 42 | match propertyDict.[propName] with 43 | | :? System.Boolean as b -> 44 | Some b 45 | | _ -> 46 | None 47 | else 48 | None 49 | 50 | let getStringPropFromDict propName (propertyDict: IDictionary) = 51 | if propertyDict.ContainsKey propName then 52 | string propertyDict.[propName] |> Some 53 | else 54 | None -------------------------------------------------------------------------------- /tools/GenApi.HighCharts/GenTrace.fsx: -------------------------------------------------------------------------------- 1 | // Script to generate Trace_IProp.cs with all relevent plot types 2 | // NOTE: Run GenApi first, as it requires all Element types to have been generated already! 3 | 4 | let templateClass = @" 5 | namespace IPlot.HighCharts 6 | { 7 | /// Base class for all Trace types 8 | public class Trace_IProp : ChartProp 9 | {##TRACES## 10 | } 11 | } 12 | " 13 | 14 | let templateProp = @" 15 | /// ##DESCRIPTION## 16 | public ##TRACE## as##TRACESHORT## 17 | { 18 | get { return new ##TRACE##() { _parent = _parent }; } 19 | }" 20 | 21 | open System.IO 22 | 23 | let elPath = Path.Combine(__SOURCE_DIRECTORY__,"../../src/IPlot.HighCharts/Elements") 24 | let propPath = Path.Combine(__SOURCE_DIRECTORY__,"../../src/IPlot.HighCharts/Props") 25 | let outFile = Path.Combine(__SOURCE_DIRECTORY__,"../../src/IPlot.HighCharts/BaseProps/Trace_IProp.cs") 26 | 27 | let makeTraceType t = 28 | "HighChart_Series_" + t + "_IProp" 29 | 30 | let makeDesc t = 31 | sprintf "Cast trace to %s type for setting specific parameters" t 32 | 33 | let traceTypes = 34 | Directory.GetFiles elPath 35 | |> Seq.map (fun f -> (f, File.ReadAllText f)) 36 | |> Seq.filter (fun (_,txt) -> txt.Contains(": Trace")) 37 | |> Seq.filter (fun (f,_) -> 38 | File.Exists( 39 | Path.Combine(propPath, makeTraceType (Path.GetFileNameWithoutExtension f) + ".cs"))) 40 | |> Seq.map (fun (f,_) -> Path.GetFileNameWithoutExtension f) 41 | 42 | printfn "Found %i trace types" (Seq.length traceTypes) 43 | 44 | let propStr = 45 | traceTypes 46 | |> Seq.map (fun t -> 47 | templateProp 48 | .Replace("##TRACE##", makeTraceType t) 49 | .Replace("##TRACESHORT##", t) 50 | .Replace("##DESCRIPTION##", makeDesc t)) 51 | |> String.concat "\n" 52 | 53 | let classStr = 54 | templateClass.Replace("##TRACES##", propStr) 55 | 56 | File.WriteAllText(outFile, classStr) 57 | 58 | printfn "Written %i chars to %s" classStr.Length outFile -------------------------------------------------------------------------------- /tests/NotebookTest.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Notebook Tests\n", 8 | "\n", 9 | "Simple interactive notebook examples showing use of Plotly and HighCharts rendered inline.\n", 10 | "\n", 11 | "> Note: this notebook requires the [.NET Interactive](https://github.com/dotnet/interactive) runtime installed." 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 1, 17 | "metadata": { 18 | "dotnet_interactive": { 19 | "language": "fsharp" 20 | } 21 | }, 22 | "source": [ 23 | "//#i \"nuget: /pkg\" // Uncomment this line for local testing\n", 24 | "#r \"nuget: IPlot, 0.0.1\"\n", 25 | "#r \"nuget: IPlot.Interactive, 0.0.1\"" 26 | ], 27 | "outputs": [] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": 1, 32 | "metadata": { 33 | "dotnet_interactive": { 34 | "language": "fsharp" 35 | } 36 | }, 37 | "source": [ 38 | "open IPlot.Plotly\n", 39 | "let xs = [ -20f..20f ]\n", 40 | "let ys = [ for x in xs do x ** 2f ]\n", 41 | "\n", 42 | "(xs, ys)\n", 43 | "||> List.zip\n", 44 | "|> Chart.Scatter" 45 | ], 46 | "outputs": [] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": 1, 51 | "metadata": { 52 | "dotnet_interactive": { 53 | "language": "fsharp" 54 | } 55 | }, 56 | "source": [ 57 | "open IPlot.HighCharts\n", 58 | "\n", 59 | "[1.2;3.1;0.4]\n", 60 | "|> Chart.Line" 61 | ], 62 | "outputs": [] 63 | } 64 | ], 65 | "metadata": { 66 | "kernelspec": { 67 | "display_name": ".NET (C#)", 68 | "language": "C#", 69 | "name": ".net-csharp" 70 | }, 71 | "language_info": { 72 | "file_extension": ".cs", 73 | "mimetype": "text/x-csharp", 74 | "name": "C#", 75 | "pygments_lexer": "csharp", 76 | "version": "8.0" 77 | } 78 | }, 79 | "nbformat": 4, 80 | "nbformat_minor": 4 81 | } -------------------------------------------------------------------------------- /tools/GenApi.Plotly/Property.fs: -------------------------------------------------------------------------------- 1 | module Property 2 | 3 | open System.Collections.Generic 4 | open Utils 5 | open Templates 6 | open ElementType 7 | 8 | type Property = 9 | { 10 | name: string 11 | value : obj option 12 | ``type``: ElementType 13 | description: string 14 | elementType: string 15 | isElementArray: bool 16 | rootElement:string option 17 | traceType:string option 18 | fullType: string 19 | } 20 | with 21 | static member FromString curPath elementType isElementArray rootElement traceType name value = 22 | { 23 | name = name 24 | value = Some value 25 | ``type`` = ElementString 26 | description = "" 27 | elementType = elementType 28 | isElementArray = isElementArray 29 | rootElement = rootElement 30 | traceType = traceType 31 | fullType = pathToPropName curPath 32 | } 33 | 34 | static member FromObj curPath elementType isElementArray rootElement traceType name (value: Dictionary) = 35 | { 36 | name = name 37 | value = None 38 | ``type`` = ElementType.FromPropType (value.["valType"].ToString()) name traceType 39 | description = if value.ContainsKey "description" then value.["description"].ToString() else "" 40 | elementType = elementType 41 | isElementArray = isElementArray 42 | rootElement = rootElement 43 | traceType = traceType 44 | fullType = pathToPropName curPath 45 | } 46 | 47 | static member ToPropertyTokens (prop: Property) : Templates.PropertyTokens = 48 | { 49 | Description = prop.description 50 | PropertyName = prop.name 51 | PropertyNullableType = prop.``type``.ToNullableTypeString() 52 | PropertyType = prop.``type``.ToTypeString() 53 | FullType = prop.fullType 54 | IsBaseType = prop.``type``.IsBaseType() 55 | } 56 | 57 | static member IsValid (prop: Property) = 58 | if prop.``type`` = ElementIgnore then 59 | false 60 | else 61 | prop.elementType <> "_deprecated" -------------------------------------------------------------------------------- /src/IPlot.Plotly/Html.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace IPlot.Plotly 5 | { 6 | /// Class containing HTML templates and browser display code 7 | public class Html 8 | { 9 | /// Plotly source URL 10 | public const string DefaultPlotlySrc = "https://cdn.plot.ly/plotly-latest.min.js"; 11 | 12 | /// HTML template for full page 13 | public const string pageTemplate = 14 | @" 15 | 16 | 17 | 18 | 19 | 20 | 21 | [CHART] 22 | 23 | "; 24 | 25 | /// Inline HTML 26 | public const string inlineTemplate = 27 | @"
28 | "; 31 | 32 | /// Javascript plotting code only 33 | public const string jsTemplate = 34 | @""; 37 | 38 | /// Template for Plotly plotting code 39 | public const string jsFunctionTemplate = 40 | @"var data = [DATA]; 41 | var layout = [LAYOUT]; 42 | Plotly.newPlot('[ID]', data, layout);"; 43 | 44 | /// Display given html markup in default browser 45 | public static void showInBrowser(string html, string pageId) 46 | { 47 | var tempPath = Path.GetTempPath(); 48 | var file = $"{pageId}.html"; 49 | var path = Path.Combine(tempPath, file); 50 | File.WriteAllText(path, html); 51 | 52 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 53 | { 54 | var psi = new System.Diagnostics.ProcessStartInfo(path); 55 | psi.UseShellExecute = true; 56 | System.Diagnostics.Process.Start(psi); 57 | } 58 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) 59 | System.Diagnostics.Process.Start("xdg-open", path); 60 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) 61 | System.Diagnostics.Process.Start("open", path); 62 | else 63 | throw new System.Exception("Invalid operation: Not supported OS platform"); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /src/IPlot.HighCharts/ModuleLoading.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace IPlot.HighCharts 5 | { 6 | /// Class to group utility functions for module loading and dependency management 7 | public static class ModuleLoading 8 | { 9 | /// List of required dependencies for each series type 10 | public static readonly Dictionary> SeriesDependencies = new Dictionary>() { 11 | { "bullet", new string[]{"bullet"} }, 12 | { "bellcurve", new string[]{"histogram-bellcurve"} }, 13 | { "cylinder", new string[]{"cylinder"} }, 14 | { "funnel3d", new string[]{"cylinder","funnel3d"} }, 15 | { "pyramid3d", new string[]{"cylinder","funnel3d","pyramid3d"} }, 16 | { "dependencywheel", new string[]{"dependency-wheel"} }, 17 | { "dumbbell", new string[]{"dumbbell"} }, 18 | { "funnel", new string[]{"funnel"} }, 19 | { "heatmap", new string[]{"heatmap"} }, 20 | { "histogram", new string[]{"histogram-bellcurve"} }, 21 | { "lollipop", new string[]{"dumbbell","lollipop"} }, 22 | { "networkgraph", new string[]{"networkgraph"} }, 23 | { "organization", new string[]{"organization"} }, 24 | { "sankey", new string[]{"sankey"} }, 25 | { "streamgraph", new string[]{"streamgraph"} }, 26 | { "sunburst", new string[]{"sunburst"} }, 27 | { "tilemap", new string[]{"heatmap","tilemap"} }, 28 | { "timeline", new string[]{"timeline"} }, 29 | { "treemap", new string[]{"treemap"} }, 30 | { "variablepie", new string[]{"variable-pie"} }, 31 | { "variwide", new string[]{"variwide"} }, 32 | { "vector", new string[]{"vector"} }, 33 | { "venn", new string[]{"venn"} }, 34 | { "windbarb", new string[]{"windbarb"} }, 35 | { "wordcloud", new string[]{"wordcloud"} }, 36 | { "xrange", new string[]{"xrange"} } 37 | }; 38 | 39 | /// Return list of dependencies for given series type 40 | public static IEnumerable GetDependencies(string seriesName) 41 | { 42 | if (SeriesDependencies.ContainsKey(seriesName)) 43 | return SeriesDependencies[seriesName]; 44 | 45 | return Enumerable.Empty(); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/IPlot.Interactive/IPlot.Interactive.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | Library 6 | $(NoWarn);NU5100 7 | true 8 | true 9 | IPlot.Interactive 10 | 0.0.1 11 | Matt Jones 12 | IPlot.Interactive 13 | A .NET Interactive Kernel Extension for generating charts with HighCharts or Plotly 14 | A .NET Interactive Kernel Extension for generating charts with HighCharts or Plotly 15 | Charting library for generating charts with HighCharts or Plotly 16 | false 17 | https://github.com/malisimo/IPlot 18 | MIT 19 | Icon.png 20 | https://github.com/malisimo/IPlot/ 21 | Icon.png 22 | iplot;plot;chart;interactive;highcharts;plotly 23 | https://github.com/malisimo/IPlot.git 24 | git 25 | master 26 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/IPlot.HighCharts/BaseElements/Trace.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace IPlot.HighCharts 5 | { 6 | /// Base class for all Series (trace) types 7 | public class Trace : ChartElement 8 | { 9 | /// An id for the series. This can be used after render time to get a pointer\nto the series object through `chart.get()`. 10 | public string id { get; set; } 11 | /// The index of the series in the chart, affecting the internal index in the\n`chart.series` array, the visible Z index as well as the order in the\nlegend. 12 | public double index { get; set; } 13 | /// The data associated with the series (seq/IEnumerable of floats) 14 | public IEnumerable data { get; set; } 15 | 16 | /// 17 | /// The data matrix associated with the series (seq/IEnumerable of seq/IEnumerable of floats). 18 | /// If plotting X/Y pairs, this is a sequence of pairs, e.g. [ [x1,y1],[x2,y2] ]. 19 | /// 20 | public IEnumerable> data_mat { get; set; } 21 | /// The sequential index of the series in the legend. 22 | public double legendIndex { get; set; } 23 | /// A map data object containing a `path` definition and optionally additional\nproperties to join in the data as per the `joinBy` option. 24 | public string mapData { get; set; } 25 | /// The name associated with the series, displayed in the legend / tooltip. 26 | public string name { get; set; } 27 | /// This option allows grouping series in a stacked chart. The stack option\ncan be a string or anything else, as long as the grouped series' stack\noptions match each other after conversion into a string. 28 | public double stack { get; set; } 29 | /// The specific type of series. 30 | public virtual string type_iplot { get; set; } 31 | /// When using dual or multiple x axes, this number defines which xAxis the\nparticular series is connected to. It refers to either the\n{@link #xAxis.id|axis id}\nor the index of the axis in the xAxis array, with 0 being the first. 32 | public double xAxis { get; set; } 33 | /// When using dual or multiple y axes, this number defines which xAxis the\nparticular series is connected to. It refers to either the\n{@link #yAxis.id|axis id}\nor the index of the axis in the yAxis array, with 0 being the first. 34 | public double yAxis { get; set; } 35 | /// Define the z index of the series. 36 | public double zIndex { get; set; } 37 | 38 | /// Deep copy of chart element and all properties 39 | public static void DeepCopy(Trace src, Trace dest) 40 | { 41 | dest.name = src.name; 42 | dest.id = src.id; 43 | dest.index = src.index; 44 | dest.data = src.data.ToList(); 45 | dest.data_mat = src.data_mat.Select(d => d.ToList()).ToList(); 46 | dest.mapData = src.name; 47 | dest.legendIndex = src.legendIndex; 48 | dest.stack = src.stack; 49 | dest.xAxis = src.xAxis; 50 | dest.yAxis = src.yAxis; 51 | dest.zIndex = src.zIndex; 52 | dest.type_iplot = src.type_iplot; 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/IPlot.Interactive/Extension.fs: -------------------------------------------------------------------------------- 1 | namespace IPlot.Interactive 2 | 3 | open System 4 | open System.Text 5 | open System.Threading.Tasks 6 | open Microsoft.DotNet.Interactive 7 | open Microsoft.DotNet.Interactive.Formatting 8 | open Microsoft.DotNet.Interactive.Http 9 | open IPlot.Plotly 10 | open IPlot.HighCharts 11 | 12 | type FormatterKernelExtension() = 13 | 14 | let getPlotlyScriptElementWithRequire (script: string) = 15 | let newScript = StringBuilder() 16 | newScript.AppendLine("""", String.Empty) 46 | divElem + getPlotlyScriptElementWithRequire js 47 | 48 | let getHighChartsHtml (chart: HighChartsChart) = 49 | let styleStr = $"width: {chart.chart.chart_iplot.width}px; height: {chart.chart.chart_iplot.height}px; background-color: #111;" 50 | let divElem = $"
" 51 | let js = chart.GetInlineJS().Replace("", String.Empty) 52 | divElem + getHighChartsScriptElementWithRequire js 53 | 54 | let registerPlotlyFormatter () = 55 | Formatter.Register 56 | ((fun (chart:PlotlyChart) writer -> 57 | let html = getPlotlyHtml chart 58 | 59 | writer.Write(html)), 60 | HtmlFormatter.MimeType) 61 | 62 | let registerHighChartsFormatter () = 63 | Formatter.Register 64 | ((fun (chart:HighChartsChart) writer -> 65 | let html = getHighChartsHtml chart 66 | 67 | writer.Write(html)), 68 | HtmlFormatter.MimeType) 69 | 70 | interface IKernelExtension with 71 | member _.OnLoadAsync _ = 72 | registerPlotlyFormatter () 73 | registerHighChartsFormatter () 74 | 75 | if isNull KernelInvocationContext.Current |> not then 76 | let message = 77 | "Added Kernel Extension including formatters for Plotly and HighCharts" 78 | 79 | KernelInvocationContext.Current.Display(message, "text/markdown") 80 | |> ignore 81 | 82 | Task.CompletedTask -------------------------------------------------------------------------------- /tools/GenApi.Plotly/ElementType.fs: -------------------------------------------------------------------------------- 1 | module ElementType 2 | open Utils 3 | 4 | type ElementType = 5 | | ElementBool 6 | | ElementInt 7 | | ElementFloat 8 | | ElementString 9 | | ElementDateTime 10 | | ElementArray of ElementType 11 | | ElementOther of string 12 | | ElementIgnore 13 | with 14 | static member FromPropType (typeName: string) (name: string) (traceType: string option)= 15 | match typeName with 16 | | "boolean" -> ElementBool 17 | | "integer" -> ElementInt 18 | | "number" | "angle" -> ElementFloat 19 | | "string" | "flaglist" | "geoid" | "sceneid" | "axisid" | "color" -> ElementString 20 | | "any" | "colorscale" | "subplotid" | "enumerated" -> ElementString 21 | | "data_array" when name = "ids" -> ElementArray(ElementString) 22 | | "data_array" when name = "customdata" -> ElementArray(ElementString) 23 | | "data_array" when name = "ticktext" -> ElementArray(ElementString) 24 | | "data_array" when name = "text" -> ElementArray(ElementString) 25 | | "data_array" when name = "hovertext" -> ElementArray(ElementString) 26 | | "data_array" when name = "colors" -> ElementArray(ElementString) 27 | | "data_array" when name = "color" -> ElementArray(ElementString) 28 | | "data_array" when name = "format" -> ElementArray(ElementString) 29 | | "data_array" when name = "labels" -> ElementArray(ElementString) 30 | | "data_array" when name = "facecolor" -> ElementArray(ElementString) 31 | | "data_array" when name = "vertexcolor" -> ElementArray(ElementString) 32 | | "data_array" when name = "columnorder" -> ElementArray(ElementInt) 33 | | "data_array" when name = "i" -> ElementArray(ElementInt) 34 | | "data_array" when name = "j" -> ElementArray(ElementInt) 35 | | "data_array" when name = "k" -> ElementArray(ElementInt) 36 | | "data_array" when name = "z" && traceType = Some("heatmap") -> ElementArray(ElementArray(ElementFloat)) 37 | | "data_array" when name = "z" && traceType = Some("heatmapgl") -> ElementArray(ElementArray(ElementFloat)) 38 | | "data_array" when name = "z" && traceType = Some("surface") -> ElementArray(ElementArray(ElementFloat)) 39 | | "data_array" when name = "values" && traceType = Some("table") -> ElementArray(ElementArray(ElementFloat)) 40 | | "data_array" when name = "surfacecolor" -> ElementArray(ElementArray(ElementString)) 41 | | "data_array" -> ElementArray(ElementFloat) 42 | | "data_date_array" -> ElementArray(ElementDateTime) 43 | | "data_str_array" -> ElementArray(ElementString) 44 | | "data_str_matrix" -> ElementArray(ElementArray(ElementString)) 45 | | "colorlist" -> ElementArray(ElementString) 46 | | "info_array" -> ElementArray(ElementString) 47 | | "_deprecated" | "_arrayAttrRegexps" -> ElementIgnore 48 | | t -> ElementOther(firstCharToUpper t) 49 | member this.ToNullableTypeString() = 50 | match this with 51 | | ElementBool -> "bool?" 52 | | ElementInt -> "int?" 53 | | ElementFloat -> "double?" 54 | | ElementString -> "string" 55 | | ElementDateTime -> "DateTime" 56 | | ElementArray e -> "IEnumerable<" + e.ToTypeString() + ">" 57 | | ElementOther(el) -> el 58 | | ElementIgnore -> "_" 59 | member this.ToTypeString() = 60 | match this with 61 | | ElementBool -> "bool" 62 | | ElementInt -> "int" 63 | | ElementFloat -> "double" 64 | | ElementString -> "string" 65 | | ElementDateTime -> "DateTime" 66 | | ElementArray e -> "IEnumerable<" + e.ToTypeString() + ">" 67 | | ElementOther(el) -> el 68 | | ElementIgnore -> "_" 69 | member this.IsArray() = 70 | match this with 71 | | ElementArray e -> true 72 | | _ -> false 73 | member this.GetArraySubtype() = 74 | match this with 75 | | ElementArray t -> Some t 76 | | _ -> None 77 | member this.IsBaseType() = 78 | match this with 79 | | ElementBool | ElementInt | ElementFloat | ElementString | ElementDateTime -> true 80 | | ElementArray _ -> true 81 | | _ -> false -------------------------------------------------------------------------------- /src/IPlot.HighCharts/ChartElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | 6 | namespace IPlot.HighCharts 7 | { 8 | /// Root ChartElement class, from which all chart elements (axes, titles, etc) derive 9 | public class ChartElement 10 | { 11 | /// Cast from a general array type to a specific one 12 | static IEnumerable CastArray(IEnumerable array, T example) 13 | { 14 | var castMethod = typeof(Enumerable).GetMethod("Cast", BindingFlags.Static | BindingFlags.Public); 15 | var castGenericMethod = castMethod.MakeGenericMethod(new Type[] { typeof(T) }); 16 | var castArr = castGenericMethod.Invoke(null, new object[]{array}); 17 | return castArr as IEnumerable; 18 | } 19 | 20 | /// Set a property value as array of specific type 21 | static void SetProperty(PropertyInfo p, ChartElement el, IEnumerable arr, Type t) 22 | { 23 | var example = Activator.CreateInstance(t); 24 | dynamic example2 = Convert.ChangeType(example, t); 25 | var castArr = CastArray(arr, example2); 26 | p.SetValue(el, castArr); 27 | } 28 | 29 | /// Use reflection to get a property from a property name 30 | public static ChartElement GetElement(string prop, ChartElement el) 31 | { 32 | ChartElement propEl = null; 33 | 34 | if (prop.Contains("#")) 35 | { 36 | int i = prop.IndexOf('#'); 37 | 38 | if (int.TryParse(prop.Substring(i + 1), out int index) && index >= 0) 39 | { 40 | var p = el.GetType().GetProperty(prop.Substring(0, i)); 41 | var arr = p.GetValue(el) as IEnumerable; 42 | var arrType = p.PropertyType.GetGenericArguments()[0]; 43 | 44 | if (arr == null) 45 | { 46 | var newArr = ((IEnumerable)Array.CreateInstance(p.PropertyType.GetGenericArguments()[0], index + 1)).ToList(); 47 | for (int j = 0; j < (index+1); j++) 48 | newArr[j] = (ChartElement)Activator.CreateInstance(p.PropertyType.GetGenericArguments()[0]); 49 | 50 | arr = newArr; 51 | SetProperty(p, el, arr, arrType); 52 | } 53 | else if (arr.Count() <= index) 54 | { 55 | var newArr = ((IEnumerable)Array.CreateInstance(p.PropertyType.GetGenericArguments()[0], index + 1)).ToList(); 56 | for (int j = 0; j < (index+1); j++) 57 | { 58 | if (j < arr.Count()) 59 | newArr[j] = arr.ElementAt(j); 60 | else 61 | newArr[j] = (ChartElement)Activator.CreateInstance(p.PropertyType.GetGenericArguments()[0]); 62 | } 63 | 64 | arr = newArr; 65 | SetProperty(p, el, arr, arrType); 66 | } 67 | 68 | return arr.ElementAt(index); 69 | } 70 | } 71 | else 72 | { 73 | var p = el.GetType().GetProperty(prop); 74 | 75 | if (p != null) 76 | { 77 | // Create a new instance on el if proprty value is null 78 | if (p.GetValue(el) == null) 79 | { 80 | var newEl = (ChartElement)Activator.CreateInstance(p.PropertyType); 81 | p.SetValue(el, newEl); 82 | } 83 | 84 | propEl = p.GetValue(el) as ChartElement; 85 | } 86 | } 87 | 88 | return propEl; 89 | } 90 | 91 | /// Base implementation of deep clone for this element and all properties 92 | public virtual ChartElement DeepClone() 93 | { 94 | return new ChartElement(); 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /tools/GenApi.Plotly/Hacks.fs: -------------------------------------------------------------------------------- 1 | module Hacks 2 | 3 | open System.Collections.Generic 4 | open Newtonsoft.Json.Linq 5 | 6 | type DataArrayType = 7 | | DateTimeArray 8 | | StringArray 9 | | StringMatrix 10 | with 11 | member this.ValType 12 | with get() = 13 | match this with 14 | | DateTimeArray -> "data_date_array" 15 | | StringArray -> "data_str_array" 16 | | StringMatrix -> "data_str_matrix" 17 | 18 | let addTraceProperties curType (propertyDict:IDictionary) = 19 | let makeDataArrayDict name (da:DataArrayType) = 20 | let jo = JObject() 21 | 22 | [("valType",da.ValType);("description",name + " data array")] 23 | |> List.map (fun (k,v) -> jo.Add(k,JToken.Parse(sprintf "\"%s\"" v))) 24 | |> ignore 25 | 26 | jo 27 | 28 | match curType with 29 | | "scatter" -> 30 | ["xt_";"yt_"] 31 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name DateTimeArray)) 32 | |> ignore 33 | ["xs_";"ys_"] 34 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name StringArray)) 35 | |> ignore 36 | | "scattergl" -> 37 | ["xt_";"yt_"] 38 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name DateTimeArray)) 39 | |> ignore 40 | ["xs_";"ys_"] 41 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name StringArray)) 42 | |> ignore 43 | | "bar" -> 44 | ["xt_";"yt_"] 45 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name DateTimeArray)) 46 | |> ignore 47 | ["xs_";"ys_"] 48 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name StringArray)) 49 | |> ignore 50 | | "box" -> 51 | ["xt_";"yt_"] 52 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name DateTimeArray)) 53 | |> ignore 54 | ["xs_";"ys_"] 55 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name StringArray)) 56 | |> ignore 57 | | "candlestick" -> 58 | ["xt_";"yt_"] 59 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name DateTimeArray)) 60 | |> ignore 61 | ["xs_";"ys_"] 62 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name StringArray)) 63 | |> ignore 64 | | "heatmap" -> 65 | ["xt_";"yt_"] 66 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name DateTimeArray)) 67 | |> ignore 68 | ["xs_";"ys_"] 69 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name StringArray)) 70 | |> ignore 71 | ["zs_"] 72 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name StringMatrix)) 73 | |> ignore 74 | | "heatmapgl" -> 75 | ["xt_";"yt_"] 76 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name DateTimeArray)) 77 | |> ignore 78 | ["xs_";"ys_"] 79 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name StringArray)) 80 | |> ignore 81 | ["zs_"] 82 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name StringMatrix)) 83 | |> ignore 84 | | "surface" -> 85 | ["xt_";"yt_"] 86 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name DateTimeArray)) 87 | |> ignore 88 | ["xs_";"ys_"] 89 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name StringArray)) 90 | |> ignore 91 | ["zs_"] 92 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name StringMatrix)) 93 | |> ignore 94 | | "waterfall" -> 95 | ["xt_";"yt_"] 96 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name DateTimeArray)) 97 | |> ignore 98 | ["xs_";"ys_"] 99 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name StringArray)) 100 | |> ignore 101 | ["zs_"] 102 | |> List.map (fun name -> propertyDict.Add(name,makeDataArrayDict name StringMatrix)) 103 | |> ignore 104 | | _ -> 105 | () 106 | -------------------------------------------------------------------------------- /src/IPlot.Plotly/BaseProps/ChartProp.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace IPlot.Plotly 4 | { 5 | /// Base class for chart property helpers 6 | public class ChartProp 7 | { 8 | /// The parent property 9 | internal ChartProp _parent { get; set; } 10 | 11 | 12 | /// Return the full path of this property, as a heirachy list 13 | public virtual List GetPath() 14 | { 15 | var isArray = false; 16 | var prop = ToPropertyName(this.GetType().Name); 17 | var propIndex = 0; 18 | 19 | if (this is IArrayProp p) 20 | { 21 | propIndex = p.Index; 22 | isArray = true; 23 | } 24 | 25 | if (_parent != null) 26 | { 27 | var props = _parent.GetPath(); 28 | 29 | var isParentArray = _parent is IArrayProp; 30 | 31 | if (isArray) 32 | { 33 | if (isParentArray) 34 | { 35 | if (props.Count > 0) 36 | props[props.Count - 1] = $"#{props[props.Count - 1]}#{propIndex}"; 37 | } 38 | else 39 | props.Add($"{prop}#{propIndex}"); 40 | } 41 | else 42 | { 43 | if (!isParentArray) 44 | props.Add(prop); 45 | } 46 | 47 | return props; 48 | } 49 | 50 | return new List() { }; 51 | } 52 | 53 | /// Convert element type to a property name 54 | public static string ToPropertyName(string typeName) 55 | { 56 | var propName = typeName; 57 | 58 | if (typeName.EndsWith("_IProp")) 59 | propName = typeName.Substring(0, typeName.Length - 6); 60 | else if (typeName.EndsWith("_IArrayProp")) 61 | propName = typeName.Substring(0, typeName.Length - 11); 62 | 63 | var i = propName.LastIndexOf('_'); 64 | if ((i >= 0) && (i < propName.Length-1)) 65 | propName = propName.Substring(i + 1); 66 | 67 | if (propName.Length > 0) 68 | propName = char.ToLower(propName[0]) + propName.Substring(1); 69 | 70 | return propName; 71 | } 72 | 73 | /// Convert a value in as best a way as possible, even if types do not match 74 | public static T SafeConvert(T _, U value) 75 | { 76 | var underlyingType = System.Nullable.GetUnderlyingType(typeof(T)); 77 | 78 | if (underlyingType == null) 79 | { 80 | if (typeof(T) == typeof(string)) 81 | return (T)(object)value.ToString(); 82 | else 83 | return (T)(object)value; 84 | } 85 | else if (underlyingType == typeof(bool)) 86 | { 87 | if (value is bool b) 88 | return (T)(object)new bool?(b); 89 | else if (value is int i) 90 | return (T)(object)new bool?(i > 0); 91 | else if (value is float f) 92 | return (T)(object)new bool?(f > 0.0f); 93 | else if (value is string s) 94 | { 95 | if (s.ToLower() == "true") 96 | return (T)(object)new bool?(true); 97 | else 98 | return (T)(object)new bool?(false); 99 | } 100 | } 101 | else if (underlyingType == typeof(int)) 102 | { 103 | if (value is bool b) 104 | return (T)(object)new int?(b ? 1 : 0); 105 | else if (value is int i) 106 | return (T)(object)new int?(i); 107 | else if (value is float f) 108 | return (T)(object)new int?((int)f); 109 | else if (value is string s) 110 | { 111 | if (int.TryParse(s, out int res)) 112 | return (T)(object)new int?(res); 113 | else 114 | return (T)(object)new int?(0); 115 | } 116 | } 117 | else if (underlyingType == typeof(double)) 118 | { 119 | if (value is bool b) 120 | return (T)(object)new double?(b ? 1.0f : 0.0f); 121 | else if (value is int i) 122 | return (T)(object)new double?((double)i); 123 | else if (value is double d) 124 | return (T)(object)new double?(d); 125 | else if (value is string s) 126 | { 127 | if (double.TryParse(s, out double res)) 128 | return (T)(object)new double?((float)res); 129 | else 130 | return (T)(object)new double?(0.0f); 131 | } 132 | } 133 | 134 | return default(T); 135 | } 136 | } 137 | } -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | # IPlot 2 | 3 | ![IPlot](https://github.com/malisimo/IPlot/raw/main/Icon.png) 4 | 5 | Charting library for .NET, rendered using Plotly or HighCharts in the browser. 6 | 7 | 8 | ![IPlot](https://user-images.githubusercontent.com/24556021/101267338-64526e80-374f-11eb-9eec-ba6804b7c51d.png) 9 | 10 | ## Table of contents 11 | 12 | - [About](#about) 13 | - [Install](#install) 14 | - [Basic Usage](#basic-usage) 15 | - [Plotly API](https://github.com/malisimo/iplot/blob/master/src/IPlot.Plotly/ReadMe.md) 16 | - [HighCharts API](https://github.com/malisimo/iplot/blob/master/src/IPlot.HighCharts/ReadMe.md) 17 | - [Saving Images](#saving-images) 18 | - [Building From Source](#building-from-source) 19 | 20 | # About 21 | 22 | This library aims to provide a fast and fluid way of curating a chart in both C# and F#, where you begin by throwing some data at a chart and later refine its properties to adjust its appearance or behaviour. Intellisense can help discovery of appropriate properties to set, and provide info on the expected arguments via static typing. This can reduce the amount of documentation lookup required to set up a plot and adjust its visual elements. 23 | 24 | > The API is intentionally similar to [XPlot.Plotly](https://fslab.org/XPlot/), where most access is achieved through interacting with the ```Chart``` element. 25 | 26 | You can choose to render charts using Plotly or HighCharts. Either import ```IPlot.Plotly``` or ```IPlot.HighCharts``` - the chart API is very similar. 27 | 28 | # Install 29 | 30 | Install as a nuget package using the dotnet CLI. Whilst in the directory containing your csproj or fsproj file, enter: 31 | 32 | ``` 33 | dotnet add package IPlot --version=0.0.1 34 | ``` 35 | 36 | # Basic Usage 37 | 38 | Using the Ploty API, a basic line chart can be generated as follows: 39 | 40 | ```csharp 41 | using IPlot.Plotly 42 | 43 | var data = new double[] { 0.0, 0.7, 0.4, 1.0 }; 44 | Chart.Line(data) 45 | .WithWidth(1200) 46 | .WithHeight(800) 47 | .WithTitle("Line plot") 48 | .With(Chart.Props.layout.plot_bgcolor("#999")) 49 | .Show(); 50 | ``` 51 | 52 | ...and in F#: 53 | 54 | ```fsharp 55 | open IPlot.Plotly 56 | 57 | [ 0.0; 0.7; 0.4; 1.0] 58 | |> Chart.Line 59 | |> Chart.WithWidth 1200 60 | |> Chart.WithHeight 800 61 | |> Chart.WithTitle "Line Plot" 62 | |> Chart.With(Chart.Props.layout.plot_bgcolor "#999") 63 | |> Chart.Show 64 | ``` 65 | 66 | In HighCharts, this would be: 67 | 68 | ```fsharp 69 | open IPlot.HighCharts 70 | 71 | [ 0.0; 0.7; 0.4; 1.0] 72 | |> Chart.Line 73 | |> Chart.WithWidth 1200 74 | |> Chart.WithHeight 800 75 | |> Chart.WithTitle "Line Plot" 76 | |> Chart.With(Chart.Props.chart_iplot.plotBackgroundColor "#999") 77 | |> Chart.Show 78 | ``` 79 | 80 | ## Unified API 81 | 82 | Regardless of the renderer (Plotly or HighCharts), the workflow is intended to feel the same. Both use the Chart functions to set up and manipulate charts, and both allow use of the Chart.With function to adjust chart element properties. 83 | 84 | ## Chart Functions 85 | 86 | Most functionality can be achieved by using the functions (static methods) contained in the Chart class. There are two main ways of generating a chart: 87 | 88 | * Traces (or *series*) can be created first, and then provided to the ```Chart.Plot``` function, before being shown. 89 | * You can directly use one of the utility methods of Chart (e.g. ```Chart.Cylinder()``` or ```Chart.Heatmap()```) to create the traces, simply passing the data. For example, ```Chart.Area()``` will create a trace of type ```Area``` and avoid the need to instantiate any specific traces or properties. 90 | 91 | ## Property setting 92 | 93 | Once a chart has been created (using either ```Chart.Plot``` or another utility method of the ```Chart``` class) then its properties can be manipulated. 94 | 95 | 96 | Calls to ```Chart.With()``` calls can be chained as follows: 97 | 98 | ```fsharp 99 | open IPlot.Plotly 100 | 101 | [trace1; trace2] 102 | |> Chart.Plot 103 | |> Chart.With (Chart.Props.traces.[0].asScatter.mode "markers") 104 | |> Chart.With (Chart.Props.traces.[0].asScatter.marker.size 12.) 105 | |> Chart.With (Chart.Props.traces.[1].asScatter.line.width 5.0) 106 | |> Chart.With (Chart.Props.traces.[1].asScatter.line.color "#44FF22") 107 | |> Chart.With (Chart.Props.layout.showlegend false) 108 | |> Chart.With (Chart.Props.layout.plot_bgcolor "#334433") 109 | |> Chart.WithWidth 1200 110 | |> Chart.Show 111 | ``` 112 | 113 | In HighCharts the access pattern is the same: 114 | 115 | ```fsharp 116 | open IPlot.HighCharts 117 | 118 | Chart.Cylinder [1.; 2.; 3.; 4.; 3.; 2.; 1.] 119 | |> Chart.With (Chart.Props.chart_iplot.options3d.enabled true) 120 | |> Chart.With (Chart.Props.chart_iplot.options3d.viewDistance 25.) 121 | |> Chart.WithWidth 700 122 | |> Chart.WithHeight 500 123 | |> Chart.Show 124 | ``` 125 | 126 | # Saving Images 127 | 128 | The highCharts API has a function that allows storing the chart as a PNG file locally: 129 | 130 | ```fsharp 131 | open IPlot.HighCharts 132 | 133 | Chart.Line [1.; 3.; 2.; 6.] 134 | |> Chart.WithWidth 700 135 | |> Chart.WithHeight 500 136 | |> Cchart.WithTitle "Image file of line chart" 137 | |> Chart.Save "MyChart.png" 138 | ``` 139 | 140 | # Building From Source 141 | 142 | The library can be built from source on the command line (in order): 143 | 144 | ``` 145 | .\gensrc.cmd 146 | .\buildsrc.cmd 147 | ``` 148 | 149 | All tests can also be run using the following command: 150 | 151 | ``` 152 | .\runtests.cmd 153 | ``` 154 | 155 | The nuget package can be created by running: 156 | 157 | ``` 158 | .\pack.cmd 159 | ``` 160 | 161 | ## .NET Interactive 162 | 163 | IPlot can be used within .NET interactive notebooks by importing IPlot.Interactive. 164 | To get the chart to render in the cell output simply skip the Chart.Show() call at the end: 165 | 166 | ``` 167 | #r "nuget: IPlot, 0.0.1" 168 | #r "nuget: IPlot.Interactive, 0.0.1" 169 | 170 | open IPlot.Plotly 171 | 172 | let xs = [ -20f..20f ] 173 | let ys = [ for x in xs do x ** 2f ] 174 | 175 | (xs, ys) 176 | ||> List.zip 177 | |> Chart.Scatter 178 | ``` 179 | 180 | An a simple example for HighCharts: 181 | 182 | ``` 183 | #r "nuget: IPlot, 0.0.1" 184 | #r "nuget: IPlot.Interactive, 0.0.1" 185 | 186 | open IPlot.HighCharts 187 | 188 | [1.2;3.1;0.4] 189 | |> Chart.Line 190 | ``` 191 | -------------------------------------------------------------------------------- /src/IPlot.HighCharts/ChartProp.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace IPlot.HighCharts 4 | { 5 | /// Base class for chart property helpers 6 | public class ChartProp 7 | { 8 | /// The parent property 9 | internal ChartProp _parent { get; set; } 10 | 11 | /// Return the full path of this property, as a heirachy list 12 | public virtual List GetPath() 13 | { 14 | var isArray = false; 15 | var prop = ToPropertyName(this.GetType().Name); 16 | var propIndex = 0; 17 | 18 | if (this is IArrayProp p) 19 | { 20 | propIndex = p.Index; 21 | isArray = true; 22 | } 23 | 24 | if (_parent != null) 25 | { 26 | var props = _parent.GetPath(); 27 | 28 | var isParentArray = _parent is IArrayProp; 29 | 30 | if (isArray) 31 | { 32 | if (isParentArray) 33 | { 34 | if (props.Count > 0) 35 | props[props.Count - 1] = $"#{props[props.Count - 1]}#{propIndex}"; 36 | } 37 | else 38 | props.Add($"{prop}#{propIndex}"); 39 | } 40 | else 41 | { 42 | if (!isParentArray) 43 | props.Add(prop); 44 | } 45 | 46 | return props; 47 | } 48 | 49 | return new List() { }; 50 | } 51 | 52 | /// Convert element type to a property name 53 | public static string ToPropertyName(string typeName) 54 | { 55 | var propName = typeName.Replace("_iplot","-iplot"); 56 | 57 | if (typeName.EndsWith("_IProp")) 58 | propName = propName.Substring(0, propName.Length - 6); 59 | else if (typeName.EndsWith("_IArrayProp")) 60 | propName = propName.Substring(0, propName.Length - 11); 61 | 62 | 63 | var i = propName.LastIndexOf('_'); 64 | if ((i >= 0) && (i < propName.Length-1)) 65 | propName = propName.Substring(i + 1); 66 | 67 | if (propName.Length > 0) 68 | propName = char.ToLower(propName[0]) + propName.Substring(1); 69 | 70 | return propName.Replace("-iplot","_iplot"); 71 | } 72 | 73 | /// Convert a value in as best a way as possible, even if types do not match 74 | public static T SafeConvert(T _, U value) 75 | { 76 | var underlyingType = System.Nullable.GetUnderlyingType(typeof(T)); 77 | 78 | if (underlyingType == null) 79 | { 80 | if (typeof(T) == typeof(string)) 81 | return (T)(object)value.ToString(); 82 | else 83 | return (T)(object)value; 84 | } 85 | else if (underlyingType == typeof(bool)) 86 | { 87 | if (value is bool b) 88 | return (T)(object)new bool?(b); 89 | else if (value is int i) 90 | return (T)(object)new bool?(i > 0); 91 | else if (value is float f) 92 | return (T)(object)new bool?(f > 0.0f); 93 | else if (value is double d) 94 | return (T)(object)new bool?(d > 0.0); 95 | else if (value is string s) 96 | { 97 | if (s.ToLower() == "true") 98 | return (T)(object)new bool?(true); 99 | else 100 | return (T)(object)new bool?(false); 101 | } 102 | } 103 | else if (underlyingType == typeof(int)) 104 | { 105 | if (value is bool b) 106 | return (T)(object)new int?(b ? 1 : 0); 107 | else if (value is int i) 108 | return (T)(object)new int?(i); 109 | else if (value is float f) 110 | return (T)(object)new int?((int)f); 111 | else if (value is double d) 112 | return (T)(object)new int?((int)d); 113 | else if (value is string s) 114 | { 115 | if (int.TryParse(s, out int res)) 116 | return (T)(object)new int?(res); 117 | else 118 | return (T)(object)new int?(0); 119 | } 120 | } 121 | else if (underlyingType == typeof(float)) 122 | { 123 | if (value is bool b) 124 | return (T)(object)new float?(b ? 1.0f : 0.0f); 125 | else if (value is int i) 126 | return (T)(object)new float?((float)i); 127 | else if (value is float f) 128 | return (T)(object)new float?(f); 129 | else if (value is double d) 130 | return (T)(object)new double?(d); 131 | else if (value is string s) 132 | { 133 | if (double.TryParse(s, out double res)) 134 | return (T)(object)new double?((float)res); 135 | else 136 | return (T)(object)new double?(0.0f); 137 | } 138 | } 139 | else if (underlyingType == typeof(double)) 140 | { 141 | if (value is bool b) 142 | return (T)(object)new double?(b ? 1.0f : 0.0f); 143 | else if (value is int i) 144 | return (T)(object)new double?((double)i); 145 | else if (value is float f) 146 | return (T)(object)new float?(f); 147 | else if (value is double d) 148 | return (T)(object)new double?(d); 149 | else if (value is string s) 150 | { 151 | if (double.TryParse(s, out double res)) 152 | return (T)(object)new double?((float)res); 153 | else 154 | return (T)(object)new double?(0.0f); 155 | } 156 | } 157 | 158 | return default(T); 159 | } 160 | } 161 | } -------------------------------------------------------------------------------- /tools/GenApi.HighCharts/Property.fs: -------------------------------------------------------------------------------- 1 | module Property 2 | 3 | open Utils 4 | open Templates 5 | 6 | type Property = 7 | { 8 | fullType: string 9 | name: string 10 | childProps : Property list 11 | types: string seq 12 | description: string 13 | elementType: string 14 | isObjectArray: bool 15 | baseType: string 16 | extends:string option 17 | isRoot: bool 18 | isChartSeries: bool 19 | } 20 | with 21 | static member TypesToTypeStr isNullable elType (types: string seq) (hasChildren: bool) = 22 | let tryMatchType nullable t = 23 | match t with 24 | | "boolean" -> if nullable then Some "bool?" else Some "bool" 25 | | "number" -> if nullable then Some "double?" else Some "double" 26 | | "string" -> Some "string" 27 | | _ -> None 28 | 29 | if hasChildren then 30 | match Property.ArrayNestCount types with 31 | | Some(c,t) when c > 0 -> 32 | match tryMatchType false t with 33 | | Some(baseType) -> 34 | surrStr "IEnumerable<" ">" c baseType 35 | | None -> 36 | surrStr "IEnumerable<" ">" c (firstCharToUpper elType) 37 | | _ -> 38 | firstCharToUpper elType 39 | else 40 | match Property.ArrayNestCount types with 41 | | Some(c,t) when c > 0 -> 42 | match tryMatchType false t with 43 | | Some(baseType) -> 44 | surrStr "IEnumerable<" ">" c baseType 45 | | None -> 46 | surrStr "IEnumerable<" ">" c "string" 47 | | _ -> 48 | types 49 | |> Seq.choose (tryMatchType isNullable) 50 | |> Seq.tryHead 51 | |> Option.defaultValue "string"//(firstCharToUpper elType) 52 | 53 | 54 | static member ArrayNestCount t = 55 | let tryParseArray (t:string) = 56 | if t.Length > 8 && t.StartsWith "Array.<" && t.EndsWith ">" then 57 | t.Substring(7, t.Length-8) 58 | |> Some 59 | else 60 | None 61 | 62 | let rec arrayNestCount count t = 63 | match tryParseArray t with 64 | | Some(tNext) -> 65 | arrayNestCount (count+1) tNext 66 | | None -> 67 | count,t 68 | 69 | arrayNestCount 0 t 70 | 71 | static member ArrayNestCount (types:string seq) = 72 | types 73 | |> Seq.map Property.ArrayNestCount 74 | |> Seq.sortByDescending fst 75 | |> Seq.tryHead 76 | 77 | static member IsBaseType (types: string seq) = 78 | let isBase t = 79 | match t with 80 | | "boolean" -> true 81 | | "number" -> true 82 | | "string" -> true 83 | | _ -> false 84 | 85 | types 86 | |> Seq.exists isBase 87 | 88 | static member ToPropertyTokens (prop: Property) : Templates.PropertyTokens = 89 | let hasChildren = Seq.isEmpty prop.childProps |> not 90 | 91 | { 92 | Description = prop.description 93 | PropertyName = prop.name 94 | PropertyNullableType = Property.TypesToTypeStr true prop.name prop.types hasChildren 95 | PropertyType = Property.TypesToTypeStr false prop.name prop.types hasChildren 96 | FullType = prop.fullType 97 | IsBaseType = hasChildren |> not 98 | IsObjectArray = prop.isObjectArray 99 | } 100 | 101 | static member Union (p1: Property) (p2: Property) = 102 | let baseType = 103 | match p1.baseType,p2.baseType with 104 | | ("Trace",_) -> "Trace" 105 | | (_,"Trace") -> "Trace" 106 | | _ -> p1.baseType 107 | 108 | { p1 with 109 | childProps = p1.childProps @ p2.childProps |> List.distinctBy (fun p -> p.name) 110 | types = Seq.concat [p1.types; p2.types] |> Seq.distinct 111 | baseType = baseType 112 | } 113 | 114 | static member UnionAll (props: Property seq) = 115 | Seq.tryHead props 116 | |> Option.map (fun hd -> Seq.fold Property.Union hd props) 117 | 118 | 119 | member this.ToNiceString() = 120 | let firstType = 121 | Seq.tryHead this.types 122 | |> Option.defaultValue "" 123 | let childNiceStrings = 124 | this.childProps 125 | |> List.map (fun c -> c.ToNiceString()) 126 | |> String.concat ";" 127 | 128 | sprintf "[%s](%s) %s:%s { %s }" this.fullType this.elementType this.name firstType childNiceStrings 129 | 130 | 131 | let rec unionProps (acc:Property list) (left:Property list) = 132 | match left with 133 | | hd::tl -> 134 | if List.exists (fun p -> p.fullType = hd.fullType) acc then 135 | unionProps acc tl 136 | else 137 | let others = 138 | left 139 | |> List.filter (fun p -> p.fullType = hd.fullType) 140 | 141 | let unionedChildProps = 142 | others 143 | |> List.collect (fun p -> p.childProps) 144 | |> fun c -> c @ hd.childProps 145 | 146 | unionProps ({ hd with childProps = unionedChildProps } :: acc) tl 147 | | [] -> 148 | acc 149 | 150 | let rec toFlatPropList (items:Property list) (prop:Property) = 151 | match prop.childProps,prop.baseType with 152 | | [],"ChartElement" -> 153 | items 154 | | _ -> 155 | let allItems = 156 | prop.childProps 157 | |> List.fold toFlatPropList items 158 | 159 | prop::allItems 160 | 161 | let rec toPropFile (prop:Property) = 162 | match prop.childProps,prop.baseType with 163 | | [],"ChartElement" -> 164 | None 165 | | _ -> 166 | if prop.isChartSeries && (prop.isObjectArray |> not) then 167 | None 168 | else 169 | let arraySubType = 170 | if prop.isObjectArray then 171 | if prop.isChartSeries then 172 | "Trace_IProp" 173 | |> Some 174 | else 175 | prop.childProps 176 | |> Seq.tryHead 177 | |> Option.map (fun p -> p.fullType) 178 | else 179 | None 180 | 181 | let childTokens = 182 | prop.childProps 183 | |> Seq.distinctBy (fun x -> x.name) 184 | |> Seq.map Property.ToPropertyTokens 185 | 186 | let fileStr = 187 | childTokens 188 | |> genPropClass prop.fullType (firstCharToUpper prop.elementType) prop.name arraySubType prop.description 189 | |> genPropFile 190 | 191 | (prop.fullType,fileStr,prop.ToNiceString()) 192 | |> Some 193 | 194 | 195 | // Group properties based on projection, but apply a different projection to key to carry extra data through 196 | let groupBy (distinctProject: Property -> 'a) (keyProject: Property -> 'b) (props: Property seq) = 197 | let keyDict = 198 | props 199 | |> Seq.distinctBy distinctProject 200 | |> Seq.map (fun p -> (distinctProject p, keyProject p)) 201 | |> dict 202 | 203 | props 204 | |> Seq.groupBy distinctProject 205 | |> Seq.map (fun (key,keyProps) -> (keyDict.[key],keyProps)) 206 | -------------------------------------------------------------------------------- /src/IPlot.Plotly/ReadMe.md: -------------------------------------------------------------------------------- 1 | # IPlot.Plotly 2 | 3 | Plotly renderer for IPlot. 4 | 5 | # Table of Contents 6 | 7 | - [Chart Functions](#chart-functions) 8 | - [Setting Basic Properties](#setting-basic-properties) 9 | - [Scatter Plots](#scatter-plots) 10 | - [Time Series Plots](#time-series) 11 | - [Heatmaps](#heatmaps) 12 | - [Surface Plots](#surface-plots) 13 | - [Polymorphic Data Types](#polymorphic-data-types) 14 | 15 | # Chart Functions 16 | 17 | ## Chart.Plot 18 | 19 | Takes a collection of traces and produces a Chart object. 20 | 21 | ## Chart.With 22 | 23 | Sets a property on an element of a Chart object. 24 | 25 | ## Chart.Show 26 | 27 | Pops up the chart in a browser. 28 | 29 | ## Common Chart Property Setters 30 | 31 | Set the chart title, width and height. 32 | 33 | ## Chart Creation Utility Functions 34 | 35 | Create traces and associated chart object for the following trace types: 36 | 37 | * Line 38 | * Scatter / ScatterGL 39 | * Heatmap / HeatmapGL 40 | * Surface 41 | 42 | # Setting Basic Properties 43 | 44 | ## Set title 45 | ```fsharp 46 | Chart.Scatter [1.;2.;3.;5.] 47 | |> Chart.With (Chart.Props.layout.title.text "Beard length") 48 | ``` 49 | 50 | ## Set name of trace 51 | 52 | ```fsharp 53 | Chart.Scatter [1.;2.;3.;5.] 54 | |> Chart.With (Chart.Props.traces.[0].asScatter.name "Mr Susan") 55 | ``` 56 | 57 | ## Choose line / marker mode 58 | 59 | ```fsharp 60 | Chart.Scatter [1.;2.;3.;5.] 61 | |> Chart.With (Chart.Props.traces.[0].asScatter.mode "lines+markers") 62 | ``` 63 | 64 | ## Set line width 65 | 66 | ```fsharp 67 | Chart.Scatter [1.;2.;3.;5.] 68 | |> Chart.With (Chart.Props.traces.[0].asScatter.line.width 5.0) 69 | ``` 70 | 71 | ## Set line color 72 | 73 | ```fsharp 74 | Chart.Scatter [1.;2.;3.;5.] 75 | |> Chart.With (Chart.Props.traces.[0].asScatter.line.color "#222") 76 | ``` 77 | 78 | # Line / Scatter plots 79 | 80 | ```fsharp 81 | let trace1 = 82 | Scatter( 83 | x = [0.; 1.; 2.; 3.], 84 | y = [0.2; 0.8; 0.5; 1.1] 85 | ) 86 | 87 | let trace2 = 88 | Scatter( 89 | x = [0.; 1.; 2.; 3.], 90 | y = [0.6; 0.1; 0.3; 0.7] 91 | ) 92 | 93 | [trace1; trace2] 94 | |> Chart.Plot 95 | |> Chart.WithWidth 700 96 | |> Chart.WithHeight 500 97 | |> Chart.WithTitle "Two lines" 98 | |> Chart.Show 99 | ``` 100 | 101 | ![PL_TwoLines](https://user-images.githubusercontent.com/24556021/101266919-4edb4580-374b-11eb-8c30-be4752a9da87.png) 102 | 103 | ```fsharp 104 | let lineTrace1 = 105 | Scatter( 106 | x = [1.; 2.; 3.; 4.], 107 | y = [10.; 15.; 13.; 17.], 108 | mode = "markers" 109 | ) 110 | 111 | let lineTrace2 = 112 | Scatter( 113 | x = [2.; 3.; 4.; 5.], 114 | y = [16.; 5.; 11.; 9.], 115 | mode = "lines" 116 | ) 117 | 118 | let lineTrace3 = 119 | Scatter( 120 | x = [1.; 2.; 3.; 4.], 121 | y = [12.; 9.; 15.; 12.], 122 | mode = "lines+markers" 123 | ) 124 | 125 | [lineTrace1; lineTrace2; lineTrace3] 126 | |> Chart.Plot 127 | |> Chart.WithWidth 700 128 | |> Chart.WithHeight 500 129 | |> Chart.WithTitle "Line and Scatter Plot" 130 | |> Chart.Show 131 | ``` 132 | 133 | ![PL_LineScatter](https://user-images.githubusercontent.com/24556021/101266912-4daa1880-374b-11eb-82ee-a43a08854e1f.png) 134 | 135 | # Time series plots 136 | 137 | ## Time series (strings) 138 | ```fsharp 139 | let x = ["2020-09-12 22:30:00";"2020-09-13 22:30:00";"2020-09-15 22:30:00";"2020-09-19 22:30:00"] 140 | 141 | [-1.; -11.; -4.; 5.] 142 | |> Chart.Line 143 | |> Chart.WithWidth 700 144 | |> Chart.WithHeight 500 145 | |> Chart.WithTitle "Time Series Plot (strings)" 146 | |> Chart.With (Chart.Props.traces.[0].asScatter.xs_ x) 147 | |> Chart.With (Chart.Props.traces.[0].asScatter.mode "lines+markers") 148 | |> Chart.Show 149 | ``` 150 | 151 | ![PL_TS_Strings](https://user-images.githubusercontent.com/24556021/101266916-4edb4580-374b-11eb-848b-5449a548b191.png) 152 | 153 | ## Time series (DateTimes) 154 | 155 | ```fsharp 156 | let n = 45 157 | let r = Random(91) 158 | let startDate = DateTime(2012,1,1,0,0,0) 159 | let t = 160 | (startDate,0) 161 | |> Seq.unfold (fun (t,i) -> 162 | if i > n then None 163 | else Some(t,(t.AddDays(1.),i+1))) 164 | 165 | t 166 | |> Seq.map (fun tt -> r.NextDouble() + exp (0.05 * (tt-startDate).TotalDays)) 167 | |> Chart.Line 168 | |> Chart.WithWidth 700 169 | |> Chart.WithHeight 500 170 | |> Chart.WithTitle "Time Series Plot (DateTimes)" 171 | |> Chart.With (Chart.Props.traces.[0].asScatter.xt_ t) 172 | |> Chart.With (Chart.Props.traces.[0].asScatter.mode "lines+markers") 173 | |> Chart.Show 174 | ``` 175 | 176 | ![PL_TS_DateTimes](https://user-images.githubusercontent.com/24556021/101266915-4edb4580-374b-11eb-8b75-694dce5ec804.png) 177 | 178 | # Heatmaps 179 | 180 | ## Time Heatmap 181 | 182 | ```fsharp 183 | let n = 365 184 | let t = 185 | (DateTime(2012,1,1,0,0,0),0) 186 | |> Seq.unfold (fun (t,i) -> 187 | if i > n then None 188 | else Some(t,(t.AddDays(1.),i+1))) 189 | 190 | [ for i in 1..n -> 191 | seq { for a in -1. .. 0.1 .. 1. -> Math.Cos(0.1 * float i) * Math.Sin(10.0*a) / Math.Exp(a) } 192 | ] 193 | |> Chart.Heatmap 194 | |> Chart.With (Chart.Props.traces.[0].asHeatmap.yt_ t) 195 | |> Chart.WithWidth 1200 196 | |> Chart.WithHeight 900 197 | |> Chart.WithTitle "Heatmap" 198 | |> Chart.Show 199 | ``` 200 | 201 | ![PL_Heatmap](https://user-images.githubusercontent.com/24556021/101266910-4d118200-374b-11eb-92ea-29a7c8aadc8b.png) 202 | 203 | ## Time HeatmapGL 204 | 205 | ```fsharp 206 | let n = 365 207 | let t = 208 | (DateTime(2012,1,1,0,0,0),0) 209 | |> Seq.unfold (fun (t,i) -> 210 | if i > n then None 211 | else Some(t,(t.AddDays(1.),i+1))) 212 | 213 | [ for i in 1..n -> 214 | seq { for a in -1. .. 0.1 .. 1. -> Math.Cos(0.1 * float i) * Math.Sin(10.0*a) / Math.Exp(a) } 215 | ] 216 | |> Chart.HeatmapGl 217 | |> Chart.With (Chart.Props.traces.[0].asHeatmapgl.yt_ t) 218 | |> Chart.WithWidth 1200 219 | |> Chart.WithHeight 900 220 | |> Chart.WithTitle "Heatmap GL" 221 | |> Chart.Show 222 | ``` 223 | 224 | ![PL_HeatmapGL](https://user-images.githubusercontent.com/24556021/101266911-4d118200-374b-11eb-9a8a-b3ad15ed4c6d.png) 225 | 226 | # Surface plots 227 | 228 | ## Basic surface plot 229 | 230 | ```fsharp 231 | let xData = seq { -2. .. 0.1 .. 2. } 232 | let yData = xData 233 | let zData = 234 | seq { 235 | for x in xData do 236 | yield [ 237 | for y in yData do 238 | yield Math.Cos(5. * (x*x + y*y)) / Math.Exp(0.6 * (x*x + y*y)) 239 | ] |> List.toSeq 240 | } 241 | 242 | (xData,yData,zData) 243 | |> Chart.Surface 244 | |> Chart.WithWidth 900 245 | |> Chart.WithHeight 700 246 | |> Chart.WithTitle "Basic surface" 247 | |> Chart.Show 248 | ``` 249 | 250 | ![PL_Surface](https://user-images.githubusercontent.com/24556021/101266913-4e42af00-374b-11eb-9801-f3a0443af36d.png) 251 | 252 | ## Time surface plot 253 | ```fsharp 254 | let n = 365 255 | let t = 256 | (DateTime(2012,1,1,0,0,0),0) 257 | |> Seq.unfold (fun (t,i) -> 258 | if i > n then None 259 | else Some(t,(t.AddDays(1.),i+1))) 260 | 261 | let nf = float n 262 | let rad = 2. * Math.PI 263 | 264 | [ for i in 1..n -> 265 | let x = ((float i) - (0.5*nf)) * rad / nf 266 | seq { for y in -rad .. 0.1 .. rad -> x * Math.Exp(-(x*x)-(y*y)) } 267 | ] 268 | |> Chart.Surface 269 | |> Chart.With (Chart.Props.traces.[0].asSurface.yt_ t) 270 | |> Chart.With (Chart.Props.layout.paper_bgcolor "#EEE") 271 | |> Chart.WithWidth 1200 272 | |> Chart.WithHeight 900 273 | |> Chart.WithTitle "Time Surface" 274 | |> Chart.Show 275 | ``` 276 | 277 | ![PL_TimeSurface](https://user-images.githubusercontent.com/24556021/101266914-4e42af00-374b-11eb-863c-7e0931704d14.png) 278 | 279 | # Polymorphic Data Types 280 | 281 | Some trace types allow using date times, and/or strings as the data arrays, which allow plotting time series and categorical data. The following trace types support these: 282 | 283 | * Scatter (and Scattergl) 284 | * Heatmap (and Heatmapgl) 285 | * Surface 286 | 287 | For these, you can use the additional ``xs_`` (string) and ``xt_`` (DateTime) properties as follows: 288 | 289 | ```fsharp 290 | let surface = 291 | Surface( 292 | xt_ = [ 293 | DateTime(2020,9,12,22,30,0) 294 | DateTime(2020,9,13,22,30,0) 295 | DateTime(2020,9,15,22,30,0) 296 | DateTime(2020,9,19,22,30,0)], 297 | z = [ 298 | [0.1;0.3;0.8] 299 | [0.2;0.35;0.85] 300 | [0.9;1.0;1.4] 301 | [1.2;1.3;1.8]] 302 | ) 303 | 304 | [surface] 305 | |> Chart.Plot 306 | |> Chart.WithWidth 1200 307 | |> Chart.WithHeight 800 308 | |> Chart.Show 309 | ``` 310 | -------------------------------------------------------------------------------- /tools/GenApi.Plotly/Gen.fs: -------------------------------------------------------------------------------- 1 | namespace GenApi.Plotly 2 | open Newtonsoft.Json.Linq 3 | 4 | module Gen = 5 | open System.IO 6 | open System.Collections.Generic 7 | open Newtonsoft.Json 8 | 9 | let jsonUrl = "https://github.com/plotly/plotly.js/blob/master/dist/plot-schema.json?raw=true" 10 | 11 | module Implementation = 12 | open System.Net 13 | open Utils 14 | open Property 15 | open ElementType 16 | 17 | let fetchJson() = 18 | use client = new WebClient() 19 | client.Encoding <- System.Text.Encoding.UTF8 20 | client.DownloadString jsonUrl 21 | 22 | let tryDeserialiseDict str = 23 | if System.Text.RegularExpressions.Regex.Match(str, @"^\s{+\s*}+\s$").Success then 24 | printfn "Found empty class - empty dict" 25 | new Dictionary() 26 | else 27 | try 28 | JsonConvert.DeserializeObject(str, typeof>) 29 | :?> Dictionary 30 | with _ -> 31 | printfn "Exception deserialising dict - empty dict" 32 | new Dictionary() 33 | 34 | // Group properties based on projection, but apply a different projection to key to carry extra data through 35 | let groupBy (distinctProject: Property -> 'a) (keyProject: Property -> 'b) (props: Property seq) = 36 | let keyDict = 37 | props 38 | |> Seq.distinctBy distinctProject 39 | |> Seq.map (fun p -> (distinctProject p, keyProject p)) 40 | |> dict 41 | 42 | props 43 | |> Seq.groupBy distinctProject 44 | |> Seq.map (fun (key,keyProps) -> (keyDict.[key],keyProps)) 45 | 46 | // Get a flat list of all properties from the current doc property 47 | let rec getPropertiesFromDict curPath curType isArray rootElement traceType (propertyDict: IDictionary) = 48 | Hacks.addTraceProperties curType propertyDict 49 | 50 | [ 51 | 52 | for p in propertyDict do 53 | let curKey = p.Key 54 | let curValue = p.Value 55 | match curValue with 56 | | :? System.String -> yield Property.FromString curPath curType false rootElement traceType curKey ("\"" + string curValue + "\"") 57 | | :? System.Boolean -> yield Property.FromString curPath curType false rootElement traceType curKey curValue 58 | | _ -> 59 | let propDict = tryDeserialiseDict (curValue.ToString()) 60 | match propDict.ContainsKey("valType") with 61 | | true -> // Property is a value or array 62 | yield Property.FromObj curPath curType isArray rootElement traceType curKey propDict 63 | | false -> // Property is a nested dictionary 64 | let el = 65 | { 66 | name = curKey 67 | value = None 68 | ``type`` = ElementType.FromPropType curKey curKey traceType 69 | description = "" 70 | elementType = curType 71 | isElementArray = isArray 72 | rootElement = rootElement 73 | traceType = traceType 74 | fullType = pathToPropName curPath 75 | } 76 | if Property.IsValid el then 77 | yield el 78 | yield! getPropertiesFromDict (curKey::curPath) curKey (el.``type``.IsArray()) None traceType propDict 79 | ] 80 | 81 | let toElementFile ((elType:string, elBaseType:string option, elDesc:string), elMembers:Property seq) = 82 | let validMembers = 83 | elMembers 84 | |> Seq.filter (fun p -> 85 | match p.name,p.``type``,p.rootElement with 86 | | "type",ElementString,Some("Trace") -> 87 | false 88 | | "name",ElementString,Some("Trace") -> 89 | false 90 | | _ -> 91 | true) 92 | 93 | let fileStr = 94 | validMembers 95 | |> Seq.distinctBy (fun x -> x.name) 96 | |> Seq.map Property.ToPropertyTokens 97 | |> Templates.genElementFile elType elDesc elBaseType 98 | 99 | elType,fileStr 100 | 101 | let toPropFile ((propType:string, elType:string, elDesc:string), props:Property seq) = 102 | let fileStr = 103 | props 104 | |> Seq.distinctBy (fun x -> x.name) 105 | |> Seq.map Property.ToPropertyTokens 106 | |> Templates.genPropClass propType elType elDesc 107 | |> Templates.genPropFile 108 | 109 | propType,fileStr 110 | 111 | open Implementation 112 | open Property 113 | 114 | let go() = 115 | let outElementPath = "src/IPlot.Plotly/Elements" 116 | let outPropPath = "src/IPlot.Plotly/Props" 117 | 118 | let prepPath p = 119 | if Directory.Exists p then 120 | let di = DirectoryInfo(p) 121 | di.EnumerateFiles() 122 | |> Seq.toArray 123 | |> Array.map (fun f -> f.Delete()) 124 | |> ignore 125 | else 126 | Directory.CreateDirectory p 127 | |> ignore 128 | 129 | prepPath outElementPath 130 | prepPath outPropPath 131 | 132 | let jsonDict = 133 | JsonConvert.DeserializeObject(fetchJson(), typeof>) 134 | :?> Dictionary 135 | 136 | let traces = 137 | jsonDict.["traces"].ToString() 138 | |> tryDeserialiseDict 139 | 140 | let layout = 141 | jsonDict.["layout"].ToString() 142 | |> tryDeserialiseDict 143 | 144 | // Get a flat list of all properties from all traces 145 | let traceProperties = 146 | [ 147 | for x in traces do 148 | let typeName = x.Key // scatter, etc 149 | let properties = 150 | x.Value.ToString() 151 | |> tryDeserialiseDict 152 | |> fun dict -> dict.["attributes"].ToString() 153 | |> tryDeserialiseDict 154 | yield! getPropertiesFromDict [typeName] typeName false (Some("Trace")) (Some(typeName)) properties 155 | ] 156 | |> List.filter Property.IsValid 157 | 158 | // Get a flat list of all properties from the layout property 159 | let layoutProperties = 160 | [ 161 | let typeName = "layout" 162 | let jObj = layout.["layoutAttributes"] 163 | let properties = 164 | jObj.ToString() 165 | |> tryDeserialiseDict 166 | yield! getPropertiesFromDict [typeName] typeName false None None properties 167 | ] 168 | |> List.filter Property.IsValid 169 | 170 | printfn "" 171 | printfn "==================" 172 | printfn "" 173 | 174 | // Export elements 175 | List.append traceProperties layoutProperties 176 | |> List.distinctBy (fun p -> (p.name,p.``type``,p.elementType)) 177 | |> groupBy (fun p -> Utils.firstCharToUpper p.elementType) (fun p -> (Utils.firstCharToUpper p.elementType, p.rootElement, p.description)) 178 | |> Seq.map toElementFile 179 | |> Seq.toArray 180 | |> Array.map (fun (typeName,str) -> 181 | printfn "Exporting %s" typeName 182 | File.WriteAllText(Path.Combine(outElementPath, typeName + ".cs"), str)) 183 | |> ignore 184 | 185 | // Export property helpers 186 | List.append traceProperties layoutProperties 187 | |> groupBy (fun p -> p.fullType) (fun p -> (p.fullType, Utils.firstCharToUpper p.elementType, p.description)) 188 | |> Seq.map toPropFile 189 | |> Seq.toArray 190 | |> Array.map (fun (typeName,str) -> 191 | printfn "Exporting %s" typeName 192 | File.WriteAllText(Path.Combine(outPropPath, typeName + ".cs"), str)) 193 | |> ignore 194 | 195 | printfn "" 196 | printfn "==================" 197 | printfn "" 198 | printfn "Done" 199 | -------------------------------------------------------------------------------- /tools/GenApi.Plotly/Templates.fs: -------------------------------------------------------------------------------- 1 | module Templates 2 | 3 | let strRep (strFrom:string) (strTo:string) (s:string) = 4 | s.Replace(strFrom, strTo) 5 | 6 | let elementClass = "##ELEMENTCLASS##" 7 | let propClass = "##PROPCLASS##" 8 | let elementMembers = "##ELEMENTMEMBERS##" 9 | let elementClone = "##ELEMENTCLONE##" 10 | let jsonPropName = "##JSONPROPNAME##" // The property name to set (e.g. colour) 11 | let propType = "##PROPTYPE##" // The property time (e.g. string) 12 | let elementType = "##ELEMENTTYPE##" // The element type (e.g. Line) 13 | let elementPropName = "##PROPNAME##" // The element property type name (e.g. Colour) 14 | let elementPropType = "##ELEMENTPROPTYPE##" // The type of an element property (e.g. bool?) 15 | let elementBase = "##ELEMENTBASE##" // The base class to derive from (typically ChartElement) 16 | let arraySubType = "##ARRAYSUBTYPE##" // The type of the items in the array (e.g. int, Line) 17 | let classBody = "##CLASS##" 18 | let body = "##BODY##" 19 | let newProp = "##NEW##" 20 | let description = "##DESCRIPTION##" 21 | let traceType = "##TRACETYPE##" 22 | let traceClone = "##TRACECLONE##" 23 | 24 | let templateFile = @" 25 | using System; 26 | using System.Collections.Generic; 27 | 28 | namespace IPlot.Plotly 29 | { 30 | ##CLASS## 31 | } 32 | " 33 | let templateElementClass = @" 34 | /// 35 | /// ##DESCRIPTION## 36 | /// 37 | public class ##ELEMENTTYPE## : ##ELEMENTBASE## 38 | { 39 | ##ELEMENTMEMBERS## 40 | ##TRACETYPE## 41 | 42 | /// Deep clone of chart element and all properties 43 | public override ChartElement DeepClone() 44 | { 45 | var obj = new ##ELEMENTTYPE##(); 46 | ##TRACECLONE## 47 | ##ELEMENTCLONE## 48 | return obj; 49 | } 50 | } 51 | " 52 | 53 | let templateElementMember = @" 54 | /// 55 | /// ##DESCRIPTION## 56 | /// 57 | public ##NEW####PROPTYPE## ##PROPNAME## { get; set; } = null;" 58 | 59 | let templateElementTraceType = @" 60 | /// The type of this series (##TRACETYPE##) 61 | public override string type_iplot { get { return ""##TRACETYPE##""; } }" 62 | 63 | let templateElementTraceClone = @" 64 | if (this is Trace trace) 65 | Trace.DeepCopy(trace, obj); 66 | " 67 | 68 | let templateElementClone = @" obj.##PROPNAME## = ##PROPNAME##;" 69 | 70 | let templatePropClass = @" 71 | /// 72 | /// ##DESCRIPTION## 73 | /// 74 | public class ##ELEMENTPROPTYPE## : ChartProp 75 | { 76 | /// 77 | /// Set ##ELEMENTPROPTYPE## property directly from element instance 78 | /// 79 | public Func Of(##ELEMENTTYPE## v) 80 | { 81 | var propPath = GetPath(); 82 | 83 | return (chart) => 84 | { 85 | var el = (ChartElement)chart; 86 | var i = 0; 87 | 88 | while ((el != null) && (i < propPath.Count)) 89 | { 90 | el = ChartElement.GetElement(propPath[i], el); 91 | i++; 92 | } 93 | 94 | if (el != null) 95 | { 96 | var p = el.GetType().GetProperty(""##JSONPROPNAME##""); 97 | p.SetValue(el,v); 98 | } 99 | 100 | return chart; 101 | }; 102 | } 103 | ##BODY## 104 | } 105 | " 106 | 107 | let templateArrayPropClass = @" 108 | /// 109 | /// ##DESCRIPTION## 110 | /// 111 | public class ##ELEMENTTYPE## : ChartProp, IArrayProp 112 | { 113 | /// Last accessed array index 114 | private int _index; 115 | 116 | /// Last accessed array index 117 | public int Index 118 | { 119 | get => _index; 120 | } 121 | 122 | /// Access specific element in this array 123 | public ##ARRAYSUBTYPE## this[int i] 124 | { 125 | get 126 | { 127 | _index = i; 128 | return new ##ARRAYSUBTYPE##() { _parent = this }; 129 | } 130 | set {} 131 | } 132 | } 133 | " 134 | 135 | let templatePropGetter = @" 136 | /// 137 | /// ##DESCRIPTION## 138 | /// 139 | public ##PROPTYPE## ##JSONPROPNAME## 140 | { 141 | get 142 | { 143 | return new ##PROPTYPE##() { _parent = this }; 144 | } 145 | }" 146 | 147 | let templatePropSetter = @" 148 | /// 149 | /// ##DESCRIPTION## 150 | /// 151 | public Func ##JSONPROPNAME##(##ELEMENTPROPTYPE## v) 152 | { 153 | var propPath = GetPath(); 154 | 155 | return (chart) => 156 | { 157 | var el = (ChartElement)chart; 158 | var i = 0; 159 | 160 | while ((el != null) && (i < propPath.Count)) 161 | { 162 | el = ChartElement.GetElement(propPath[i], el); 163 | i++; 164 | } 165 | 166 | var thisElement = el as ##ELEMENTTYPE##; 167 | if (thisElement != null) 168 | thisElement.##PROPNAME## = ChartProp.SafeConvert(thisElement.##PROPNAME##, v); 169 | 170 | return chart; 171 | }; 172 | }" 173 | 174 | type PropertyTokens = { 175 | Description: string 176 | PropertyName: string 177 | PropertyNullableType: string 178 | PropertyType: string 179 | FullType: string 180 | IsBaseType: bool 181 | } 182 | with 183 | member this.ToFullPropertyType() = 184 | if this.FullType.EndsWith("_IProp") then 185 | this.FullType.Substring(0,this.FullType.Length-6) + "_" + Utils.firstCharToUpper this.PropertyName + "_IProp" 186 | else 187 | this.FullType + "_" + Utils.firstCharToUpper this.PropertyName + "_IProp" 188 | 189 | let makeSafeDesc tabs (desc:string) = 190 | let tabStr = 191 | Array.init tabs (fun _ -> " ") 192 | |> String.concat "" 193 | 194 | desc 195 | .Replace("\r","") 196 | .Replace("\n",sprintf "\n%s/// " tabStr) 197 | .Replace("<","") 198 | .Replace(">","") 199 | .Replace("&","and") 200 | 201 | let genElementFile elType elDesc elBaseType (props:PropertyTokens seq) = 202 | let validProps = 203 | props 204 | |> Seq.filter (fun p -> 205 | let name = Utils.makeSafeTypeName p.PropertyName 206 | elType <> Utils.firstCharToUpper name) 207 | 208 | let elMembers = 209 | validProps 210 | |> Seq.map (fun p -> 211 | let name = Utils.makeSafeTypeName p.PropertyName 212 | let desc = if p.Description <> "" then p.Description else p.PropertyName 213 | let newStr = 214 | match elBaseType with 215 | | Some(_) when name = "name" -> "new " 216 | | _ -> "" 217 | 218 | templateElementMember 219 | |> strRep description (makeSafeDesc 2 desc) 220 | |> strRep elementPropName name 221 | |> strRep propType p.PropertyNullableType 222 | |> strRep newProp newStr) 223 | |> String.concat "\n" 224 | 225 | let elClone = 226 | validProps 227 | |> Seq.map (fun p -> 228 | let name = Utils.makeSafeTypeName p.PropertyName 229 | templateElementClone 230 | |> strRep elementPropName name) 231 | |> String.concat "\n" 232 | 233 | let traceTypeStr,traceCloneStr = 234 | match elBaseType with 235 | | Some(baseType) when baseType <> "ChartElement" -> 236 | let tt = 237 | templateElementTraceType 238 | |> strRep traceType (Utils.firstCharToLower elType) 239 | 240 | tt,templateElementTraceClone 241 | | _ -> 242 | "","" 243 | 244 | let elClass = 245 | templateElementClass 246 | |> strRep elementType elType 247 | |> strRep description (makeSafeDesc 1 elDesc) 248 | |> strRep elementMembers elMembers 249 | |> strRep elementClone elClone 250 | |> strRep elementBase (elBaseType |> Option.defaultValue "ChartElement") 251 | |> strRep traceType traceTypeStr 252 | |> strRep traceClone traceCloneStr 253 | 254 | templateFile 255 | |> strRep classBody elClass 256 | 257 | let genPropFile propClass = 258 | templateFile 259 | |> strRep classBody propClass 260 | 261 | let genPropClass elPropType elType elDesc (props:PropertyTokens seq) = 262 | let validProps = 263 | props 264 | |> Seq.filter (fun p -> 265 | let name = Utils.makeSafeTypeName p.PropertyName 266 | elType <> Utils.firstCharToUpper name) 267 | 268 | let propGetters = 269 | validProps 270 | |> Seq.filter (fun p -> p.IsBaseType |> not) 271 | |> Seq.map (fun p -> 272 | templatePropGetter 273 | |> strRep jsonPropName (Utils.makeSafeTypeName p.PropertyName) 274 | |> strRep description (makeSafeDesc 2 p.Description) 275 | |> strRep propType (p.ToFullPropertyType())) 276 | |> String.concat "\n" 277 | 278 | let propSetters = 279 | validProps 280 | |> Seq.filter (fun p -> p.IsBaseType) 281 | |> Seq.map (fun p -> 282 | templatePropSetter 283 | |> strRep jsonPropName (Utils.makeSafeTypeName p.PropertyName) 284 | |> strRep description (makeSafeDesc 2 p.Description) 285 | |> strRep elementPropName (Utils.makeSafeTypeName p.PropertyName) 286 | |> strRep elementPropType p.PropertyType 287 | |> strRep elementType elType) 288 | |> String.concat "\n" 289 | 290 | templatePropClass 291 | |> strRep elementPropType elPropType 292 | |> strRep elementType elType 293 | |> strRep description (makeSafeDesc 1 elDesc) 294 | |> strRep jsonPropName (Utils.makeSafeTypeName (Utils.firstCharToLower elType)) 295 | |> strRep body (propGetters + propSetters) 296 | 297 | let genArrayPropClass elType elSubType elDesc = 298 | templateArrayPropClass 299 | |> strRep elementType elType 300 | |> strRep arraySubType elSubType 301 | |> strRep description (makeSafeDesc 1 elDesc) -------------------------------------------------------------------------------- /tools/GenApi.HighCharts/Templates.fs: -------------------------------------------------------------------------------- 1 | module Templates 2 | 3 | let strRep (strFrom:string) (strTo:string) (s:string) = 4 | s.Replace(strFrom, strTo) 5 | 6 | let elementClass = "##ELEMENTCLASS##" 7 | let propClass = "##PROPCLASS##" 8 | let elementMembers = "##ELEMENTMEMBERS##" 9 | let elementClone = "##ELEMENTCLONE##" 10 | let jsonPropName = "##JSONPROPNAME##" // The property name to set (e.g. colour) 11 | let propType = "##PROPTYPE##" // The property time (e.g. string) 12 | let elementType = "##ELEMENTTYPE##" // The element type (e.g. Line) 13 | let elementPropName = "##PROPNAME##" // The element property type name (e.g. Colour) 14 | let elementPropType = "##ELEMENTPROPTYPE##" // The type of an element property (e.g. bool?) 15 | let elementBase = "##ELEMENTBASE##" // The base class to derive from (typically ChartElement) 16 | let arraySubType = "##ARRAYSUBTYPE##" // The type of the items in the array (e.g. int, Line) 17 | let classBody = "##CLASS##" 18 | let body = "##BODY##" 19 | let newProp = "##NEW##" 20 | let description = "##DESCRIPTION##" 21 | let traceType = "##TRACETYPE##" 22 | let traceClone = "##TRACECLONE##" 23 | 24 | let templateFile = @" 25 | using System; 26 | using System.Collections.Generic; 27 | 28 | namespace IPlot.HighCharts 29 | { 30 | ##CLASS## 31 | } 32 | " 33 | let templateElementClass = @" 34 | /// 35 | /// ##DESCRIPTION## 36 | /// 37 | public class ##ELEMENTTYPE## : ##ELEMENTBASE## 38 | { 39 | ##ELEMENTMEMBERS## 40 | ##TRACETYPE## 41 | 42 | /// Deep clone of chart element and all properties 43 | public override ChartElement DeepClone() 44 | { 45 | var obj = new ##ELEMENTTYPE##(); 46 | ##TRACECLONE## 47 | ##ELEMENTCLONE## 48 | return obj; 49 | } 50 | } 51 | " 52 | 53 | let templateElementMember = @" 54 | /// 55 | /// ##DESCRIPTION## 56 | /// 57 | public ##NEW####PROPTYPE## ##PROPNAME## { get; set; } = null;" 58 | 59 | let templateElementTraceType = @" 60 | /// The type of this series (##TRACETYPE##) 61 | public override string type_iplot { get { return ""##TRACETYPE##""; } }" 62 | 63 | let templateElementTraceClone = @" 64 | if (this is Trace t) 65 | Trace.DeepCopy(t, obj); 66 | " 67 | 68 | let templateElementClone = @" obj.##PROPNAME## = ##PROPNAME##;" 69 | 70 | let templatePropClass = @" 71 | /// 72 | /// ##DESCRIPTION## 73 | /// 74 | public class ##ELEMENTPROPTYPE## : ChartProp 75 | { 76 | /// 77 | /// Set ##ELEMENTPROPTYPE## property directly from element instance 78 | /// 79 | public Func Of(##ELEMENTTYPE## v) 80 | { 81 | var propPath = GetPath(); 82 | 83 | return (chart) => 84 | { 85 | var el = (ChartElement)chart.chart; 86 | var i = 0; 87 | 88 | while ((el != null) && (i < propPath.Count)) 89 | { 90 | el = ChartElement.GetElement(propPath[i], el); 91 | i++; 92 | } 93 | 94 | if (el != null) 95 | { 96 | var p = el.GetType().GetProperty(""##JSONPROPNAME##""); 97 | p.SetValue(el,v); 98 | } 99 | 100 | return chart; 101 | }; 102 | } 103 | ##BODY## 104 | } 105 | " 106 | 107 | let templateArrayPropClass = @" 108 | /// 109 | /// ##DESCRIPTION## 110 | /// 111 | public class ##ELEMENTPROPTYPE## : ChartProp, IArrayProp 112 | { 113 | /// Last accessed array index 114 | private int _index; 115 | 116 | /// Last accessed array index 117 | public int Index 118 | { 119 | get => _index; 120 | } 121 | 122 | /// Access specific element in this array 123 | public ##ARRAYSUBTYPE## this[int i] 124 | { 125 | get 126 | { 127 | _index = i; 128 | return new ##ARRAYSUBTYPE##() { _parent = this }; 129 | } 130 | set {} 131 | } 132 | } 133 | " 134 | 135 | let templatePropGetter = @" 136 | /// 137 | /// ##DESCRIPTION## 138 | /// 139 | public ##PROPTYPE## ##JSONPROPNAME## 140 | { 141 | get 142 | { 143 | return new ##PROPTYPE##() { _parent = this }; 144 | } 145 | }" 146 | 147 | let templatePropSetter = @" 148 | /// 149 | /// ##DESCRIPTION## 150 | /// 151 | public Func ##JSONPROPNAME##(##ELEMENTPROPTYPE## v) 152 | { 153 | var propPath = GetPath(); 154 | 155 | return (chart) => 156 | { 157 | var el = (ChartElement)chart.chart; 158 | var i = 0; 159 | 160 | while ((el != null) && (i < propPath.Count)) 161 | { 162 | el = ChartElement.GetElement(propPath[i], el); 163 | i++; 164 | } 165 | 166 | var thisElement = el as ##ELEMENTTYPE##; 167 | if (thisElement != null) 168 | thisElement.##PROPNAME## = ChartProp.SafeConvert(thisElement.##PROPNAME##, v); 169 | 170 | return chart; 171 | }; 172 | }" 173 | 174 | type PropertyTokens = { 175 | Description: string 176 | PropertyName: string 177 | PropertyNullableType: string 178 | PropertyType: string 179 | FullType: string 180 | IsBaseType: bool 181 | IsObjectArray: bool 182 | } 183 | with 184 | member this.ToFullPropertyType() = 185 | if this.FullType.EndsWith("_IProp") then 186 | this.FullType.Substring(0,this.FullType.Length-6) + "_IProp" 187 | else 188 | this.FullType + "_" + Utils.firstCharToUpper this.PropertyName + "_IProp" 189 | 190 | let makeSafeDesc tabs (desc:string) = 191 | let tabStr = 192 | Array.init tabs (fun _ -> " ") 193 | |> String.concat "" 194 | 195 | desc 196 | .Replace("\r","") 197 | .Replace("\n",sprintf "\n%s/// " tabStr) 198 | .Replace("<","") 199 | .Replace(">","") 200 | .Replace("&","and") 201 | 202 | 203 | let genElementFile elType elDesc baseType isRootElement (props:PropertyTokens seq) = 204 | let validProps = 205 | props 206 | |> Seq.filter (fun p -> 207 | let name = Utils.makeSafeTypeName p.PropertyName 208 | elType <> Utils.firstCharToUpper name) 209 | 210 | let elMembers = 211 | validProps 212 | |> Seq.map (fun p -> 213 | let name = Utils.makeSafeTypeName p.PropertyName 214 | let desc = if p.Description <> "" then p.Description else p.PropertyName 215 | let propTypeStr = 216 | if isRootElement && p.PropertyName = "series" then 217 | "IEnumerable" 218 | else 219 | (Utils.makeSafeTypeName p.PropertyNullableType) 220 | 221 | templateElementMember 222 | |> strRep description (makeSafeDesc 2 desc) 223 | |> strRep elementPropName name 224 | |> strRep propType propTypeStr 225 | |> strRep newProp "") 226 | |> String.concat "\n" 227 | 228 | let elClone = 229 | validProps 230 | |> Seq.map (fun p -> 231 | let name = Utils.makeSafeTypeName p.PropertyName 232 | templateElementClone 233 | |> strRep elementPropName name) 234 | |> String.concat "\n" 235 | 236 | let traceTypeStr,traceCloneStr = 237 | if baseType <> "ChartElement" then 238 | let tt = 239 | templateElementTraceType 240 | |> strRep traceType (Utils.firstCharToLower elType) 241 | 242 | tt,templateElementTraceClone 243 | else 244 | "","" 245 | 246 | let elClass = 247 | templateElementClass 248 | |> strRep elementType (Utils.makeSafeTypeName elType) 249 | |> strRep description (makeSafeDesc 1 elDesc) 250 | |> strRep elementMembers elMembers 251 | |> strRep elementClone elClone 252 | |> strRep elementBase baseType 253 | |> strRep traceType traceTypeStr 254 | |> strRep traceClone traceCloneStr 255 | 256 | templateFile 257 | |> strRep classBody elClass 258 | 259 | let genPropFile propClass = 260 | templateFile 261 | |> strRep classBody propClass 262 | 263 | let genArrayPropClass elPropType elSubType elDesc = 264 | templateArrayPropClass 265 | |> strRep elementPropType elPropType 266 | |> strRep arraySubType elSubType 267 | |> strRep description (makeSafeDesc 1 elDesc) 268 | 269 | let genPropClass elPropType elType elJsonName arraySubType elDesc (props:PropertyTokens seq) = 270 | match arraySubType with 271 | | Some(subType) -> 272 | genArrayPropClass elPropType subType elDesc 273 | | None -> 274 | let validProps = 275 | props 276 | |> Seq.filter (fun p -> 277 | let name = Utils.makeSafeTypeName p.PropertyName 278 | elType <> Utils.firstCharToUpper name) 279 | 280 | let propGetters = 281 | validProps 282 | |> Seq.filter (fun p -> p.IsBaseType |> not) 283 | |> Seq.map (fun p -> 284 | templatePropGetter 285 | |> strRep jsonPropName (Utils.makeSafeTypeName p.PropertyName) 286 | |> strRep description (makeSafeDesc 2 p.Description) 287 | |> strRep propType (Utils.makeSafeTypeName (p.ToFullPropertyType()))) 288 | |> String.concat "\n" 289 | 290 | let propSetters = 291 | validProps 292 | |> Seq.filter (fun p -> p.IsBaseType) 293 | |> Seq.map (fun p -> 294 | templatePropSetter 295 | |> strRep jsonPropName (Utils.makeSafeTypeName p.PropertyName) 296 | |> strRep description (makeSafeDesc 2 p.Description) 297 | |> strRep elementPropName (Utils.makeSafeTypeName p.PropertyName) 298 | |> strRep elementPropType (Utils.makeSafeTypeName p.PropertyType) 299 | |> strRep elementType (Utils.makeSafeTypeName elType)) 300 | |> String.concat "\n" 301 | 302 | templatePropClass 303 | |> strRep elementPropType (Utils.makeSafeTypeName elPropType) 304 | |> strRep elementType (Utils.makeSafeTypeName elType) 305 | |> strRep description (makeSafeDesc 1 elDesc) 306 | |> strRep jsonPropName (Utils.makeSafeTypeName (Utils.firstCharToLower elJsonName)) 307 | |> strRep body (propGetters + propSetters) -------------------------------------------------------------------------------- /src/IPlot.HighCharts/ReadMe.md: -------------------------------------------------------------------------------- 1 | # IPlot.HighCharts API Documentation 2 | 3 | HighCharts renderer for IPlot. 4 | 5 | ## Table of Contents 6 | 7 | - [Chart Functions](#chart-functions) 8 | - [Setting Basic Properties](#setting-basic-properties) 9 | - [Line / Scatter Plots](#line-/-scatter-plots) 10 | - [Scatter Plots](#scatter-plots) 11 | - [3D Charts](#3d-charts) 12 | - [Heatmaps](#heatmaps) 13 | - [Streamgraphs](#streamgraphs) 14 | - [Spline Charts](#spline-charts) 15 | - [Vector Plots](#vector-plots) 16 | - [Trees](#trees) 17 | - [Tilemaps](#tilemaps) 18 | - [Polymorphic Data Types](#polymorphic-data-types) 19 | 20 | # Chart Functions 21 | 22 | ## Chart.Plot 23 | 24 | Takes a collection of traces and produces a Chart object. 25 | 26 | ## Chart.With 27 | 28 | Sets a property on an element of a Chart object. 29 | 30 | ## Chart.Show 31 | 32 | Pops up the chart in a browser. 33 | 34 | ## Common Chart Property Setters 35 | 36 | Set the chart title, width and height. 37 | 38 | ## Chart Creation Utility Functions 39 | 40 | The following types of HighCharts series types are available: 41 | 42 | * Line / Scatter 43 | * Column / Bar 44 | * Error series 45 | * Cylinder 46 | * Funnel 47 | * Heatmap 48 | * Tilemap 49 | * Treemap 50 | * Venn 51 | * .. many others 52 | 53 | # Setting Basic Properties 54 | 55 | ## Set title 56 | ```fsharp 57 | Chart.Scatter [1.;2.;3.;5.] 58 | |> Chart.WithTitle "Lovejoy" 59 | ``` 60 | 61 | ## Set name of trace 62 | ```fsharp 63 | Chart.Scatter [1.;2.;3.;5.] 64 | |> Chart.With (Chart.Props.series.[0].name "Mr Susan") 65 | ``` 66 | 67 | ## Set line width 68 | ```fsharp 69 | Chart.Scatter [1.;2.;3.;5.] 70 | |> Chart.With (Chart.Props.series.[0].asScatter.lineWidth 5.) 71 | ``` 72 | 73 | ## Set line color 74 | ```fsharp 75 | Chart.Scatter [1.;2.;3.;5.] 76 | |> Chart.With (Chart.Props.series.[0].asScatter.color "#14b") 77 | ``` 78 | 79 | # Line / Scatter plots 80 | 81 | ```fsharp 82 | let trace1 = 83 | Line( 84 | data = [0.2; 0.8; 0.5; 1.1] 85 | ) 86 | 87 | let trace2 = 88 | Line( 89 | data = [0.6; 0.1; 0.3; 0.7] 90 | ) 91 | 92 | [trace1; trace2] 93 | |> Chart.Plot 94 | |> Chart.WithWidth 700 95 | |> Chart.WithHeight 500 96 | |> Chart.WithTitle "Two lines" 97 | |> Chart.Show 98 | ``` 99 | 100 | ![HC_TwoLines](https://user-images.githubusercontent.com/24556021/101266907-4c78eb80-374b-11eb-9a7c-4eae4e80ec31.png) 101 | 102 | # Scatter plots 103 | 104 | ## X/Y Scatter Plot 105 | 106 | ```fsharp 107 | [[1.;-1.]; [2.;1.5]; [3.;-0.5]; [4.;4.8]] 108 | |> Chart.Scatter 109 | |> Chart.With (Chart.Props.series.[0].name "XY scatter plot") 110 | |> Chart.With (Chart.Props.plotOptions.scatter.marker.symbol "diamond") 111 | |> Chart.WithWidth 700 112 | |> Chart.WithHeight 500 113 | |> Chart.Show 114 | ``` 115 | 116 | ![HC_XYScatter](https://user-images.githubusercontent.com/24556021/101266909-4d118200-374b-11eb-962e-f5cb13cafe85.png) 117 | 118 | # 3D Charts 119 | 120 | ## 3D Cylinder 121 | 122 | ```fsharp 123 | [1.; 2.; 3.; 4.; 3.; 2.; 1.] 124 | |> Chart.Cylinder 125 | |> Chart.With (Chart.Props.series.[0].asCylinder.colorByPoint true) 126 | |> Chart.WithWidth 700 127 | |> Chart.WithHeight 500 128 | |> Chart.Show 129 | ``` 130 | 131 | ![HC_Cylinder](https://user-images.githubusercontent.com/24556021/101266897-4aaf2800-374b-11eb-880c-eb1e431b3f8f.png) 132 | 133 | ## 3D Funnel plot 134 | 135 | ```fsharp 136 | [101.; 202.; 96.; 46.; 66.; 20.; 121.] 137 | |> Chart.Funnel3d 138 | |> Chart.With (Chart.Props.subtitle.text "Getting bigger") 139 | |> Chart.With (Chart.Props.subtitle.x 160.) 140 | |> Chart.With (Chart.Props.subtitle.y 220.) 141 | |> Chart.WithWidth 700 142 | |> Chart.WithHeight 500 143 | |> Chart.Show 144 | 145 | ``` 146 | 147 | ![HC_Funnel3d](https://user-images.githubusercontent.com/24556021/101266899-4b47be80-374b-11eb-93f3-f8141590ae1a.png) 148 | 149 | ## 3D Pyramid plot 150 | 151 | ```fsharp 152 | [31.; 16.; 29.; 4.; 11.; 19.; 22.] 153 | |> Chart.Pyramid3d 154 | |> Chart.With (Chart.Props.subtitle.text "Getting smaller") 155 | |> Chart.With (Chart.Props.subtitle.x 160.) 156 | |> Chart.With (Chart.Props.subtitle.y 220.) 157 | |> Chart.WithWidth 700 158 | |> Chart.WithHeight 500 159 | |> Chart.Show 160 | ``` 161 | 162 | ![HC_Pyramid](https://user-images.githubusercontent.com/24556021/101266901-4b47be80-374b-11eb-81d8-952f82bc3b4d.png) 163 | 164 | # Error Bars 165 | 166 | ## Simple error bar plot 167 | 168 | ```fsharp 169 | let trace1 = 170 | Errorbar( 171 | data_mat = [[22.;48.];[41.;49.];[31.;48.];[19.;24.];[11.;15.];[40.;49.]] 172 | ) 173 | 174 | [trace1] 175 | |> Chart.Plot 176 | |> Chart.With (Chart.Props.chart_iplot.backgroundColor "#353535") 177 | |> Chart.With (Chart.Props.plotOptions.errorbar.lineWidth 5.0) 178 | |> Chart.With (Chart.Props.plotOptions.errorbar.color "#76F") 179 | |> Chart.With (Chart.Props.series.[0].asErrorbar.whiskerWidth 6.0) 180 | |> Chart.WithWidth 700 181 | |> Chart.WithHeight 500 182 | |> Chart.Show 183 | ``` 184 | 185 | ![HC_ErrorBar](https://user-images.githubusercontent.com/24556021/101266898-4aaf2800-374b-11eb-8a84-582d5db3d0b9.png) 186 | 187 | # Heatmaps 188 | 189 | ## Colourful heatmap 190 | 191 | ```fsharp 192 | let r = Random(931) 193 | let trace1 = 194 | Heatmap( 195 | data_mat = [ for x in 1..100 do 196 | for y in 1..40 do 197 | yield [ float x; float y; r.NextDouble() ] ], 198 | name = "Heatmap" 199 | ) 200 | 201 | [trace1] 202 | |> Chart.Plot 203 | |> Chart.With (Chart.Props.series.[0].asHeatmap.borderWidth 0.) 204 | |> Chart.With (Chart.Props.colorAxis.[0].min 0.) 205 | |> Chart.With (Chart.Props.colorAxis.[0].max 1.) 206 | |> Chart.With (Chart.Props.colorAxis.[0].minColor "#033") 207 | |> Chart.With (Chart.Props.colorAxis.[0].maxColor "#f33") 208 | |> Chart.WithWidth 700 209 | |> Chart.WithHeight 500 210 | |> Chart.Show 211 | ``` 212 | 213 | ![HC_Heatmap](https://user-images.githubusercontent.com/24556021/101266900-4b47be80-374b-11eb-802a-9e78f234148f.png) 214 | 215 | # Streamgraphs 216 | 217 | ## Basic streamgraph 218 | 219 | ```fsharp 220 | let r = Random(931) 221 | let trace1 = 222 | Streamgraph( 223 | data = [ for x in 1..100 -> r.NextDouble() ], 224 | name = "Stream A" 225 | ) 226 | 227 | [trace1] 228 | |> Chart.Plot 229 | |> Chart.With (Chart.Props.series.[0].asStreamgraph.borderColor "#f32") 230 | |> Chart.With (Chart.Props.series.[0].asStreamgraph.borderWidth 5.) 231 | |> Chart.WithWidth 700 232 | |> Chart.WithHeight 500 233 | |> Chart.Show 234 | ``` 235 | 236 | ![HC_Streamgraph](https://user-images.githubusercontent.com/24556021/101266904-4be05500-374b-11eb-9de4-48535f4254a4.png) 237 | 238 | # Spline charts 239 | 240 | ## Basic Spline chart 241 | ```fsharp 242 | let r = Random(78) 243 | let trace1 = 244 | Spline( 245 | data_mat = [ for x in 1..7 -> [r.NextDouble(); r.NextDouble()] ], 246 | name = "Spline" 247 | ) 248 | 249 | [trace1] 250 | |> Chart.Plot 251 | |> Chart.With (Chart.Props.series.[0].asSpline.dashStyle "ShortDashDot") 252 | |> Chart.With (Chart.Props.series.[0].asSpline.lineWidth 6.) 253 | |> Chart.WithWidth 700 254 | |> Chart.WithHeight 500 255 | |> Chart.Show 256 | ``` 257 | 258 | ![HC_Spline](https://user-images.githubusercontent.com/24556021/101266902-4be05500-374b-11eb-85d9-1409bc2ca2b4.png) 259 | 260 | # Vector plots 261 | 262 | ## Vector Flow 263 | 264 | ```fsharp 265 | let trace1 = 266 | Vector( 267 | data_mat = [ 268 | for x in 0.0..0.1..1.0 do 269 | for y in 0.0..0.1..1.0 do 270 | yield [x; y; 3.0+4.0*x*x+y*y; Math.Atan2(y,x)*180./Math.PI]; ], 271 | name = "Vector flow" 272 | ) 273 | 274 | [trace1] 275 | |> Chart.Plot 276 | |> Chart.With (Chart.Props.series.[0].asVector.lineWidth 5.) 277 | |> Chart.With (Chart.Props.series.[0].asVector.color "red") 278 | |> Chart.With (Chart.Props.yAxis.[0].max "1.0") 279 | |> Chart.WithWidth 700 280 | |> Chart.WithHeight 500 281 | |> Chart.Show 282 | ``` 283 | 284 | ![HC_Vector](https://user-images.githubusercontent.com/24556021/101266908-4c78eb80-374b-11eb-8c2c-c5ca3e3f0f38.png) 285 | 286 | # Trees 287 | 288 | ## Sunburst 289 | ```fsharp 290 | let r = Random(77) 291 | let rec makeTree parent curDepth curName = 292 | let curVal = r.Next(10) + 1 |> float 293 | let cur = 294 | match parent with 295 | | None -> 296 | Data_obj( 297 | name = curName, 298 | id = curName, 299 | value = Nullable(curVal) 300 | ) 301 | | Some(p) -> 302 | Data_obj( 303 | name = curName, 304 | id = curName, 305 | parent = p, 306 | value = Nullable(curVal) 307 | ) 308 | [ 309 | yield cur 310 | if curDepth < 4 then 311 | let numChildren = r.Next(5) 312 | let childTrees = 313 | [ for child in 1..numChildren -> makeTree (Some(curName)) (curDepth+1) (sprintf "%s_%i" curName child) ] 314 | |> List.concat 315 | 316 | yield! childTrees 317 | ] 318 | 319 | 320 | let trace1 = 321 | Sunburst( 322 | data_obj = makeTree None 0 "Base", 323 | name = "Sunburst", 324 | levels = [ 325 | Levels( 326 | level = Nullable(2.), 327 | colorByPoint = Nullable(true)) 328 | ] 329 | ) 330 | 331 | [trace1] 332 | |> Chart.Plot 333 | |> Chart.With (Chart.Props.series.[0].asSunburst.lineWidth 2.0) 334 | |> Chart.With (Chart.Props.series.[0].asSunburst.colorByPoint true) 335 | |> Chart.WithWidth 700 336 | |> Chart.WithHeight 500 337 | |> Chart.Show 338 | ``` 339 | 340 | ![HC_Sunburst](https://user-images.githubusercontent.com/24556021/101266905-4be05500-374b-11eb-83f4-a38f0c2f2d35.png) 341 | 342 | # Tilemaps 343 | 344 | ## Simple tilemap 345 | 346 | ```fsharp 347 | let trace1 = 348 | Tilemap( 349 | data_obj = [ 350 | Data_obj( 351 | name="AA", 352 | x=Nullable(1.), 353 | y=Nullable(3.), 354 | color="#52a" 355 | ); 356 | Data_obj( 357 | name="AB", 358 | x=Nullable(2.), 359 | y=Nullable(3.), 360 | color="#39b" 361 | ); 362 | Data_obj( 363 | name="VZ", 364 | x=Nullable(1.), 365 | y=Nullable(2.), 366 | color="#3cc" 367 | ); 368 | Data_obj( 369 | name="PO", 370 | x=Nullable(2.), 371 | y=Nullable(2.), 372 | color="#33a" 373 | ) 374 | ], 375 | name = "Tilemap" 376 | ) 377 | 378 | [trace1] 379 | |> Chart.Plot 380 | |> Chart.With (Chart.Props.series.[0].asTilemap.dataLabels.[0].enabled true) 381 | |> Chart.With (Chart.Props.series.[0].asTilemap.dataLabels.[0].format "{point.name}") 382 | |> Chart.With (Chart.Props.series.[0].asTilemap.pointPadding 4.) 383 | |> Chart.With (Chart.Props.plotOptions.tilemap.borderColor "#4c4") 384 | |> Chart.WithWidth 600 385 | |> Chart.WithHeight 600 386 | |> Chart.Show 387 | ``` 388 | 389 | ![HC_Tilemap](https://user-images.githubusercontent.com/24556021/101266906-4c78eb80-374b-11eb-8165-35d966043ee0.png) 390 | 391 | # Polymorphic Data Types 392 | 393 | Some series types allow using datapoints which are objects with numerous fields. For these, you can use the additional ``data_obj`` property to set the properties of these datapoint objects. 394 | -------------------------------------------------------------------------------- /src/IPlot.Plotly/BaseProps/Trace_IProp.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace IPlot.Plotly 3 | { 4 | /// Base class for all Series types 5 | public class Trace_IProp : ChartProp 6 | { 7 | /// Cast trace to Bar type for setting specific parameters 8 | public Bar_IProp asBar 9 | { 10 | get { return new Bar_IProp() { _parent = _parent }; } 11 | } 12 | 13 | /// Cast trace to Barpolar type for setting specific parameters 14 | public Barpolar_IProp asBarpolar 15 | { 16 | get { return new Barpolar_IProp() { _parent = _parent }; } 17 | } 18 | 19 | /// Cast trace to Box type for setting specific parameters 20 | public Box_IProp asBox 21 | { 22 | get { return new Box_IProp() { _parent = _parent }; } 23 | } 24 | 25 | /// Cast trace to Candlestick type for setting specific parameters 26 | public Candlestick_IProp asCandlestick 27 | { 28 | get { return new Candlestick_IProp() { _parent = _parent }; } 29 | } 30 | 31 | /// Cast trace to Carpet type for setting specific parameters 32 | public Carpet_IProp asCarpet 33 | { 34 | get { return new Carpet_IProp() { _parent = _parent }; } 35 | } 36 | 37 | /// Cast trace to Choropleth type for setting specific parameters 38 | public Choropleth_IProp asChoropleth 39 | { 40 | get { return new Choropleth_IProp() { _parent = _parent }; } 41 | } 42 | 43 | /// Cast trace to Choroplethmapbox type for setting specific parameters 44 | public Choroplethmapbox_IProp asChoroplethmapbox 45 | { 46 | get { return new Choroplethmapbox_IProp() { _parent = _parent }; } 47 | } 48 | 49 | /// Cast trace to Cone type for setting specific parameters 50 | public Cone_IProp asCone 51 | { 52 | get { return new Cone_IProp() { _parent = _parent }; } 53 | } 54 | 55 | /// Cast trace to Contour type for setting specific parameters 56 | public Contour_IProp asContour 57 | { 58 | get { return new Contour_IProp() { _parent = _parent }; } 59 | } 60 | 61 | /// Cast trace to Contourcarpet type for setting specific parameters 62 | public Contourcarpet_IProp asContourcarpet 63 | { 64 | get { return new Contourcarpet_IProp() { _parent = _parent }; } 65 | } 66 | 67 | /// Cast trace to Densitymapbox type for setting specific parameters 68 | public Densitymapbox_IProp asDensitymapbox 69 | { 70 | get { return new Densitymapbox_IProp() { _parent = _parent }; } 71 | } 72 | 73 | /// Cast trace to Funnel type for setting specific parameters 74 | public Funnel_IProp asFunnel 75 | { 76 | get { return new Funnel_IProp() { _parent = _parent }; } 77 | } 78 | 79 | /// Cast trace to Funnelarea type for setting specific parameters 80 | public Funnelarea_IProp asFunnelarea 81 | { 82 | get { return new Funnelarea_IProp() { _parent = _parent }; } 83 | } 84 | 85 | /// Cast trace to Heatmap type for setting specific parameters 86 | public Heatmap_IProp asHeatmap 87 | { 88 | get { return new Heatmap_IProp() { _parent = _parent }; } 89 | } 90 | 91 | /// Cast trace to Heatmapgl type for setting specific parameters 92 | public Heatmapgl_IProp asHeatmapgl 93 | { 94 | get { return new Heatmapgl_IProp() { _parent = _parent }; } 95 | } 96 | 97 | /// Cast trace to Histogram type for setting specific parameters 98 | public Histogram_IProp asHistogram 99 | { 100 | get { return new Histogram_IProp() { _parent = _parent }; } 101 | } 102 | 103 | /// Cast trace to Histogram2d type for setting specific parameters 104 | public Histogram2d_IProp asHistogram2d 105 | { 106 | get { return new Histogram2d_IProp() { _parent = _parent }; } 107 | } 108 | 109 | /// Cast trace to Histogram2dcontour type for setting specific parameters 110 | public Histogram2dcontour_IProp asHistogram2dcontour 111 | { 112 | get { return new Histogram2dcontour_IProp() { _parent = _parent }; } 113 | } 114 | 115 | /// Cast trace to Image type for setting specific parameters 116 | public Image_IProp asImage 117 | { 118 | get { return new Image_IProp() { _parent = _parent }; } 119 | } 120 | 121 | /// Cast trace to Indicator type for setting specific parameters 122 | public Indicator_IProp asIndicator 123 | { 124 | get { return new Indicator_IProp() { _parent = _parent }; } 125 | } 126 | 127 | /// Cast trace to Isosurface type for setting specific parameters 128 | public Isosurface_IProp asIsosurface 129 | { 130 | get { return new Isosurface_IProp() { _parent = _parent }; } 131 | } 132 | 133 | /// Cast trace to Mesh3d type for setting specific parameters 134 | public Mesh3d_IProp asMesh3d 135 | { 136 | get { return new Mesh3d_IProp() { _parent = _parent }; } 137 | } 138 | 139 | /// Cast trace to Ohlc type for setting specific parameters 140 | public Ohlc_IProp asOhlc 141 | { 142 | get { return new Ohlc_IProp() { _parent = _parent }; } 143 | } 144 | 145 | /// Cast trace to Parcats type for setting specific parameters 146 | public Parcats_IProp asParcats 147 | { 148 | get { return new Parcats_IProp() { _parent = _parent }; } 149 | } 150 | 151 | /// Cast trace to Parcoords type for setting specific parameters 152 | public Parcoords_IProp asParcoords 153 | { 154 | get { return new Parcoords_IProp() { _parent = _parent }; } 155 | } 156 | 157 | /// Cast trace to Pie type for setting specific parameters 158 | public Pie_IProp asPie 159 | { 160 | get { return new Pie_IProp() { _parent = _parent }; } 161 | } 162 | 163 | /// Cast trace to Pointcloud type for setting specific parameters 164 | public Pointcloud_IProp asPointcloud 165 | { 166 | get { return new Pointcloud_IProp() { _parent = _parent }; } 167 | } 168 | 169 | /// Cast trace to Sankey type for setting specific parameters 170 | public Sankey_IProp asSankey 171 | { 172 | get { return new Sankey_IProp() { _parent = _parent }; } 173 | } 174 | 175 | /// Cast trace to Scatter type for setting specific parameters 176 | public Scatter_IProp asScatter 177 | { 178 | get { return new Scatter_IProp() { _parent = _parent }; } 179 | } 180 | 181 | /// Cast trace to Scatter3d type for setting specific parameters 182 | public Scatter3d_IProp asScatter3d 183 | { 184 | get { return new Scatter3d_IProp() { _parent = _parent }; } 185 | } 186 | 187 | /// Cast trace to Scattercarpet type for setting specific parameters 188 | public Scattercarpet_IProp asScattercarpet 189 | { 190 | get { return new Scattercarpet_IProp() { _parent = _parent }; } 191 | } 192 | 193 | /// Cast trace to Scattergeo type for setting specific parameters 194 | public Scattergeo_IProp asScattergeo 195 | { 196 | get { return new Scattergeo_IProp() { _parent = _parent }; } 197 | } 198 | 199 | /// Cast trace to Scattergl type for setting specific parameters 200 | public Scattergl_IProp asScattergl 201 | { 202 | get { return new Scattergl_IProp() { _parent = _parent }; } 203 | } 204 | 205 | /// Cast trace to Scattermapbox type for setting specific parameters 206 | public Scattermapbox_IProp asScattermapbox 207 | { 208 | get { return new Scattermapbox_IProp() { _parent = _parent }; } 209 | } 210 | 211 | /// Cast trace to Scatterpolar type for setting specific parameters 212 | public Scatterpolar_IProp asScatterpolar 213 | { 214 | get { return new Scatterpolar_IProp() { _parent = _parent }; } 215 | } 216 | 217 | /// Cast trace to Scatterpolargl type for setting specific parameters 218 | public Scatterpolargl_IProp asScatterpolargl 219 | { 220 | get { return new Scatterpolargl_IProp() { _parent = _parent }; } 221 | } 222 | 223 | /// Cast trace to Scatterternary type for setting specific parameters 224 | public Scatterternary_IProp asScatterternary 225 | { 226 | get { return new Scatterternary_IProp() { _parent = _parent }; } 227 | } 228 | 229 | /// Cast trace to Splom type for setting specific parameters 230 | public Splom_IProp asSplom 231 | { 232 | get { return new Splom_IProp() { _parent = _parent }; } 233 | } 234 | 235 | /// Cast trace to Streamtube type for setting specific parameters 236 | public Streamtube_IProp asStreamtube 237 | { 238 | get { return new Streamtube_IProp() { _parent = _parent }; } 239 | } 240 | 241 | /// Cast trace to Sunburst type for setting specific parameters 242 | public Sunburst_IProp asSunburst 243 | { 244 | get { return new Sunburst_IProp() { _parent = _parent }; } 245 | } 246 | 247 | /// Cast trace to Surface type for setting specific parameters 248 | public Surface_IProp asSurface 249 | { 250 | get { return new Surface_IProp() { _parent = _parent }; } 251 | } 252 | 253 | /// Cast trace to Table type for setting specific parameters 254 | public Table_IProp asTable 255 | { 256 | get { return new Table_IProp() { _parent = _parent }; } 257 | } 258 | 259 | /// Cast trace to Treemap type for setting specific parameters 260 | public Treemap_IProp asTreemap 261 | { 262 | get { return new Treemap_IProp() { _parent = _parent }; } 263 | } 264 | 265 | /// Cast trace to Violin type for setting specific parameters 266 | public Violin_IProp asViolin 267 | { 268 | get { return new Violin_IProp() { _parent = _parent }; } 269 | } 270 | 271 | /// Cast trace to Volume type for setting specific parameters 272 | public Volume_IProp asVolume 273 | { 274 | get { return new Volume_IProp() { _parent = _parent }; } 275 | } 276 | 277 | /// Cast trace to Waterfall type for setting specific parameters 278 | public Waterfall_IProp asWaterfall 279 | { 280 | get { return new Waterfall_IProp() { _parent = _parent }; } 281 | } 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /src/IPlot.HighCharts/Html.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace IPlot.HighCharts 5 | { 6 | /// Class containing HTML templates and browser display code 7 | public class Html 8 | { 9 | /// HighCharts source URL 10 | public const string DefaultHighChartsSrc = "https://code.highcharts.com/"; 11 | 12 | /// HTML template for full page 13 | public const string pageTemplate = 14 | @" 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | [MODULESRC] 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | [CHART] 31 | 32 | "; 33 | 34 | /// Template for HighCharts module includes 35 | public const string moduleTemplate = 36 | @""; 37 | 38 | /// Inline HTML 39 | public const string inlineTemplate = 40 | @"
41 | "; 44 | 45 | /// Default theme properties 46 | public const string themeString = 47 | @"Highcharts.theme = { 48 | colors: ['#DDDF0C', '#54BF3A', '#EC272A', '#41D8FE', '#5B47D1', 49 | '#5B47D1', '#E978FC', '#C51F8D', '#DCDCDC'], 50 | chart: { 51 | backgroundColor: 'rgba(17, 17, 17, .01)', 52 | borderColor: '#222', 53 | className: 'dark-container', 54 | plotBackgroundColor: 'rgba(255, 255, 255, .1)', 55 | plotBorderColor: '#A0A0A0', 56 | plotBorderWidth: 1 57 | }, 58 | title: { 59 | style: { 60 | color: '#C0C0C0' 61 | } 62 | }, 63 | subtitle: { 64 | style: { 65 | color: '#666' 66 | } 67 | }, 68 | xAxis: { 69 | gridLineColor: '#444', 70 | gridLineWidth: 1, 71 | labels: { 72 | style: { 73 | color: '#A0A0A0' 74 | } 75 | }, 76 | lineColor: '#A0A0A0', 77 | tickColor: '#A0A0A0', 78 | title: { 79 | style: { 80 | color: '#CCC' 81 | } 82 | } 83 | }, 84 | yAxis: { 85 | gridLineColor: '#444', 86 | labels: { 87 | style: { 88 | color: '#A0A0A0' 89 | } 90 | }, 91 | lineColor: '#A0A0A0', 92 | minorTickInterval: null, 93 | tickColor: '#A0A0A0', 94 | tickWidth: 1, 95 | title: { 96 | style: { 97 | color: '#CCC' 98 | } 99 | } 100 | }, 101 | tooltip: { 102 | backgroundColor: 'rgba(0, 0, 0, 0.75)', 103 | style: { 104 | color: '#F0F0F0' 105 | } 106 | }, 107 | toolbar: { 108 | itemStyle: { 109 | color: 'silver' 110 | } 111 | }, 112 | plotOptions: { 113 | line: { 114 | dataLabels: { 115 | color: '#CCC' 116 | }, 117 | marker: { 118 | lineColor: '#333' 119 | } 120 | } 121 | }, 122 | legend: { 123 | backgroundColor: 'rgba(0, 0, 0, 0.3)', 124 | itemStyle: { 125 | color: '#A0A0A0' 126 | }, 127 | itemHoverStyle: { 128 | color: '#FFF' 129 | }, 130 | itemHiddenStyle: { 131 | color: '#444' 132 | }, 133 | title: { 134 | style: { 135 | color: '#C0C0C0' 136 | } 137 | } 138 | }, 139 | credits: { 140 | style: { 141 | color: '#666' 142 | } 143 | }, 144 | labels: { 145 | style: { 146 | color: '#CCC' 147 | } 148 | }, 149 | navigation: { 150 | buttonOptions: { 151 | symbolStroke: '#DDDDDD', 152 | theme: { 153 | fill: { 154 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 155 | stops: [ 156 | [0.4, '#606060'], 157 | [0.6, '#333333'] 158 | ] 159 | }, 160 | stroke: '#000000' 161 | }, 162 | align: 'left' 163 | } 164 | }, 165 | // scroll charts 166 | rangeSelector: { 167 | buttonTheme: { 168 | fill: { 169 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 170 | stops: [ 171 | [0.4, '#888'], 172 | [0.6, '#555'] 173 | ] 174 | }, 175 | stroke: '#000000', 176 | style: { 177 | color: '#CCC', 178 | fontWeight: 'bold' 179 | }, 180 | states: { 181 | hover: { 182 | fill: { 183 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 184 | stops: [ 185 | [0.4, '#BBB'], 186 | [0.6, '#888'] 187 | ] 188 | }, 189 | stroke: '#000000', 190 | style: { 191 | color: 'white' 192 | } 193 | }, 194 | select: { 195 | fill: { 196 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 197 | stops: [ 198 | [0.1, '#000'], 199 | [0.3, '#333'] 200 | ] 201 | }, 202 | stroke: '#000000', 203 | style: { 204 | color: 'yellow' 205 | } 206 | } 207 | } 208 | }, 209 | inputStyle: { 210 | backgroundColor: '#333', 211 | color: 'silver' 212 | }, 213 | labelStyle: { 214 | color: 'silver' 215 | } 216 | }, 217 | navigator: { 218 | handles: { 219 | backgroundColor: '#666', 220 | borderColor: '#AAA' 221 | }, 222 | outlineColor: '#CCC', 223 | maskFill: 'rgba(16, 16, 16, 0.5)', 224 | series: { 225 | color: '#7798BF', 226 | lineColor: '#A6C7ED' 227 | } 228 | }, 229 | scrollbar: { 230 | barBackgroundColor: { 231 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 232 | stops: [ 233 | [0.4, '#888'], 234 | [0.6, '#555'] 235 | ] 236 | }, 237 | barBorderColor: '#CCC', 238 | buttonArrowColor: '#CCC', 239 | buttonBackgroundColor: { 240 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 241 | stops: [ 242 | [0.4, '#888'], 243 | [0.6, '#555'] 244 | ] 245 | }, 246 | buttonBorderColor: '#CCC', 247 | rifleColor: '#FFF', 248 | trackBackgroundColor: { 249 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 250 | stops: [ 251 | [0, '#000'], 252 | [1, '#333'] 253 | ] 254 | }, 255 | trackBorderColor: '#666' 256 | } 257 | }; 258 | 259 | Highcharts.setOptions(Highcharts.theme);"; 260 | 261 | /// Javascript plotting code only 262 | public const string jsTemplate = 263 | @""; 266 | 267 | /// Template for Plotly plotting code 268 | public const string jsFunctionTemplate = 269 | @"[THEME] 270 | Highcharts.chart('[ID]', [CHARTOBJ]);"; 271 | 272 | /// Display given html markup in default browser 273 | public static void showInBrowser(string html, string pageId) 274 | { 275 | var tempPath = Path.GetTempPath(); 276 | var file = $"{pageId}.html"; 277 | var path = Path.Combine(tempPath, file); 278 | File.WriteAllText(path, html); 279 | 280 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 281 | { 282 | var psi = new System.Diagnostics.ProcessStartInfo(path); 283 | psi.UseShellExecute = true; 284 | System.Diagnostics.Process.Start(psi); 285 | } 286 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) 287 | System.Diagnostics.Process.Start("xdg-open", path); 288 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) 289 | System.Diagnostics.Process.Start("open", path); 290 | else 291 | throw new System.Exception("Invalid operation: Not supported OS platform"); 292 | } 293 | } 294 | } -------------------------------------------------------------------------------- /src/IPlot.HighCharts/HighChartsChart.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Microsoft.FSharp.Core; 6 | using Newtonsoft.Json; 7 | 8 | namespace IPlot.HighCharts 9 | { 10 | /// The HighCharts chart object, containing all chart properties and traces 11 | public class HighChartsChart : ChartElement 12 | { 13 | /// Constructor with default chart property initialisation 14 | public HighChartsChart() 15 | { 16 | this.chart.chart_iplot = new Chart_iplot() 17 | { 18 | width = 900, 19 | height = 500 20 | }; 21 | this.chart.title = new Title() 22 | { 23 | text = "" 24 | }; 25 | } 26 | 27 | /// The chart object containing all chart properties 28 | public HighChart chart { get; set; } = new HighChart(); 29 | 30 | /// The chart's container div id 31 | public string id { get; set; } = Guid.NewGuid().ToString(); 32 | 33 | /// The highcharts.js src 34 | public string highchartsSrc { get; set; } = Html.DefaultHighChartsSrc; 35 | 36 | /// The collection of trace labels 37 | private IEnumerable _labels; 38 | 39 | /// Set a specific chart property, using the Chart.Props helper (F# style) 40 | public static FSharpFunc, FSharpFunc> WithFs 41 | { 42 | get 43 | { 44 | Func, HighChartsChart, HighChartsChart> lam = (propFun, HighChartsChart) => propFun((HighChartsChart)HighChartsChart.DeepClone()); 45 | var f = FuncConvert.FromFunc, HighChartsChart, HighChartsChart>(lam); 46 | 47 | return f; 48 | } 49 | } 50 | 51 | /// Set a specific chart property, using the Chart.Props helper 52 | public static HighChartsChart With(Func propFun, HighChartsChart HighChartsChart) 53 | { 54 | return propFun((HighChartsChart)HighChartsChart.DeepClone()); 55 | } 56 | 57 | /// Set a specific chart property, using the Chart.Props helper 58 | public HighChartsChart With(Func propFun) 59 | { 60 | return With(propFun, this); 61 | } 62 | 63 | /// Deep clone of the chart and all its properties 64 | public override ChartElement DeepClone() 65 | { 66 | var highchartsChart = new HighChartsChart(); 67 | highchartsChart.chart = (HighChart)this.chart.DeepClone(); 68 | highchartsChart.id = this.id; 69 | 70 | return highchartsChart; 71 | } 72 | 73 | /// Inline markup that can be embedded in a HTML document 74 | public string GetInlineHtml() 75 | { 76 | var plotting = GetPlottingJS(); 77 | return 78 | Html.inlineTemplate 79 | .Replace("[ID]", this.id) 80 | .Replace("[WIDTH]", this.chart.chart_iplot.width.ToString()) 81 | .Replace("[HEIGHT]", this.chart.chart_iplot.height.ToString()) 82 | .Replace("[PLOTTING]", plotting); 83 | } 84 | 85 | /// Inline javascript that can be embedded in a HTML document 86 | public string GetInlineJS() 87 | { 88 | var plotting = GetPlottingJS(); 89 | return 90 | Html.jsTemplate 91 | .Replace("[PLOTTING]", plotting); 92 | } 93 | 94 | /// Serialise chart object to JSON 95 | public string SerializeChart() 96 | { 97 | return JsonConvert.SerializeObject(this.chart, Formatting.None, new JsonSerializerSettings 98 | { 99 | NullValueHandling = NullValueHandling.Ignore 100 | }) 101 | .Replace("_iplot", string.Empty) 102 | .Replace("data_mat", "data") 103 | .Replace("data_obj", "data"); 104 | } 105 | 106 | /// Returns the JS to load relevant HighCharts modules 107 | public static IEnumerable GetModules(HighChartsChart chart) 108 | { 109 | var seriesTypes = chart.chart.series.Select(s => s.type_iplot); 110 | var modules = 111 | seriesTypes 112 | .SelectMany(t => ModuleLoading.GetDependencies(t)) 113 | .Distinct() 114 | .ToArray(); 115 | 116 | var moduleImports = 117 | modules 118 | .Select(m => 119 | Html.moduleTemplate 120 | .Replace("[MODULENAME]", m) 121 | .Replace("[HIGHCHARTSSRC]", chart.highchartsSrc)); 122 | 123 | return moduleImports; 124 | } 125 | 126 | /// Returns the chart's full HTML source. 127 | private string GetHtml() 128 | { 129 | var chartMarkup = GetInlineHtml(); 130 | var moduleSrc = String.Join("\n", GetModules(this)); 131 | 132 | return 133 | Html.pageTemplate 134 | .Replace("[CHART]", chartMarkup) 135 | .Replace("[MODULESRC]", moduleSrc) 136 | .Replace("[HIGHCHARTSSRC]", highchartsSrc); 137 | } 138 | 139 | /// The chart's plotting JavaScript code. 140 | private string GetPlottingJS() 141 | { 142 | var chartJson = SerializeChart(); 143 | 144 | return 145 | Html.jsFunctionTemplate 146 | .Replace("[THEME]", Html.themeString) 147 | .Replace("[ID]", this.id) 148 | .Replace("[CHARTOBJ]", chartJson); 149 | } 150 | 151 | /// Generate a chart from a collection of traces 152 | public void Plot(IEnumerable data) 153 | { 154 | this.chart.series = data.ToList(); 155 | } 156 | 157 | /// Display the chart in a browser 158 | public void Show() 159 | { 160 | var html = GetHtml(); 161 | Html.showInBrowser(html, this.id); 162 | } 163 | 164 | /// Combine charts together and display as a single page in default browser 165 | public static void ShowAll(IEnumerable charts) 166 | { 167 | var html = string.Join("",charts.Select(c => c.GetInlineHtml())); 168 | var highChartsSrc = charts.Any() ? charts.First().highchartsSrc : Html.DefaultHighChartsSrc; 169 | var modules = 170 | charts 171 | .SelectMany(c => GetModules(c)) 172 | .Distinct() 173 | .ToArray(); 174 | var moduleSrc = String.Join("\n", modules); 175 | 176 | var pageHtml = 177 | Html.pageTemplate 178 | .Replace("[CHART]", html) 179 | .Replace("[MODULESRC]", moduleSrc) 180 | .Replace("[HIGHCHARTSSRC]", highChartsSrc); 181 | 182 | var combinedChartId = Guid.NewGuid().ToString(); 183 | Html.showInBrowser(pageHtml, combinedChartId); 184 | } 185 | 186 | /// Save the chart to an HTML file 187 | public void SaveHtml(string path) 188 | { 189 | var html = GetHtml(); 190 | var dir = Path.GetDirectoryName(path); 191 | 192 | if (!String.IsNullOrEmpty(dir)) 193 | { 194 | if (!Directory.Exists(dir)) 195 | Directory.CreateDirectory(dir); 196 | } 197 | 198 | File.WriteAllText(path, html); 199 | } 200 | 201 | /// Combine charts together and save as a single HTML file 202 | public static void SaveHtmlAll(string path, IEnumerable charts) 203 | { 204 | var html = string.Join("",charts.Select(c => c.GetInlineHtml())); 205 | var highChartsSrc = charts.Any() ? charts.First().highchartsSrc : Html.DefaultHighChartsSrc; 206 | var modules = 207 | charts 208 | .SelectMany(c => GetModules(c)) 209 | .Distinct(); 210 | var moduleSrc = String.Join("\n", modules); 211 | 212 | var pageHtml = 213 | Html.pageTemplate 214 | .Replace("[CHART]", html) 215 | .Replace("[MODULESRC]", moduleSrc) 216 | .Replace("[HIGHCHARTSSRC]", highChartsSrc); 217 | 218 | var dir = Path.GetDirectoryName(path); 219 | 220 | if (!String.IsNullOrEmpty(dir)) 221 | { 222 | if (!Directory.Exists(dir)) 223 | Directory.CreateDirectory(dir); 224 | } 225 | 226 | File.WriteAllText(path, pageHtml); 227 | } 228 | 229 | /// Sets the chart's title 230 | public HighChartsChart WithTitle(string title) 231 | { 232 | this.chart.title.text = title; 233 | return this; 234 | } 235 | 236 | /// Sets the chart's X-axis title 237 | public HighChartsChart WithXTitle(string xTitle) 238 | { 239 | if (this.chart.xAxis == null) 240 | this.chart.xAxis = new XAxis[] { new XAxis() { title = new Title() { text = xTitle } } }; 241 | else if (!this.chart.xAxis.Any()) 242 | this.chart.xAxis = new XAxis[] { new XAxis() { title = new Title() { text = xTitle } } }; 243 | else 244 | { 245 | if (this.chart.xAxis.First().title == null) 246 | this.chart.xAxis.First().title = new Title() { text = xTitle }; 247 | else 248 | this.chart.xAxis.First().title.text = xTitle; 249 | } 250 | 251 | return this; 252 | } 253 | 254 | /// Sets the chart's Y-axis title 255 | public HighChartsChart WithYTitle(string yTitle) 256 | { 257 | if (this.chart.yAxis == null) 258 | this.chart.yAxis = new YAxis[] { new YAxis() { title = new Title() { text = yTitle } } }; 259 | else if (!this.chart.yAxis.Any()) 260 | this.chart.yAxis = new YAxis[] { new YAxis() { title = new Title() { text = yTitle } } }; 261 | else 262 | { 263 | if (this.chart.yAxis.First().title == null) 264 | this.chart.yAxis.First().title = new Title() { text = yTitle }; 265 | else 266 | this.chart.yAxis.First().title.text = yTitle; 267 | } 268 | 269 | return this; 270 | } 271 | 272 | /// Sets the chart's width 273 | public HighChartsChart WithWidth(int width) 274 | { 275 | this.chart.chart_iplot.width = width; 276 | return this; 277 | } 278 | 279 | /// Sets the chart's height 280 | public HighChartsChart WithHeight(int height) 281 | { 282 | this.chart.chart_iplot.height = height; 283 | return this; 284 | } 285 | 286 | /// Sets the chart's container div id 287 | public HighChartsChart WithId(string id) 288 | { 289 | this.id = id; 290 | return this; 291 | } 292 | 293 | /// Sets the data series label. Use this member if the 294 | /// chart's data is a single series 295 | public HighChartsChart WithLabel(string label) 296 | { 297 | _labels = new string[] { label }; 298 | return this; 299 | } 300 | 301 | /// Sets the data series labels. Use this method if the 302 | /// chart's data is a series collection 303 | public HighChartsChart WithLabels(IEnumerable labels) 304 | { 305 | _labels = labels.ToArray(); 306 | return this; 307 | } 308 | 309 | /// Sets the chart's width and height 310 | public HighChartsChart WithSize(int width, int height) 311 | { 312 | WithWidth(width); 313 | WithHeight(height); 314 | return this; 315 | } 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /src/IPlot.Plotly/PlotlyChart.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Microsoft.FSharp.Core; 6 | using Newtonsoft.Json; 7 | 8 | namespace IPlot.Plotly 9 | { 10 | /// The Plotly chart object, containing all chart properties and traces 11 | public class PlotlyChart : ChartElement 12 | { 13 | /// The collection of traces associated with the chart 14 | public Trace[] traces { get; set; } = new Trace[] { }; 15 | 16 | /// The layout properties of the chart 17 | public Layout layout { get; set; } = null; 18 | 19 | /// The width of the chart container element 20 | public int width { get; set; } = 900; 21 | 22 | /// The height of the chart container element 23 | public int height { get; set; } = 500; 24 | 25 | /// The chart's container div id 26 | public string id { get; set; } = Guid.NewGuid().ToString(); 27 | 28 | /// The plotly.js src 29 | public string plotlySrc { get; set; } = Html.DefaultPlotlySrc; 30 | 31 | /// The collection of trace labels 32 | private IEnumerable _labels; 33 | 34 | /// Set a specific chart property, using the Chart.Props helper (F# style) 35 | public static FSharpFunc, FSharpFunc> WithFs 36 | { 37 | get 38 | { 39 | Func, PlotlyChart, PlotlyChart> lam = (propFun, PlotlyChart) => propFun((PlotlyChart)PlotlyChart.DeepClone()); 40 | var f = FuncConvert.FromFunc(lam); 41 | 42 | return f; 43 | } 44 | } 45 | 46 | /// Set a specific chart property, using the Chart.Props helper 47 | public static PlotlyChart With(Func propFun, PlotlyChart PlotlyChart) 48 | { 49 | return propFun((PlotlyChart)PlotlyChart.DeepClone()); 50 | } 51 | 52 | /// Set a specific chart property, using the Chart.Props helper 53 | public PlotlyChart With(Func propFun) 54 | { 55 | return With(propFun, this); 56 | } 57 | 58 | /// Deep clone of the chart and all its properties 59 | public override ChartElement DeepClone() 60 | { 61 | var plotlyChart = new PlotlyChart(); 62 | 63 | if (this.traces.Length > 0) 64 | { 65 | plotlyChart.traces = new Trace[this.traces.Length]; 66 | for (int i = 0; i < this.traces.Length; i++) 67 | { 68 | plotlyChart.traces[i] = (Trace)this.traces[i].DeepClone(); 69 | } 70 | } 71 | 72 | if (this.layout != null) 73 | plotlyChart.layout = (Layout)this.layout.DeepClone(); 74 | 75 | plotlyChart.width = this.width; 76 | plotlyChart.height = this.height; 77 | plotlyChart.id = this.id; 78 | plotlyChart.plotlySrc = this.plotlySrc; 79 | 80 | if (this._labels != null) 81 | { 82 | var newLabels = new List(); 83 | foreach (var label in this._labels) 84 | newLabels.Add(label); 85 | 86 | plotlyChart._labels = newLabels; 87 | } 88 | 89 | return plotlyChart; 90 | } 91 | 92 | /// Inline markup that can be embedded in a HTML document 93 | public string GetInlineHtml() 94 | { 95 | var plotting = GetPlottingJS(); 96 | return 97 | Html.inlineTemplate 98 | .Replace("[ID]", this.id) 99 | .Replace("[WIDTH]", this.width.ToString()) 100 | .Replace("[HEIGHT]", this.height.ToString()) 101 | .Replace("[PLOTTING]", plotting); 102 | } 103 | 104 | /// Inline javascript that can be embedded in a HTML document 105 | public string GetInlineJS() 106 | { 107 | var plotting = GetPlottingJS(); 108 | return 109 | Html.jsTemplate 110 | .Replace("[PLOTTING]", plotting); 111 | } 112 | 113 | /// Serialise all traces to JSON 114 | private string SerializeTraces() 115 | { 116 | Func,string> serialiseFunc = traceArr => 117 | { 118 | return JsonConvert.SerializeObject(this.traces, Formatting.None, new JsonSerializerSettings 119 | { 120 | NullValueHandling = NullValueHandling.Ignore 121 | }) 122 | .Replace("_iplot", string.Empty) 123 | .Replace("xt_", "x") 124 | .Replace("xs_", "x") 125 | .Replace("yt_", "y") 126 | .Replace("ys_", "y") 127 | .Replace("zs_", "z"); 128 | }; 129 | 130 | if ((this._labels == null) || !this._labels.Any()) 131 | return serialiseFunc(traces); 132 | 133 | var namedTraces = 134 | this._labels 135 | .Zip(traces, (n,t) => (n,t)) 136 | .Select((nt,i) => 137 | { 138 | nt.Item2.name = nt.Item1; 139 | return nt.Item2; 140 | }).ToArray(); 141 | 142 | return serialiseFunc(namedTraces); 143 | } 144 | 145 | /// Returns the chart's full HTML source 146 | private string GetHtml() 147 | { 148 | var chartMarkup = GetInlineHtml(); 149 | return 150 | Html.pageTemplate 151 | .Replace("[PLOTLYSRC]", this.plotlySrc) 152 | .Replace("[CHART]", chartMarkup); 153 | } 154 | 155 | /// The chart's plotting JavaScript code 156 | private string GetPlottingJS() 157 | { 158 | var tracesJson = SerializeTraces(); 159 | var layout = this.layout == null ? new Layout() : (Layout)this.layout.DeepClone(); 160 | layout.width = this.width; 161 | layout.height = this.height; 162 | 163 | var layoutJson = JsonConvert.SerializeObject(layout, Formatting.None, new JsonSerializerSettings 164 | { 165 | NullValueHandling = NullValueHandling.Ignore 166 | }).Replace("_iplot", string.Empty); 167 | 168 | return 169 | Html.jsFunctionTemplate 170 | .Replace("[ID]", this.id) 171 | .Replace("[DATA]", tracesJson) 172 | .Replace("[LAYOUT]", layoutJson); 173 | } 174 | 175 | /// Generate a chart from a collection of traces, with optional layout 176 | public void Plot(IEnumerable data, Layout layout = null, IEnumerable labels = null) 177 | { 178 | this.traces = data.ToArray(); 179 | this.layout = layout; 180 | _labels = labels; 181 | } 182 | 183 | /// Display the chart in a browser 184 | public void Show() 185 | { 186 | var html = GetHtml(); 187 | Html.showInBrowser(html, this.id); 188 | } 189 | 190 | /// Combine charts together and display as a single page in default browser 191 | public static void ShowAll(IEnumerable charts) 192 | { 193 | var html = string.Join("",charts.Select(c => c.GetInlineHtml())); 194 | var plotlysrc = charts.Any() ? charts.First().plotlySrc : Html.DefaultPlotlySrc; 195 | 196 | var pageHtml = 197 | Html.pageTemplate 198 | .Replace("[PLOTLYSRC]", plotlysrc) 199 | .Replace("[CHART]", html); 200 | 201 | var combinedChartId = Guid.NewGuid().ToString(); 202 | Html.showInBrowser(pageHtml, combinedChartId); 203 | } 204 | 205 | /// Save the chart to an HTML file 206 | public void SaveHtml(string path) 207 | { 208 | var html = GetHtml(); 209 | var dir = Path.GetDirectoryName(path); 210 | 211 | if (!String.IsNullOrEmpty(dir)) 212 | { 213 | if (!Directory.Exists(dir)) 214 | Directory.CreateDirectory(dir); 215 | } 216 | 217 | File.WriteAllText(path, html); 218 | } 219 | 220 | /// Combine charts together and save as a single HTML file 221 | public static void SaveHtmlAll(string path, IEnumerable charts) 222 | { 223 | var html = string.Join("",charts.Select(c => c.GetInlineHtml())); 224 | var plotlysrc = charts.Any() ? charts.First().plotlySrc : Html.DefaultPlotlySrc; 225 | 226 | var pageHtml = 227 | Html.pageTemplate 228 | .Replace("[PLOTLYSRC]", plotlysrc) 229 | .Replace("[CHART]", html); 230 | 231 | var dir = Path.GetDirectoryName(path); 232 | 233 | if (!String.IsNullOrEmpty(dir)) 234 | { 235 | if (!Directory.Exists(dir)) 236 | Directory.CreateDirectory(dir); 237 | } 238 | 239 | File.WriteAllText(path, pageHtml); 240 | } 241 | 242 | /// Sets the chart's plotly.js src 243 | public PlotlyChart WithPlotlySrc(string src) 244 | { 245 | this.plotlySrc = src; 246 | return this; 247 | } 248 | 249 | /// Sets the chart's height 250 | public PlotlyChart WithHeight(int height) 251 | { 252 | this.height = height; 253 | return this; 254 | } 255 | 256 | /// Sets the chart's container div id 257 | public PlotlyChart WithId(string id) 258 | { 259 | this.id = id; 260 | return this; 261 | } 262 | 263 | /// Sets the data series label. Use this member if the 264 | /// chart's data is a single series 265 | public PlotlyChart WithLabel(string label) 266 | { 267 | _labels = new string[] { label }; 268 | return this; 269 | } 270 | 271 | /// Sets the data series labels. Use this method if the 272 | /// chart's data is a series collection 273 | public PlotlyChart WithLabels(IEnumerable labels) 274 | { 275 | _labels = labels.ToArray(); 276 | return this; 277 | } 278 | 279 | /// Sets the chart's configuration options 280 | public PlotlyChart WithLayout(Layout layout) 281 | { 282 | this.layout = layout; 283 | return this; 284 | } 285 | 286 | /// Display/hide the legend 287 | public PlotlyChart WithLegend(bool enabled) 288 | { 289 | if (this.layout == null) 290 | this.layout = new Layout() { showlegend = enabled }; 291 | else 292 | this.layout.showlegend = enabled; 293 | 294 | return this; 295 | } 296 | 297 | /// Sets the chart's width and height 298 | public PlotlyChart WithSize(int width, int height) 299 | { 300 | this.height = height; 301 | this.width = width; 302 | return this; 303 | } 304 | 305 | /// Sets the chart's title 306 | public PlotlyChart WithTitle(string title) 307 | { 308 | if (this.layout == null) 309 | this.layout = new Layout() { title = new Title() { text = title } }; 310 | else 311 | this.layout.title = new Title() { text = title }; 312 | 313 | return this; 314 | } 315 | 316 | /// Sets the chart's width 317 | public PlotlyChart WithWidth(int width) 318 | { 319 | this.width = width; 320 | return this; 321 | } 322 | 323 | /// Sets the chart's X-axis title 324 | public PlotlyChart WithXTitle(string xTitle) 325 | { 326 | if (this.layout == null) 327 | this.layout = new Layout() { xaxis = new Xaxis() { title = new Title() { text = xTitle } } }; 328 | else 329 | { 330 | if (this.layout.xaxis == null) 331 | this.layout.xaxis = new Xaxis() { title = new Title() { text = xTitle } }; 332 | else if (this.layout.xaxis.title == null) 333 | this.layout.xaxis.title = new Title() { text = xTitle }; 334 | else 335 | this.layout.xaxis.title.text = xTitle; 336 | 337 | if (this.layout.scene == null) 338 | this.layout.scene = new Scene() { xaxis = new Xaxis() { title = new Title() { text = xTitle } } }; 339 | else if (this.layout.scene.xaxis == null) 340 | this.layout.scene.xaxis = new Xaxis() { title = new Title() { text = xTitle } }; 341 | else if (this.layout.scene.xaxis.title == null) 342 | this.layout.scene.xaxis.title = new Title() { text = xTitle }; 343 | } 344 | 345 | return this; 346 | } 347 | 348 | /// Sets the chart's Y-axis title 349 | public PlotlyChart WithYTitle(string yTitle) 350 | { 351 | if (this.layout == null) 352 | this.layout = new Layout() { yaxis = new Yaxis() { title = new Title() { text = yTitle } } }; 353 | else 354 | { 355 | if (this.layout.yaxis == null) 356 | this.layout.yaxis = new Yaxis() { title = new Title() { text = yTitle } }; 357 | else if (this.layout.yaxis.title == null) 358 | this.layout.yaxis.title = new Title() { text = yTitle }; 359 | else 360 | this.layout.yaxis.title.text = yTitle; 361 | 362 | if (this.layout.scene == null) 363 | this.layout.scene = new Scene() { yaxis = new Yaxis() { title = new Title() { text = yTitle } } }; 364 | else if (this.layout.scene.yaxis == null) 365 | this.layout.scene.yaxis = new Yaxis() { title = new Title() { text = yTitle } }; 366 | else if (this.layout.scene.yaxis.title == null) 367 | this.layout.scene.yaxis.title = new Title() { text = yTitle }; 368 | } 369 | 370 | return this; 371 | } 372 | 373 | /// Sets the chart's Z-axis title 374 | public PlotlyChart WithZTitle(string zTitle) 375 | { 376 | if (this.layout == null) 377 | this.layout = new Layout() { scene = new Scene() { zaxis = new Zaxis() { title = new Title() { text = zTitle } } } }; 378 | else if (this.layout.scene == null) 379 | this.layout.scene = new Scene() { zaxis = new Zaxis() { title = new Title() { text = zTitle } } }; 380 | else if (this.layout.scene.zaxis == null) 381 | this.layout.scene.zaxis = new Zaxis() { title = new Title() { text = zTitle } }; 382 | else if (this.layout.scene.zaxis.title == null) 383 | this.layout.scene.zaxis.title = new Title() { text = zTitle }; 384 | else 385 | this.layout.scene.zaxis.title.text = zTitle; 386 | 387 | return this; 388 | } 389 | } 390 | } 391 | -------------------------------------------------------------------------------- /src/IPlot/HighCharts.Chart.fs: -------------------------------------------------------------------------------- 1 | namespace IPlot.HighCharts 2 | 3 | open System 4 | open System.Net.Http 5 | open System.Text 6 | open System.Net 7 | open System.IO 8 | 9 | type key = IConvertible 10 | type value = IConvertible 11 | 12 | type Chart() = 13 | static member val Props = HighChart_IProp() with get 14 | 15 | static member internal DateTimeToHighCharts (dt:DateTime) = 16 | dt.Subtract(DateTime(1970,1,1,0,0,0,DateTimeKind.Utc)).TotalMilliseconds 17 | 18 | static member With (propFun: Func) (chart: HighChartsChart) = 19 | propFun.Invoke (chart.DeepClone() :?> HighChartsChart) 20 | 21 | static member Plot data = 22 | let chart = HighChartsChart() 23 | chart.Plot [data] 24 | chart 25 | 26 | static member Plot (data: seq<#Trace>) = 27 | let chart = HighChartsChart() 28 | chart.Plot (Seq.cast data) 29 | chart 30 | 31 | /// Displays a chart in the default browser 32 | static member Show (chart: HighChartsChart) = chart.Show() 33 | 34 | /// Combine charts together and display as a single page in default browser 35 | static member ShowAll(charts: seq) = HighChartsChart.ShowAll charts 36 | 37 | static member Save (filename:string) (chart: HighChartsChart) = 38 | let json = chart.SerializeChart() 39 | 40 | try 41 | use clientHandler = new HttpClientHandler() 42 | clientHandler.ServerCertificateCustomValidationCallback <- (fun sender cert chain sslPolicyErrors -> true) 43 | 44 | use client = new HttpClient(clientHandler) 45 | client.Timeout <- TimeSpan.FromSeconds(3.) 46 | 47 | let jsonTemplate = "{ 48 | \"options\":##JSON##, 49 | \"type\":\"image/png\", 50 | \"async\":false 51 | }" 52 | 53 | let contentStr = jsonTemplate.Replace("##JSON##", json) 54 | use content = new StringContent(contentStr, Encoding.UTF8, "application/json") 55 | 56 | let resp = 57 | client.PostAsync("https://export.highcharts.com/", content) 58 | |> Async.AwaitTask 59 | |> Async.RunSynchronously 60 | 61 | match resp.StatusCode with 62 | | HttpStatusCode.OK -> 63 | let content = 64 | resp.Content.ReadAsStreamAsync() 65 | |> Async.AwaitTask 66 | |> Async.RunSynchronously 67 | 68 | let p = Path.GetDirectoryName filename 69 | if String.IsNullOrEmpty p |> not && Directory.Exists p |> not then 70 | Directory.CreateDirectory p 71 | |> ignore 72 | 73 | use fs = File.Create(filename) 74 | content.Seek(0L, SeekOrigin.Begin) |> ignore 75 | content.CopyTo(fs) 76 | printfn "Written %i bytes to %s" content.Length filename 77 | | _ -> 78 | printfn "Failed to save chart: %s" (string resp.StatusCode) 79 | with ex -> 80 | printfn "Exception saving chart: %s" (string ex) 81 | 82 | /// Displays a chart in the default browser 83 | static member SaveHtml (path: string) (chart: HighChartsChart) = chart.SaveHtml path 84 | 85 | /// Combine charts together and display as a single page in default browser 86 | static member SaveHtmlAll (path: string) (charts: seq) = HighChartsChart.SaveHtmlAll(path, charts) 87 | 88 | /// Sets the chart's width. 89 | static member WithTitle title (chart:HighChartsChart) = 90 | chart.WithTitle title 91 | 92 | /// Sets the chart's X-axis title. 93 | static member WithXTitle xTitle (chart:HighChartsChart) = 94 | chart.WithXTitle xTitle 95 | 96 | /// Sets the chart's Y-axis title. 97 | static member WithYTitle yTitle (chart:HighChartsChart) = 98 | chart.WithYTitle yTitle 99 | 100 | /// Sets the chart's width. 101 | static member WithWidth width (chart:HighChartsChart) = 102 | chart.WithWidth width 103 | 104 | /// Sets the chart's height. 105 | static member WithHeight height (chart:HighChartsChart) = 106 | chart.WithHeight height 107 | 108 | /// Sets the chart's container div id. 109 | static member WithId id (chart:HighChartsChart) = 110 | chart.WithId id 111 | 112 | /// Sets the data series label. Use this member if the 113 | /// chart's data is a single series. 114 | static member WithLabel label (chart:HighChartsChart) = 115 | chart.WithLabel label 116 | 117 | /// Sets the data series labels. Use this member if the 118 | /// chart's data is a series collection. 119 | static member WithLabels labels (chart:HighChartsChart) = 120 | chart.WithLabels labels 121 | 122 | /// Sets the chart's height. 123 | static member WithSize size (chart:HighChartsChart) = 124 | chart.WithSize size 125 | 126 | static member internal ToFloatArray s = 127 | s 128 | |> Seq.map (fun v -> (v :> IConvertible).ToDouble(null)) 129 | |> Seq.toArray 130 | 131 | static member internal ToFloatArray2d s = 132 | s 133 | |> Seq.map (fun (x,y) -> seq {(x :> IConvertible).ToDouble(null);(y :> IConvertible).ToDouble(null)}) 134 | |> Seq.toArray 135 | 136 | static member internal ToFloatArray2d (s:(float*float) seq) = 137 | s 138 | |> Seq.map (fun (x,y) -> seq { x; y }) 139 | |> Seq.toArray 140 | 141 | static member internal ToTimeFloatArray s = 142 | s 143 | |> Seq.map (fun (x:DateTime,y:float) -> seq { Chart.DateTimeToHighCharts x; y }) 144 | |> Seq.toArray 145 | 146 | static member internal ToStringArray s = 147 | s 148 | |> Seq.map (fun s -> s.ToString()) 149 | |> Seq.toArray 150 | 151 | static member Cylinder(data:seq<#value>) = 152 | let cylinder = Cylinder(data = (data |> Chart.ToFloatArray)) 153 | Chart.Plot [cylinder] 154 | |> Chart.With (Chart.Props.chart_iplot.options3d.enabled true) 155 | |> Chart.With (Chart.Props.chart_iplot.options3d.alpha 15.) 156 | |> Chart.With (Chart.Props.chart_iplot.options3d.beta 15.) 157 | |> Chart.With (Chart.Props.chart_iplot.options3d.depth 20.) 158 | |> Chart.With (Chart.Props.chart_iplot.options3d.viewDistance 500.) 159 | 160 | static member Funnel(data:seq<#value>) = 161 | let funnel = Funnel(data = (data |> Chart.ToFloatArray)) 162 | Chart.Plot [funnel] 163 | 164 | static member Funnel3d(data:seq<#value>) = 165 | let funnel = Funnel3d(data = (data |> Chart.ToFloatArray)) 166 | Chart.Plot [funnel] 167 | |> Chart.With (Chart.Props.chart_iplot.options3d.enabled true) 168 | |> Chart.With (Chart.Props.chart_iplot.options3d.alpha 10.) 169 | |> Chart.With (Chart.Props.chart_iplot.options3d.beta 100.) 170 | |> Chart.With (Chart.Props.chart_iplot.options3d.depth 200.) 171 | |> Chart.With (Chart.Props.chart_iplot.options3d.viewDistance 50.) 172 | 173 | static member Line(data:seq<#value>) = 174 | let scatter = 175 | Line(data = (data |> Chart.ToFloatArray), 176 | marker = Marker(enabled = Nullable(false))) 177 | Chart.Plot [scatter] 178 | 179 | static member Line(data:seq) = 180 | let scatter = 181 | Line(data = data, 182 | marker = Marker(enabled = Nullable(false))) 183 | Chart.Plot [scatter] 184 | 185 | static member Line(data:seq<#key * #value>) = 186 | let scatter = 187 | Line( 188 | data_mat = Chart.ToFloatArray2d data, 189 | marker = Marker(enabled = Nullable(false))) 190 | Chart.Plot [scatter] 191 | 192 | static member Line(data:seq) = 193 | let scatter = 194 | Line( 195 | data_mat = Chart.ToFloatArray2d data, 196 | marker = Marker(enabled = Nullable(false))) 197 | Chart.Plot [scatter] 198 | 199 | static member Line(data:seq) = 200 | let scatter = 201 | Line( 202 | data_mat = Chart.ToTimeFloatArray data, 203 | marker = Marker(enabled = Nullable(false))) 204 | Chart.Plot [scatter] 205 | |> Chart.With (Chart.Props.xAxis.[0].type_iplot "datetime") 206 | 207 | static member Line(data:seq>) = 208 | let scatters = 209 | data 210 | |> Seq.map (fun series -> 211 | Line( 212 | data = (series |> Chart.ToFloatArray), 213 | marker = Marker(enabled = Nullable(false)) 214 | )) 215 | Chart.Plot scatters 216 | 217 | static member Line(data:seq>) = 218 | let scatters = 219 | data 220 | |> Seq.map (fun series -> 221 | Line( 222 | data = series, 223 | marker = Marker(enabled = Nullable(false)) 224 | )) 225 | Chart.Plot scatters 226 | 227 | static member Line(data:seq) = 228 | let scatters = 229 | data 230 | |> Seq.map (fun series -> 231 | Line( 232 | data = series, 233 | marker = Marker(enabled = Nullable(false)) 234 | )) 235 | Chart.Plot scatters 236 | 237 | static member Line(data:seq) = 238 | let scatters = 239 | data 240 | |> Seq.map (fun series -> 241 | Line( 242 | data = series, 243 | marker = Marker(enabled = Nullable(false)) 244 | )) 245 | Chart.Plot scatters 246 | 247 | static member Line(data:seq<#seq<#key * #value>>) = 248 | let scatters = 249 | data 250 | |> Seq.map (fun series -> 251 | Line( 252 | data_mat = Chart.ToFloatArray2d series, 253 | marker = Marker(enabled = Nullable(false)))) 254 | Chart.Plot scatters 255 | 256 | static member Line(data:seq>) = 257 | let scatters = 258 | data 259 | |> Seq.map (fun series -> 260 | Line( 261 | data_mat = (series |> Seq.map (fun (x,y) -> seq { x; y })), 262 | marker = Marker(enabled = Nullable(false)))) 263 | Chart.Plot scatters 264 | 265 | static member Line(data:seq<(float * float) []>) = 266 | let scatters = 267 | data 268 | |> Seq.map (fun series -> 269 | Line( 270 | data_mat = (series |> Seq.map (fun (x,y) -> seq { x; y })), 271 | marker = Marker(enabled = Nullable(false)))) 272 | Chart.Plot scatters 273 | 274 | static member Line(data:seq<(float * float) list>) = 275 | let scatters = 276 | data 277 | |> Seq.map (fun series -> 278 | Line( 279 | data_mat = (series |> Seq.map (fun (x,y) -> seq { x; y })), 280 | marker = Marker(enabled = Nullable(false)))) 281 | Chart.Plot scatters 282 | 283 | static member Line(data:seq>) = 284 | let scatters = 285 | data 286 | |> Seq.map (fun series -> 287 | Line( 288 | data_mat = (series |> Seq.map (fun (x,y) -> seq { Chart.DateTimeToHighCharts x; y })), 289 | marker = Marker(enabled = Nullable(false)))) 290 | Chart.Plot scatters 291 | |> Chart.With (Chart.Props.xAxis.[0].type_iplot "datetime") 292 | 293 | static member Line(data:seq<(DateTime * float) []>) = 294 | let scatters = 295 | data 296 | |> Seq.map (fun series -> 297 | Line( 298 | data_mat = (series |> Array.map (fun (x,y) -> seq { Chart.DateTimeToHighCharts x; y })), 299 | marker = Marker(enabled = Nullable(false)))) 300 | Chart.Plot scatters 301 | |> Chart.With (Chart.Props.xAxis.[0].type_iplot "datetime") 302 | 303 | static member Line(data:seq<(DateTime * float) list>) = 304 | let scatters = 305 | data 306 | |> Seq.map (fun series -> 307 | Line( 308 | data_mat = (series |> List.map (fun (x,y) -> seq { Chart.DateTimeToHighCharts x; y })), 309 | marker = Marker(enabled = Nullable(false)))) 310 | Chart.Plot scatters 311 | |> Chart.With (Chart.Props.xAxis.[0].type_iplot "datetime") 312 | 313 | static member Pyramid3d(data:seq<#value>) = 314 | let pyramid = Pyramid3d(data = (data |> Chart.ToFloatArray)) 315 | Chart.Plot [pyramid :> Trace] 316 | |> Chart.With (Chart.Props.chart_iplot.options3d.enabled true) 317 | |> Chart.With (Chart.Props.chart_iplot.options3d.alpha 10.) 318 | |> Chart.With (Chart.Props.chart_iplot.options3d.beta 100.) 319 | |> Chart.With (Chart.Props.chart_iplot.options3d.depth 200.) 320 | |> Chart.With (Chart.Props.chart_iplot.options3d.viewDistance 50.) 321 | 322 | static member Scatter(data:seq<#value>) = 323 | let scatter = 324 | Scatter(data = (data |> Chart.ToFloatArray)) 325 | Chart.Plot [scatter] 326 | 327 | static member Scatter(data:seq) = 328 | let scatter = Scatter(data = data) 329 | Chart.Plot [scatter] 330 | 331 | static member Scatter(data:seq) = 332 | let scatter = Scatter(data_mat = Chart.ToFloatArray2d data) 333 | Chart.Plot [scatter] 334 | 335 | static member Scatter(data:seq) = 336 | let scatter = Scatter(data_mat = Chart.ToTimeFloatArray data) 337 | Chart.Plot [scatter] 338 | |> Chart.With (Chart.Props.xAxis.[0].type_iplot "datetime") 339 | 340 | static member Scatter(data:seq>) = 341 | let scatters = 342 | data 343 | |> Seq.map (fun series -> 344 | Scatter(data = (series |> Chart.ToFloatArray))) 345 | Chart.Plot scatters 346 | 347 | static member Scatter(data:seq>) = 348 | let scatter = Scatter(data_mat = data) 349 | Chart.Plot [scatter] 350 | 351 | static member Scatter(data:seq) = 352 | let scatter = Scatter(data_mat = (data |> Seq.map (Seq.ofArray))) 353 | Chart.Plot [scatter] 354 | 355 | static member Scatter(data:seq) = 356 | let scatter = Scatter(data_mat = (data |> Seq.map (Seq.ofList))) 357 | Chart.Plot [scatter] 358 | 359 | static member Scatter(data:seq<#seq<#key * #value>>) = 360 | let scatters = 361 | data 362 | |> Seq.map (fun series -> 363 | Scatter(data_mat = Chart.ToFloatArray2d series)) 364 | Chart.Plot scatters 365 | 366 | static member Scatter(data:seq>) = 367 | let scatters = 368 | data 369 | |> Seq.map (fun series -> 370 | Scatter(data_mat = (series |> Seq.map (fun (x,y) -> seq { x; y })))) 371 | Chart.Plot scatters 372 | 373 | static member Scatter(data:seq<(float * float) []>) = 374 | let scatters = 375 | data 376 | |> Seq.map (fun series -> 377 | Scatter(data_mat = (series |> Array.map (fun (x,y) -> seq { x; y })))) 378 | Chart.Plot scatters 379 | 380 | static member Scatter(data:seq<(float * float) list>) = 381 | let scatters = 382 | data 383 | |> Seq.map (fun series -> 384 | Scatter(data_mat = (series |> List.map (fun (x,y) -> seq { x; y })))) 385 | Chart.Plot scatters 386 | 387 | static member Scatter(data:seq>) = 388 | let scatters = 389 | data 390 | |> Seq.map (fun series -> 391 | Scatter( 392 | data_mat = (series |> Seq.map (fun (x,y) -> seq { Chart.DateTimeToHighCharts x; y })) 393 | )) 394 | Chart.Plot scatters 395 | |> Chart.With (Chart.Props.xAxis.[0].type_iplot "datetime") 396 | 397 | static member Scatter(data:seq<(DateTime * float) []>) = 398 | let scatters = 399 | data 400 | |> Seq.map (fun series -> 401 | Scatter( 402 | data_mat = (series |> Array.map (fun (x,y) -> seq { Chart.DateTimeToHighCharts x; y })) 403 | )) 404 | Chart.Plot scatters 405 | |> Chart.With (Chart.Props.xAxis.[0].type_iplot "datetime") 406 | 407 | static member Scatter(data:seq<(DateTime * float) list>) = 408 | let scatters = 409 | data 410 | |> Seq.map (fun series -> 411 | Scatter( 412 | data_mat = (series |> List.map (fun (x,y) -> seq { Chart.DateTimeToHighCharts x; y })) 413 | )) 414 | Chart.Plot scatters 415 | |> Chart.With (Chart.Props.xAxis.[0].type_iplot "datetime") 416 | 417 | // static member Area 418 | // static member Arearange 419 | // static member Areaspline 420 | // static member Areasplinerange 421 | // static member Bar 422 | // static member Bellcurve 423 | // static member Boxplot 424 | // static member Bubble 425 | // static member Bullet 426 | // static member Column 427 | // static member Columnpyramid 428 | // static member Columnrange 429 | // static member Dependencywheel 430 | // static member Dumbbell 431 | // static member Errorbar 432 | // static member Gauge 433 | // static member Heatmap 434 | // static member Histogram 435 | // static member Item 436 | // static member Line 437 | // static member Lollipop 438 | // static member Map 439 | // static member Mapbubble 440 | // static member Mapline 441 | // static member Mappoint 442 | // static member Networkgraph 443 | // static member Organization 444 | // static member Pareto 445 | // static member Pie 446 | // static member Polygon 447 | // static member Pyramid 448 | // static member Sankey 449 | // static member Scatter3d 450 | // static member Solidgauge 451 | // static member Spline 452 | // static member Streamgraph 453 | // static member Sunburst 454 | // static member Tilemap 455 | // static member Timeline 456 | // static member Treemap 457 | // static member Variablepie 458 | // static member Variwide 459 | // static member Vector 460 | // static member Venn 461 | // static member Waterfall 462 | // static member Windbarb 463 | // static member Wordcloud 464 | // static member Xrange 465 | --------------------------------------------------------------------------------