prographs 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/README.md +6 -4
  3. package/dist/AutoGraph-PDPEAIXL.cjs +145 -0
  4. package/dist/GraphType.js +19 -0
  5. package/dist/autograph/chartIndex.js +521 -0
  6. package/dist/autograph/chartModules.js +2 -1
  7. package/dist/autograph/index.cjs +98 -93
  8. package/dist/catalog-DI4XZ46L.cjs +142 -0
  9. package/dist/catalog.js +2 -1
  10. package/dist/charts/biologycharts/BiologySamples.js +59 -0
  11. package/dist/charts/biologycharts/PunnettSquare.js +342 -0
  12. package/dist/charts/biologycharts/biologyEnums.js +22 -1
  13. package/dist/charts/biologycharts/index.js +5 -3
  14. package/dist/charts/doecharts/DoeSamples.js +78 -0
  15. package/dist/charts/doecharts/SipocDiagram.js +453 -0
  16. package/dist/charts/doecharts/index.js +4 -2
  17. package/dist/charts/fieldcharts/SpeciesAccumulationCurve.js +350 -0
  18. package/dist/charts/fieldcharts/fieldEnums.js +8 -1
  19. package/dist/charts/fieldcharts/index.js +5 -3
  20. package/dist/charts/signals/AntennaPatternChart.js +492 -0
  21. package/dist/charts/signals/SignalSamples.js +17 -0
  22. package/dist/charts/signals/index.js +5 -3
  23. package/dist/charts/softwarediagrams/ComponentDiagram.js +504 -0
  24. package/dist/charts/softwarediagrams/DeploymentDiagram.js +419 -0
  25. package/dist/charts/softwarediagrams/SoftwareSamples.js +283 -1
  26. package/dist/charts/softwarediagrams/index.js +7 -3
  27. package/dist/charts/softwarediagrams/softwareEnums.js +45 -1
  28. package/dist/charts/sportscharts/PowerDurationCurve.js +301 -0
  29. package/dist/charts/sportscharts/SportsSamples.js +102 -0
  30. package/dist/charts/sportscharts/TrainingLoadChart.js +325 -0
  31. package/dist/charts/sportscharts/index.js +6 -2
  32. package/dist/charts/strategycharts/BassDiffusionChart.js +362 -0
  33. package/dist/charts/strategycharts/BassModel.js +96 -0
  34. package/dist/charts/strategycharts/BurnRunwayChart.js +284 -0
  35. package/dist/charts/strategycharts/CapTableChart.js +366 -0
  36. package/dist/charts/strategycharts/KanbanBoard.js +324 -0
  37. package/dist/charts/strategycharts/OkrTree.js +339 -0
  38. package/dist/charts/strategycharts/StrategyPane.js +27 -0
  39. package/dist/charts/strategycharts/StrategySamples.js +251 -0
  40. package/dist/charts/strategycharts/index.js +15 -3
  41. package/dist/charts/threefields/BsfcMap3DChart.js +231 -0
  42. package/dist/charts/threefields/CompressorMap3DChart.js +217 -0
  43. package/dist/charts/threefields/FieldGrid.js +50 -0
  44. package/dist/charts/threefields/LossLandscape3DChart.js +201 -0
  45. package/dist/charts/threefields/MapSheet.js +77 -0
  46. package/dist/charts/threefields/TerrainRoute3DChart.js +201 -0
  47. package/dist/charts/threefields/TerrainRouteMath.js +40 -0
  48. package/dist/charts/threefields/ThreeFieldBase.js +117 -0
  49. package/dist/charts/threefields/ThreeFieldsSamples.js +80 -0
  50. package/dist/charts/threefields/WaferMap3DChart.js +218 -0
  51. package/dist/charts/threefields/index.js +19 -0
  52. package/dist/charts/threesurfaces/index.js +2 -2
  53. package/dist/charts/tradingcharts/KeltnerChannelChart.js +171 -0
  54. package/dist/charts/tradingcharts/TradingMath.js +20 -0
  55. package/dist/charts/tradingcharts/index.js +4 -2
  56. package/dist/{chunk-ZL6XDXMK.cjs → chunk-26PSLEP4.cjs} +8 -8
  57. package/dist/{chunk-W3US6EFP.cjs → chunk-2BO5DZUP.cjs} +12 -12
  58. package/dist/{chunk-5DP7OUSE.cjs → chunk-2GG5YYSC.cjs} +6 -6
  59. package/dist/{chunk-7FG4JSVO.cjs → chunk-2GL2VNJU.cjs} +253 -33
  60. package/dist/{chunk-LCWMRCLF.cjs → chunk-2PYOI7OY.cjs} +21 -21
  61. package/dist/{chunk-JFWJAEBD.cjs → chunk-3U4MCSKI.cjs} +4 -4
  62. package/dist/{chunk-CUPLHZRR.cjs → chunk-3ZMM3FSK.cjs} +4 -4
  63. package/dist/{chunk-L2AV62DP.cjs → chunk-4767WQJ7.cjs} +6 -6
  64. package/dist/{chunk-DA2EYIIP.cjs → chunk-4E7FSSBF.cjs} +1210 -168
  65. package/dist/{chunk-VS5LQEZB.cjs → chunk-526STI2S.cjs} +5 -5
  66. package/dist/{chunk-RXIMEK6C.cjs → chunk-55767YPI.cjs} +20 -20
  67. package/dist/{chunk-JXLSKIUV.cjs → chunk-5FBQOMMT.cjs} +26 -26
  68. package/dist/{chunk-27PZV5BR.cjs → chunk-5N4Z7MTS.cjs} +4 -4
  69. package/dist/{chunk-KEWCUIQA.cjs → chunk-5WEV6YWA.cjs} +22 -22
  70. package/dist/chunk-5XOVVC5X.cjs +3281 -0
  71. package/dist/{chunk-HK5J7Q2W.cjs → chunk-6HLXVWXU.cjs} +9 -9
  72. package/dist/{chunk-7JG47RW4.cjs → chunk-6LQT4WJ5.cjs} +25 -25
  73. package/dist/{chunk-OCVTI6X4.cjs → chunk-6YTWOUFD.cjs} +329 -36
  74. package/dist/{chunk-W7IQCAVN.cjs → chunk-7F6W6HKL.cjs} +11 -11
  75. package/dist/{chunk-F2DCJXDM.cjs → chunk-7L6KYU5A.cjs} +21 -21
  76. package/dist/{chunk-GTGR6LPR.cjs → chunk-A4GIN4FE.cjs} +17 -17
  77. package/dist/{chunk-B2CETSR6.cjs → chunk-A4US7SA7.cjs} +5 -5
  78. package/dist/{chunk-3DIF2YYW.cjs → chunk-AOXPBLPW.cjs} +10 -10
  79. package/dist/{chunk-7TTR4UF5.cjs → chunk-ATI45Z4M.cjs} +2 -2
  80. package/dist/{chunk-5BFCIX2V.cjs → chunk-CPPMO4IQ.cjs} +434 -148
  81. package/dist/{chunk-AYZZQ6AC.cjs → chunk-CZ5KGF6Q.cjs} +9 -9
  82. package/dist/{chunk-7THJ3ZBK.cjs → chunk-D4I35MCE.cjs} +2 -2
  83. package/dist/{chunk-MCPPFRLN.cjs → chunk-DCGAAN3Z.cjs} +2 -2
  84. package/dist/{chunk-VABL6CVX.cjs → chunk-DEY36N37.cjs} +21 -21
  85. package/dist/{chunk-RNCKGJCC.cjs → chunk-DXHXD3DK.cjs} +3 -3
  86. package/dist/{chunk-A56ZGMVZ.cjs → chunk-E7B45DBZ.cjs} +11 -11
  87. package/dist/{chunk-DOR6L4MT.cjs → chunk-EJC6EAC5.cjs} +18 -18
  88. package/dist/{chunk-UPGNM7MA.cjs → chunk-EULJOOMA.cjs} +11 -11
  89. package/dist/{chunk-G4AZL4GO.cjs → chunk-EYM6LEIF.cjs} +474 -70
  90. package/dist/{chunk-NJ4X6XGV.cjs → chunk-EZTYX2GH.cjs} +5 -5
  91. package/dist/chunk-FDD4UP73.cjs +521 -0
  92. package/dist/{chunk-WJ3VH2Q4.cjs → chunk-GEAIGM3J.cjs} +110 -615
  93. package/dist/{chunk-P3SBHF43.cjs → chunk-GRUYVXTS.cjs} +2 -2
  94. package/dist/{chunk-NX4Z5LMS.cjs → chunk-H3SZXND5.cjs} +5 -5
  95. package/dist/{chunk-EE4S4TOE.cjs → chunk-HS45TOFJ.cjs} +21 -21
  96. package/dist/chunk-INGOG6OV.cjs +899 -0
  97. package/dist/{chunk-UDKA6DGT.cjs → chunk-J3CDXZHF.cjs} +4 -4
  98. package/dist/{chunk-YO6G7TKI.cjs → chunk-JAU5ADEU.cjs} +11 -11
  99. package/dist/{chunk-22LBK4GA.cjs → chunk-JQEBSHX3.cjs} +4 -4
  100. package/dist/{chunk-RXX3U7VQ.cjs → chunk-KLXGLMFU.cjs} +10 -10
  101. package/dist/chunk-KVOX36J3.cjs +327 -0
  102. package/dist/chunk-L6BGNHFL.cjs +139 -0
  103. package/dist/{chunk-GMER35L3.cjs → chunk-L7TB7PU5.cjs} +20 -20
  104. package/dist/{chunk-AFXYNBL2.cjs → chunk-LBTRYEM2.cjs} +4 -4
  105. package/dist/{chunk-WYYCKIOD.cjs → chunk-LLULNJSP.cjs} +11 -11
  106. package/dist/{chunk-J27XOSLP.cjs → chunk-LSO46AFV.cjs} +8 -8
  107. package/dist/{chunk-5XJO45ES.cjs → chunk-LZ2KJ7ZX.cjs} +11 -11
  108. package/dist/chunk-M4IQ5LIZ.cjs +422 -0
  109. package/dist/{chunk-Y2KIUKJF.cjs → chunk-MPDHDKYZ.cjs} +11 -11
  110. package/dist/{chunk-4EMPKRZU.cjs → chunk-MRQ42ZJE.cjs} +3 -3
  111. package/dist/{chunk-EU5D2MF4.cjs → chunk-MYV4KZWP.cjs} +9 -9
  112. package/dist/{chunk-XD7KN4UN.cjs → chunk-MZXVLC24.cjs} +6 -6
  113. package/dist/{chunk-PPSONARU.cjs → chunk-NLBVDFIR.cjs} +41 -2
  114. package/dist/{chunk-L3PEGMGM.cjs → chunk-NY3TBZY4.cjs} +33 -33
  115. package/dist/{chunk-6YNYXXB6.cjs → chunk-OGVVSFAR.cjs} +3 -3
  116. package/dist/{chunk-MLV7QLB6.cjs → chunk-OMTQIQMN.cjs} +6 -6
  117. package/dist/{chunk-BEEOSNK7.cjs → chunk-PEJJ6TXJ.cjs} +15 -144
  118. package/dist/{chunk-SOKOWUF5.cjs → chunk-PPGWHIU7.cjs} +2 -2
  119. package/dist/{chunk-IELWOM2J.cjs → chunk-PPO6SDAN.cjs} +6 -6
  120. package/dist/{chunk-6JTZZCSL.cjs → chunk-PUMKX5F2.cjs} +3 -3
  121. package/dist/{chunk-RFOXFY2U.cjs → chunk-PXBYWCNB.cjs} +190 -799
  122. package/dist/{chunk-3BYXOISI.cjs → chunk-Q3HB3IX6.cjs} +4 -4
  123. package/dist/{chunk-RXCOVONX.cjs → chunk-RBMUOIXE.cjs} +289 -34
  124. package/dist/{chunk-MSBNHCSE.cjs → chunk-RJHGCUUD.cjs} +6 -6
  125. package/dist/{chunk-I6VG7F6Z.cjs → chunk-RSONDMYW.cjs} +708 -141
  126. package/dist/{chunk-GZDNPHMT.cjs → chunk-SHMPFCUE.cjs} +19 -19
  127. package/dist/{chunk-ZIBVCOW6.cjs → chunk-SN4FCXFC.cjs} +19 -19
  128. package/dist/{chunk-7KM52NMN.cjs → chunk-SN5T7WYO.cjs} +2 -2
  129. package/dist/{chunk-7MSZR65Z.cjs → chunk-SQJZGVDR.cjs} +7 -7
  130. package/dist/chunk-SYABFMD7.cjs +633 -0
  131. package/dist/{chunk-2RRFEYFH.cjs → chunk-TICB3MAC.cjs} +16 -16
  132. package/dist/{chunk-AOOKHUV5.cjs → chunk-U7Z7XYR2.cjs} +5 -5
  133. package/dist/{chunk-JEWQAXVG.cjs → chunk-UA2THQFV.cjs} +6 -6
  134. package/dist/{chunk-LHZPAHU3.cjs → chunk-VIK4L2YF.cjs} +112 -521
  135. package/dist/{chunk-4YWMAQ4J.cjs → chunk-VM2Y6BDY.cjs} +4 -4
  136. package/dist/{chunk-IRRKE2ZE.cjs → chunk-VMS3KWYI.cjs} +21 -21
  137. package/dist/{chunk-4SLARW6F.cjs → chunk-VU5NUD46.cjs} +17 -17
  138. package/dist/{chunk-XHY6TVYG.cjs → chunk-VV5CVOF7.cjs} +13 -13
  139. package/dist/{chunk-CCIBPGPZ.cjs → chunk-VYSAWPSS.cjs} +57 -57
  140. package/dist/{chunk-TTNEMQ2A.cjs → chunk-W7YRWEWQ.cjs} +22 -22
  141. package/dist/{chunk-C7MUOGEW.cjs → chunk-X7R5SY62.cjs} +6 -6
  142. package/dist/{chunk-ILR5KBMB.cjs → chunk-XOK3KTSS.cjs} +6 -6
  143. package/dist/{chunk-2CZM2I3T.cjs → chunk-XUIX45VU.cjs} +19 -19
  144. package/dist/{chunk-TGRMPS3A.cjs → chunk-XWH46PVJ.cjs} +165 -43
  145. package/dist/{chunk-WQYXXECR.cjs → chunk-Y5XVMK3K.cjs} +6 -6
  146. package/dist/{chunk-AK4TX2WG.cjs → chunk-YCNKEDTG.cjs} +13 -13
  147. package/dist/{chunk-PJJB24DU.cjs → chunk-YEF7SE3X.cjs} +20 -20
  148. package/dist/{chunk-RVLV4XW6.cjs → chunk-YMTVPMU3.cjs} +22 -22
  149. package/dist/data/index.cjs +2 -2
  150. package/dist/entries/autograph-lazy.cjs +2 -2
  151. package/dist/entries/charts/acoustictransport.cjs +3 -3
  152. package/dist/entries/charts/analysischarts.cjs +3 -3
  153. package/dist/entries/charts/astrocharts.cjs +3 -3
  154. package/dist/entries/charts/bars.cjs +3 -3
  155. package/dist/entries/charts/bayesiancharts.cjs +3 -3
  156. package/dist/entries/charts/biocharts.cjs +3 -3
  157. package/dist/entries/charts/biologycharts.cjs +11 -3
  158. package/dist/entries/charts/biologycharts.d.ts +1 -1
  159. package/dist/entries/charts/biologycharts.js +3 -2
  160. package/dist/entries/charts/business.cjs +3 -3
  161. package/dist/entries/charts/carecharts.cjs +3 -3
  162. package/dist/entries/charts/chemistrycharts.cjs +3 -3
  163. package/dist/entries/charts/civicscharts.cjs +3 -3
  164. package/dist/entries/charts/clinicalcharts.cjs +3 -3
  165. package/dist/entries/charts/comparison.cjs +3 -3
  166. package/dist/entries/charts/demography.cjs +3 -3
  167. package/dist/entries/charts/density.cjs +3 -3
  168. package/dist/entries/charts/diagrams.cjs +3 -3
  169. package/dist/entries/charts/distribution.cjs +3 -3
  170. package/dist/entries/charts/doecharts.cjs +13 -3
  171. package/dist/entries/charts/doecharts.d.ts +1 -1
  172. package/dist/entries/charts/doecharts.js +2 -1
  173. package/dist/entries/charts/earthscience.cjs +3 -3
  174. package/dist/entries/charts/economicscharts.cjs +3 -3
  175. package/dist/entries/charts/energycharts.cjs +3 -3
  176. package/dist/entries/charts/engineering.cjs +3 -3
  177. package/dist/entries/charts/fieldcharts.cjs +7 -3
  178. package/dist/entries/charts/fieldcharts.d.ts +1 -1
  179. package/dist/entries/charts/fieldcharts.js +3 -2
  180. package/dist/entries/charts/financial.cjs +3 -3
  181. package/dist/entries/charts/flow.cjs +3 -3
  182. package/dist/entries/charts/frameworks.cjs +3 -3
  183. package/dist/entries/charts/genomics.cjs +3 -3
  184. package/dist/entries/charts/geoflows.cjs +3 -3
  185. package/dist/entries/charts/geomaps.cjs +3 -3
  186. package/dist/entries/charts/geosciencecharts.cjs +3 -3
  187. package/dist/entries/charts/hierarchy.cjs +3 -3
  188. package/dist/entries/charts/indicators.cjs +3 -3
  189. package/dist/entries/charts/inferencecharts.cjs +3 -3
  190. package/dist/entries/charts/layoutcharts.cjs +3 -3
  191. package/dist/entries/charts/lifescience.cjs +4 -4
  192. package/dist/entries/charts/linesareas.cjs +3 -3
  193. package/dist/entries/charts/logisticscharts.cjs +3 -3
  194. package/dist/entries/charts/machinecharts.cjs +7 -3
  195. package/dist/entries/charts/machinecharts.d.ts +1 -1
  196. package/dist/entries/charts/machinecharts.js +2 -1
  197. package/dist/entries/charts/massive.cjs +3 -3
  198. package/dist/entries/charts/mathematical.cjs +3 -3
  199. package/dist/entries/charts/matrix.cjs +3 -3
  200. package/dist/entries/charts/medicalcharts.cjs +4 -4
  201. package/dist/entries/charts/mlcharts.cjs +4 -4
  202. package/dist/entries/charts/modelcharts.cjs +6 -5
  203. package/dist/entries/charts/multivariate.cjs +3 -3
  204. package/dist/entries/charts/musiccharts.cjs +3 -3
  205. package/dist/entries/charts/network.cjs +3 -3
  206. package/dist/entries/charts/physicscharts.cjs +3 -3
  207. package/dist/entries/charts/pictorial.cjs +4 -4
  208. package/dist/entries/charts/portfoliocharts.cjs +3 -3
  209. package/dist/entries/charts/productcharts.cjs +3 -3
  210. package/dist/entries/charts/proportion.cjs +3 -3
  211. package/dist/entries/charts/qualitycharts.cjs +3 -3
  212. package/dist/entries/charts/radial.cjs +3 -3
  213. package/dist/entries/charts/reliabilitycharts.cjs +5 -3
  214. package/dist/entries/charts/scatter.cjs +3 -3
  215. package/dist/entries/charts/scientific.cjs +3 -3
  216. package/dist/entries/charts/seriescharts.cjs +3 -3
  217. package/dist/entries/charts/signals.cjs +7 -3
  218. package/dist/entries/charts/signals.d.ts +1 -1
  219. package/dist/entries/charts/signals.js +2 -1
  220. package/dist/entries/charts/softwarediagrams.cjs +21 -3
  221. package/dist/entries/charts/softwarediagrams.d.ts +1 -1
  222. package/dist/entries/charts/softwarediagrams.js +4 -2
  223. package/dist/entries/charts/sportscharts.cjs +13 -3
  224. package/dist/entries/charts/sportscharts.d.ts +1 -1
  225. package/dist/entries/charts/sportscharts.js +3 -1
  226. package/dist/entries/charts/statistics.cjs +6 -6
  227. package/dist/entries/charts/strategycharts.cjs +30 -3
  228. package/dist/entries/charts/strategycharts.d.ts +1 -1
  229. package/dist/entries/charts/strategycharts.js +6 -1
  230. package/dist/entries/charts/structure.cjs +3 -3
  231. package/dist/entries/charts/surveycharts.cjs +3 -3
  232. package/dist/entries/charts/systemscharts.cjs +3 -3
  233. package/dist/entries/charts/textcharts.cjs +5 -5
  234. package/dist/entries/charts/thematicmaps.cjs +4 -4
  235. package/dist/entries/charts/threebars.cjs +3 -3
  236. package/dist/entries/charts/threefields.cjs +53 -0
  237. package/dist/entries/charts/threefields.d.ts +1 -0
  238. package/dist/entries/charts/threefields.js +6 -0
  239. package/dist/entries/charts/threeglobe.cjs +3 -3
  240. package/dist/entries/charts/threenetwork.cjs +3 -3
  241. package/dist/entries/charts/threestrata.cjs +3 -3
  242. package/dist/entries/charts/threesurfaces.cjs +7 -3
  243. package/dist/entries/charts/threesurfaces.d.ts +1 -1
  244. package/dist/entries/charts/threesurfaces.js +2 -1
  245. package/dist/entries/charts/threevolume.cjs +4 -4
  246. package/dist/entries/charts/timeline.cjs +3 -3
  247. package/dist/entries/charts/tracecharts.cjs +3 -3
  248. package/dist/entries/charts/trackscharts.cjs +3 -3
  249. package/dist/entries/charts/tradingcharts.cjs +6 -4
  250. package/dist/entries/charts/tradingcharts.d.ts +1 -1
  251. package/dist/entries/charts/tradingcharts.js +2 -1
  252. package/dist/entries/three.cjs +60 -20
  253. package/dist/entries/three.d.ts +1 -1
  254. package/dist/entries/three.js +7 -1
  255. package/dist/index.cjs +258 -97
  256. package/dist/index.d.cts +1953 -10
  257. package/dist/index.d.ts +1953 -10
  258. package/dist/index.js +26 -5
  259. package/dist/page/index.cjs +2 -2
  260. package/dist/react/components.js +58 -1
  261. package/dist/react/index.cjs +135 -92
  262. package/dist/react/index.d.ts +1 -1
  263. package/dist/react/index.js +2 -2
  264. package/package.json +1 -1
  265. package/dist/AutoGraph-XLBJQDRB.cjs +0 -140
  266. package/dist/catalog-O7XGDD2H.cjs +0 -137
  267. package/dist/chunk-77K2HTRC.cjs +0 -3220
  268. package/dist/chunk-XEK47TVD.cjs +0 -323
  269. /package/dist/{chunk-UMEKIIHJ.cjs → chunk-LNWTC72Y.cjs} +0 -0
@@ -0,0 +1,362 @@
1
+ import { CartesianChart } from "../../core/CartesianChart.js";
2
+ import { ChartFamily, CurveKind, DataShape, Dimension } from "../../core/enums.js";
3
+ import { Scales } from "../../core/Scales.js";
4
+ import { ZoomAxis } from "../../core/ChartViewport.js";
5
+ import { FrameworkText } from "../frameworks/FrameworkText.js";
6
+ import { BassModel } from "./BassModel.js";
7
+ import { StrategyPane } from "./StrategyPane.js";
8
+ import { StrategyRows } from "./StrategyRows.js";
9
+ import { StrategySamples } from "./StrategySamples.js";
10
+ import { StrategyWords } from "./StrategyWords.js";
11
+ import { area, line, max, scaleLinear, sum } from "d3";
12
+ // src/charts/strategycharts/BassDiffusionChart.ts
13
+ /** The bell of new adopters over the S-curve of cumulative adopters, or either alone. */
14
+ var BassDiffusionView = /* @__PURE__ */ function(BassDiffusionView) {
15
+ BassDiffusionView["Both"] = "both";
16
+ BassDiffusionView["Adopters"] = "adopters";
17
+ BassDiffusionView["Cumulative"] = "cumulative";
18
+ return BassDiffusionView;
19
+ }({});
20
+ var OBSERVED = "Observed";
21
+ var MODEL = "Bass model";
22
+ var SOURCES = ["Innovators", "Imitators"];
23
+ var TITLES = {
24
+ ["adopters"]: "New adopters per period",
25
+ ["cumulative"]: "Cumulative adopters"
26
+ };
27
+ var LIMIT = 60;
28
+ var NEARLY = .985;
29
+ var PANE_GAP = 20;
30
+ var SIZE = 10.5;
31
+ var figure = (value) => value.toLocaleString("en", { maximumFractionDigits: Math.abs(value) < 100 ? 1 : 0 });
32
+ /**
33
+ * The Bass diffusion curve of a new product: new adopters per period rise to a peak and fall away, and cumulative
34
+ * adopters follow an S-curve up to the market potential. The model is fitted to the observed periods and carried on
35
+ * as a dashed projection. Use it to see where a product sits on its curve and how much comes from imitation.
36
+ */
37
+ var BassDiffusionChart = class extends CartesianChart {
38
+ static meta = {
39
+ id: "bass-diffusion",
40
+ name: "Bass diffusion curve",
41
+ family: ChartFamily.Trend,
42
+ dimension: Dimension.Two,
43
+ shape: DataShape.Table,
44
+ description: "Fits the Bass model to observed adoptions and draws new adopters per period and the cumulative S-curve, dashed where they run past the data.",
45
+ tags: [
46
+ "diffusion of innovations",
47
+ "adoption",
48
+ "s-curve",
49
+ "forecast",
50
+ "innovation",
51
+ "imitation",
52
+ "marketing"
53
+ ],
54
+ sample: (seed) => StrategySamples.bass(seed),
55
+ variants: {
56
+ view: [
57
+ "both",
58
+ "adopters",
59
+ "cumulative"
60
+ ],
61
+ split: [false, true],
62
+ peak: [false, true]
63
+ }
64
+ };
65
+ #x = null;
66
+ #ticks = 6;
67
+ defaults() {
68
+ return {
69
+ period: "period",
70
+ adopters: "adopters",
71
+ view: "both",
72
+ split: false,
73
+ peak: false
74
+ };
75
+ }
76
+ yTicks() {
77
+ return this.options.yTicks ?? this.#ticks;
78
+ }
79
+ /** The panes have a scale each, so with both drawn only the periods can zoom. */
80
+ zoomAxes() {
81
+ return this.options.view === "both" ? ZoomAxis.X : ZoomAxis.XY;
82
+ }
83
+ #rows() {
84
+ const { period, adopters } = this.options;
85
+ return StrategyRows.of(this.options.data).flatMap((row) => {
86
+ const seen = StrategyRows.number(row[adopters]);
87
+ return seen === null ? [] : [{
88
+ period: row[period],
89
+ adopters: Math.max(0, seen)
90
+ }];
91
+ });
92
+ }
93
+ /** The coefficients in use, and the model run from the first period until nearly the whole market has adopted. */
94
+ get model() {
95
+ const { innovation, imitation, market } = this.options, observed = this.#rows().map((row) => row.adopters);
96
+ const given = (value, most = Infinity) => value !== void 0 && Number.isFinite(value) && value > 0 && value <= most ? value : void 0;
97
+ const fit = BassModel.fit(observed), total = sum(observed);
98
+ const p = given(innovation, 1) ?? fit?.p ?? .03, q = given(imitation) ?? fit?.q ?? .38;
99
+ const m = given(market) ?? fit?.m ?? (total > 0 ? BassModel.market(p, q, total, observed.length) : 100);
100
+ const coefficients = {
101
+ p,
102
+ q,
103
+ m
104
+ }, done = BassModel.run(coefficients, LIMIT).findIndex((period) => period.cumulative >= NEARLY * m);
105
+ const count = Math.max(observed.length + 2, Math.min(LIMIT, Math.max(6, done < 0 ? LIMIT : done + 2)));
106
+ return {
107
+ coefficients,
108
+ fitted: !!fit && [
109
+ innovation,
110
+ imitation,
111
+ market
112
+ ].every((value) => given(value) === void 0),
113
+ observed,
114
+ periods: BassModel.run(coefficients, count)
115
+ };
116
+ }
117
+ /** Names period `t`, counted from 1: the observed names, carried on where they are evenly spaced numbers and as +1, +2 where they are not. */
118
+ #namer() {
119
+ const cells = this.#rows().map((row) => row.period), numbers = cells.map((cell) => StrategyRows.number(cell)), step = cells.length > 1 ? numbers[1] - numbers[0] : 1;
120
+ if (!cells.length) return (t) => `${t}`;
121
+ if (step && numbers.every((value, i) => value !== null && Math.abs(value - (numbers[0] + i * step)) < 1e-9)) return (t) => `${+(numbers[0] + (t - 1) * step).toFixed(6)}`;
122
+ return (t) => t <= cells.length ? FrameworkText.clean(cells[t - 1]) || `${t}` : `+${t - cells.length}`;
123
+ }
124
+ tickFormat(scale) {
125
+ if (scale !== this.#x) return super.tickFormat(scale);
126
+ const name = this.#namer(), last = this.model.periods.length;
127
+ return (value) => Number.isInteger(+value) && +value >= 1 && +value <= last ? FrameworkText.fit(name(+value), 64, 11) : "";
128
+ }
129
+ #panes(height, { coefficients, observed, periods }) {
130
+ const { view, peak } = this.options;
131
+ const kinds = view === "both" ? ["adopters", "cumulative"] : [view === "cumulative" ? "cumulative" : "adopters"];
132
+ const upper = kinds.length > 1 ? Math.round((height - PANE_GAP) * .54) : height;
133
+ return kinds.map((kind, i) => {
134
+ const top = i ? upper + PANE_GAP : 0, bottom = i ? height : upper, bell = kind === "adopters";
135
+ const head = bell ? peak ? 36 : 18 : i ? 18 : 32;
136
+ const high = bell ? max([...periods.map((period) => period.adopters), ...observed]) : Math.max(coefficients.m, sum(observed));
137
+ const scale = scaleLinear([0, high || 1], [bottom, Math.min(bottom - 1, top + head)]);
138
+ return {
139
+ kind,
140
+ first: !i,
141
+ top,
142
+ bottom,
143
+ scale: bell ? scale.nice(4) : scale
144
+ };
145
+ });
146
+ }
147
+ scales(ctx) {
148
+ const fit = this.model;
149
+ this.#x = scaleLinear([.5, fit.periods.length + .5], [0, ctx.plot.width]);
150
+ const [first] = this.#panes(ctx.plot.height, fit);
151
+ const [low, high] = first.scale.range();
152
+ this.#ticks = Math.max(2, Math.min(6, Math.floor((low - high) / 24)));
153
+ return {
154
+ x: this.#x,
155
+ y: first.scale
156
+ };
157
+ }
158
+ plotMarks(ctx, { x, y }) {
159
+ const { split, peak } = this.options, { palette, theme, plot } = ctx;
160
+ const fit = this.model, { observed, periods, coefficients } = fit, name = this.#namer(), seen = observed.length;
161
+ const colors = {
162
+ observed: seen ? palette.color(OBSERVED) : theme.inkMuted,
163
+ model: palette.color(MODEL),
164
+ sources: split ? SOURCES.map((source) => palette.color(source)) : []
165
+ };
166
+ const panes = this.#panes(plot.height, fit).map((pane) => pane.first ? {
167
+ ...pane,
168
+ scale: y
169
+ } : pane);
170
+ const top = BassModel.peak(periods);
171
+ const columns = ctx.marks("periods", "rect", periods, (period) => period.t).attr("fill", "transparent");
172
+ ctx.set(columns, {
173
+ x: (period) => x(period.t - .5),
174
+ y: 0,
175
+ width: (period) => Math.max(0, x(period.t + .5) - x(period.t - .5)),
176
+ height: plot.height
177
+ });
178
+ ctx.hover(columns, (period) => ({
179
+ title: name(period.t),
180
+ color: colors.model,
181
+ rows: [
182
+ ...period.t <= seen ? [[OBSERVED, figure(observed[period.t - 1])]] : [],
183
+ ["Model adopters", figure(period.adopters)],
184
+ [SOURCES[0], figure(period.innovators)],
185
+ [SOURCES[1], figure(period.imitators)],
186
+ ["Cumulative", figure(period.cumulative)],
187
+ ["Share of market", `${figure(period.cumulative / coefficients.m * 100)}%`]
188
+ ]
189
+ }));
190
+ const words = [];
191
+ let innovators = 0, counted = 0;
192
+ const running = periods.map((period) => {
193
+ innovators += period.innovators;
194
+ if (period.t <= seen) counted += observed[period.t - 1];
195
+ return {
196
+ innovators,
197
+ counted
198
+ };
199
+ });
200
+ for (const pane of panes) {
201
+ const bell = pane.kind === "adopters";
202
+ const levels = periods.map((period, i) => bell ? {
203
+ t: period.t,
204
+ total: period.adopters,
205
+ innovators: period.innovators,
206
+ seen: period.t <= seen ? observed[i] : null
207
+ } : {
208
+ t: period.t,
209
+ total: period.cumulative,
210
+ innovators: running[i].innovators,
211
+ seen: period.t <= seen ? running[i].counted : null
212
+ });
213
+ if (!pane.first) StrategyPane.axis(ctx, pane.kind, pane.scale, Math.max(2, Math.min(4, Math.floor((pane.bottom - pane.top) / 28))), StrategyPane.compact);
214
+ words.push(...this.#drawPane(ctx, pane, x, levels, colors, fit, top, name));
215
+ }
216
+ words.push(...this.#heading(panes[0], fit, plot.width));
217
+ const rules = ctx.marks("peak-rule", "line", peak && top ? panes : [], (pane) => pane.kind).attr("stroke", theme.axis).attr("stroke-width", 1);
218
+ ctx.set(rules, {
219
+ x1: x(top?.t ?? 0),
220
+ x2: x(top?.t ?? 0),
221
+ y1: (pane) => pane.scale.range()[1] - 6,
222
+ y2: (pane) => pane.bottom
223
+ });
224
+ StrategyWords.draw(ctx, "words", words);
225
+ }
226
+ /** The pane's title at the left and, on the first pane, the coefficients at the right where they fit beside it. */
227
+ #heading(pane, { coefficients: { p, q, m }, fitted, observed }, width) {
228
+ const figures = `p ${p.toFixed(3)}, q ${q.toFixed(3)}, m ${StrategyPane.compact(m)}`, whole = fitted ? `Fitted to ${observed.length} periods: ${figures}` : figures;
229
+ const room = width - FrameworkText.width(TITLES[pane.kind], SIZE) - 24, text = [whole, figures].find((option) => FrameworkText.width(option, SIZE) <= room);
230
+ return text ? [{
231
+ key: "coefficients",
232
+ text,
233
+ x: width - 2,
234
+ y: pane.top + 7,
235
+ anchor: "end",
236
+ size: SIZE,
237
+ halo: true
238
+ }] : [];
239
+ }
240
+ #drawPane(ctx, pane, x, levels, colors, { observed, coefficients }, top, name) {
241
+ const { peak } = this.options, { theme, plot } = ctx, { kind, scale } = pane, bell = kind === "adopters";
242
+ const seen = observed.length, ahead = seen > 0 && seen < levels.length;
243
+ const spans = ahead ? [{
244
+ key: "fit",
245
+ from: 1,
246
+ to: seen,
247
+ faded: false
248
+ }, {
249
+ key: "ahead",
250
+ from: seen,
251
+ to: levels.length,
252
+ faded: true
253
+ }] : [{
254
+ key: "fit",
255
+ from: 1,
256
+ to: levels.length,
257
+ faded: false
258
+ }];
259
+ const within = (span) => levels.filter((level) => level.t >= span.from && level.t <= span.to);
260
+ const curve = Scales.curve(CurveKind.Monotone);
261
+ const market = bell ? [] : [coefficients.m];
262
+ const ceiling = ctx.marks(`axis-${kind}-market`, "line", market).attr("stroke", theme.axis).attr("stroke-dasharray", "4 3");
263
+ ctx.set(ceiling, {
264
+ x1: 0,
265
+ x2: plot.width,
266
+ y1: (level) => scale(level),
267
+ y2: (level) => scale(level)
268
+ });
269
+ const layers = colors.sources.length ? spans.flatMap((span) => [0, 1].map((source) => ({
270
+ key: `${SOURCES[source]}/${span.key}`,
271
+ source,
272
+ faded: span.faded,
273
+ levels: within(span)
274
+ }))) : [];
275
+ const stack = area().x((level) => x(level.t)).curve(curve);
276
+ const fills = ctx.marks(`${kind}-sources`, "path", layers.filter((layer) => layer.levels.length > 1), (layer) => layer.key).attr("fill", (layer) => colors.sources[layer.source]).attr("fill-opacity", (layer) => layer.faded ? .28 : .72);
277
+ ctx.set(fills, { d: (layer) => stack.y0((level) => scale(layer.source ? level.innovators : 0)).y1((level) => scale(layer.source ? level.total : level.innovators))(layer.levels) ?? "" });
278
+ const whole = levels.at(-1);
279
+ ctx.hover(fills, (layer) => ({
280
+ title: SOURCES[layer.source],
281
+ color: colors.sources[layer.source],
282
+ rows: [["Span", layer.faded ? "Projected" : ahead ? "Fitted to the observed periods" : "Model"], ...whole && !bell ? [["Share of all adopters", `${figure((layer.source ? whole.total - whole.innovators : whole.innovators) / (whole.total || 1) * 100)}%`]] : []]
283
+ }));
284
+ const path = line().x((level) => x(level.t)).y((level) => scale(level.total)).curve(curve);
285
+ const drawn = ctx.marks(`${kind}-model`, "path", spans.filter((span) => !span.faded).map(within).filter((span) => span.length > 1)).attr("fill", "none").attr("stroke", colors.model).attr("stroke-width", 2).attr("stroke-linecap", "round");
286
+ ctx.set(drawn, { d: (span) => path(span) ?? "" });
287
+ ctx.drawOn(drawn);
288
+ const projected = ctx.marks(`${kind}-projection`, "path", spans.filter((span) => span.faded).map(within).filter((span) => span.length > 1)).attr("fill", "none").attr("stroke", colors.model).attr("stroke-width", 2).attr("stroke-dasharray", "6 4");
289
+ ctx.set(projected, { d: (span) => path(span) ?? "" });
290
+ const dots = ctx.marks(`${kind}-observed`, "circle", levels.filter((level) => level.seen !== null), (level) => level.t).attr("fill", colors.observed).attr("stroke", theme.surface).attr("stroke-width", 2);
291
+ ctx.set(dots, {
292
+ cx: (level) => x(level.t),
293
+ cy: (level) => scale(level.seen),
294
+ r: 4
295
+ }, { r: 0 }, 12);
296
+ ctx.hover(dots, (level) => ({
297
+ title: name(level.t),
298
+ color: colors.observed,
299
+ rows: [[OBSERVED, figure(level.seen)], ["Model", figure(level.total)]]
300
+ }));
301
+ const crest = peak && top ? levels.filter((level) => level.t === top.t) : [];
302
+ const turn = ctx.marks(`${kind}-peak`, "circle", crest, (level) => level.t).attr("fill", colors.model).attr("stroke", theme.surface).attr("stroke-width", 2);
303
+ ctx.set(turn, {
304
+ cx: (level) => x(level.t),
305
+ cy: (level) => scale(level.total),
306
+ r: 5
307
+ }, { r: 0 });
308
+ ctx.hover(turn, (level) => ({
309
+ title: `Peak in ${name(level.t)}`,
310
+ color: colors.model,
311
+ rows: [["New adopters", figure(top.adopters)], ["Adopted by then", figure(top.cumulative)]]
312
+ }));
313
+ const words = [{
314
+ key: `title/${kind}`,
315
+ text: FrameworkText.fit(TITLES[kind], plot.width - 4, SIZE),
316
+ x: 2,
317
+ y: pane.top + 7,
318
+ size: SIZE,
319
+ weight: 600,
320
+ tone: "ink",
321
+ halo: true
322
+ }];
323
+ if (!bell) {
324
+ const text = `Market potential ${StrategyPane.compact(coefficients.m)}`;
325
+ if (FrameworkText.width(text, SIZE) + FrameworkText.width(TITLES[kind], SIZE) + 24 <= plot.width || pane.first) words.push({
326
+ key: `market/${kind}`,
327
+ text: FrameworkText.fit(text, plot.width - 4, SIZE),
328
+ x: plot.width - 2,
329
+ y: scale(coefficients.m) - 8,
330
+ anchor: "end",
331
+ size: SIZE,
332
+ halo: true
333
+ });
334
+ }
335
+ for (const level of crest) {
336
+ const text = `Peak ${name(level.t)}`, at = x(level.t), half = FrameworkText.width(text, SIZE) / 2;
337
+ if (bell) words.push({
338
+ key: `peak/${kind}`,
339
+ text,
340
+ x: StrategyRows.clamp(at, half, Math.max(half, plot.width - half)),
341
+ y: scale.range()[1] - 13,
342
+ anchor: "middle",
343
+ size: SIZE,
344
+ weight: 600,
345
+ tone: "ink",
346
+ halo: true
347
+ });
348
+ else if (pane.first && at + 10 + half * 2 <= plot.width) words.push({
349
+ key: `peak/${kind}`,
350
+ text,
351
+ x: at + 10,
352
+ y: scale(level.total) + 9,
353
+ size: SIZE,
354
+ weight: 600,
355
+ tone: "ink",
356
+ halo: true
357
+ });
358
+ }
359
+ return words;
360
+ }
361
+ };
362
+ export { BassDiffusionChart, BassDiffusionView };
@@ -0,0 +1,96 @@
1
+ // src/charts/strategycharts/BassModel.ts
2
+ var LEAST = 3;
3
+ var positive = (value) => Number.isFinite(value) && value > 0;
4
+ /**
5
+ * The Bass diffusion model (Bass, 1969) in discrete time: each period's adopters are (p + q N / m)(m - N), where N
6
+ * have adopted already. The p part adopts unprompted and the q part follows those who went before.
7
+ */
8
+ var BassModel = class BassModel {
9
+ /** Periods 1 to `count` of the model. */
10
+ static run({ p, q, m }, count) {
11
+ const periods = [];
12
+ let adopted = 0;
13
+ for (let t = 1; t <= count; t++) {
14
+ const left = Math.max(0, m - adopted), innovators = Math.min(left, p * left), imitators = Math.min(left - innovators, m > 0 ? q * (adopted / m) * left : 0);
15
+ adopted += innovators + imitators;
16
+ periods.push({
17
+ t,
18
+ innovators,
19
+ imitators,
20
+ adopters: innovators + imitators,
21
+ cumulative: adopted
22
+ });
23
+ }
24
+ return periods;
25
+ }
26
+ /**
27
+ * Bass's own estimate: least squares of each period's adopters on the total adopted before it and that total squared,
28
+ * solved for p, q and m. Null with fewer than three periods, or when the fit is not a diffusion: no positive
29
+ * coefficients, or a market smaller than what has already adopted.
30
+ */
31
+ static fit(adopters) {
32
+ if (adopters.length < LEAST) return null;
33
+ const total = adopters.reduce((sum, value) => sum + value, 0);
34
+ if (!positive(total)) return null;
35
+ let before = 0;
36
+ const rows = adopters.map((value) => {
37
+ const x = before / total;
38
+ before += value;
39
+ return [
40
+ 1,
41
+ x,
42
+ x * x,
43
+ value
44
+ ];
45
+ });
46
+ const normal = [
47
+ 0,
48
+ 1,
49
+ 2
50
+ ].map((i) => [...[
51
+ 0,
52
+ 1,
53
+ 2
54
+ ].map((j) => rows.reduce((sum, row) => sum + row[i] * row[j], 0)), rows.reduce((sum, row) => sum + row[i] * row[3], 0)]);
55
+ const solved = BassModel.#solve(normal);
56
+ if (!solved) return null;
57
+ const a = solved[0], b = solved[1] / total, c = solved[2] / (total * total), root = b * b - 4 * a * c;
58
+ if (!(solved[2] < -1e-6 * Math.abs(a)) || !(a > 0) || root < 0) return null;
59
+ const m = (-b - Math.sqrt(root)) / (2 * c), p = a / m, q = -c * m;
60
+ return positive(m) && positive(p) && positive(q) && p < 1 && m >= total ? {
61
+ p,
62
+ q,
63
+ m
64
+ } : null;
65
+ }
66
+ /** Gaussian elimination with partial pivoting on an augmented matrix. Null when the rows are not independent. */
67
+ static #solve(matrix) {
68
+ const size = matrix.length;
69
+ for (let column = 0; column < size; column++) {
70
+ let pivot = column;
71
+ for (let row = column + 1; row < size; row++) if (Math.abs(matrix[row][column]) > Math.abs(matrix[pivot][column])) pivot = row;
72
+ if (Math.abs(matrix[pivot][column]) < 1e-12) return null;
73
+ [matrix[column], matrix[pivot]] = [matrix[pivot], matrix[column]];
74
+ for (let row = 0; row < size; row++) {
75
+ if (row === column) continue;
76
+ const factor = matrix[row][column] / matrix[column][column];
77
+ for (let k = column; k <= size; k++) matrix[row][k] -= factor * matrix[column][k];
78
+ }
79
+ }
80
+ return matrix.map((row, i) => row[size] / row[i]);
81
+ }
82
+ /** The market that makes the model with `p` and `q` reach `total` adopters by the end of period `count`. */
83
+ static market(p, q, total, count) {
84
+ const share = BassModel.run({
85
+ p,
86
+ q,
87
+ m: 1
88
+ }, count).at(-1)?.cumulative ?? 0;
89
+ return share > 0 ? total / share : total;
90
+ }
91
+ /** The period in which the model's new adopters peak, or null when it has no periods. */
92
+ static peak(periods) {
93
+ return periods.reduce((best, period) => best && best.adopters >= period.adopters ? best : period, null);
94
+ }
95
+ };
96
+ export { BassModel };