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,201 @@
1
+ import { ChartFamily, DataShape, Dimension, ScaleKind } from "../../core/enums.js";
2
+ import { ElevationMath } from "../machinecharts/ElevationMath.js";
3
+ import { ColorRamp } from "../threesurfaces/ColorRamp.js";
4
+ import { SurfaceField } from "../threesurfaces/SurfaceField.js";
5
+ import { SurfaceGeometry } from "../threesurfaces/SurfaceGeometry.js";
6
+ import { SurfaceMesh } from "../threesurfaces/SurfaceMesh.js";
7
+ import { CEIL, FLOOR, SurfaceSpace } from "../threesurfaces/SurfaceSpace.js";
8
+ import { TubeBatch } from "../threesurfaces/TubeBatch.js";
9
+ import { FieldGrid } from "./FieldGrid.js";
10
+ import { TerrainRouteMath } from "./TerrainRouteMath.js";
11
+ import { ThreeFieldBase } from "./ThreeFieldBase.js";
12
+ import { ThreeFieldsSamples } from "./ThreeFieldsSamples.js";
13
+ import { interpolateRgb } from "d3";
14
+ // src/charts/threefields/TerrainRoute3DChart.ts
15
+ /** `Tube` lays the route on the ground. `Curtain` also hangs a ribbon from it to the base and fades the terrain, so the profile reads along the route. */
16
+ var TerrainRoute3DForm = /* @__PURE__ */ function(TerrainRoute3DForm) {
17
+ TerrainRoute3DForm["Tube"] = "route-tube";
18
+ TerrainRoute3DForm["Curtain"] = "route-curtain";
19
+ return TerrainRoute3DForm;
20
+ }({});
21
+ var STEEP = [
22
+ {
23
+ steep: -1,
24
+ label: "Descent",
25
+ tone: -.6
26
+ },
27
+ {
28
+ steep: 0,
29
+ label: "Under 3%",
30
+ tone: 0
31
+ },
32
+ {
33
+ steep: 1,
34
+ label: "3 to 6%",
35
+ tone: .4
36
+ },
37
+ {
38
+ steep: 2,
39
+ label: "6 to 9%",
40
+ tone: .72
41
+ },
42
+ {
43
+ steep: 3,
44
+ label: "Over 9%",
45
+ tone: 1
46
+ }
47
+ ];
48
+ var SIDES = 6;
49
+ var tenth = (value) => (+value.toFixed(1)).toLocaleString("en");
50
+ /**
51
+ * A route over the ground it crosses: the terrain as a surface and the route as a tube that follows it, coloured by
52
+ * steepness, with the climbs named. Use it when where a climb lies matters as much as how steep it is; an elevation
53
+ * profile reads the gradients more exactly.
54
+ */
55
+ var TerrainRoute3DChart = class extends ThreeFieldBase {
56
+ static meta = {
57
+ id: "terrain-route-3d",
58
+ name: "Terrain route profile",
59
+ family: ChartFamily.Geospatial,
60
+ dimension: Dimension.Three,
61
+ shape: DataShape.Grid,
62
+ description: "Draws an elevation grid as terrain with a route laid over it as a tube coloured by gradient class, the climbs pinned, and optionally a curtain from the route down to the base.",
63
+ tags: [
64
+ "route profile",
65
+ "elevation",
66
+ "gradient",
67
+ "climb",
68
+ "cycling",
69
+ "hiking",
70
+ "dem",
71
+ "track"
72
+ ],
73
+ sample: (seed) => ThreeFieldsSamples.terrainRoute(seed),
74
+ variants: {
75
+ form: Object.values(TerrainRoute3DForm),
76
+ climbs: [true, false],
77
+ contours: [false, true],
78
+ solidBase: [true, false]
79
+ },
80
+ valid: ({ form, solidBase }) => form === "route-tube" || solidBase === false
81
+ };
82
+ defaults() {
83
+ return {
84
+ ...super.defaults(),
85
+ elevation: 32,
86
+ form: "route-tube",
87
+ climbs: true,
88
+ contours: false,
89
+ solidBase: true,
90
+ segment: 1,
91
+ rise: 1e3,
92
+ distanceUnit: "km",
93
+ elevationUnit: "m"
94
+ };
95
+ }
96
+ table() {
97
+ return this.gridTable(SurfaceField.from(this.options.data));
98
+ }
99
+ build(ctx) {
100
+ const { form, climbs, contours, solidBase, segment, rise, distanceUnit, elevationUnit } = this.options;
101
+ const curtain = form === "route-curtain", { theme, palette } = ctx;
102
+ const field = SurfaceField.from(this.options.data).capped(110);
103
+ const bottom = FLOOR + (solidBase && !curtain ? .2 : .02);
104
+ const space = new SurfaceSpace(field, ScaleKind.Linear, [bottom, bottom + .85]), grid = new FieldGrid(field, space);
105
+ const [low, high] = field.zDomain, [x0, x1] = field.xDomain, [y0, y1] = field.yDomain;
106
+ const ground = new ColorRamp(interpolateRgb(theme.grid, theme.axis));
107
+ const shade = (value) => ground.at((value - low) / (high - low));
108
+ const whole = (value) => `${Math.round(value).toLocaleString("en")} ${elevationUnit}`, far = (value) => `${tenth(value)} ${distanceUnit}`;
109
+ this.box(ctx, {
110
+ x: this.ticks(field.xDomain, (v) => (v - x0) / (x1 - x0 || 1) * 2 - 1),
111
+ y: this.ticks(field.yDomain, (v) => 1 - (v - y0) / (y1 - y0 || 1) * 2),
112
+ z: this.ticks(field.zDomain, space.level, 3)
113
+ }, { bottom: FLOOR });
114
+ const lift = this.rise(ctx);
115
+ this.solid(ctx, SurfaceGeometry.grid(field.nx, field.ny, (i, j) => space.at(i, j), (i, j) => shade(field.at(i, j))), lift, (k) => {
116
+ const i = k % field.nx, j = Math.floor(k / field.nx);
117
+ return { rows: [
118
+ ["Elevation", whole(field.at(i, j))],
119
+ ["East", far(field.x[i])],
120
+ ["North", far(field.y[j])]
121
+ ] };
122
+ }, curtain ? {
123
+ roughness: .85,
124
+ transparent: true,
125
+ opacity: .4,
126
+ depthWrite: false
127
+ } : { roughness: .85 });
128
+ if (!field.hasData) return;
129
+ if (solidBase && !curtain) this.skirt(ctx, space, () => ctx.color(theme.grid), lift);
130
+ if (contours) this.contourLines(ctx, space, lift, 7);
131
+ const steps = TerrainRouteMath.profile(grid, this.options.data?.route), length = segment > 0 ? segment : 1;
132
+ const segments = TerrainRouteMath.grade(steps, length, rise);
133
+ const laid = steps.map((step) => ({
134
+ step,
135
+ at: grid.at(step.x, step.y, .014)
136
+ })).filter(({ at }, k, all) => {
137
+ const before = all[k - 1]?.at;
138
+ return !before || (at[0] - before[0]) ** 2 + (at[1] - before[1]) ** 2 + (at[2] - before[2]) ** 2 > 1e-8;
139
+ });
140
+ if (laid.length < 2) return;
141
+ const tones = new Map(STEEP.map(({ steep, tone }) => [steep, ctx.color(palette.diverging(tone))]));
142
+ const tone = (k) => tones.get(laid[k].step.steep);
143
+ const told = (k) => {
144
+ const { step } = laid[Math.min(laid.length - 1, k)], part = segments[Math.min(segments.length - 1, Math.floor(step.distance / length))];
145
+ return {
146
+ title: `${far(step.distance)} along the route`,
147
+ rows: [["Elevation", whole(step.elevation)], ...part ? [["Gradient", `${tenth(part.grade)}%`]] : []]
148
+ };
149
+ };
150
+ const tube = new SurfaceMesh(new TubeBatch(.011, SIDES).add(laid.map(({ at }) => at), tone).geometry(), this.material({
151
+ side: 0,
152
+ roughness: .5
153
+ }));
154
+ tube.castShadow = true;
155
+ lift.add(tube);
156
+ this.casing(laid.map(({ at }) => at), .011, theme.ink, lift);
157
+ ctx.pick(tube, (vertex) => told(Math.floor(vertex / SIDES)));
158
+ if (curtain) this.solid(ctx, SurfaceGeometry.curtain(laid.map(({ at }) => at), FLOOR + .002, tone, tone), lift, (vertex) => told(vertex % laid.length));
159
+ this.rings(ctx, [laid[0].at], .034, () => ctx.color(theme.ink), lift, () => ({
160
+ title: "Start",
161
+ rows: [["Elevation", whole(laid[0].step.elevation)]]
162
+ }));
163
+ this.dots(ctx, [laid.at(-1).at], .03, () => ctx.color(theme.ink), lift, () => ({
164
+ title: "Finish",
165
+ rows: [["Distance", far(laid.at(-1).step.distance)], ["Elevation", whole(laid.at(-1).step.elevation)]]
166
+ }));
167
+ const points = steps.map((step) => [step.distance, step.elevation]);
168
+ const name = ({ category }) => category === null ? "Climb" : category === 0 ? "HC" : `Cat ${category}`;
169
+ const found = climbs ? ElevationMath.climbs(points, { rise }).sort((a, b) => b.gain - a.gain).slice(0, 3).sort((a, b) => a.to - b.to) : [];
170
+ const tip = (climb) => () => ({
171
+ title: name(climb),
172
+ rows: [
173
+ ["Length", far(climb.length)],
174
+ ["Gain", whole(climb.gain)],
175
+ ["Mean gradient", `${tenth(climb.grade)}%`],
176
+ ["Summit", whole(climb.end)]
177
+ ]
178
+ });
179
+ const on = (distance) => {
180
+ const step = TerrainRouteMath.near(steps, distance);
181
+ return grid.at(step.x, step.y, .014);
182
+ };
183
+ this.dots(ctx, found.map((climb) => on(climb.from)), .02, () => ctx.color(theme.ink), lift, (k) => tip(found[k])());
184
+ this.pins(ctx, found.map((climb) => ({
185
+ at: on(climb.to),
186
+ text: `${name(climb)} · ${far(climb.length)} at ${tenth(climb.grade)}%`,
187
+ pick: tip(climb)
188
+ })), lift);
189
+ const used = new Set(laid.map(({ step }) => step.steep));
190
+ this.key(ctx, STEEP.filter(({ steep }) => used.has(steep)).map(({ label, tone: share }) => ({
191
+ label,
192
+ color: palette.diverging(share)
193
+ })));
194
+ ctx.label(`Total ascent ${whole(ElevationMath.ascent(points, 3))} over ${far(steps.at(-1).distance)}`, [
195
+ 0,
196
+ CEIL + .16,
197
+ -1
198
+ ], "ink", 11);
199
+ }
200
+ };
201
+ export { TerrainRoute3DChart, TerrainRoute3DForm };
@@ -0,0 +1,40 @@
1
+ import { ElevationMath } from "../machinecharts/ElevationMath.js";
2
+ // src/charts/threefields/TerrainRouteMath.ts
3
+ /** Turns a route over an elevation grid into the profile a rider or a vehicle meets. Distances are in the grid's own units. */
4
+ var TerrainRouteMath = class {
5
+ /**
6
+ * The route cut into pieces no longer than `cell` grid cells, each with its distance over the ground and the elevation
7
+ * under it. Points that repeat the one before, or that sit over a gap in the grid, are left out.
8
+ */
9
+ static profile(grid, route, cell = .5) {
10
+ const track = (Array.isArray(route) ? route : []).map((point) => [+point?.[0], +point?.[1]]).filter(([x, y]) => Number.isFinite(x) && Number.isFinite(y));
11
+ const steps = [];
12
+ const add = (x, y) => {
13
+ const last = steps[steps.length - 1], elevation = grid.value(x, y), run = last ? Math.hypot(x - last.x, y - last.y) : 0;
14
+ if (Number.isFinite(elevation) && (!last || run > 0)) steps.push({
15
+ x,
16
+ y,
17
+ distance: (last?.distance ?? 0) + run,
18
+ elevation,
19
+ steep: 0
20
+ });
21
+ };
22
+ track.forEach(([x, y], k) => {
23
+ const [px, py] = track[k - 1] ?? [x, y], from = grid.index(px, py), to = grid.index(x, y);
24
+ const pieces = Math.max(1, Math.ceil(Math.max(Math.abs(to[0] - from[0]), Math.abs(to[1] - from[1])) / cell));
25
+ for (let piece = 1; piece <= pieces; piece++) add(px + (x - px) * piece / pieces, py + (y - py) * piece / pieces);
26
+ });
27
+ return steps;
28
+ }
29
+ /** Stretches of `length` with their mean gradient, and each step tagged with the class of the stretch it falls in. */
30
+ static grade(steps, length, rise = 1e3) {
31
+ const segments = ElevationMath.segments(steps.map((step) => [step.distance, step.elevation]), length, rise);
32
+ for (const step of steps) step.steep = segments[Math.min(segments.length - 1, Math.floor(step.distance / length))]?.steep ?? 0;
33
+ return segments;
34
+ }
35
+ /** The step nearest a distance along the route. */
36
+ static near(steps, distance) {
37
+ return steps.reduce((best, step) => !best || Math.abs(step.distance - distance) < Math.abs(best.distance - distance) ? step : best, void 0);
38
+ }
39
+ };
40
+ export { TerrainRouteMath };
@@ -0,0 +1,117 @@
1
+ import { SurfaceBase } from "../threesurfaces/SurfaceBase.js";
2
+ import { CEIL } from "../threesurfaces/SurfaceSpace.js";
3
+ import { TubeBatch } from "../threesurfaces/TubeBatch.js";
4
+ import { color } from "d3";
5
+ import { BackSide, BoxGeometry, InstancedMesh, Mesh, MeshStandardMaterial, Object3D, TorusGeometry } from "three";
6
+ // src/charts/threefields/ThreeFieldBase.ts
7
+ var RAD = Math.PI / 180;
8
+ /** Marks the field charts share: tags and pins that name a point, the ring that opens a path, a casing that parts a tube from the ground, and a colour key. */
9
+ var ThreeFieldBase = class extends SurfaceBase {
10
+ /** A label on a plate of the surface colour, for text that can lie over any tone of the field. */
11
+ tag(ctx, text, at, parent) {
12
+ const plate = color(ctx.theme.surface);
13
+ Object.assign(ctx.label(text, at, "ink", 11, parent).element.style, {
14
+ background: plate ? `${plate.copy({ opacity: .82 })}` : "",
15
+ padding: "1px 5px",
16
+ borderRadius: "4px"
17
+ });
18
+ }
19
+ /**
20
+ * A ball on each point, with a post up to its tag. A post grows until its tag clears the ones before it, as seen from
21
+ * where the camera starts; text is taken as 6.2px a character.
22
+ */
23
+ pins(ctx, pins, parent) {
24
+ const { azimuth = 38, elevation = 26, distance = 5.4 } = this.options;
25
+ const turn = azimuth * RAD, tilt = elevation * RAD, unit = this.size.height / (2 * distance * Math.tan(19 * RAD));
26
+ const placed = [];
27
+ for (const { at, text, pick } of pins) {
28
+ if (!at.every(Number.isFinite)) continue;
29
+ const across = (at[0] * Math.cos(turn) - at[2] * Math.sin(turn)) * unit, half = (text.length * 6.2 + 12) / 2;
30
+ const up = (rise) => ((at[1] + rise) * Math.cos(tilt) - (at[0] * Math.sin(turn) + at[2] * Math.cos(turn)) * Math.sin(tilt)) * unit;
31
+ let rise = .17;
32
+ while (rise < .6 && placed.some((tag) => Math.abs(tag.across - across) < tag.half + half && Math.abs(tag.up - up(rise)) < 20)) rise += .04;
33
+ placed.push({
34
+ across,
35
+ up: up(rise),
36
+ half
37
+ });
38
+ const top = [
39
+ at[0],
40
+ at[1] + rise,
41
+ at[2]
42
+ ];
43
+ this.tubes(new TubeBatch(.003).add([at, top]), parent, ctx.theme.ink);
44
+ this.dots(ctx, [at], .024, () => ctx.color(ctx.theme.ink), parent, pick);
45
+ this.tag(ctx, text, [
46
+ top[0],
47
+ top[1] + .055,
48
+ top[2]
49
+ ], parent);
50
+ }
51
+ }
52
+ /** A second skin around a tube, seen only past its edges: the 3D form of the ring that parts a mark from what lies behind it. */
53
+ casing(path, radius, color, parent) {
54
+ const batch = new TubeBatch(radius * 1.5).add(path);
55
+ if (!batch.empty) parent.add(new Mesh(batch.geometry(), this.material({
56
+ vertexColors: false,
57
+ side: BackSide,
58
+ color,
59
+ roughness: 1
60
+ })));
61
+ }
62
+ /** Flat rings on one instanced mesh, for the points where paths begin. */
63
+ rings(ctx, points, radius, paint, parent, pick) {
64
+ const shown = points.map((point, k) => ({
65
+ point,
66
+ k
67
+ })).filter(({ point }) => point.every(Number.isFinite));
68
+ if (!shown.length) return;
69
+ const mesh = new InstancedMesh(new TorusGeometry(1, .3, 8, 28).rotateX(Math.PI / 2), new MeshStandardMaterial({ roughness: .45 }), shown.length);
70
+ const dummy = new Object3D();
71
+ shown.forEach(({ point, k }, at) => {
72
+ dummy.position.set(...point);
73
+ dummy.scale.setScalar(radius);
74
+ dummy.updateMatrix();
75
+ mesh.setMatrixAt(at, dummy.matrix);
76
+ mesh.setColorAt(at, paint(k));
77
+ });
78
+ mesh.castShadow = true;
79
+ parent.add(mesh);
80
+ if (pick) ctx.pick(mesh, (at) => pick(shown[at].k));
81
+ }
82
+ /** Colour key: a chip per class stacked beside the far right edge of the box, first entry lowest, each with its label to the right. */
83
+ key(ctx, entries) {
84
+ if (!entries.length) return;
85
+ const chips = new InstancedMesh(new BoxGeometry(.08, .08, .08), new MeshStandardMaterial({ roughness: .6 }), entries.length);
86
+ const dummy = new Object3D();
87
+ entries.forEach(({ label, color }, k) => {
88
+ const y = CEIL - (entries.length - 1 - k) * .15;
89
+ dummy.position.set(1.14, y, -1);
90
+ dummy.updateMatrix();
91
+ chips.setMatrixAt(k, dummy.matrix);
92
+ chips.setColorAt(k, ctx.color(color));
93
+ ctx.label(label, [
94
+ 1.23,
95
+ y,
96
+ -1
97
+ ], "muted", 10).center.set(0, .5);
98
+ });
99
+ ctx.root.add(chips);
100
+ }
101
+ /** One row per grid node, for CSV and keyboard access. */
102
+ gridTable(field) {
103
+ return {
104
+ columns: [
105
+ "x",
106
+ "y",
107
+ "z"
108
+ ],
109
+ rows: Array.from({ length: field.ny }, (_, j) => Array.from({ length: field.nx }, (_, i) => [
110
+ field.x[i],
111
+ field.y[j],
112
+ field.at(i, j)
113
+ ])).flat()
114
+ };
115
+ }
116
+ };
117
+ export { ThreeFieldBase };
@@ -0,0 +1,80 @@
1
+ import { SampleData } from "../../data/SampleData.js";
2
+ // src/charts/threefields/ThreeFieldsSamples.ts
3
+ var HILLS = [
4
+ [
5
+ 8,
6
+ 6,
7
+ 520,
8
+ 4.2
9
+ ],
10
+ [
11
+ 14,
12
+ 16,
13
+ 760,
14
+ 5
15
+ ],
16
+ [
17
+ 22,
18
+ 8,
19
+ 640,
20
+ 4.6
21
+ ],
22
+ [
23
+ 25,
24
+ 19,
25
+ 880,
26
+ 5.5
27
+ ],
28
+ [
29
+ 5,
30
+ 19,
31
+ 420,
32
+ 3.8
33
+ ],
34
+ [
35
+ 17,
36
+ 3,
37
+ 300,
38
+ 3.5
39
+ ]
40
+ ];
41
+ var WAYPOINTS = [
42
+ [2, 2],
43
+ [7, 9],
44
+ [12.5, 14],
45
+ [16.5, 11],
46
+ [20, 13.5],
47
+ [22.5, 17.5],
48
+ [25, 19]
49
+ ];
50
+ var spline = (a, b, c, d, t) => .5 * (2 * b + (c - a) * t + (2 * a - 5 * b + 4 * c - d) * t * t + (3 * b - a - 3 * c + d) * t ** 3);
51
+ /** Invented data for the field charts, seeded through `SampleData.rng`. No real place. */
52
+ var ThreeFieldsSamples = class {
53
+ /** A 30 by 24 km block of hill country in metres, on a grid in km, with a road that crosses a shoulder and finishes on the highest summit. */
54
+ static terrainRoute(seed) {
55
+ const { u } = SampleData.rng(seed);
56
+ const hills = HILLS.map(([east, north, rise, width]) => [
57
+ east + (u() - .5) * 2,
58
+ north + (u() - .5) * 2,
59
+ rise * (.8 + u() * .4),
60
+ width * (.9 + u() * .25)
61
+ ]);
62
+ const phase = u() * 6;
63
+ const x = Array.from({ length: 61 }, (_, i) => i * .5), y = Array.from({ length: 49 }, (_, j) => j * .5);
64
+ const ground = (east, north) => 420 + 4 * east + 6 * north + 24 * Math.sin(.8 * east + phase) * Math.cos(.7 * north) + hills.reduce((sum, [cx, cy, rise, width]) => sum + rise * Math.exp(-(((east - cx) ** 2 + (north - cy) ** 2) / width ** 2)), 0);
65
+ const summit = hills[3];
66
+ const via = WAYPOINTS.map(([east, north], k) => k === WAYPOINTS.length - 1 ? [summit[0], summit[1]] : [east + (u() - .5) * 1.2, north + (u() - .5) * 1.2]);
67
+ const knot = (k) => via[Math.max(0, Math.min(via.length - 1, k))];
68
+ const route = Array.from({ length: (via.length - 1) * 12 + 1 }, (_, step) => {
69
+ const k = Math.min(via.length - 2, Math.floor(step / 12)), t = step / 12 - k;
70
+ return [0, 1].map((axis) => +spline(knot(k - 1)[axis], knot(k)[axis], knot(k + 1)[axis], knot(k + 2)[axis], t).toFixed(2));
71
+ });
72
+ return {
73
+ x,
74
+ y,
75
+ z: y.map((north) => x.map((east) => Math.round(ground(east, north)))),
76
+ route
77
+ };
78
+ }
79
+ };
80
+ export { ThreeFieldsSamples };
@@ -0,0 +1,218 @@
1
+ import { ChartFamily, DataShape, Dimension } from "../../core/enums.js";
2
+ import { ReliabilitySamples } from "../reliabilitycharts/ReliabilitySamples.js";
3
+ import { WaferMap } from "../reliabilitycharts/WaferMap.js";
4
+ import { Marks3D } from "../threebars/Marks3D.js";
5
+ import { ThreeFieldBase } from "./ThreeFieldBase.js";
6
+ import { BoxGeometry, CylinderGeometry, DoubleSide, ExtrudeGeometry, Mesh, Shape, Vector2 } from "three";
7
+ // src/charts/threefields/WaferMap3DChart.ts
8
+ /** `Value` raises and colours each die by a measurement. `Bin` lays flat tiles coloured by test bin. */
9
+ var WaferMap3DEncoding = /* @__PURE__ */ function(WaferMap3DEncoding) {
10
+ WaferMap3DEncoding["Value"] = "value-height";
11
+ WaferMap3DEncoding["Bin"] = "bin-tiles";
12
+ return WaferMap3DEncoding;
13
+ }({});
14
+ /** The orientation mark on the wafer's edge. */
15
+ var WaferMap3DMark = /* @__PURE__ */ function(WaferMap3DMark) {
16
+ WaferMap3DMark["Notch"] = "notch";
17
+ WaferMap3DMark["Flat"] = "flat";
18
+ return WaferMap3DMark;
19
+ }({});
20
+ /** `Fitted` spends the whole height on the spread between the lowest and highest die, `Zero` grows every die from 0. */
21
+ var WaferMap3DBaseline = /* @__PURE__ */ function(WaferMap3DBaseline) {
22
+ WaferMap3DBaseline["Fitted"] = "fitted-base";
23
+ WaferMap3DBaseline["Zero"] = "zero-base";
24
+ return WaferMap3DBaseline;
25
+ }({});
26
+ var RADIUS = .98;
27
+ var TOP = -.32;
28
+ var SLAB = .035;
29
+ var TALLEST = .62;
30
+ var ZONES = [
31
+ "Centre",
32
+ "Middle",
33
+ "Edge"
34
+ ];
35
+ var percent = (good, total) => total ? `${(100 * good / total).toFixed(1)}%` : "n/a";
36
+ var figure = (value) => `${+value.toPrecision(4)}`;
37
+ /**
38
+ * A wafer with each die raised by a measurement, so a tilt, a bowl or an edge roll-off across the wafer shows as relief.
39
+ * Use it for parametric data; for pass and fail the flat bin tiles, or the 2D wafer map of the same rows, read more plainly.
40
+ */
41
+ var WaferMap3DChart = class WaferMap3DChart extends ThreeFieldBase {
42
+ static meta = {
43
+ id: "wafer-map-3d",
44
+ name: "3D wafer map",
45
+ family: ChartFamily.Matrix,
46
+ dimension: Dimension.Three,
47
+ shape: DataShape.Table,
48
+ description: "Stands the dies of a semiconductor wafer on a notched disc as boxes whose height and colour show a measured value, or as flat tiles coloured by test bin, with the yield. The sample wafer is invented.",
49
+ tags: [
50
+ "wafer",
51
+ "semiconductor",
52
+ "die",
53
+ "yield",
54
+ "bin map",
55
+ "probe",
56
+ "parametric"
57
+ ],
58
+ sample: (seed) => ReliabilitySamples.wafer(seed),
59
+ variants: {
60
+ encoding: Object.values(WaferMap3DEncoding),
61
+ mark: Object.values(WaferMap3DMark),
62
+ baseline: Object.values(WaferMap3DBaseline),
63
+ zones: [false, true]
64
+ },
65
+ valid: ({ encoding, baseline }) => encoding === "value-height" || baseline === "fitted-base"
66
+ };
67
+ defaults() {
68
+ return {
69
+ azimuth: 24,
70
+ elevation: 40,
71
+ distance: 4.3,
72
+ x: "x",
73
+ y: "y",
74
+ bin: "bin",
75
+ value: "value",
76
+ passBin: "Pass",
77
+ encoding: "value-height",
78
+ mark: "notch",
79
+ baseline: "fitted-base",
80
+ zones: false
81
+ };
82
+ }
83
+ /** Dies, wafer radius in die pitches and yield, exactly as the 2D wafer map counts them. */
84
+ summary() {
85
+ const { data, x, y, bin, value, passBin } = this.options;
86
+ return new WaferMap({
87
+ data,
88
+ x,
89
+ y,
90
+ bin,
91
+ value,
92
+ passBin
93
+ }).summary();
94
+ }
95
+ table() {
96
+ const { x, y, bin, value } = this.options;
97
+ return {
98
+ columns: [
99
+ x,
100
+ y,
101
+ bin,
102
+ value
103
+ ],
104
+ rows: this.summary().dies.map((die) => [
105
+ die.x,
106
+ die.y,
107
+ die.bin,
108
+ die.value
109
+ ])
110
+ };
111
+ }
112
+ /**
113
+ * The wafer's edge as a polygon around the origin, with the orientation mark towards +y: a V notch, or a flat no
114
+ * nearer the centre than `floor`, the outermost die edge on that side.
115
+ */
116
+ static outline(radius, mark, floor = 0, steps = 96) {
117
+ const flat = mark === "flat", depth = radius * .06;
118
+ const chord = Math.min(radius * .995, Math.max(radius * .93, floor + radius * .01));
119
+ const gap = flat ? Math.acos(chord / radius) : Math.asin(depth * .8 / radius);
120
+ const arc = Array.from({ length: steps + 1 }, (_, k) => {
121
+ const angle = Math.PI / 2 + gap + (2 * Math.PI - 2 * gap) * k / steps;
122
+ return [radius * Math.cos(angle), radius * Math.sin(angle)];
123
+ });
124
+ return flat ? arc : [...arc, [0, radius - depth]];
125
+ }
126
+ build(ctx) {
127
+ const { encoding, mark, baseline, zones, bin, value } = this.options, { theme, palette } = ctx;
128
+ const { dies, centre, radius, good, zones: rings } = this.summary();
129
+ if (!dies.length) return;
130
+ const pitch = RADIUS / radius, side = pitch * .9, byValue = encoding === "value-height";
131
+ const values = dies.map((die) => die.value).filter((v) => v !== null);
132
+ const low = Math.min(...values), high = Math.max(...values), spread = high - low || 1;
133
+ const zero = baseline === "zero-base", from = zero ? Math.min(0, low) : low, to = zero ? Math.max(0, high) : high;
134
+ const level = (v) => TOP + (zero ? 0 : .03) + (v - from) / (to - from || 1) * (TALLEST - (zero ? 0 : .03));
135
+ const counts = /* @__PURE__ */ new Map();
136
+ for (const die of dies) counts.set(die.bin, (counts.get(die.bin) ?? 0) + 1);
137
+ if (!byValue) [...counts].sort((a, b) => b[1] - a[1]).forEach(([name]) => palette.color(name || "Unbinned"));
138
+ const tiles = dies.map((die) => {
139
+ const measured = byValue && die.value !== null;
140
+ return {
141
+ die,
142
+ x: (die.x - centre[0]) * pitch,
143
+ z: (die.y - centre[1]) * pitch,
144
+ from: measured && zero ? level(0) : TOP,
145
+ to: measured ? level(die.value) : TOP + (byValue ? .012 : .03),
146
+ color: byValue ? die.value === null ? theme.grid : palette.sequential((die.value - low) / spread) : palette.color(die.bin || "Unbinned")
147
+ };
148
+ });
149
+ Marks3D.instanced(ctx, new BoxGeometry(1, 1, 1), tiles, {
150
+ place: (dummy, tile, progress) => {
151
+ const tall = Math.max(.004, Math.abs(tile.to - tile.from) * progress);
152
+ dummy.position.set(tile.x, tile.from + (tile.to < tile.from ? -tall : tall) / 2, tile.z);
153
+ dummy.scale.set(side, tall, side);
154
+ },
155
+ color: (tile) => tile.color,
156
+ tip: ({ die, color }) => ({
157
+ title: `Die ${die.x}, ${die.y}`,
158
+ color,
159
+ rows: [
160
+ [bin, die.bin || "n/a"],
161
+ ...die.value === null ? [] : [[value, figure(die.value)]],
162
+ ["Ring", ZONES[Math.min(2, Math.floor(die.reach * 3))]]
163
+ ]
164
+ })
165
+ });
166
+ const floor = Math.max(0, ...tiles.map((tile) => tile.z + pitch / 2));
167
+ const shape = new Shape(WaferMap3DChart.outline(RADIUS, mark, floor).map(([x, y]) => new Vector2(x, y)));
168
+ const disc = new Mesh(new ExtrudeGeometry(shape, {
169
+ depth: SLAB,
170
+ bevelEnabled: false
171
+ }).rotateX(Math.PI / 2).translate(0, TOP, 0), Marks3D.matte({ color: theme.grid }));
172
+ disc.receiveShadow = true;
173
+ ctx.root.add(disc);
174
+ const crest = Math.max(...tiles.map((tile) => Math.max(tile.from, tile.to)));
175
+ if (zones) {
176
+ const tall = crest - TOP + .035;
177
+ for (const share of [1 / 3, 2 / 3]) {
178
+ const fence = new Mesh(new CylinderGeometry(share * RADIUS, share * RADIUS, tall, 72, 1, true).translate(0, TOP + tall / 2, 0), Marks3D.matte({
179
+ color: theme.ink,
180
+ transparent: true,
181
+ opacity: .22,
182
+ depthWrite: false,
183
+ side: DoubleSide
184
+ }));
185
+ ctx.root.add(fence);
186
+ }
187
+ ctx.label(rings.map((ring) => `${ring.name} ${percent(ring.good, ring.total)}`).join(" · "), [
188
+ 0,
189
+ -.42000000000000004,
190
+ 1.38
191
+ ], "secondary", 11);
192
+ }
193
+ if (byValue && values.length > 1 && high > low) {
194
+ const peak = tiles.find((tile) => tile.die.value === high), trough = tiles.find((tile) => tile.die.value === low);
195
+ this.pins(ctx, [{
196
+ at: [
197
+ peak.x,
198
+ peak.to,
199
+ peak.z
200
+ ],
201
+ text: `Highest ${figure(high)}`
202
+ }, {
203
+ at: [
204
+ trough.x,
205
+ trough.to,
206
+ trough.z
207
+ ],
208
+ text: `Lowest ${figure(low)}`
209
+ }], ctx.root);
210
+ }
211
+ ctx.label(`Yield ${percent(good, dies.length)} · ${good} of ${dies.length} dies`, [
212
+ 0,
213
+ -.42000000000000004,
214
+ 1.2
215
+ ], "ink", 11);
216
+ }
217
+ };
218
+ export { WaferMap3DBaseline, WaferMap3DChart, WaferMap3DEncoding, WaferMap3DMark };
@@ -0,0 +1,19 @@
1
+ import { MapSheet } from "./MapSheet.js";
2
+ import { ThreeFieldBase } from "./ThreeFieldBase.js";
3
+ import { BsfcMap3DBoundary, BsfcMap3DChart, BsfcMap3DContours, BsfcMap3DOptimum } from "./BsfcMap3DChart.js";
4
+ import { CompressorMap3DChart, CompressorMap3DSurge } from "./CompressorMap3DChart.js";
5
+ import { FieldGrid } from "./FieldGrid.js";
6
+ import { LossLandscape3DChart, LossLandscape3DTrail } from "./LossLandscape3DChart.js";
7
+ import { TerrainRouteMath } from "./TerrainRouteMath.js";
8
+ import { ThreeFieldsSamples } from "./ThreeFieldsSamples.js";
9
+ import { TerrainRoute3DChart, TerrainRoute3DForm } from "./TerrainRoute3DChart.js";
10
+ import { WaferMap3DBaseline, WaferMap3DChart, WaferMap3DEncoding, WaferMap3DMark } from "./WaferMap3DChart.js";
11
+ // src/charts/threefields/index.ts
12
+ var threefieldsCatalog = [
13
+ LossLandscape3DChart,
14
+ CompressorMap3DChart,
15
+ BsfcMap3DChart,
16
+ TerrainRoute3DChart,
17
+ WaferMap3DChart
18
+ ];
19
+ export { BsfcMap3DBoundary, BsfcMap3DChart, BsfcMap3DContours, BsfcMap3DOptimum, CompressorMap3DChart, CompressorMap3DSurge, FieldGrid, LossLandscape3DChart, LossLandscape3DTrail, MapSheet, TerrainRoute3DChart, TerrainRoute3DForm, TerrainRouteMath, ThreeFieldBase, ThreeFieldsSamples, WaferMap3DBaseline, WaferMap3DChart, WaferMap3DEncoding, WaferMap3DMark, threefieldsCatalog };