Browse Source

Merge pull request #464 from sharkdp/plotting

Basic plotting support
David Peter 1 year ago
parent
commit
b175c315ae

+ 500 - 3
Cargo.lock

@@ -2,6 +2,19 @@
 # It is not intended for manual editing.
 version = 3
 
+[[package]]
+name = "ahash"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
+dependencies = [
+ "cfg-if",
+ "getrandom",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
 [[package]]
 name = "aho-corasick"
 version = "1.1.3"
@@ -11,6 +24,21 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "anes"
 version = "0.1.6"
@@ -121,6 +149,21 @@ version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
 
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "basic-toml"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "bitflags"
 version = "2.6.0"
@@ -177,6 +220,19 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
+[[package]]
+name = "chrono"
+version = "0.4.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "num-traits",
+ "serde",
+ "windows-targets 0.52.5",
+]
+
 [[package]]
 name = "ciborium"
 version = "0.2.2"
@@ -292,6 +348,12 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
 [[package]]
 name = "cpufeatures"
 version = "0.2.12"
@@ -378,6 +440,51 @@ dependencies = [
  "typenum",
 ]
 
+[[package]]
+name = "darling"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+ "serde",
+]
+
 [[package]]
 name = "difflib"
 version = "0.4.0"
@@ -421,6 +528,12 @@ version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
 
+[[package]]
+name = "dyn-clone"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
+
 [[package]]
 name = "either"
 version = "1.13.0"
@@ -445,6 +558,16 @@ version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 
+[[package]]
+name = "erased-serde"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d"
+dependencies = [
+ "serde",
+ "typeid",
+]
+
 [[package]]
 name = "errno"
 version = "0.3.9"
@@ -533,6 +656,12 @@ dependencies = [
  "crunchy",
 ]
 
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
 [[package]]
 name = "hashbrown"
 version = "0.14.5"
@@ -560,6 +689,12 @@ version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
 
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
 [[package]]
 name = "home"
 version = "0.5.9"
@@ -589,6 +724,44 @@ dependencies = [
  "itoa",
 ]
 
+[[package]]
+name = "humansize"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
+dependencies = [
+ "libm",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
 [[package]]
 name = "idna"
 version = "0.5.0"
@@ -599,6 +772,17 @@ dependencies = [
  "unicode-normalization",
 ]
 
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown 0.12.3",
+ "serde",
+]
+
 [[package]]
 name = "indexmap"
 version = "2.2.6"
@@ -606,7 +790,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
 dependencies = [
  "equivalent",
- "hashbrown",
+ "hashbrown 0.14.5",
+ "serde",
 ]
 
 [[package]]
@@ -710,6 +895,12 @@ version = "0.2.155"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
 
+[[package]]
+name = "libm"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
+
 [[package]]
 name = "libredox"
 version = "0.1.3"
@@ -732,6 +923,16 @@ version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
 
+[[package]]
+name = "lock_api"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
 [[package]]
 name = "log"
 version = "0.4.22"
@@ -753,6 +954,28 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "mime_guess"
+version = "2.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
+dependencies = [
+ "mime",
+ "unicase",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
 [[package]]
 name = "nibble_vec"
 version = "0.1.0"
@@ -773,6 +996,16 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
 [[package]]
 name = "normalize-line-endings"
 version = "0.3.0"
@@ -789,6 +1022,12 @@ dependencies = [
  "num-traits",
 ]
 
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
 [[package]]
 name = "num-format"
 version = "0.4.4"
@@ -838,7 +1077,7 @@ dependencies = [
  "glob",
  "heck 0.4.1",
  "html-escape",
- "indexmap",
+ "indexmap 2.2.6",
  "insta",
  "itertools 0.12.1",
  "jiff",
@@ -850,6 +1089,7 @@ dependencies = [
  "num-traits",
  "numbat-exchange-rates",
  "once_cell",
+ "plotly",
  "pretty_dtoa",
  "rand",
  "rust-embed",
@@ -895,6 +1135,18 @@ version = "1.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
 
+[[package]]
+name = "once_map"
+version = "0.4.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30c7f82d6d446dd295845094f3a76bcdc5e6183b66667334e169f019cd05e5a0"
+dependencies = [
+ "ahash",
+ "hashbrown 0.14.5",
+ "parking_lot",
+ "stable_deref_trait",
+]
+
 [[package]]
 name = "oorandom"
 version = "11.1.3"
@@ -907,12 +1159,63 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
 
+[[package]]
+name = "parking_lot"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.52.5",
+]
+
 [[package]]
 name = "percent-encoding"
 version = "2.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
 
+[[package]]
+name = "plotly"
+version = "0.9.1"
+source = "git+https://github.com/plotly/plotly.rs#5d265b7ac77680a56fb2c7009b773f2783afd006"
+dependencies = [
+ "dyn-clone",
+ "erased-serde",
+ "once_cell",
+ "plotly_derive",
+ "rand",
+ "rinja",
+ "serde",
+ "serde_json",
+ "serde_repr",
+ "serde_with",
+]
+
+[[package]]
+name = "plotly_derive"
+version = "0.9.1"
+source = "git+https://github.com/plotly/plotly.rs#5d265b7ac77680a56fb2c7009b773f2783afd006"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "plotters"
 version = "0.3.6"
@@ -941,6 +1244,12 @@ dependencies = [
  "plotters-backend",
 ]
 
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
 [[package]]
 name = "ppv-lite86"
 version = "0.2.17"
@@ -1073,6 +1382,15 @@ dependencies = [
  "crossbeam-utils",
 ]
 
+[[package]]
+name = "redox_syscall"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
+dependencies = [
+ "bitflags",
+]
+
 [[package]]
 name = "redox_users"
 version = "0.4.5"
@@ -1128,6 +1446,51 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
+[[package]]
+name = "rinja"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5bf96e290b3578af4ca20699abca2b90d5aba9eeae4acd123fd83c8547165eb4"
+dependencies = [
+ "humansize",
+ "itoa",
+ "num-traits",
+ "percent-encoding",
+ "rinja_derive",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "rinja_derive"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fa31df46c7a8c125cfe8ec5f0b1d2689b65d4652fd8de9bf0ac41d6e0ac68f3"
+dependencies = [
+ "basic-toml",
+ "memchr",
+ "mime",
+ "mime_guess",
+ "once_map",
+ "proc-macro2",
+ "quote",
+ "rinja_parser",
+ "rustc-hash",
+ "serde",
+ "syn",
+]
+
+[[package]]
+name = "rinja_parser"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ee3ef25da2517861878f58ecbd7b8ba6bebd5634fe1b4421df20d100fb75745"
+dependencies = [
+ "memchr",
+ "nom",
+ "serde",
+]
+
 [[package]]
 name = "rust-embed"
 version = "8.4.0"
@@ -1163,6 +1526,12 @@ dependencies = [
  "walkdir",
 ]
 
+[[package]]
+name = "rustc-hash"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
+
 [[package]]
 name = "rustix"
 version = "0.38.34"
@@ -1262,6 +1631,12 @@ dependencies = [
  "winapi-util",
 ]
 
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
 [[package]]
 name = "serde"
 version = "1.0.203"
@@ -1293,6 +1668,17 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "serde_repr"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "serde_spanned"
 version = "0.6.6"
@@ -1302,6 +1688,36 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "serde_with"
+version = "3.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857"
+dependencies = [
+ "base64",
+ "chrono",
+ "hex",
+ "indexmap 1.9.3",
+ "indexmap 2.2.6",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "serde_with_macros",
+ "time",
+]
+
+[[package]]
+name = "serde_with_macros"
+version = "3.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "sha2"
 version = "0.10.8"
@@ -1340,6 +1756,12 @@ version = "0.9.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
 
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
 [[package]]
 name = "strfmt"
 version = "0.2.4"
@@ -1414,6 +1836,37 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "time"
+version = "0.3.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
+dependencies = [
+ "deranged",
+ "itoa",
+ "num-conv",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+
+[[package]]
+name = "time-macros"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
 [[package]]
 name = "tinytemplate"
 version = "1.2.1"
@@ -1466,19 +1919,34 @@ version = "0.22.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
 dependencies = [
- "indexmap",
+ "indexmap 2.2.6",
  "serde",
  "serde_spanned",
  "toml_datetime",
  "winnow",
 ]
 
+[[package]]
+name = "typeid"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e"
+
 [[package]]
 name = "typenum"
 version = "1.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
 
+[[package]]
+name = "unicase"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
+dependencies = [
+ "version_check",
+]
+
 [[package]]
 name = "unicode-bidi"
 version = "0.3.15"
@@ -1676,6 +2144,15 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.5",
+]
+
 [[package]]
 name = "windows-sys"
 version = "0.48.0"
@@ -1824,6 +2301,26 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "zerocopy"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "zeroize"
 version = "1.8.1"

+ 11 - 0
examples/interactive/bar_chart.nbt

@@ -0,0 +1,11 @@
+let names = ["Iron", "Copper", "Silver", "Gold", "Platinum"]
+let elements = map(element, names)
+
+fn density(e: ChemicalElement) -> MassDensity = e.density
+
+let densities = map(density, elements)
+
+bar_chart(densities) |>
+  xlabels(names) |>
+  value_label("Density") |>
+  show

+ 10 - 0
examples/interactive/line_plot.nbt

@@ -0,0 +1,10 @@
+let A0 = 3 cm
+let ω = 20 Hz
+let λ = 1.5 s
+
+fn amplitude(t) = A0 exp(-t/λ) cos(ω t)
+
+line_plot(amplitude, 0 s, 8 s) |>
+  xlabel("Time") |>
+  ylabel("Amplitude") |>
+  show

File diff suppressed because it is too large
+ 599 - 80
numbat-wasm/Cargo.lock


+ 1 - 0
numbat/Cargo.toml

@@ -36,6 +36,7 @@ rand = "0.8.5"
 strfmt = "0.2.4"
 indexmap = "2.2.6"
 mendeleev = "0.8.1"
+plotly = { version = "0.9.1", git = "https://github.com/plotly/plotly.rs" }
 
 [features]
 default = ["fetch-exchangerates"]

+ 37 - 0
numbat/modules/plot/bar_chart.nbt

@@ -0,0 +1,37 @@
+use core::quantities
+use core::strings
+use core::lists
+use plot::common
+
+struct BarChart {
+  value_label: String,
+  value_unit: String,
+  values: List<Scalar>,
+  x_labels: List<String>,
+}
+
+fn _default_label(n: Scalar) -> String = "{n}"
+
+fn bar_chart<A: Dim>(values: List<A>) -> BarChart =
+  BarChart {
+    value_label: "",
+    value_unit: if _is_scalar(head(values)) then "" else _unit_name(head(values)),
+    values: map(value_of, values),
+    x_labels: map(_default_label, range(1, len(values))),
+  }
+
+fn xlabels(ls: List<String>, chart: BarChart) -> BarChart =
+  BarChart {
+    value_label: chart.value_label,
+    value_unit: chart.value_unit,
+    values: chart.values,
+    x_labels: ls,
+  }
+
+fn value_label(label: String, chart: BarChart) -> BarChart =
+  BarChart {
+    value_label: label,
+    value_unit: chart.value_unit,
+    values: chart.values,
+    x_labels: chart.x_labels,
+  }

+ 10 - 0
numbat/modules/plot/common.nbt

@@ -0,0 +1,10 @@
+use core::quantities
+use core::strings
+
+fn _unit_name<A: Dim>(x: A) -> String = str_replace("{unit_of(x)}", "1 ", "")
+
+fn _is_scalar<A: Dim>(x: A) -> Bool = "{unit_of(x)}" == "1"
+
+# TODO: this function is overly generic, but we don't have bounded
+# polymorphism yet.
+fn show<Plot>(plot: Plot) -> String

+ 44 - 0
numbat/modules/plot/line_plot.nbt

@@ -0,0 +1,44 @@
+use core::quantities
+use core::lists
+use plot::common
+
+struct LinePlot {
+  x_label: String,
+  x_unit: String,
+  y_label: String,
+  y_unit: String,
+  xs: List<Scalar>,
+  ys: List<Scalar>,
+}
+let _num_points_for_line_plot = 2000
+
+fn line_plot<A: Dim, B: Dim>(f: Fn[(A) -> B], x_start: A, x_end: A) -> LinePlot =
+  LinePlot {
+    x_label: "",
+    x_unit: if _is_scalar(x_end) then "" else _unit_name(x_end),
+    y_label: "",
+    y_unit: if _is_scalar(f(x_end)) then "" else _unit_name(f(x_end)),
+    xs: linspace(x_start / unit_of(x_start), x_end / unit_of(x_end), _num_points_for_line_plot),
+    ys: map(value_of, map(f, linspace(x_start, x_end, _num_points_for_line_plot))),
+  }
+
+
+fn xlabel(label: String, plot: LinePlot) -> LinePlot =
+  LinePlot {  # TODO: this would be much nicer with some form of struct update syntax: `plot { x_label: label }`
+    x_label: label,
+    x_unit: plot.x_unit,
+    y_label: plot.y_label,
+    y_unit: plot.y_unit,
+    xs: plot.xs,
+    ys: plot.ys,
+  }
+
+fn ylabel(label: String, plot: LinePlot) -> LinePlot =
+  LinePlot {
+    x_label: plot.x_label,
+    x_unit: plot.x_unit,
+    y_label: label,
+    y_unit: plot.y_unit,
+    xs: plot.xs,
+    ys: plot.ys,
+  }

+ 3 - 0
numbat/modules/prelude.nbt

@@ -42,3 +42,6 @@ use chemistry::elements
 
 use datetime::functions
 use datetime::human
+
+use plot::line_plot
+use plot::bar_chart

+ 4 - 0
numbat/src/ffi/functions.rs

@@ -14,6 +14,7 @@ pub(crate) fn functions() -> &'static HashMap<String, ForeignFunction> {
     use super::lists::*;
     use super::lookup::*;
     use super::math::*;
+    use super::plot::*;
     use super::strings::*;
 
     FFI_FUNCTIONS.get_or_init(|| {
@@ -106,6 +107,9 @@ pub(crate) fn functions() -> &'static HashMap<String, ForeignFunction> {
         // Database lookup
         insert_function!(_get_chemical_element_data_raw, 1..=1);
 
+        // Plotting
+        insert_function!(show, 1..=1);
+
         m
     })
 }

+ 1 - 0
numbat/src/ffi/mod.rs

@@ -5,6 +5,7 @@ mod lists;
 mod lookup;
 mod macros;
 mod math;
+mod plot;
 mod procedures;
 mod strings;
 

+ 119 - 0
numbat/src/ffi/plot.rs

@@ -0,0 +1,119 @@
+use plotly::Plot;
+
+use super::macros::*;
+use super::Args;
+use super::Result;
+use crate::value::Value;
+use crate::RuntimeError;
+
+fn line_plot(mut args: Args) -> Plot {
+    let mut fields = arg!(args).unsafe_as_struct_fields();
+    let ys = fields.pop().unwrap();
+    let xs = fields.pop().unwrap();
+    let y_unit = fields.pop().unwrap().unsafe_as_string();
+    let y_label = fields.pop().unwrap().unsafe_as_string();
+    let x_unit = fields.pop().unwrap().unsafe_as_string();
+    let x_label = fields.pop().unwrap().unsafe_as_string();
+
+    let x_label = format!(
+        "{x_label}{x_unit}",
+        x_unit = if x_unit.is_empty() {
+            "".into()
+        } else {
+            format!(" [{}]", x_unit)
+        }
+    );
+    let y_label = format!(
+        "{y_label}{y_unit}",
+        y_unit = if y_unit.is_empty() {
+            "".into()
+        } else {
+            format!(" [{}]", y_unit)
+        }
+    );
+
+    let xs = xs
+        .unsafe_as_list()
+        .iter()
+        .cloned()
+        .map(|e| e.unsafe_as_quantity().unsafe_value().to_f64())
+        .collect::<Vec<_>>();
+    let ys = ys
+        .unsafe_as_list()
+        .iter()
+        .cloned()
+        .map(|e| e.unsafe_as_quantity().unsafe_value().to_f64())
+        .collect::<Vec<_>>();
+
+    crate::plot::line_plot(xs, ys, &x_label, &y_label)
+}
+
+fn bar_chart(mut args: Args) -> Plot {
+    let mut fields = arg!(args).unsafe_as_struct_fields();
+    let x_labels = fields.pop().unwrap();
+    let values = fields.pop().unwrap();
+    let value_unit = fields.pop().unwrap().unsafe_as_string();
+    let value_label = fields.pop().unwrap().unsafe_as_string();
+
+    let x_labels = x_labels
+        .unsafe_as_list()
+        .iter()
+        .cloned()
+        .map(|e| e.unsafe_as_string())
+        .collect::<Vec<_>>();
+    let values = values
+        .unsafe_as_list()
+        .iter()
+        .cloned()
+        .map(|e| e.unsafe_as_quantity().unsafe_value().to_f64())
+        .collect::<Vec<_>>();
+
+    let value_label = format!(
+        "{value_label}{value_unit}",
+        value_unit = if value_unit.is_empty() {
+            "".into()
+        } else {
+            format!(" [{}]", value_unit)
+        }
+    );
+
+    crate::plot::bar_chart(values, x_labels, &value_label)
+}
+
+#[cfg(not(target_family = "wasm"))]
+fn show_plot(plot: Plot) -> String {
+    plot.show();
+
+    "Plot will be opened in the browser".into()
+}
+
+#[cfg(target_family = "wasm")]
+fn show_plot(_plot: Plot) -> String {
+    // The way we could implement this would be to return plot.to_inline_html(..).
+    // This would have to be retrieved on the JS side and then rendered using plotly.js.
+
+    "Plotting is currently not supported on this platform.".into()
+}
+
+pub fn show(args: Args) -> Result<Value> {
+    // Dynamic dispatch hack since we don't have bounded polymorphism.
+    // And no real support for generics in the FFI.
+    let Value::StructInstance(info, _) = args.front().unwrap() else {
+        return Err(RuntimeError::UserError(format!(
+            "Unsupported argument to 'show'.",
+        )));
+    };
+
+    let plot = if info.name == "LinePlot" {
+        line_plot(args)
+    } else if info.name == "BarChart" {
+        bar_chart(args)
+    } else {
+        return Err(RuntimeError::UserError(format!(
+            "Unsupported plot type: {}",
+            info.name
+        )));
+    };
+
+    return_string!(show_plot(plot))
+}

+ 1 - 0
numbat/src/lib.rs

@@ -23,6 +23,7 @@ pub mod module_importer;
 mod name_resolution;
 mod number;
 mod parser;
+mod plot;
 mod prefix;
 mod prefix_parser;
 mod prefix_transformer;

+ 48 - 0
numbat/src/plot.rs

@@ -0,0 +1,48 @@
+use plotly::{
+    color::Rgb,
+    common::{Font, Line},
+    layout::Axis,
+    Bar, Configuration, Layout, Plot, Scatter,
+};
+
+pub fn line_plot(xs: Vec<f64>, ys: Vec<f64>, x_label: &str, y_label: &str) -> Plot {
+    let x_min = xs[0];
+    let x_max = xs[xs.len() - 1];
+    let y_min = ys.iter().fold(f64::INFINITY, |a, &b| a.min(b));
+    let y_max = ys.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b));
+
+    let y_max = y_max + 0.1 * (y_max - y_min);
+    let y_min = y_min - 0.1 * (y_max - y_min);
+
+    let mut plot = Plot::new();
+    let trace = Scatter::new(xs, ys).line(Line::new().color(Rgb::new(0x00, 0x77, 0xff)));
+    plot.add_trace(trace);
+
+    let layout = Layout::new()
+        .x_axis(Axis::new().title(x_label).range(vec![x_min, x_max]))
+        .y_axis(Axis::new().title(y_label).range(vec![y_min, y_max]))
+        .font(Font::new().size(16).family("Lato, Roboto, sans-serif"))
+        .width(1200);
+    plot.set_layout(layout);
+
+    plot.set_configuration(Configuration::new());
+
+    plot
+}
+
+pub fn bar_chart(values: Vec<f64>, x_labels: Vec<String>, value_label: &str) -> Plot {
+    let mut plot = Plot::new();
+
+    let trace = Bar::new(x_labels, values);
+    plot.add_trace(trace);
+
+    let layout = Layout::new()
+        .y_axis(Axis::new().title(value_label))
+        .font(Font::new().size(16).family("Lato, Roboto, sans-serif"))
+        .width(1200);
+    plot.set_layout(layout);
+
+    plot.set_configuration(Configuration::new());
+
+    plot
+}

Some files were not shown because too many files changed in this diff