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.
- package/CHANGELOG.md +9 -1
- package/README.md +6 -4
- package/dist/AutoGraph-PDPEAIXL.cjs +145 -0
- package/dist/GraphType.js +19 -0
- package/dist/autograph/chartIndex.js +521 -0
- package/dist/autograph/chartModules.js +2 -1
- package/dist/autograph/index.cjs +98 -93
- package/dist/catalog-DI4XZ46L.cjs +142 -0
- package/dist/catalog.js +2 -1
- package/dist/charts/biologycharts/BiologySamples.js +59 -0
- package/dist/charts/biologycharts/PunnettSquare.js +342 -0
- package/dist/charts/biologycharts/biologyEnums.js +22 -1
- package/dist/charts/biologycharts/index.js +5 -3
- package/dist/charts/doecharts/DoeSamples.js +78 -0
- package/dist/charts/doecharts/SipocDiagram.js +453 -0
- package/dist/charts/doecharts/index.js +4 -2
- package/dist/charts/fieldcharts/SpeciesAccumulationCurve.js +350 -0
- package/dist/charts/fieldcharts/fieldEnums.js +8 -1
- package/dist/charts/fieldcharts/index.js +5 -3
- package/dist/charts/signals/AntennaPatternChart.js +492 -0
- package/dist/charts/signals/SignalSamples.js +17 -0
- package/dist/charts/signals/index.js +5 -3
- package/dist/charts/softwarediagrams/ComponentDiagram.js +504 -0
- package/dist/charts/softwarediagrams/DeploymentDiagram.js +419 -0
- package/dist/charts/softwarediagrams/SoftwareSamples.js +283 -1
- package/dist/charts/softwarediagrams/index.js +7 -3
- package/dist/charts/softwarediagrams/softwareEnums.js +45 -1
- package/dist/charts/sportscharts/PowerDurationCurve.js +301 -0
- package/dist/charts/sportscharts/SportsSamples.js +102 -0
- package/dist/charts/sportscharts/TrainingLoadChart.js +325 -0
- package/dist/charts/sportscharts/index.js +6 -2
- package/dist/charts/strategycharts/BassDiffusionChart.js +362 -0
- package/dist/charts/strategycharts/BassModel.js +96 -0
- package/dist/charts/strategycharts/BurnRunwayChart.js +284 -0
- package/dist/charts/strategycharts/CapTableChart.js +366 -0
- package/dist/charts/strategycharts/KanbanBoard.js +324 -0
- package/dist/charts/strategycharts/OkrTree.js +339 -0
- package/dist/charts/strategycharts/StrategyPane.js +27 -0
- package/dist/charts/strategycharts/StrategySamples.js +251 -0
- package/dist/charts/strategycharts/index.js +15 -3
- package/dist/charts/threefields/BsfcMap3DChart.js +231 -0
- package/dist/charts/threefields/CompressorMap3DChart.js +217 -0
- package/dist/charts/threefields/FieldGrid.js +50 -0
- package/dist/charts/threefields/LossLandscape3DChart.js +201 -0
- package/dist/charts/threefields/MapSheet.js +77 -0
- package/dist/charts/threefields/TerrainRoute3DChart.js +201 -0
- package/dist/charts/threefields/TerrainRouteMath.js +40 -0
- package/dist/charts/threefields/ThreeFieldBase.js +117 -0
- package/dist/charts/threefields/ThreeFieldsSamples.js +80 -0
- package/dist/charts/threefields/WaferMap3DChart.js +218 -0
- package/dist/charts/threefields/index.js +19 -0
- package/dist/charts/threesurfaces/index.js +2 -2
- package/dist/charts/tradingcharts/KeltnerChannelChart.js +171 -0
- package/dist/charts/tradingcharts/TradingMath.js +20 -0
- package/dist/charts/tradingcharts/index.js +4 -2
- package/dist/{chunk-ZL6XDXMK.cjs → chunk-26PSLEP4.cjs} +8 -8
- package/dist/{chunk-W3US6EFP.cjs → chunk-2BO5DZUP.cjs} +12 -12
- package/dist/{chunk-5DP7OUSE.cjs → chunk-2GG5YYSC.cjs} +6 -6
- package/dist/{chunk-7FG4JSVO.cjs → chunk-2GL2VNJU.cjs} +253 -33
- package/dist/{chunk-LCWMRCLF.cjs → chunk-2PYOI7OY.cjs} +21 -21
- package/dist/{chunk-JFWJAEBD.cjs → chunk-3U4MCSKI.cjs} +4 -4
- package/dist/{chunk-CUPLHZRR.cjs → chunk-3ZMM3FSK.cjs} +4 -4
- package/dist/{chunk-L2AV62DP.cjs → chunk-4767WQJ7.cjs} +6 -6
- package/dist/{chunk-DA2EYIIP.cjs → chunk-4E7FSSBF.cjs} +1210 -168
- package/dist/{chunk-VS5LQEZB.cjs → chunk-526STI2S.cjs} +5 -5
- package/dist/{chunk-RXIMEK6C.cjs → chunk-55767YPI.cjs} +20 -20
- package/dist/{chunk-JXLSKIUV.cjs → chunk-5FBQOMMT.cjs} +26 -26
- package/dist/{chunk-27PZV5BR.cjs → chunk-5N4Z7MTS.cjs} +4 -4
- package/dist/{chunk-KEWCUIQA.cjs → chunk-5WEV6YWA.cjs} +22 -22
- package/dist/chunk-5XOVVC5X.cjs +3281 -0
- package/dist/{chunk-HK5J7Q2W.cjs → chunk-6HLXVWXU.cjs} +9 -9
- package/dist/{chunk-7JG47RW4.cjs → chunk-6LQT4WJ5.cjs} +25 -25
- package/dist/{chunk-OCVTI6X4.cjs → chunk-6YTWOUFD.cjs} +329 -36
- package/dist/{chunk-W7IQCAVN.cjs → chunk-7F6W6HKL.cjs} +11 -11
- package/dist/{chunk-F2DCJXDM.cjs → chunk-7L6KYU5A.cjs} +21 -21
- package/dist/{chunk-GTGR6LPR.cjs → chunk-A4GIN4FE.cjs} +17 -17
- package/dist/{chunk-B2CETSR6.cjs → chunk-A4US7SA7.cjs} +5 -5
- package/dist/{chunk-3DIF2YYW.cjs → chunk-AOXPBLPW.cjs} +10 -10
- package/dist/{chunk-7TTR4UF5.cjs → chunk-ATI45Z4M.cjs} +2 -2
- package/dist/{chunk-5BFCIX2V.cjs → chunk-CPPMO4IQ.cjs} +434 -148
- package/dist/{chunk-AYZZQ6AC.cjs → chunk-CZ5KGF6Q.cjs} +9 -9
- package/dist/{chunk-7THJ3ZBK.cjs → chunk-D4I35MCE.cjs} +2 -2
- package/dist/{chunk-MCPPFRLN.cjs → chunk-DCGAAN3Z.cjs} +2 -2
- package/dist/{chunk-VABL6CVX.cjs → chunk-DEY36N37.cjs} +21 -21
- package/dist/{chunk-RNCKGJCC.cjs → chunk-DXHXD3DK.cjs} +3 -3
- package/dist/{chunk-A56ZGMVZ.cjs → chunk-E7B45DBZ.cjs} +11 -11
- package/dist/{chunk-DOR6L4MT.cjs → chunk-EJC6EAC5.cjs} +18 -18
- package/dist/{chunk-UPGNM7MA.cjs → chunk-EULJOOMA.cjs} +11 -11
- package/dist/{chunk-G4AZL4GO.cjs → chunk-EYM6LEIF.cjs} +474 -70
- package/dist/{chunk-NJ4X6XGV.cjs → chunk-EZTYX2GH.cjs} +5 -5
- package/dist/chunk-FDD4UP73.cjs +521 -0
- package/dist/{chunk-WJ3VH2Q4.cjs → chunk-GEAIGM3J.cjs} +110 -615
- package/dist/{chunk-P3SBHF43.cjs → chunk-GRUYVXTS.cjs} +2 -2
- package/dist/{chunk-NX4Z5LMS.cjs → chunk-H3SZXND5.cjs} +5 -5
- package/dist/{chunk-EE4S4TOE.cjs → chunk-HS45TOFJ.cjs} +21 -21
- package/dist/chunk-INGOG6OV.cjs +899 -0
- package/dist/{chunk-UDKA6DGT.cjs → chunk-J3CDXZHF.cjs} +4 -4
- package/dist/{chunk-YO6G7TKI.cjs → chunk-JAU5ADEU.cjs} +11 -11
- package/dist/{chunk-22LBK4GA.cjs → chunk-JQEBSHX3.cjs} +4 -4
- package/dist/{chunk-RXX3U7VQ.cjs → chunk-KLXGLMFU.cjs} +10 -10
- package/dist/chunk-KVOX36J3.cjs +327 -0
- package/dist/chunk-L6BGNHFL.cjs +139 -0
- package/dist/{chunk-GMER35L3.cjs → chunk-L7TB7PU5.cjs} +20 -20
- package/dist/{chunk-AFXYNBL2.cjs → chunk-LBTRYEM2.cjs} +4 -4
- package/dist/{chunk-WYYCKIOD.cjs → chunk-LLULNJSP.cjs} +11 -11
- package/dist/{chunk-J27XOSLP.cjs → chunk-LSO46AFV.cjs} +8 -8
- package/dist/{chunk-5XJO45ES.cjs → chunk-LZ2KJ7ZX.cjs} +11 -11
- package/dist/chunk-M4IQ5LIZ.cjs +422 -0
- package/dist/{chunk-Y2KIUKJF.cjs → chunk-MPDHDKYZ.cjs} +11 -11
- package/dist/{chunk-4EMPKRZU.cjs → chunk-MRQ42ZJE.cjs} +3 -3
- package/dist/{chunk-EU5D2MF4.cjs → chunk-MYV4KZWP.cjs} +9 -9
- package/dist/{chunk-XD7KN4UN.cjs → chunk-MZXVLC24.cjs} +6 -6
- package/dist/{chunk-PPSONARU.cjs → chunk-NLBVDFIR.cjs} +41 -2
- package/dist/{chunk-L3PEGMGM.cjs → chunk-NY3TBZY4.cjs} +33 -33
- package/dist/{chunk-6YNYXXB6.cjs → chunk-OGVVSFAR.cjs} +3 -3
- package/dist/{chunk-MLV7QLB6.cjs → chunk-OMTQIQMN.cjs} +6 -6
- package/dist/{chunk-BEEOSNK7.cjs → chunk-PEJJ6TXJ.cjs} +15 -144
- package/dist/{chunk-SOKOWUF5.cjs → chunk-PPGWHIU7.cjs} +2 -2
- package/dist/{chunk-IELWOM2J.cjs → chunk-PPO6SDAN.cjs} +6 -6
- package/dist/{chunk-6JTZZCSL.cjs → chunk-PUMKX5F2.cjs} +3 -3
- package/dist/{chunk-RFOXFY2U.cjs → chunk-PXBYWCNB.cjs} +190 -799
- package/dist/{chunk-3BYXOISI.cjs → chunk-Q3HB3IX6.cjs} +4 -4
- package/dist/{chunk-RXCOVONX.cjs → chunk-RBMUOIXE.cjs} +289 -34
- package/dist/{chunk-MSBNHCSE.cjs → chunk-RJHGCUUD.cjs} +6 -6
- package/dist/{chunk-I6VG7F6Z.cjs → chunk-RSONDMYW.cjs} +708 -141
- package/dist/{chunk-GZDNPHMT.cjs → chunk-SHMPFCUE.cjs} +19 -19
- package/dist/{chunk-ZIBVCOW6.cjs → chunk-SN4FCXFC.cjs} +19 -19
- package/dist/{chunk-7KM52NMN.cjs → chunk-SN5T7WYO.cjs} +2 -2
- package/dist/{chunk-7MSZR65Z.cjs → chunk-SQJZGVDR.cjs} +7 -7
- package/dist/chunk-SYABFMD7.cjs +633 -0
- package/dist/{chunk-2RRFEYFH.cjs → chunk-TICB3MAC.cjs} +16 -16
- package/dist/{chunk-AOOKHUV5.cjs → chunk-U7Z7XYR2.cjs} +5 -5
- package/dist/{chunk-JEWQAXVG.cjs → chunk-UA2THQFV.cjs} +6 -6
- package/dist/{chunk-LHZPAHU3.cjs → chunk-VIK4L2YF.cjs} +112 -521
- package/dist/{chunk-4YWMAQ4J.cjs → chunk-VM2Y6BDY.cjs} +4 -4
- package/dist/{chunk-IRRKE2ZE.cjs → chunk-VMS3KWYI.cjs} +21 -21
- package/dist/{chunk-4SLARW6F.cjs → chunk-VU5NUD46.cjs} +17 -17
- package/dist/{chunk-XHY6TVYG.cjs → chunk-VV5CVOF7.cjs} +13 -13
- package/dist/{chunk-CCIBPGPZ.cjs → chunk-VYSAWPSS.cjs} +57 -57
- package/dist/{chunk-TTNEMQ2A.cjs → chunk-W7YRWEWQ.cjs} +22 -22
- package/dist/{chunk-C7MUOGEW.cjs → chunk-X7R5SY62.cjs} +6 -6
- package/dist/{chunk-ILR5KBMB.cjs → chunk-XOK3KTSS.cjs} +6 -6
- package/dist/{chunk-2CZM2I3T.cjs → chunk-XUIX45VU.cjs} +19 -19
- package/dist/{chunk-TGRMPS3A.cjs → chunk-XWH46PVJ.cjs} +165 -43
- package/dist/{chunk-WQYXXECR.cjs → chunk-Y5XVMK3K.cjs} +6 -6
- package/dist/{chunk-AK4TX2WG.cjs → chunk-YCNKEDTG.cjs} +13 -13
- package/dist/{chunk-PJJB24DU.cjs → chunk-YEF7SE3X.cjs} +20 -20
- package/dist/{chunk-RVLV4XW6.cjs → chunk-YMTVPMU3.cjs} +22 -22
- package/dist/data/index.cjs +2 -2
- package/dist/entries/autograph-lazy.cjs +2 -2
- package/dist/entries/charts/acoustictransport.cjs +3 -3
- package/dist/entries/charts/analysischarts.cjs +3 -3
- package/dist/entries/charts/astrocharts.cjs +3 -3
- package/dist/entries/charts/bars.cjs +3 -3
- package/dist/entries/charts/bayesiancharts.cjs +3 -3
- package/dist/entries/charts/biocharts.cjs +3 -3
- package/dist/entries/charts/biologycharts.cjs +11 -3
- package/dist/entries/charts/biologycharts.d.ts +1 -1
- package/dist/entries/charts/biologycharts.js +3 -2
- package/dist/entries/charts/business.cjs +3 -3
- package/dist/entries/charts/carecharts.cjs +3 -3
- package/dist/entries/charts/chemistrycharts.cjs +3 -3
- package/dist/entries/charts/civicscharts.cjs +3 -3
- package/dist/entries/charts/clinicalcharts.cjs +3 -3
- package/dist/entries/charts/comparison.cjs +3 -3
- package/dist/entries/charts/demography.cjs +3 -3
- package/dist/entries/charts/density.cjs +3 -3
- package/dist/entries/charts/diagrams.cjs +3 -3
- package/dist/entries/charts/distribution.cjs +3 -3
- package/dist/entries/charts/doecharts.cjs +13 -3
- package/dist/entries/charts/doecharts.d.ts +1 -1
- package/dist/entries/charts/doecharts.js +2 -1
- package/dist/entries/charts/earthscience.cjs +3 -3
- package/dist/entries/charts/economicscharts.cjs +3 -3
- package/dist/entries/charts/energycharts.cjs +3 -3
- package/dist/entries/charts/engineering.cjs +3 -3
- package/dist/entries/charts/fieldcharts.cjs +7 -3
- package/dist/entries/charts/fieldcharts.d.ts +1 -1
- package/dist/entries/charts/fieldcharts.js +3 -2
- package/dist/entries/charts/financial.cjs +3 -3
- package/dist/entries/charts/flow.cjs +3 -3
- package/dist/entries/charts/frameworks.cjs +3 -3
- package/dist/entries/charts/genomics.cjs +3 -3
- package/dist/entries/charts/geoflows.cjs +3 -3
- package/dist/entries/charts/geomaps.cjs +3 -3
- package/dist/entries/charts/geosciencecharts.cjs +3 -3
- package/dist/entries/charts/hierarchy.cjs +3 -3
- package/dist/entries/charts/indicators.cjs +3 -3
- package/dist/entries/charts/inferencecharts.cjs +3 -3
- package/dist/entries/charts/layoutcharts.cjs +3 -3
- package/dist/entries/charts/lifescience.cjs +4 -4
- package/dist/entries/charts/linesareas.cjs +3 -3
- package/dist/entries/charts/logisticscharts.cjs +3 -3
- package/dist/entries/charts/machinecharts.cjs +7 -3
- package/dist/entries/charts/machinecharts.d.ts +1 -1
- package/dist/entries/charts/machinecharts.js +2 -1
- package/dist/entries/charts/massive.cjs +3 -3
- package/dist/entries/charts/mathematical.cjs +3 -3
- package/dist/entries/charts/matrix.cjs +3 -3
- package/dist/entries/charts/medicalcharts.cjs +4 -4
- package/dist/entries/charts/mlcharts.cjs +4 -4
- package/dist/entries/charts/modelcharts.cjs +6 -5
- package/dist/entries/charts/multivariate.cjs +3 -3
- package/dist/entries/charts/musiccharts.cjs +3 -3
- package/dist/entries/charts/network.cjs +3 -3
- package/dist/entries/charts/physicscharts.cjs +3 -3
- package/dist/entries/charts/pictorial.cjs +4 -4
- package/dist/entries/charts/portfoliocharts.cjs +3 -3
- package/dist/entries/charts/productcharts.cjs +3 -3
- package/dist/entries/charts/proportion.cjs +3 -3
- package/dist/entries/charts/qualitycharts.cjs +3 -3
- package/dist/entries/charts/radial.cjs +3 -3
- package/dist/entries/charts/reliabilitycharts.cjs +5 -3
- package/dist/entries/charts/scatter.cjs +3 -3
- package/dist/entries/charts/scientific.cjs +3 -3
- package/dist/entries/charts/seriescharts.cjs +3 -3
- package/dist/entries/charts/signals.cjs +7 -3
- package/dist/entries/charts/signals.d.ts +1 -1
- package/dist/entries/charts/signals.js +2 -1
- package/dist/entries/charts/softwarediagrams.cjs +21 -3
- package/dist/entries/charts/softwarediagrams.d.ts +1 -1
- package/dist/entries/charts/softwarediagrams.js +4 -2
- package/dist/entries/charts/sportscharts.cjs +13 -3
- package/dist/entries/charts/sportscharts.d.ts +1 -1
- package/dist/entries/charts/sportscharts.js +3 -1
- package/dist/entries/charts/statistics.cjs +6 -6
- package/dist/entries/charts/strategycharts.cjs +30 -3
- package/dist/entries/charts/strategycharts.d.ts +1 -1
- package/dist/entries/charts/strategycharts.js +6 -1
- package/dist/entries/charts/structure.cjs +3 -3
- package/dist/entries/charts/surveycharts.cjs +3 -3
- package/dist/entries/charts/systemscharts.cjs +3 -3
- package/dist/entries/charts/textcharts.cjs +5 -5
- package/dist/entries/charts/thematicmaps.cjs +4 -4
- package/dist/entries/charts/threebars.cjs +3 -3
- package/dist/entries/charts/threefields.cjs +53 -0
- package/dist/entries/charts/threefields.d.ts +1 -0
- package/dist/entries/charts/threefields.js +6 -0
- package/dist/entries/charts/threeglobe.cjs +3 -3
- package/dist/entries/charts/threenetwork.cjs +3 -3
- package/dist/entries/charts/threestrata.cjs +3 -3
- package/dist/entries/charts/threesurfaces.cjs +7 -3
- package/dist/entries/charts/threesurfaces.d.ts +1 -1
- package/dist/entries/charts/threesurfaces.js +2 -1
- package/dist/entries/charts/threevolume.cjs +4 -4
- package/dist/entries/charts/timeline.cjs +3 -3
- package/dist/entries/charts/tracecharts.cjs +3 -3
- package/dist/entries/charts/trackscharts.cjs +3 -3
- package/dist/entries/charts/tradingcharts.cjs +6 -4
- package/dist/entries/charts/tradingcharts.d.ts +1 -1
- package/dist/entries/charts/tradingcharts.js +2 -1
- package/dist/entries/three.cjs +60 -20
- package/dist/entries/three.d.ts +1 -1
- package/dist/entries/three.js +7 -1
- package/dist/index.cjs +258 -97
- package/dist/index.d.cts +1953 -10
- package/dist/index.d.ts +1953 -10
- package/dist/index.js +26 -5
- package/dist/page/index.cjs +2 -2
- package/dist/react/components.js +58 -1
- package/dist/react/index.cjs +135 -92
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +2 -2
- package/package.json +1 -1
- package/dist/AutoGraph-XLBJQDRB.cjs +0 -140
- package/dist/catalog-O7XGDD2H.cjs +0 -137
- package/dist/chunk-77K2HTRC.cjs +0 -3220
- package/dist/chunk-XEK47TVD.cjs +0 -323
- /package/dist/{chunk-UMEKIIHJ.cjs → chunk-LNWTC72Y.cjs} +0 -0
|
@@ -0,0 +1,899 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6;
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
var _chunkSYABFMD7cjs = require('./chunk-SYABFMD7.cjs');
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
var _chunkCM7X3KFUcjs = require('./chunk-CM7X3KFU.cjs');
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
var _chunkM4IQ5LIZcjs = require('./chunk-M4IQ5LIZ.cjs');
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
var _chunkL6BGNHFLcjs = require('./chunk-L6BGNHFL.cjs');
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
var _chunkSST7OZN5cjs = require('./chunk-SST7OZN5.cjs');
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
var _chunkFDD4UP73cjs = require('./chunk-FDD4UP73.cjs');
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
var _chunkPY4R3IFFcjs = require('./chunk-PY4R3IFF.cjs');
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
var _chunkLNWTC72Ycjs = require('./chunk-LNWTC72Y.cjs');
|
|
37
|
+
|
|
38
|
+
// src/charts/threefields/BsfcMap3DChart.ts
|
|
39
|
+
var _d3 = require('d3');
|
|
40
|
+
|
|
41
|
+
// src/charts/threefields/MapSheet.ts
|
|
42
|
+
|
|
43
|
+
var span = (values) => {
|
|
44
|
+
const [low, high] = _d3.extent.call(void 0, values.filter(Number.isFinite));
|
|
45
|
+
return low === void 0 || high === void 0 ? [0, 1] : high > low ? [low, high] : [low - 1, low + 1];
|
|
46
|
+
};
|
|
47
|
+
var mix = (a, b, t) => ({ x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t, z: a.z + (b.z - a.z) * t });
|
|
48
|
+
var MapSheet = (_class = class {
|
|
49
|
+
/** `points` are the measured nodes the axes must hold. `near` brings the high end of y to the front. */
|
|
50
|
+
constructor(mesh, points, near = false, range2 = [_chunkSYABFMD7cjs.FLOOR + 0.3, _chunkSYABFMD7cjs.CEIL]) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);
|
|
51
|
+
this.mesh = mesh;
|
|
52
|
+
this.near = near;
|
|
53
|
+
this.range = range2;
|
|
54
|
+
const all = [...mesh.nodes.flat(), ...points];
|
|
55
|
+
this.x = span(all.map((node) => node.x));
|
|
56
|
+
this.y = span(all.map((node) => node.y));
|
|
57
|
+
this.z = span(all.map((node) => node.z));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
__init() {this.across = (x) => (x - this.x[0]) / (this.x[1] - this.x[0]) * 2 - 1}
|
|
66
|
+
__init2() {this.depth = (y) => (1 - (y - this.y[0]) / (this.y[1] - this.y[0]) * 2) * (this.near ? -1 : 1)}
|
|
67
|
+
__init3() {this.level = (z) => this.range[0] + (z - this.z[0]) / (this.z[1] - this.z[0]) * (this.range[1] - this.range[0])}
|
|
68
|
+
/** A node on the sheet. One with no height lies on the floor. */
|
|
69
|
+
place(node, lift = 0) {
|
|
70
|
+
return [this.across(node.x), (Number.isFinite(node.z) ? this.level(node.z) : _chunkSYABFMD7cjs.FLOOR) + lift, this.depth(node.y)];
|
|
71
|
+
}
|
|
72
|
+
floor(node, lift = 0) {
|
|
73
|
+
return [this.across(node.x), _chunkSYABFMD7cjs.FLOOR + lift, this.depth(node.y)];
|
|
74
|
+
}
|
|
75
|
+
/** The map at fractional mesh coordinates. */
|
|
76
|
+
node(column, row) {
|
|
77
|
+
const { nodes, columns, rows } = this.mesh;
|
|
78
|
+
const i = Math.max(0, Math.min(columns - 2, Math.floor(column))), j = Math.max(0, Math.min(rows - 2, Math.floor(row)));
|
|
79
|
+
return mix(mix(nodes[j][i], nodes[j][i + 1], column - i), mix(nodes[j + 1][i], nodes[j + 1][i + 1], column - i), row - j);
|
|
80
|
+
}
|
|
81
|
+
geometry(paint) {
|
|
82
|
+
const { nodes, columns, rows } = this.mesh;
|
|
83
|
+
return _chunkSYABFMD7cjs.SurfaceGeometry.grid(columns, rows, (i, j) => this.place(nodes[j][i]), (i, j) => paint(nodes[j][i].z));
|
|
84
|
+
}
|
|
85
|
+
/** Lines of equal height across the sheet, each node carrying the level as its z. */
|
|
86
|
+
contours(levels) {
|
|
87
|
+
const { columns, rows, values } = this.mesh;
|
|
88
|
+
if (!rows || !levels.length) return [];
|
|
89
|
+
const field = new (0, _chunkSYABFMD7cjs.SurfaceField)(Array.from({ length: columns }, (_, i) => i), Array.from({ length: rows }, (_, j) => j), Float64Array.from(values));
|
|
90
|
+
return field.lines(levels).map(({ value, paths }) => ({ value, paths: paths.map((path) => path.map(([i, j]) => ({ ...this.node(i, j), z: value }))) }));
|
|
91
|
+
}
|
|
92
|
+
}, _class);
|
|
93
|
+
|
|
94
|
+
// src/charts/threefields/ThreeFieldBase.ts
|
|
95
|
+
|
|
96
|
+
var _three = require('./vendor/three.js');
|
|
97
|
+
var RAD = Math.PI / 180;
|
|
98
|
+
var ThreeFieldBase = class extends _chunkSYABFMD7cjs.SurfaceBase {
|
|
99
|
+
/** A label on a plate of the surface colour, for text that can lie over any tone of the field. */
|
|
100
|
+
tag(ctx, text, at, parent) {
|
|
101
|
+
const plate = _d3.color.call(void 0, ctx.theme.surface);
|
|
102
|
+
Object.assign(ctx.label(text, at, "ink", 11, parent).element.style, { background: plate ? `${plate.copy({ opacity: 0.82 })}` : "", padding: "1px 5px", borderRadius: "4px" });
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 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
|
|
106
|
+
* where the camera starts; text is taken as 6.2px a character.
|
|
107
|
+
*/
|
|
108
|
+
pins(ctx, pins, parent) {
|
|
109
|
+
const { azimuth = 38, elevation = 26, distance = 5.4 } = this.options;
|
|
110
|
+
const turn = azimuth * RAD, tilt = elevation * RAD, unit = this.size.height / (2 * distance * Math.tan(19 * RAD));
|
|
111
|
+
const placed = [];
|
|
112
|
+
for (const { at, text, pick } of pins) {
|
|
113
|
+
if (!at.every(Number.isFinite)) continue;
|
|
114
|
+
const across = (at[0] * Math.cos(turn) - at[2] * Math.sin(turn)) * unit, half = (text.length * 6.2 + 12) / 2;
|
|
115
|
+
const up = (rise2) => ((at[1] + rise2) * Math.cos(tilt) - (at[0] * Math.sin(turn) + at[2] * Math.cos(turn)) * Math.sin(tilt)) * unit;
|
|
116
|
+
let rise = 0.17;
|
|
117
|
+
while (rise < 0.6 && placed.some((tag) => Math.abs(tag.across - across) < tag.half + half && Math.abs(tag.up - up(rise)) < 20)) rise += 0.04;
|
|
118
|
+
placed.push({ across, up: up(rise), half });
|
|
119
|
+
const top = [at[0], at[1] + rise, at[2]];
|
|
120
|
+
this.tubes(new (0, _chunkSYABFMD7cjs.TubeBatch)(3e-3).add([at, top]), parent, ctx.theme.ink);
|
|
121
|
+
this.dots(ctx, [at], 0.024, () => ctx.color(ctx.theme.ink), parent, pick);
|
|
122
|
+
this.tag(ctx, text, [top[0], top[1] + 0.055, top[2]], parent);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/** 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. */
|
|
126
|
+
casing(path, radius, color, parent) {
|
|
127
|
+
const batch = new (0, _chunkSYABFMD7cjs.TubeBatch)(radius * 1.5).add(path);
|
|
128
|
+
if (!batch.empty) parent.add(new (0, _three.Mesh)(batch.geometry(), this.material({ vertexColors: false, side: _three.BackSide, color, roughness: 1 })));
|
|
129
|
+
}
|
|
130
|
+
/** Flat rings on one instanced mesh, for the points where paths begin. */
|
|
131
|
+
rings(ctx, points, radius, paint, parent, pick) {
|
|
132
|
+
const shown = points.map((point, k) => ({ point, k })).filter(({ point }) => point.every(Number.isFinite));
|
|
133
|
+
if (!shown.length) return;
|
|
134
|
+
const mesh = new (0, _three.InstancedMesh)(new (0, _three.TorusGeometry)(1, 0.3, 8, 28).rotateX(Math.PI / 2), new (0, _three.MeshStandardMaterial)({ roughness: 0.45 }), shown.length);
|
|
135
|
+
const dummy = new (0, _three.Object3D)();
|
|
136
|
+
shown.forEach(({ point, k }, at) => {
|
|
137
|
+
dummy.position.set(...point);
|
|
138
|
+
dummy.scale.setScalar(radius);
|
|
139
|
+
dummy.updateMatrix();
|
|
140
|
+
mesh.setMatrixAt(at, dummy.matrix);
|
|
141
|
+
mesh.setColorAt(at, paint(k));
|
|
142
|
+
});
|
|
143
|
+
mesh.castShadow = true;
|
|
144
|
+
parent.add(mesh);
|
|
145
|
+
if (pick) ctx.pick(mesh, (at) => pick(shown[at].k));
|
|
146
|
+
}
|
|
147
|
+
/** 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. */
|
|
148
|
+
key(ctx, entries) {
|
|
149
|
+
if (!entries.length) return;
|
|
150
|
+
const chips = new (0, _three.InstancedMesh)(new (0, _three.BoxGeometry)(0.08, 0.08, 0.08), new (0, _three.MeshStandardMaterial)({ roughness: 0.6 }), entries.length);
|
|
151
|
+
const dummy = new (0, _three.Object3D)();
|
|
152
|
+
entries.forEach(({ label, color }, k) => {
|
|
153
|
+
const y = _chunkSYABFMD7cjs.CEIL - (entries.length - 1 - k) * 0.15;
|
|
154
|
+
dummy.position.set(1.14, y, -1);
|
|
155
|
+
dummy.updateMatrix();
|
|
156
|
+
chips.setMatrixAt(k, dummy.matrix);
|
|
157
|
+
chips.setColorAt(k, ctx.color(color));
|
|
158
|
+
ctx.label(label, [1.23, y, -1], "muted", 10).center.set(0, 0.5);
|
|
159
|
+
});
|
|
160
|
+
ctx.root.add(chips);
|
|
161
|
+
}
|
|
162
|
+
/** One row per grid node, for CSV and keyboard access. */
|
|
163
|
+
gridTable(field) {
|
|
164
|
+
const rows = Array.from({ length: field.ny }, (_, j) => Array.from({ length: field.nx }, (_2, i) => [field.x[i], field.y[j], field.at(i, j)])).flat();
|
|
165
|
+
return { columns: ["x", "y", "z"], rows };
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// src/charts/threefields/BsfcMap3DChart.ts
|
|
170
|
+
var BsfcMap3DContours = /* @__PURE__ */ ((BsfcMap3DContours2) => {
|
|
171
|
+
BsfcMap3DContours2["Draped"] = "draped-contours";
|
|
172
|
+
BsfcMap3DContours2["Floor"] = "floor-contours";
|
|
173
|
+
BsfcMap3DContours2["None"] = "no-contours";
|
|
174
|
+
return BsfcMap3DContours2;
|
|
175
|
+
})(BsfcMap3DContours || {});
|
|
176
|
+
var BsfcMap3DBoundary = /* @__PURE__ */ ((BsfcMap3DBoundary2) => {
|
|
177
|
+
BsfcMap3DBoundary2["Wall"] = "full-load-wall";
|
|
178
|
+
BsfcMap3DBoundary2["Rail"] = "full-load-rail";
|
|
179
|
+
return BsfcMap3DBoundary2;
|
|
180
|
+
})(BsfcMap3DBoundary || {});
|
|
181
|
+
var BsfcMap3DOptimum = /* @__PURE__ */ ((BsfcMap3DOptimum2) => {
|
|
182
|
+
BsfcMap3DOptimum2["Point"] = "best-point";
|
|
183
|
+
BsfcMap3DOptimum2["Line"] = "economy-line";
|
|
184
|
+
BsfcMap3DOptimum2["None"] = "no-optimum";
|
|
185
|
+
return BsfcMap3DOptimum2;
|
|
186
|
+
})(BsfcMap3DOptimum || {});
|
|
187
|
+
var BSFC = "BSFC";
|
|
188
|
+
var ECONOMY = "Best economy line";
|
|
189
|
+
var BsfcMap3DChart = (_class2 = class _BsfcMap3DChart extends ThreeFieldBase {
|
|
190
|
+
static __initStatic() {this.meta = {
|
|
191
|
+
id: "bsfc-map-3d",
|
|
192
|
+
name: "3D engine BSFC map",
|
|
193
|
+
family: "scientific" /* Scientific */,
|
|
194
|
+
dimension: 3 /* Three */,
|
|
195
|
+
shape: "table" /* Table */,
|
|
196
|
+
description: "Raises brake specific fuel consumption into a surface over engine speed and torque, bounded by the full-load curve, with the minimum pinned and constant-power lines across it.",
|
|
197
|
+
tags: ["engine map", "fuel consumption", "fuel island", "torque curve", "automotive", "powertrain", "full load"],
|
|
198
|
+
sample: (seed) => _chunkFDD4UP73cjs.MachineSamples.bsfcMap(seed),
|
|
199
|
+
variants: { contours: Object.values(BsfcMap3DContours), boundary: Object.values(BsfcMap3DBoundary), power: [true, false], optimum: Object.values(BsfcMap3DOptimum) }
|
|
200
|
+
}}
|
|
201
|
+
defaults() {
|
|
202
|
+
return {
|
|
203
|
+
...super.defaults(),
|
|
204
|
+
x: "speed",
|
|
205
|
+
y: "torque",
|
|
206
|
+
bsfc: "bsfc",
|
|
207
|
+
contours: "draped-contours" /* Draped */,
|
|
208
|
+
boundary: "full-load-wall" /* Wall */,
|
|
209
|
+
power: true,
|
|
210
|
+
optimum: "best-point" /* Point */,
|
|
211
|
+
xLabel: "Engine speed (rpm)",
|
|
212
|
+
yLabel: "Torque (N\xB7m)",
|
|
213
|
+
zLabel: "BSFC (g/kWh)"
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
get sweeps() {
|
|
217
|
+
const { data, x, y, bsfc } = this.options;
|
|
218
|
+
return _chunkFDD4UP73cjs.BsfcMath.sweeps((Array.isArray(data) ? data : []).filter((row) => row !== null && typeof row === "object"), x, y, bsfc);
|
|
219
|
+
}
|
|
220
|
+
table() {
|
|
221
|
+
const { x, y, bsfc } = this.options;
|
|
222
|
+
return { columns: [x, y, bsfc], rows: this.sweeps.flat().map((node) => [node.x, node.y, node.z]) };
|
|
223
|
+
}
|
|
224
|
+
/** BSFC at a speed and torque, read off the mesh the surface is drawn from. NaN outside the operating range. */
|
|
225
|
+
static consumption(mesh, speed, torque) {
|
|
226
|
+
const rows = mesh.nodes, last = rows.length - 1;
|
|
227
|
+
if (last < 1 || !(speed >= rows[0][0].x && speed <= rows[last][0].x)) return NaN;
|
|
228
|
+
let j = 0;
|
|
229
|
+
while (j < last - 1 && rows[j + 1][0].x < speed) j++;
|
|
230
|
+
const t = (speed - rows[j][0].x) / (rows[j + 1][0].x - rows[j][0].x || 1);
|
|
231
|
+
const sweep = rows[j].map((node, i2) => ({ y: node.y + (rows[j + 1][i2].y - node.y) * t, z: node.z + (rows[j + 1][i2].z - node.z) * t }));
|
|
232
|
+
if (!(torque >= sweep[0].y && torque <= sweep[sweep.length - 1].y)) return NaN;
|
|
233
|
+
let i = 0;
|
|
234
|
+
while (i < sweep.length - 2 && sweep[i + 1].y < torque) i++;
|
|
235
|
+
return sweep[i].z + (sweep[i + 1].z - sweep[i].z) * (torque - sweep[i].y) / (sweep[i + 1].y - sweep[i].y || 1);
|
|
236
|
+
}
|
|
237
|
+
/** A uniform cubic B-spline through `points`, held at both ends: the smoothing the 2D map gives its economy line. */
|
|
238
|
+
static eased(points, pieces = 4) {
|
|
239
|
+
if (points.length < 3) return points.map(([x, y]) => [x, y]);
|
|
240
|
+
const knots = [points[0], points[0], ...points, points[points.length - 1], points[points.length - 1]];
|
|
241
|
+
const out = knots.slice(0, -3).flatMap((_, i) => Array.from({ length: pieces }, (_2, piece) => {
|
|
242
|
+
const t = piece / pieces, weights = [(1 - t) ** 3, 3 * t ** 3 - 6 * t * t + 4, -3 * t ** 3 + 3 * t * t + 3 * t + 1, t ** 3];
|
|
243
|
+
return [0, 1].map((axis) => weights.reduce((sum, weight, k) => sum + weight * knots[i + k][axis], 0) / 6);
|
|
244
|
+
}));
|
|
245
|
+
return [...out, [points[points.length - 1][0], points[points.length - 1][1]]];
|
|
246
|
+
}
|
|
247
|
+
build(ctx) {
|
|
248
|
+
const { contours, boundary, power, optimum } = this.options, { theme, palette } = ctx;
|
|
249
|
+
const sweeps = this.sweeps, measured = sweeps.flat();
|
|
250
|
+
const mesh = new (0, _chunkFDD4UP73cjs.MachineMesh)(sweeps, 24, 4), sheet = new MapSheet(mesh, measured, true, [_chunkSYABFMD7cjs.FLOOR + (contours === "floor-contours" /* Floor */ ? 0.55 : 0.3), _chunkSYABFMD7cjs.CEIL]);
|
|
251
|
+
const ramp = new (0, _chunkSYABFMD7cjs.ColorRamp)(palette.sequential), [low, high] = sheet.z;
|
|
252
|
+
const paint = (value) => ramp.at(0.08 + 0.85 * (high - value) / (high - low));
|
|
253
|
+
const tone = palette.color(BSFC);
|
|
254
|
+
const kwh = (value) => `${_chunkFDD4UP73cjs.MachineChart.short(value, 4)} g/kWh`;
|
|
255
|
+
const told = (node, title) => ({
|
|
256
|
+
title,
|
|
257
|
+
rows: [["BSFC", kwh(node.z)], ["Speed", `${Math.round(node.x).toLocaleString("en")} rpm`], ["Torque", `${_chunkFDD4UP73cjs.MachineChart.short(node.y)} N\xB7m`], ["Power", `${_chunkFDD4UP73cjs.MachineChart.short(_chunkFDD4UP73cjs.BsfcMath.power(node.x, node.y))} kW`]]
|
|
258
|
+
});
|
|
259
|
+
this.box(ctx, {
|
|
260
|
+
x: this.ticks(sheet.x, sheet.across),
|
|
261
|
+
y: this.ticks(sheet.y, sheet.depth),
|
|
262
|
+
z: measured.length ? _d3.scaleLinear.call(void 0, sheet.z, [0, 1]).ticks(4).map((value) => [sheet.level(value), _chunkPY4R3IFFcjs.Scales.format(value, high - low)]) : []
|
|
263
|
+
});
|
|
264
|
+
if (!measured.length) return;
|
|
265
|
+
const lift = this.rise(ctx);
|
|
266
|
+
if (!mesh.rows) {
|
|
267
|
+
this.dots(ctx, measured.map((node) => sheet.place(node)), 0.02, (k) => paint(measured[k].z), lift, (k) => told(measured[k]));
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
this.solid(ctx, sheet.geometry(paint), lift, (k) => told(mesh.nodes[Math.floor(k / mesh.columns)][k % mesh.columns]));
|
|
271
|
+
const full = mesh.nodes.map((row) => row[row.length - 1]), edge = full.map((node) => sheet.place(node, 0.01));
|
|
272
|
+
this.tubes(new (0, _chunkSYABFMD7cjs.TubeBatch)(0.011).add(edge), lift, theme.ink);
|
|
273
|
+
if (boundary === "full-load-wall" /* Wall */) this.solid(ctx, _chunkSYABFMD7cjs.SurfaceGeometry.curtain(full.map((node) => sheet.place(node)), _chunkSYABFMD7cjs.FLOOR + 2e-3, (k) => paint(full[k].z), () => paint(high)), lift);
|
|
274
|
+
else this.tubes(new (0, _chunkSYABFMD7cjs.TubeBatch)(6e-3).add(full.map((node) => sheet.floor(node, 7e-3))), ctx.root, theme.ink);
|
|
275
|
+
const [mx, my, mz] = edge[Math.floor(edge.length / 2)];
|
|
276
|
+
this.tag(ctx, "Full load", [mx, (my + _chunkSYABFMD7cjs.FLOOR) / 2, mz + 0.02], lift);
|
|
277
|
+
const levels = contours === "no-contours" /* None */ ? [] : sheet.contours(mesh.levels(10));
|
|
278
|
+
const draped = contours === "draped-contours" /* Draped */, isolines = new (0, _chunkSYABFMD7cjs.TubeBatch)(4e-3);
|
|
279
|
+
for (const { value, paths } of levels) for (const path of paths) isolines.add(path.map((node) => draped ? sheet.place(node, 4e-3) : sheet.floor(node, 6e-3)), draped ? void 0 : paint(value));
|
|
280
|
+
if (draped) this.tubes(isolines, lift, theme.ink, 0.55);
|
|
281
|
+
else this.tubes(isolines, ctx.root);
|
|
282
|
+
const peak = _nullishCoalesce(_d3.max.call(void 0, full, (node) => _chunkFDD4UP73cjs.BsfcMath.power(node.x, node.y)), () => ( 0)), [first, last] = [full[0].x, full[full.length - 1].x];
|
|
283
|
+
const on = (speed, torque) => ({ x: speed, y: torque, z: _BsfcMap3DChart.consumption(mesh, speed, torque) });
|
|
284
|
+
const lay = (nodes, lifted) => nodes.map((node) => Number.isFinite(node.z) ? sheet.place(node, lifted) : [NaN, NaN, NaN]);
|
|
285
|
+
const speeds = _d3.range.call(void 0, 0, 81).map((i) => first + (last - first) * i / 80);
|
|
286
|
+
const hyperbolas = new (0, _chunkSYABFMD7cjs.TubeBatch)(4e-3);
|
|
287
|
+
for (const kw of power && peak > 0 ? _d3.ticks.call(void 0, 0, peak, 4).filter((value) => value > 0) : []) {
|
|
288
|
+
const nodes = speeds.map((speed) => on(speed, _chunkFDD4UP73cjs.BsfcMath.torque(speed, kw))), end = nodes.findLast((node) => Number.isFinite(node.z));
|
|
289
|
+
if (nodes.filter((node) => Number.isFinite(node.z)).length < 2 || !end) continue;
|
|
290
|
+
hyperbolas.add(lay(nodes, 6e-3));
|
|
291
|
+
const [x, y, z] = sheet.place(end);
|
|
292
|
+
ctx.label(`${kw} kW`, [x + 0.11, y + 0.02, z], "muted", 10, lift);
|
|
293
|
+
}
|
|
294
|
+
this.tubes(hyperbolas, lift, theme.surface, 0.85);
|
|
295
|
+
if (optimum === "economy-line" /* Line */) {
|
|
296
|
+
const economy = _BsfcMap3DChart.eased(_chunkFDD4UP73cjs.BsfcMath.economy(mesh, _d3.range.call(void 0, 1, 25).map((i) => peak * i / 25))).map(([speed, torque]) => on(speed, torque));
|
|
297
|
+
const tube = this.tubes(new (0, _chunkSYABFMD7cjs.TubeBatch)(9e-3).add(lay(economy, 0.012)), lift, palette.color(ECONOMY));
|
|
298
|
+
if (tube) ctx.pick(tube, () => ({ title: ECONOMY, color: palette.color(ECONOMY), rows: [["Joins", "the lowest BSFC at each power"]] }));
|
|
299
|
+
}
|
|
300
|
+
const best = optimum === "best-point" /* Point */ ? measured.reduce((lowest, node) => node.z < lowest.z ? node : lowest) : null;
|
|
301
|
+
if (!best) return;
|
|
302
|
+
const drawn = _BsfcMap3DChart.consumption(mesh, best.x, best.y);
|
|
303
|
+
this.pins(ctx, [{ at: sheet.place({ ...best, z: Number.isFinite(drawn) ? drawn : best.z }, 4e-3), text: `Lowest ${kwh(best.z)}`, pick: () => ({ ...told(best, "Lowest BSFC"), color: tone }) }], lift);
|
|
304
|
+
}
|
|
305
|
+
}, _class2.__initStatic(), _class2);
|
|
306
|
+
|
|
307
|
+
// src/charts/threefields/CompressorMap3DChart.ts
|
|
308
|
+
|
|
309
|
+
var CompressorMap3DSurge = /* @__PURE__ */ ((CompressorMap3DSurge2) => {
|
|
310
|
+
CompressorMap3DSurge2["Wall"] = "surge-wall";
|
|
311
|
+
CompressorMap3DSurge2["Ribbon"] = "surge-ribbon";
|
|
312
|
+
CompressorMap3DSurge2["None"] = "no-surge";
|
|
313
|
+
return CompressorMap3DSurge2;
|
|
314
|
+
})(CompressorMap3DSurge || {});
|
|
315
|
+
var EFFICIENCY = "Efficiency";
|
|
316
|
+
var SPEED = "Speed lines";
|
|
317
|
+
var PEAK = "Peak efficiency";
|
|
318
|
+
var BETWEEN = 5;
|
|
319
|
+
var CompressorMap3DChart = (_class3 = class extends ThreeFieldBase {
|
|
320
|
+
static __initStatic2() {this.meta = {
|
|
321
|
+
id: "compressor-map-3d",
|
|
322
|
+
name: "3D compressor map",
|
|
323
|
+
family: "scientific" /* Scientific */,
|
|
324
|
+
dimension: 3 /* Three */,
|
|
325
|
+
shape: "table" /* Table */,
|
|
326
|
+
description: "Raises isentropic efficiency into a surface over corrected mass flow and pressure ratio, with the speed lines drawn on it and the surge line as a wall or ribbon.",
|
|
327
|
+
tags: ["turbomachinery", "turbocharger", "surge line", "efficiency island", "speed line", "gas turbine", "performance map"],
|
|
328
|
+
sample: (seed) => _chunkFDD4UP73cjs.MachineSamples.compressor(seed),
|
|
329
|
+
variants: { surge: Object.values(CompressorMap3DSurge), ridge: [false, true], markers: [true, false], floorMap: [true, false] }
|
|
330
|
+
}}
|
|
331
|
+
defaults() {
|
|
332
|
+
return {
|
|
333
|
+
...super.defaults(),
|
|
334
|
+
x: "flow",
|
|
335
|
+
y: "pressureRatio",
|
|
336
|
+
series: "speed",
|
|
337
|
+
efficiency: "efficiency",
|
|
338
|
+
surge: "surge-wall" /* Wall */,
|
|
339
|
+
ridge: false,
|
|
340
|
+
markers: true,
|
|
341
|
+
floorMap: true,
|
|
342
|
+
xLabel: "Mass flow (kg/s)",
|
|
343
|
+
yLabel: "Pressure ratio",
|
|
344
|
+
zLabel: "Efficiency"
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
get rows() {
|
|
348
|
+
return (Array.isArray(this.options.data) ? this.options.data : []).filter((row) => row !== null && typeof row === "object");
|
|
349
|
+
}
|
|
350
|
+
/** Speed lines from surge to choke, slowest first: lower speeds pass less flow. */
|
|
351
|
+
lines() {
|
|
352
|
+
const { x, y, series, efficiency } = this.options;
|
|
353
|
+
const points = this.rows.flatMap((row) => {
|
|
354
|
+
const flow = _chunkFDD4UP73cjs.MachineChart.number(row[x]), ratio = _chunkFDD4UP73cjs.MachineChart.number(row[y]);
|
|
355
|
+
return flow === null || ratio === null ? [] : [{ speed: String(_nullishCoalesce(row[series], () => ( "Speed"))), x: flow, y: ratio, z: _nullishCoalesce(_chunkFDD4UP73cjs.MachineChart.number(row[efficiency]), () => ( NaN)) }];
|
|
356
|
+
});
|
|
357
|
+
return [..._d3.group.call(void 0, points, (d) => d.speed)].map(([speed, own]) => ({ speed, points: own.sort((a, b) => a.x - b.x) })).sort((a, b) => _d3.mean.call(void 0, a.points, (d) => d.x) - _d3.mean.call(void 0, b.points, (d) => d.x));
|
|
358
|
+
}
|
|
359
|
+
table() {
|
|
360
|
+
const { x, y, series, efficiency } = this.options;
|
|
361
|
+
return { columns: [series, x, y, efficiency], rows: this.lines().flatMap((line) => line.points.map((d) => [d.speed, d.x, d.y, Number.isFinite(d.z) ? d.z : null])) };
|
|
362
|
+
}
|
|
363
|
+
build(ctx) {
|
|
364
|
+
const { surge, ridge, markers, floorMap } = this.options, { theme, palette } = ctx;
|
|
365
|
+
const lines = this.lines().map((line) => line.points.every((d) => Number.isFinite(d.z)) ? line : { ...line, points: line.points.map((d) => ({ ...d, z: NaN })) });
|
|
366
|
+
const all = lines.flatMap((line) => line.points), measured = lines.filter((line) => line.points.length > 1 && Number.isFinite(line.points[0].z));
|
|
367
|
+
const mesh = new (0, _chunkFDD4UP73cjs.MachineMesh)(measured.map((line) => line.points), 24, BETWEEN), sheet = new MapSheet(mesh, all, false, [_chunkSYABFMD7cjs.FLOOR + (floorMap ? 0.42 : 0.3), _chunkSYABFMD7cjs.CEIL]);
|
|
368
|
+
const percent2 = (_nullishCoalesce(_d3.max.call(void 0, all, (d) => d.z), () => ( 0))) <= 1.5;
|
|
369
|
+
const efficiency = (value) => percent2 ? `${_chunkFDD4UP73cjs.MachineChart.short(value * 100)}%` : _chunkFDD4UP73cjs.MachineChart.short(value);
|
|
370
|
+
const ramp = new (0, _chunkSYABFMD7cjs.ColorRamp)(palette.sequential), [low, high] = sheet.z;
|
|
371
|
+
const paint = (value) => ramp.at(0.08 + 0.9 * (value - low) / (high - low));
|
|
372
|
+
palette.color(EFFICIENCY);
|
|
373
|
+
const ink = palette.color(SPEED), dark = ctx.color(theme.ink);
|
|
374
|
+
this.box(ctx, {
|
|
375
|
+
x: this.ticks(sheet.x, sheet.across),
|
|
376
|
+
y: this.ticks(sheet.y, sheet.depth),
|
|
377
|
+
z: mesh.rows ? _d3.scaleLinear.call(void 0, sheet.z, [0, 1]).ticks(4).map((value) => [sheet.level(value), efficiency(value)]) : []
|
|
378
|
+
});
|
|
379
|
+
if (!all.length) return;
|
|
380
|
+
const lift = this.rise(ctx);
|
|
381
|
+
const tip = (d) => ({
|
|
382
|
+
title: `Speed ${d.speed}`,
|
|
383
|
+
color: ink,
|
|
384
|
+
rows: [["Flow", _chunkFDD4UP73cjs.MachineChart.short(d.x, 4)], ["Pressure ratio", _chunkFDD4UP73cjs.MachineChart.short(d.y, 4)], ...Number.isFinite(d.z) ? [["Efficiency", efficiency(d.z)]] : []]
|
|
385
|
+
});
|
|
386
|
+
if (mesh.rows) {
|
|
387
|
+
this.solid(ctx, sheet.geometry(paint), lift, (k) => {
|
|
388
|
+
const node = mesh.nodes[Math.floor(k / mesh.columns)][k % mesh.columns];
|
|
389
|
+
return { rows: [["Efficiency", efficiency(node.z)], ["Flow", _chunkFDD4UP73cjs.MachineChart.short(node.x, 4)], ["Pressure ratio", _chunkFDD4UP73cjs.MachineChart.short(node.y, 4)]] };
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
const traced = lines.filter((line) => line.points.length > 1).map((line) => {
|
|
393
|
+
const row = measured.indexOf(line);
|
|
394
|
+
return { line, nodes: row < 0 || !mesh.rows ? line.points : mesh.nodes[row * BETWEEN] };
|
|
395
|
+
});
|
|
396
|
+
for (const { line, nodes } of traced) {
|
|
397
|
+
const tube = this.tubes(new (0, _chunkSYABFMD7cjs.TubeBatch)(7e-3).add(nodes.map((node) => sheet.place(node, 8e-3))), lift, ink);
|
|
398
|
+
if (tube) ctx.pick(tube, () => ({ title: `Speed ${line.speed}`, color: ink, rows: [["Flow at surge", _chunkFDD4UP73cjs.MachineChart.short(line.points[0].x)], ["Flow at choke", _chunkFDD4UP73cjs.MachineChart.short(line.points.at(-1).x)]] }));
|
|
399
|
+
const [x, y, z] = nodes.map((node) => sheet.place(node)).reduce((crest2, point) => point[1] > crest2[1] ? point : crest2);
|
|
400
|
+
ctx.label(line.speed, [x, y + 0.085, z], "secondary", 10, lift);
|
|
401
|
+
}
|
|
402
|
+
const shown = markers ? all : [];
|
|
403
|
+
this.dots(ctx, shown.map((d) => sheet.place(d, 8e-3)), 0.014, () => ctx.color(ink), lift, (k) => tip(shown[k]));
|
|
404
|
+
const ends = (pick) => (mesh.rows && measured.length === traced.length ? mesh.nodes : traced.map(({ nodes }) => nodes)).map(pick);
|
|
405
|
+
const surged = traced.length > 1 ? ends((nodes) => nodes[0]) : [], choked = traced.length > 1 ? ends((nodes) => nodes[nodes.length - 1]) : [];
|
|
406
|
+
if (surge !== "no-surge" /* None */ && surged.length) {
|
|
407
|
+
const edge = surged.map((node) => sheet.place(node, 0.01));
|
|
408
|
+
this.tubes(new (0, _chunkSYABFMD7cjs.TubeBatch)(0.011).add(edge), lift, theme.ink);
|
|
409
|
+
const wall = surge === "surge-wall" /* Wall */;
|
|
410
|
+
const band = wall ? _chunkSYABFMD7cjs.SurfaceGeometry.curtain(edge.map(([x2, , z2]) => [x2, _chunkSYABFMD7cjs.CEIL + 0.06, z2]), _chunkSYABFMD7cjs.FLOOR + 2e-3, () => dark, () => dark) : _chunkSYABFMD7cjs.SurfaceGeometry.curtain(edge.map(([x2, y2, z2]) => [x2, y2 + 0.07, z2]), (k) => edge[k][1] - 0.07, () => dark, () => dark);
|
|
411
|
+
this.solid(ctx, band, lift, void 0, wall ? { transparent: true, opacity: 0.16, depthWrite: false } : {}).castShadow = !wall;
|
|
412
|
+
const [x, y, z] = edge[Math.floor(edge.length / 2)];
|
|
413
|
+
this.tag(ctx, "Surge line", [x, wall ? _chunkSYABFMD7cjs.CEIL + 0.14 : y + 0.21, z], lift);
|
|
414
|
+
}
|
|
415
|
+
const peaks = ridge ? measured.map((line) => line.points.reduce((best, d) => d.z > best.z ? d : best)) : [];
|
|
416
|
+
const crest = peaks.length ? palette.color(PEAK) : "";
|
|
417
|
+
if (peaks.length > 1) this.tubes(new (0, _chunkSYABFMD7cjs.TubeBatch)(6e-3).add(peaks.map((d) => sheet.place(d, 0.012))), lift, crest);
|
|
418
|
+
this.dots(ctx, peaks.map((d) => sheet.place(d, 0.012)), 0.024, () => ctx.color(crest), lift, (k) => ({ ...tip(peaks[k]), title: `Peak efficiency at ${peaks[k].speed}`, color: crest }));
|
|
419
|
+
if (!floorMap) return;
|
|
420
|
+
const flat = new (0, _chunkSYABFMD7cjs.TubeBatch)(4e-3);
|
|
421
|
+
for (const { nodes } of traced) flat.add(nodes.map((node) => sheet.floor(node, 6e-3)));
|
|
422
|
+
this.tubes(flat, ctx.root, ink);
|
|
423
|
+
const islands = new (0, _chunkSYABFMD7cjs.TubeBatch)(4e-3);
|
|
424
|
+
for (const { value, paths } of sheet.contours(mesh.levels())) for (const path of paths) islands.add(path.map((node) => sheet.floor(node, 6e-3)), paint(value));
|
|
425
|
+
this.tubes(islands, ctx.root);
|
|
426
|
+
this.tubes(new (0, _chunkSYABFMD7cjs.TubeBatch)(7e-3).add(surged.map((node) => sheet.floor(node, 7e-3))), ctx.root, theme.ink);
|
|
427
|
+
this.tubes(new (0, _chunkSYABFMD7cjs.TubeBatch)(4e-3).add(choked.map((node) => sheet.floor(node, 7e-3))), ctx.root, theme.inkMuted);
|
|
428
|
+
}
|
|
429
|
+
}, _class3.__initStatic2(), _class3);
|
|
430
|
+
|
|
431
|
+
// src/charts/threefields/LossLandscape3DChart.ts
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
// src/charts/threefields/FieldGrid.ts
|
|
435
|
+
var FieldGrid = class _FieldGrid {
|
|
436
|
+
constructor(field, space) {
|
|
437
|
+
this.field = field;
|
|
438
|
+
this.space = space;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
/** Fractional index of `value` along an axis that runs one way, held inside it. */
|
|
443
|
+
static index(axis, value) {
|
|
444
|
+
const last = axis.length - 1, falling = axis[0] > axis[last];
|
|
445
|
+
let i = 0;
|
|
446
|
+
while (i < last - 1 && (falling ? axis[i + 1] > value : axis[i + 1] < value)) i++;
|
|
447
|
+
const span2 = axis[i + 1] - axis[i];
|
|
448
|
+
return i + (span2 ? Math.max(0, Math.min(1, (value - axis[i]) / span2)) : 0);
|
|
449
|
+
}
|
|
450
|
+
index(x, y) {
|
|
451
|
+
return [_FieldGrid.index(this.field.x, x), _FieldGrid.index(this.field.y, y)];
|
|
452
|
+
}
|
|
453
|
+
/** The gridded value under a point; a point past the grid reads its nearest edge. */
|
|
454
|
+
value(x, y) {
|
|
455
|
+
return this.field.sample(...this.index(x, y));
|
|
456
|
+
}
|
|
457
|
+
/** World position on the surface under a point. */
|
|
458
|
+
at(x, y, lift = 0) {
|
|
459
|
+
return this.space.at(...this.index(x, y), lift);
|
|
460
|
+
}
|
|
461
|
+
/** Lays a track on the surface, cutting each leg so no piece spans more than `cell` grid cells and the line cannot dive under a ridge. */
|
|
462
|
+
drape(track, lift = 0, cell = 0.5) {
|
|
463
|
+
const path = [], knots = [];
|
|
464
|
+
let before = null;
|
|
465
|
+
for (const [x, y] of track) {
|
|
466
|
+
const here = this.index(x, y);
|
|
467
|
+
const pieces = before ? Math.max(1, Math.ceil(Math.max(Math.abs(here[0] - before[0]), Math.abs(here[1] - before[1])) / cell)) : 1;
|
|
468
|
+
for (let piece = 1; piece < pieces; piece++) {
|
|
469
|
+
const t = piece / pieces;
|
|
470
|
+
path.push(this.space.at(before[0] + (here[0] - before[0]) * t, before[1] + (here[1] - before[1]) * t, lift));
|
|
471
|
+
}
|
|
472
|
+
knots.push(path.length);
|
|
473
|
+
path.push(this.space.at(here[0], here[1], lift));
|
|
474
|
+
before = here;
|
|
475
|
+
}
|
|
476
|
+
return { path, knots };
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
// src/charts/threefields/LossLandscape3DChart.ts
|
|
481
|
+
var LossLandscape3DTrail = /* @__PURE__ */ ((LossLandscape3DTrail2) => {
|
|
482
|
+
LossLandscape3DTrail2["Steps"] = "step-markers";
|
|
483
|
+
LossLandscape3DTrail2["Path"] = "path-only";
|
|
484
|
+
LossLandscape3DTrail2["None"] = "no-path";
|
|
485
|
+
return LossLandscape3DTrail2;
|
|
486
|
+
})(LossLandscape3DTrail || {});
|
|
487
|
+
var MOST_DOTS = 90;
|
|
488
|
+
var figure = _d3.format.call(void 0, ".3~g");
|
|
489
|
+
var LossLandscape3DChart = (_class4 = class _LossLandscape3DChart extends ThreeFieldBase {
|
|
490
|
+
static __initStatic3() {this.meta = {
|
|
491
|
+
id: "loss-landscape-3d",
|
|
492
|
+
name: "3D loss landscape",
|
|
493
|
+
family: "scientific" /* Scientific */,
|
|
494
|
+
dimension: 3 /* Three */,
|
|
495
|
+
shape: "grid" /* Grid */,
|
|
496
|
+
description: "Raises a loss over two parameter directions into a surface on a linear or log height, and lays the path of each optimiser on it as a tube with a marker per step.",
|
|
497
|
+
tags: ["machine learning", "optimisation", "gradient descent", "loss surface", "training", "optimizer path"],
|
|
498
|
+
sample: (seed) => _chunkL6BGNHFLcjs.ModelSamples.lossSurface(seed),
|
|
499
|
+
variants: {
|
|
500
|
+
scale: ["linear" /* Linear */, "log" /* Log */],
|
|
501
|
+
trail: Object.values(LossLandscape3DTrail),
|
|
502
|
+
overlay: ["contour-lines" /* Contours */, "mesh-lines" /* Mesh */, "bare" /* None */],
|
|
503
|
+
floorMap: [false, true]
|
|
504
|
+
}
|
|
505
|
+
}}
|
|
506
|
+
defaults() {
|
|
507
|
+
return {
|
|
508
|
+
...super.defaults(),
|
|
509
|
+
scale: "linear" /* Linear */,
|
|
510
|
+
trail: "step-markers" /* Steps */,
|
|
511
|
+
overlay: "contour-lines" /* Contours */,
|
|
512
|
+
floorMap: false,
|
|
513
|
+
levels: 9,
|
|
514
|
+
xLabel: "Direction 1",
|
|
515
|
+
yLabel: "Direction 2",
|
|
516
|
+
zLabel: "Loss"
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
/** The grid, with its depth axis turned when that brings the lower of its two ends to the front, where no wall hides the valley floor. */
|
|
520
|
+
get losses() {
|
|
521
|
+
const field = _chunkSYABFMD7cjs.SurfaceField.from(this.options.data).capped(90), { nx, ny } = field;
|
|
522
|
+
const end = (j) => _nullishCoalesce(_d3.mean.call(void 0, Array.from({ length: nx }, (_, i) => field.at(i, j)).filter(Number.isFinite)), () => ( 0));
|
|
523
|
+
if (!(end(ny - 1) < end(0))) return field;
|
|
524
|
+
const turned = new Float64Array(nx * ny);
|
|
525
|
+
for (let j = 0; j < ny; j++) turned.set(field.z.subarray((ny - 1 - j) * nx, (ny - j) * nx), j * nx);
|
|
526
|
+
return new (0, _chunkSYABFMD7cjs.SurfaceField)(field.x, [...field.y].reverse(), turned);
|
|
527
|
+
}
|
|
528
|
+
table() {
|
|
529
|
+
return this.gridTable(_chunkSYABFMD7cjs.SurfaceField.from(this.options.data));
|
|
530
|
+
}
|
|
531
|
+
/** `Log` is log10 while every loss is positive, and asinh around zero otherwise, so zero and negative losses stay on the chart. */
|
|
532
|
+
static warp({ zMin, zMax, zDomain }, scale = "linear" /* Linear */) {
|
|
533
|
+
if (scale !== "log" /* Log */) return { to: (loss) => loss, ticks: _d3.scaleLinear.call(void 0, zDomain, [0, 1]).ticks(4) };
|
|
534
|
+
if (!(zMin > 0)) {
|
|
535
|
+
const bend = Math.max(zMax - zMin, 1e-9) / 50;
|
|
536
|
+
return { to: (loss) => Math.asinh(loss / bend), ticks: _d3.scaleLinear.call(void 0, zDomain, [0, 1]).ticks(4) };
|
|
537
|
+
}
|
|
538
|
+
const all = _d3.scaleLog.call(void 0, [zMin, Math.max(zMax, zMin * 1.0001)], [0, 1]).ticks(), tens = all.filter((tick) => Math.abs(Math.log10(tick) % 1) < 1e-9);
|
|
539
|
+
return { to: Math.log10, ticks: tens.length >= 2 ? tens : all.filter((_, i) => i % Math.ceil(all.length / 4) === 0) };
|
|
540
|
+
}
|
|
541
|
+
/** Each run with the loss under every step. A step past the grid sits on its nearest edge, and one over a gap is left out. */
|
|
542
|
+
runs(ctx, losses, grid) {
|
|
543
|
+
const paths = _optionalChain([this, 'access', _3 => _3.options, 'access', _4 => _4.data, 'optionalAccess', _5 => _5.paths]);
|
|
544
|
+
if (this.options.trail === "no-path" /* None */ || !Array.isArray(paths)) return [];
|
|
545
|
+
return paths.filter((path) => path && Array.isArray(path.points)).map((path, i) => {
|
|
546
|
+
const name = String(_nullishCoalesce(path.name, () => ( `Path ${i + 1}`)));
|
|
547
|
+
const read = path.points.map((point, index) => ({ index, x: +_optionalChain([point, 'optionalAccess', _6 => _6[0]]), y: +_optionalChain([point, 'optionalAccess', _7 => _7[1]]) })).filter((step) => Number.isFinite(step.x) && Number.isFinite(step.y)).map((step) => ({ ...step, loss: losses.sample(...grid.index(step.x, step.y)) })).filter((step) => Number.isFinite(step.loss));
|
|
548
|
+
const { path: laid, knots } = grid.drape(read.map((step) => [step.x, step.y]), 0.012);
|
|
549
|
+
return { name, color: "", steps: read.map((step, k) => ({ ...step, at: laid[knots[k]] })), path: laid };
|
|
550
|
+
}).filter((run) => run.steps.length).map((run) => ({ ...run, color: ctx.palette.color(run.name) }));
|
|
551
|
+
}
|
|
552
|
+
build(ctx) {
|
|
553
|
+
const { scale, trail, overlay, floorMap, levels, xLabel, yLabel, zLabel } = this.options;
|
|
554
|
+
const losses = this.losses, warp = _LossLandscape3DChart.warp(losses, scale);
|
|
555
|
+
const field = losses.map(warp.to);
|
|
556
|
+
const space = new (0, _chunkSYABFMD7cjs.SurfaceSpace)(field, "linear" /* Linear */, [_chunkSYABFMD7cjs.FLOOR + (floorMap ? 0.42 : 0.04), _chunkSYABFMD7cjs.CEIL]), grid = new FieldGrid(field, space);
|
|
557
|
+
const [low, high] = field.zDomain, ramp = new (0, _chunkSYABFMD7cjs.ColorRamp)(ctx.palette.sequential);
|
|
558
|
+
const paint = (value) => ramp.at(0.05 + 0.95 * (value - low) / (high - low));
|
|
559
|
+
const [x0, x1] = field.xDomain, [y0, y1] = field.yDomain, reach = losses.zDomain[1] - losses.zDomain[0];
|
|
560
|
+
this.box(ctx, {
|
|
561
|
+
x: this.ticks(field.xDomain, (v) => (v - x0) / (x1 - x0 || 1) * 2 - 1),
|
|
562
|
+
y: this.ticks(field.yDomain, (v) => 1 - (v - y0) / (y1 - y0 || 1) * 2),
|
|
563
|
+
z: warp.ticks.map((loss) => [space.level(warp.to(loss)), scale === "log" /* Log */ ? figure(loss) : _chunkPY4R3IFFcjs.Scales.format(loss, reach)])
|
|
564
|
+
});
|
|
565
|
+
const lift = this.rise(ctx);
|
|
566
|
+
this.solid(ctx, _chunkSYABFMD7cjs.SurfaceGeometry.grid(field.nx, field.ny, (i, j) => space.at(i, j), (i, j) => paint(field.at(i, j))), lift, (k) => {
|
|
567
|
+
const i = k % field.nx, j = Math.floor(k / field.nx);
|
|
568
|
+
return { rows: [[zLabel, figure(losses.at(i, j))], [xLabel, figure(field.x[i])], [yLabel, figure(field.y[j])]] };
|
|
569
|
+
});
|
|
570
|
+
if (!field.hasData) return;
|
|
571
|
+
if (overlay === "mesh-lines" /* Mesh */) this.meshLines(ctx, space, lift);
|
|
572
|
+
if (overlay === "contour-lines" /* Contours */) this.contourLines(ctx, space, lift, levels);
|
|
573
|
+
if (floorMap) this.floorContours(ctx, space, paint, levels);
|
|
574
|
+
const runs = this.runs(ctx, losses, grid);
|
|
575
|
+
const tip = (run, step, title = `${run.name}, step ${step.index}`) => ({ title, color: run.color, rows: [[zLabel, figure(step.loss)], [xLabel, figure(step.x)], [yLabel, figure(step.y)]] });
|
|
576
|
+
for (const run of runs) {
|
|
577
|
+
this.casing(run.path, 8e-3, ctx.theme.surface, lift);
|
|
578
|
+
const tube = this.tubes(new (0, _chunkSYABFMD7cjs.TubeBatch)(8e-3).add(run.path), lift, run.color);
|
|
579
|
+
if (tube) ctx.pick(tube, () => ({ title: run.name, color: run.color, rows: [["Steps", run.steps.length - 1], ["Loss at start", figure(run.steps[0].loss)], ["Loss at end", figure(run.steps.at(-1).loss)]] }));
|
|
580
|
+
if (floorMap) this.tubes(new (0, _chunkSYABFMD7cjs.TubeBatch)(4e-3).add(run.path.map(([x, , z]) => [x, _chunkSYABFMD7cjs.FLOOR + 8e-3, z])), ctx.root, run.color);
|
|
581
|
+
}
|
|
582
|
+
const between = trail === "step-markers" /* Steps */ ? runs.flatMap((run) => {
|
|
583
|
+
const every = Math.max(1, Math.ceil(run.steps.length / MOST_DOTS));
|
|
584
|
+
return run.steps.slice(1, -1).filter((_, k) => (k + 1) % every === 0).map((step) => ({ run, step }));
|
|
585
|
+
}) : [];
|
|
586
|
+
this.dots(ctx, between.map(({ step }) => step.at), 0.014, (k) => ctx.color(between[k].run.color), lift, (k) => tip(between[k].run, between[k].step));
|
|
587
|
+
this.rings(ctx, runs.map((run) => run.steps[0].at), 0.03, (k) => ctx.color(runs[k].color), lift, (k) => tip(runs[k], runs[k].steps[0], `${runs[k].name}, start`));
|
|
588
|
+
const ended = runs.filter((run) => run.steps.length > 1);
|
|
589
|
+
this.dots(ctx, ended.map((run) => run.steps.at(-1).at), 0.03, (k) => ctx.color(ended[k].color), lift, (k) => tip(ended[k], ended[k].steps.at(-1), `${ended[k].name}, end after ${ended[k].steps.at(-1).index} steps`));
|
|
590
|
+
const lowest = losses.z.indexOf(losses.zMin);
|
|
591
|
+
if (lowest >= 0) this.pins(ctx, [{ at: space.at(lowest % field.nx, Math.floor(lowest / field.nx)), text: `Lowest loss ${figure(losses.zMin)}` }], lift);
|
|
592
|
+
}
|
|
593
|
+
}, _class4.__initStatic3(), _class4);
|
|
594
|
+
|
|
595
|
+
// src/charts/threefields/TerrainRoute3DChart.ts
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
// src/charts/threefields/TerrainRouteMath.ts
|
|
599
|
+
var TerrainRouteMath = class {
|
|
600
|
+
/**
|
|
601
|
+
* The route cut into pieces no longer than `cell` grid cells, each with its distance over the ground and the elevation
|
|
602
|
+
* under it. Points that repeat the one before, or that sit over a gap in the grid, are left out.
|
|
603
|
+
*/
|
|
604
|
+
static profile(grid, route, cell = 0.5) {
|
|
605
|
+
const track = (Array.isArray(route) ? route : []).map((point) => [+_optionalChain([point, 'optionalAccess', _8 => _8[0]]), +_optionalChain([point, 'optionalAccess', _9 => _9[1]])]).filter(([x, y]) => Number.isFinite(x) && Number.isFinite(y));
|
|
606
|
+
const steps = [];
|
|
607
|
+
const add = (x, y) => {
|
|
608
|
+
const last = steps[steps.length - 1], elevation = grid.value(x, y), run = last ? Math.hypot(x - last.x, y - last.y) : 0;
|
|
609
|
+
if (Number.isFinite(elevation) && (!last || run > 0)) steps.push({ x, y, distance: (_nullishCoalesce(_optionalChain([last, 'optionalAccess', _10 => _10.distance]), () => ( 0))) + run, elevation, steep: 0 });
|
|
610
|
+
};
|
|
611
|
+
track.forEach(([x, y], k) => {
|
|
612
|
+
const [px, py] = _nullishCoalesce(track[k - 1], () => ( [x, y])), from = grid.index(px, py), to = grid.index(x, y);
|
|
613
|
+
const pieces = Math.max(1, Math.ceil(Math.max(Math.abs(to[0] - from[0]), Math.abs(to[1] - from[1])) / cell));
|
|
614
|
+
for (let piece = 1; piece <= pieces; piece++) add(px + (x - px) * piece / pieces, py + (y - py) * piece / pieces);
|
|
615
|
+
});
|
|
616
|
+
return steps;
|
|
617
|
+
}
|
|
618
|
+
/** Stretches of `length` with their mean gradient, and each step tagged with the class of the stretch it falls in. */
|
|
619
|
+
static grade(steps, length, rise = 1e3) {
|
|
620
|
+
const segments = _chunkFDD4UP73cjs.ElevationMath.segments(steps.map((step) => [step.distance, step.elevation]), length, rise);
|
|
621
|
+
for (const step of steps) step.steep = _nullishCoalesce(_optionalChain([segments, 'access', _11 => _11[Math.min(segments.length - 1, Math.floor(step.distance / length))], 'optionalAccess', _12 => _12.steep]), () => ( 0));
|
|
622
|
+
return segments;
|
|
623
|
+
}
|
|
624
|
+
/** The step nearest a distance along the route. */
|
|
625
|
+
static near(steps, distance) {
|
|
626
|
+
return steps.reduce((best, step) => !best || Math.abs(step.distance - distance) < Math.abs(best.distance - distance) ? step : best, void 0);
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
// src/charts/threefields/ThreeFieldsSamples.ts
|
|
631
|
+
var HILLS = [[8, 6, 520, 4.2], [14, 16, 760, 5], [22, 8, 640, 4.6], [25, 19, 880, 5.5], [5, 19, 420, 3.8], [17, 3, 300, 3.5]];
|
|
632
|
+
var WAYPOINTS = [[2, 2], [7, 9], [12.5, 14], [16.5, 11], [20, 13.5], [22.5, 17.5], [25, 19]];
|
|
633
|
+
var spline = (a, b, c, d, t) => 0.5 * (2 * b + (c - a) * t + (2 * a - 5 * b + 4 * c - d) * t * t + (3 * b - a - 3 * c + d) * t ** 3);
|
|
634
|
+
var ThreeFieldsSamples = class {
|
|
635
|
+
/** 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. */
|
|
636
|
+
static terrainRoute(seed) {
|
|
637
|
+
const { u } = _chunkLNWTC72Ycjs.SampleData.rng(seed);
|
|
638
|
+
const hills = HILLS.map(([east, north, rise, width]) => [east + (u() - 0.5) * 2, north + (u() - 0.5) * 2, rise * (0.8 + u() * 0.4), width * (0.9 + u() * 0.25)]);
|
|
639
|
+
const phase = u() * 6;
|
|
640
|
+
const x = Array.from({ length: 61 }, (_, i) => i * 0.5), y = Array.from({ length: 49 }, (_, j) => j * 0.5);
|
|
641
|
+
const ground = (east, north) => 420 + 4 * east + 6 * north + 24 * Math.sin(0.8 * east + phase) * Math.cos(0.7 * north) + hills.reduce((sum, [cx, cy, rise, width]) => sum + rise * Math.exp(-(((east - cx) ** 2 + (north - cy) ** 2) / width ** 2)), 0);
|
|
642
|
+
const summit = hills[3];
|
|
643
|
+
const via = WAYPOINTS.map(([east, north], k) => k === WAYPOINTS.length - 1 ? [summit[0], summit[1]] : [east + (u() - 0.5) * 1.2, north + (u() - 0.5) * 1.2]);
|
|
644
|
+
const knot = (k) => via[Math.max(0, Math.min(via.length - 1, k))];
|
|
645
|
+
const route = Array.from({ length: (via.length - 1) * 12 + 1 }, (_, step) => {
|
|
646
|
+
const k = Math.min(via.length - 2, Math.floor(step / 12)), t = step / 12 - k;
|
|
647
|
+
return [0, 1].map((axis) => +spline(knot(k - 1)[axis], knot(k)[axis], knot(k + 1)[axis], knot(k + 2)[axis], t).toFixed(2));
|
|
648
|
+
});
|
|
649
|
+
return { x, y, z: y.map((north) => x.map((east) => Math.round(ground(east, north)))), route };
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
// src/charts/threefields/TerrainRoute3DChart.ts
|
|
654
|
+
var TerrainRoute3DForm = /* @__PURE__ */ ((TerrainRoute3DForm2) => {
|
|
655
|
+
TerrainRoute3DForm2["Tube"] = "route-tube";
|
|
656
|
+
TerrainRoute3DForm2["Curtain"] = "route-curtain";
|
|
657
|
+
return TerrainRoute3DForm2;
|
|
658
|
+
})(TerrainRoute3DForm || {});
|
|
659
|
+
var STEEP = [{ steep: -1, label: "Descent", tone: -0.6 }, { steep: 0, label: "Under 3%", tone: 0 }, { steep: 1, label: "3 to 6%", tone: 0.4 }, { steep: 2, label: "6 to 9%", tone: 0.72 }, { steep: 3, label: "Over 9%", tone: 1 }];
|
|
660
|
+
var SIDES = 6;
|
|
661
|
+
var tenth = (value) => (+value.toFixed(1)).toLocaleString("en");
|
|
662
|
+
var TerrainRoute3DChart = (_class5 = class extends ThreeFieldBase {
|
|
663
|
+
static __initStatic4() {this.meta = {
|
|
664
|
+
id: "terrain-route-3d",
|
|
665
|
+
name: "Terrain route profile",
|
|
666
|
+
family: "geospatial" /* Geospatial */,
|
|
667
|
+
dimension: 3 /* Three */,
|
|
668
|
+
shape: "grid" /* Grid */,
|
|
669
|
+
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.",
|
|
670
|
+
tags: ["route profile", "elevation", "gradient", "climb", "cycling", "hiking", "dem", "track"],
|
|
671
|
+
sample: (seed) => ThreeFieldsSamples.terrainRoute(seed),
|
|
672
|
+
variants: { form: Object.values(TerrainRoute3DForm), climbs: [true, false], contours: [false, true], solidBase: [true, false] },
|
|
673
|
+
valid: ({ form, solidBase }) => form === "route-tube" /* Tube */ || solidBase === false
|
|
674
|
+
}}
|
|
675
|
+
defaults() {
|
|
676
|
+
return { ...super.defaults(), elevation: 32, form: "route-tube" /* Tube */, climbs: true, contours: false, solidBase: true, segment: 1, rise: 1e3, distanceUnit: "km", elevationUnit: "m" };
|
|
677
|
+
}
|
|
678
|
+
table() {
|
|
679
|
+
return this.gridTable(_chunkSYABFMD7cjs.SurfaceField.from(this.options.data));
|
|
680
|
+
}
|
|
681
|
+
build(ctx) {
|
|
682
|
+
const { form, climbs, contours, solidBase, segment, rise, distanceUnit, elevationUnit } = this.options;
|
|
683
|
+
const curtain = form === "route-curtain" /* Curtain */, { theme, palette } = ctx;
|
|
684
|
+
const field = _chunkSYABFMD7cjs.SurfaceField.from(this.options.data).capped(110);
|
|
685
|
+
const bottom = _chunkSYABFMD7cjs.FLOOR + (solidBase && !curtain ? 0.2 : 0.02);
|
|
686
|
+
const space = new (0, _chunkSYABFMD7cjs.SurfaceSpace)(field, "linear" /* Linear */, [bottom, bottom + 0.85]), grid = new FieldGrid(field, space);
|
|
687
|
+
const [low, high] = field.zDomain, [x0, x1] = field.xDomain, [y0, y1] = field.yDomain;
|
|
688
|
+
const ground = new (0, _chunkSYABFMD7cjs.ColorRamp)(_d3.interpolateRgb.call(void 0, theme.grid, theme.axis));
|
|
689
|
+
const shade = (value) => ground.at((value - low) / (high - low));
|
|
690
|
+
const whole = (value) => `${Math.round(value).toLocaleString("en")} ${elevationUnit}`, far = (value) => `${tenth(value)} ${distanceUnit}`;
|
|
691
|
+
this.box(ctx, {
|
|
692
|
+
x: this.ticks(field.xDomain, (v) => (v - x0) / (x1 - x0 || 1) * 2 - 1),
|
|
693
|
+
y: this.ticks(field.yDomain, (v) => 1 - (v - y0) / (y1 - y0 || 1) * 2),
|
|
694
|
+
z: this.ticks(field.zDomain, space.level, 3)
|
|
695
|
+
}, { bottom: _chunkSYABFMD7cjs.FLOOR });
|
|
696
|
+
const lift = this.rise(ctx);
|
|
697
|
+
this.solid(ctx, _chunkSYABFMD7cjs.SurfaceGeometry.grid(field.nx, field.ny, (i, j) => space.at(i, j), (i, j) => shade(field.at(i, j))), lift, (k) => {
|
|
698
|
+
const i = k % field.nx, j = Math.floor(k / field.nx);
|
|
699
|
+
return { rows: [["Elevation", whole(field.at(i, j))], ["East", far(field.x[i])], ["North", far(field.y[j])]] };
|
|
700
|
+
}, curtain ? { roughness: 0.85, transparent: true, opacity: 0.4, depthWrite: false } : { roughness: 0.85 });
|
|
701
|
+
if (!field.hasData) return;
|
|
702
|
+
if (solidBase && !curtain) this.skirt(ctx, space, () => ctx.color(theme.grid), lift);
|
|
703
|
+
if (contours) this.contourLines(ctx, space, lift, 7);
|
|
704
|
+
const steps = TerrainRouteMath.profile(grid, _optionalChain([this, 'access', _13 => _13.options, 'access', _14 => _14.data, 'optionalAccess', _15 => _15.route])), length = segment > 0 ? segment : 1;
|
|
705
|
+
const segments = TerrainRouteMath.grade(steps, length, rise);
|
|
706
|
+
const laid = steps.map((step) => ({ step, at: grid.at(step.x, step.y, 0.014) })).filter(({ at }, k, all) => {
|
|
707
|
+
const before = _optionalChain([all, 'access', _16 => _16[k - 1], 'optionalAccess', _17 => _17.at]);
|
|
708
|
+
return !before || (at[0] - before[0]) ** 2 + (at[1] - before[1]) ** 2 + (at[2] - before[2]) ** 2 > 1e-8;
|
|
709
|
+
});
|
|
710
|
+
if (laid.length < 2) return;
|
|
711
|
+
const tones = new Map(STEEP.map(({ steep, tone: tone2 }) => [steep, ctx.color(palette.diverging(tone2))]));
|
|
712
|
+
const tone = (k) => tones.get(laid[k].step.steep);
|
|
713
|
+
const told = (k) => {
|
|
714
|
+
const { step } = laid[Math.min(laid.length - 1, k)], part = segments[Math.min(segments.length - 1, Math.floor(step.distance / length))];
|
|
715
|
+
return { title: `${far(step.distance)} along the route`, rows: [["Elevation", whole(step.elevation)], ...part ? [["Gradient", `${tenth(part.grade)}%`]] : []] };
|
|
716
|
+
};
|
|
717
|
+
const tube = new (0, _chunkCM7X3KFUcjs.SurfaceMesh)(new (0, _chunkSYABFMD7cjs.TubeBatch)(0.011, SIDES).add(laid.map(({ at }) => at), tone).geometry(), this.material({ side: 0, roughness: 0.5 }));
|
|
718
|
+
tube.castShadow = true;
|
|
719
|
+
lift.add(tube);
|
|
720
|
+
this.casing(laid.map(({ at }) => at), 0.011, theme.ink, lift);
|
|
721
|
+
ctx.pick(tube, (vertex) => told(Math.floor(vertex / SIDES)));
|
|
722
|
+
if (curtain) this.solid(ctx, _chunkSYABFMD7cjs.SurfaceGeometry.curtain(laid.map(({ at }) => at), _chunkSYABFMD7cjs.FLOOR + 2e-3, tone, tone), lift, (vertex) => told(vertex % laid.length));
|
|
723
|
+
this.rings(ctx, [laid[0].at], 0.034, () => ctx.color(theme.ink), lift, () => ({ title: "Start", rows: [["Elevation", whole(laid[0].step.elevation)]] }));
|
|
724
|
+
this.dots(ctx, [laid.at(-1).at], 0.03, () => ctx.color(theme.ink), lift, () => ({ title: "Finish", rows: [["Distance", far(laid.at(-1).step.distance)], ["Elevation", whole(laid.at(-1).step.elevation)]] }));
|
|
725
|
+
const points = steps.map((step) => [step.distance, step.elevation]);
|
|
726
|
+
const name = ({ category }) => category === null ? "Climb" : category === 0 ? "HC" : `Cat ${category}`;
|
|
727
|
+
const found = climbs ? _chunkFDD4UP73cjs.ElevationMath.climbs(points, { rise }).sort((a, b) => b.gain - a.gain).slice(0, 3).sort((a, b) => a.to - b.to) : [];
|
|
728
|
+
const tip = (climb) => () => ({ title: name(climb), rows: [["Length", far(climb.length)], ["Gain", whole(climb.gain)], ["Mean gradient", `${tenth(climb.grade)}%`], ["Summit", whole(climb.end)]] });
|
|
729
|
+
const on = (distance) => {
|
|
730
|
+
const step = TerrainRouteMath.near(steps, distance);
|
|
731
|
+
return grid.at(step.x, step.y, 0.014);
|
|
732
|
+
};
|
|
733
|
+
this.dots(ctx, found.map((climb) => on(climb.from)), 0.02, () => ctx.color(theme.ink), lift, (k) => tip(found[k])());
|
|
734
|
+
this.pins(ctx, found.map((climb) => ({ at: on(climb.to), text: `${name(climb)} \xB7 ${far(climb.length)} at ${tenth(climb.grade)}%`, pick: tip(climb) })), lift);
|
|
735
|
+
const used = new Set(laid.map(({ step }) => step.steep));
|
|
736
|
+
this.key(ctx, STEEP.filter(({ steep }) => used.has(steep)).map(({ label, tone: share }) => ({ label, color: palette.diverging(share) })));
|
|
737
|
+
ctx.label(`Total ascent ${whole(_chunkFDD4UP73cjs.ElevationMath.ascent(points, 3))} over ${far(steps.at(-1).distance)}`, [0, _chunkSYABFMD7cjs.CEIL + 0.16, -1], "ink", 11);
|
|
738
|
+
}
|
|
739
|
+
}, _class5.__initStatic4(), _class5);
|
|
740
|
+
|
|
741
|
+
// src/charts/threefields/WaferMap3DChart.ts
|
|
742
|
+
|
|
743
|
+
var WaferMap3DEncoding = /* @__PURE__ */ ((WaferMap3DEncoding2) => {
|
|
744
|
+
WaferMap3DEncoding2["Value"] = "value-height";
|
|
745
|
+
WaferMap3DEncoding2["Bin"] = "bin-tiles";
|
|
746
|
+
return WaferMap3DEncoding2;
|
|
747
|
+
})(WaferMap3DEncoding || {});
|
|
748
|
+
var WaferMap3DMark = /* @__PURE__ */ ((WaferMap3DMark2) => {
|
|
749
|
+
WaferMap3DMark2["Notch"] = "notch";
|
|
750
|
+
WaferMap3DMark2["Flat"] = "flat";
|
|
751
|
+
return WaferMap3DMark2;
|
|
752
|
+
})(WaferMap3DMark || {});
|
|
753
|
+
var WaferMap3DBaseline = /* @__PURE__ */ ((WaferMap3DBaseline2) => {
|
|
754
|
+
WaferMap3DBaseline2["Fitted"] = "fitted-base";
|
|
755
|
+
WaferMap3DBaseline2["Zero"] = "zero-base";
|
|
756
|
+
return WaferMap3DBaseline2;
|
|
757
|
+
})(WaferMap3DBaseline || {});
|
|
758
|
+
var RADIUS = 0.98;
|
|
759
|
+
var TOP = -0.32;
|
|
760
|
+
var SLAB = 0.035;
|
|
761
|
+
var TALLEST = 0.62;
|
|
762
|
+
var ZONES = ["Centre", "Middle", "Edge"];
|
|
763
|
+
var percent = (good, total) => total ? `${(100 * good / total).toFixed(1)}%` : "n/a";
|
|
764
|
+
var figure2 = (value) => `${+value.toPrecision(4)}`;
|
|
765
|
+
var WaferMap3DChart = (_class6 = class _WaferMap3DChart extends ThreeFieldBase {
|
|
766
|
+
static __initStatic5() {this.meta = {
|
|
767
|
+
id: "wafer-map-3d",
|
|
768
|
+
name: "3D wafer map",
|
|
769
|
+
family: "matrix" /* Matrix */,
|
|
770
|
+
dimension: 3 /* Three */,
|
|
771
|
+
shape: "table" /* Table */,
|
|
772
|
+
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.",
|
|
773
|
+
tags: ["wafer", "semiconductor", "die", "yield", "bin map", "probe", "parametric"],
|
|
774
|
+
sample: (seed) => _chunkM4IQ5LIZcjs.ReliabilitySamples.wafer(seed),
|
|
775
|
+
variants: {
|
|
776
|
+
encoding: Object.values(WaferMap3DEncoding),
|
|
777
|
+
mark: Object.values(WaferMap3DMark),
|
|
778
|
+
baseline: Object.values(WaferMap3DBaseline),
|
|
779
|
+
zones: [false, true]
|
|
780
|
+
},
|
|
781
|
+
valid: ({ encoding, baseline }) => encoding === "value-height" /* Value */ || baseline === "fitted-base" /* Fitted */
|
|
782
|
+
}}
|
|
783
|
+
defaults() {
|
|
784
|
+
return {
|
|
785
|
+
azimuth: 24,
|
|
786
|
+
elevation: 40,
|
|
787
|
+
distance: 4.3,
|
|
788
|
+
x: "x",
|
|
789
|
+
y: "y",
|
|
790
|
+
bin: "bin",
|
|
791
|
+
value: "value",
|
|
792
|
+
passBin: "Pass",
|
|
793
|
+
encoding: "value-height" /* Value */,
|
|
794
|
+
mark: "notch" /* Notch */,
|
|
795
|
+
baseline: "fitted-base" /* Fitted */,
|
|
796
|
+
zones: false
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
/** Dies, wafer radius in die pitches and yield, exactly as the 2D wafer map counts them. */
|
|
800
|
+
summary() {
|
|
801
|
+
const { data, x, y, bin, value, passBin } = this.options;
|
|
802
|
+
return new (0, _chunkM4IQ5LIZcjs.WaferMap)({ data, x, y, bin, value, passBin }).summary();
|
|
803
|
+
}
|
|
804
|
+
table() {
|
|
805
|
+
const { x, y, bin, value } = this.options;
|
|
806
|
+
return { columns: [x, y, bin, value], rows: this.summary().dies.map((die) => [die.x, die.y, die.bin, die.value]) };
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* The wafer's edge as a polygon around the origin, with the orientation mark towards +y: a V notch, or a flat no
|
|
810
|
+
* nearer the centre than `floor`, the outermost die edge on that side.
|
|
811
|
+
*/
|
|
812
|
+
static outline(radius, mark, floor = 0, steps = 96) {
|
|
813
|
+
const flat = mark === "flat" /* Flat */, depth = radius * 0.06;
|
|
814
|
+
const chord = Math.min(radius * 0.995, Math.max(radius * 0.93, floor + radius * 0.01));
|
|
815
|
+
const gap = flat ? Math.acos(chord / radius) : Math.asin(depth * 0.8 / radius);
|
|
816
|
+
const arc = Array.from({ length: steps + 1 }, (_, k) => {
|
|
817
|
+
const angle = Math.PI / 2 + gap + (2 * Math.PI - 2 * gap) * k / steps;
|
|
818
|
+
return [radius * Math.cos(angle), radius * Math.sin(angle)];
|
|
819
|
+
});
|
|
820
|
+
return flat ? arc : [...arc, [0, radius - depth]];
|
|
821
|
+
}
|
|
822
|
+
build(ctx) {
|
|
823
|
+
const { encoding, mark, baseline, zones, bin, value } = this.options, { theme, palette } = ctx;
|
|
824
|
+
const { dies, centre, radius, good, zones: rings } = this.summary();
|
|
825
|
+
if (!dies.length) return;
|
|
826
|
+
const pitch = RADIUS / radius, side = pitch * 0.9, byValue = encoding === "value-height" /* Value */;
|
|
827
|
+
const values = dies.map((die) => die.value).filter((v) => v !== null);
|
|
828
|
+
const low = Math.min(...values), high = Math.max(...values), spread = high - low || 1;
|
|
829
|
+
const zero = baseline === "zero-base" /* Zero */, from = zero ? Math.min(0, low) : low, to = zero ? Math.max(0, high) : high;
|
|
830
|
+
const level = (v) => TOP + (zero ? 0 : 0.03) + (v - from) / (to - from || 1) * (TALLEST - (zero ? 0 : 0.03));
|
|
831
|
+
const counts = /* @__PURE__ */ new Map();
|
|
832
|
+
for (const die of dies) counts.set(die.bin, (_nullishCoalesce(counts.get(die.bin), () => ( 0))) + 1);
|
|
833
|
+
if (!byValue) [...counts].sort((a, b) => b[1] - a[1]).forEach(([name]) => palette.color(name || "Unbinned"));
|
|
834
|
+
const tiles = dies.map((die) => {
|
|
835
|
+
const measured = byValue && die.value !== null;
|
|
836
|
+
return {
|
|
837
|
+
die,
|
|
838
|
+
x: (die.x - centre[0]) * pitch,
|
|
839
|
+
z: (die.y - centre[1]) * pitch,
|
|
840
|
+
from: measured && zero ? level(0) : TOP,
|
|
841
|
+
to: measured ? level(die.value) : TOP + (byValue ? 0.012 : 0.03),
|
|
842
|
+
color: byValue ? die.value === null ? theme.grid : palette.sequential((die.value - low) / spread) : palette.color(die.bin || "Unbinned")
|
|
843
|
+
};
|
|
844
|
+
});
|
|
845
|
+
_chunkSST7OZN5cjs.Marks3D.instanced(ctx, new (0, _three.BoxGeometry)(1, 1, 1), tiles, {
|
|
846
|
+
place: (dummy, tile, progress) => {
|
|
847
|
+
const tall = Math.max(4e-3, Math.abs(tile.to - tile.from) * progress);
|
|
848
|
+
dummy.position.set(tile.x, tile.from + (tile.to < tile.from ? -tall : tall) / 2, tile.z);
|
|
849
|
+
dummy.scale.set(side, tall, side);
|
|
850
|
+
},
|
|
851
|
+
color: (tile) => tile.color,
|
|
852
|
+
tip: ({ die, color }) => ({
|
|
853
|
+
title: `Die ${die.x}, ${die.y}`,
|
|
854
|
+
color,
|
|
855
|
+
rows: [[bin, die.bin || "n/a"], ...die.value === null ? [] : [[value, figure2(die.value)]], ["Ring", ZONES[Math.min(2, Math.floor(die.reach * 3))]]]
|
|
856
|
+
})
|
|
857
|
+
});
|
|
858
|
+
const floor = Math.max(0, ...tiles.map((tile) => tile.z + pitch / 2));
|
|
859
|
+
const shape = new (0, _three.Shape)(_WaferMap3DChart.outline(RADIUS, mark, floor).map(([x, y]) => new (0, _three.Vector2)(x, y)));
|
|
860
|
+
const disc = new (0, _three.Mesh)(new (0, _three.ExtrudeGeometry)(shape, { depth: SLAB, bevelEnabled: false }).rotateX(Math.PI / 2).translate(0, TOP, 0), _chunkSST7OZN5cjs.Marks3D.matte({ color: theme.grid }));
|
|
861
|
+
disc.receiveShadow = true;
|
|
862
|
+
ctx.root.add(disc);
|
|
863
|
+
const crest = Math.max(...tiles.map((tile) => Math.max(tile.from, tile.to)));
|
|
864
|
+
if (zones) {
|
|
865
|
+
const tall = crest - TOP + 0.035;
|
|
866
|
+
for (const share of [1 / 3, 2 / 3]) {
|
|
867
|
+
const fence = new (0, _three.Mesh)(new (0, _three.CylinderGeometry)(share * RADIUS, share * RADIUS, tall, 72, 1, true).translate(0, TOP + tall / 2, 0), _chunkSST7OZN5cjs.Marks3D.matte({ color: theme.ink, transparent: true, opacity: 0.22, depthWrite: false, side: _three.DoubleSide }));
|
|
868
|
+
ctx.root.add(fence);
|
|
869
|
+
}
|
|
870
|
+
ctx.label(rings.map((ring) => `${ring.name} ${percent(ring.good, ring.total)}`).join(" \xB7 "), [0, TOP - 0.1, RADIUS + 0.4], "secondary", 11);
|
|
871
|
+
}
|
|
872
|
+
if (byValue && values.length > 1 && high > low) {
|
|
873
|
+
const peak = tiles.find((tile) => tile.die.value === high), trough = tiles.find((tile) => tile.die.value === low);
|
|
874
|
+
this.pins(ctx, [{ at: [peak.x, peak.to, peak.z], text: `Highest ${figure2(high)}` }, { at: [trough.x, trough.to, trough.z], text: `Lowest ${figure2(low)}` }], ctx.root);
|
|
875
|
+
}
|
|
876
|
+
ctx.label(`Yield ${percent(good, dies.length)} \xB7 ${good} of ${dies.length} dies`, [0, TOP - 0.1, RADIUS + 0.22], "ink", 11);
|
|
877
|
+
}
|
|
878
|
+
}, _class6.__initStatic5(), _class6);
|
|
879
|
+
|
|
880
|
+
// src/charts/threefields/index.ts
|
|
881
|
+
var threefieldsCatalog = [LossLandscape3DChart, CompressorMap3DChart, BsfcMap3DChart, TerrainRoute3DChart, WaferMap3DChart];
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
|
|
899
|
+
exports.BsfcMap3DContours = BsfcMap3DContours; exports.BsfcMap3DBoundary = BsfcMap3DBoundary; exports.BsfcMap3DOptimum = BsfcMap3DOptimum; exports.BsfcMap3DChart = BsfcMap3DChart; exports.CompressorMap3DSurge = CompressorMap3DSurge; exports.CompressorMap3DChart = CompressorMap3DChart; exports.LossLandscape3DTrail = LossLandscape3DTrail; exports.LossLandscape3DChart = LossLandscape3DChart; exports.TerrainRoute3DForm = TerrainRoute3DForm; exports.TerrainRoute3DChart = TerrainRoute3DChart; exports.WaferMap3DEncoding = WaferMap3DEncoding; exports.WaferMap3DMark = WaferMap3DMark; exports.WaferMap3DBaseline = WaferMap3DBaseline; exports.WaferMap3DChart = WaferMap3DChart; exports.threefieldsCatalog = threefieldsCatalog;
|