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,217 @@
|
|
|
1
|
+
import { ChartFamily, DataShape, Dimension } from "../../core/enums.js";
|
|
2
|
+
import { MachineChart } from "../machinecharts/MachineChart.js";
|
|
3
|
+
import { MachineMesh } from "../machinecharts/MachineMesh.js";
|
|
4
|
+
import { MachineSamples } from "../machinecharts/MachineSamples.js";
|
|
5
|
+
import { ColorRamp } from "../threesurfaces/ColorRamp.js";
|
|
6
|
+
import { SurfaceGeometry } from "../threesurfaces/SurfaceGeometry.js";
|
|
7
|
+
import { CEIL, FLOOR } from "../threesurfaces/SurfaceSpace.js";
|
|
8
|
+
import { TubeBatch } from "../threesurfaces/TubeBatch.js";
|
|
9
|
+
import { MapSheet } from "./MapSheet.js";
|
|
10
|
+
import { ThreeFieldBase } from "./ThreeFieldBase.js";
|
|
11
|
+
import { group, max, mean, scaleLinear } from "d3";
|
|
12
|
+
// src/charts/threefields/CompressorMap3DChart.ts
|
|
13
|
+
/** The surge line as a wall from floor to ceiling, as a ribbon standing on the edge of the surface, or left out. */
|
|
14
|
+
var CompressorMap3DSurge = /* @__PURE__ */ function(CompressorMap3DSurge) {
|
|
15
|
+
CompressorMap3DSurge["Wall"] = "surge-wall";
|
|
16
|
+
CompressorMap3DSurge["Ribbon"] = "surge-ribbon";
|
|
17
|
+
CompressorMap3DSurge["None"] = "no-surge";
|
|
18
|
+
return CompressorMap3DSurge;
|
|
19
|
+
}({});
|
|
20
|
+
var EFFICIENCY = "Efficiency";
|
|
21
|
+
var SPEED = "Speed lines";
|
|
22
|
+
var PEAK = "Peak efficiency";
|
|
23
|
+
var BETWEEN = 5;
|
|
24
|
+
/**
|
|
25
|
+
* A compressor map with efficiency as height: the operating range as a sheet over corrected flow and pressure ratio, the
|
|
26
|
+
* speed lines drawn across it and the surge line as a wall. Use it to show how sharply efficiency falls away from the
|
|
27
|
+
* design point; the 2D compressor map takes the same rows and reads values more exactly.
|
|
28
|
+
*/
|
|
29
|
+
var CompressorMap3DChart = class extends ThreeFieldBase {
|
|
30
|
+
static meta = {
|
|
31
|
+
id: "compressor-map-3d",
|
|
32
|
+
name: "3D compressor map",
|
|
33
|
+
family: ChartFamily.Scientific,
|
|
34
|
+
dimension: Dimension.Three,
|
|
35
|
+
shape: DataShape.Table,
|
|
36
|
+
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.",
|
|
37
|
+
tags: [
|
|
38
|
+
"turbomachinery",
|
|
39
|
+
"turbocharger",
|
|
40
|
+
"surge line",
|
|
41
|
+
"efficiency island",
|
|
42
|
+
"speed line",
|
|
43
|
+
"gas turbine",
|
|
44
|
+
"performance map"
|
|
45
|
+
],
|
|
46
|
+
sample: (seed) => MachineSamples.compressor(seed),
|
|
47
|
+
variants: {
|
|
48
|
+
surge: Object.values(CompressorMap3DSurge),
|
|
49
|
+
ridge: [false, true],
|
|
50
|
+
markers: [true, false],
|
|
51
|
+
floorMap: [true, false]
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
defaults() {
|
|
55
|
+
return {
|
|
56
|
+
...super.defaults(),
|
|
57
|
+
x: "flow",
|
|
58
|
+
y: "pressureRatio",
|
|
59
|
+
series: "speed",
|
|
60
|
+
efficiency: "efficiency",
|
|
61
|
+
surge: "surge-wall",
|
|
62
|
+
ridge: false,
|
|
63
|
+
markers: true,
|
|
64
|
+
floorMap: true,
|
|
65
|
+
xLabel: "Mass flow (kg/s)",
|
|
66
|
+
yLabel: "Pressure ratio",
|
|
67
|
+
zLabel: "Efficiency"
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
get rows() {
|
|
71
|
+
return (Array.isArray(this.options.data) ? this.options.data : []).filter((row) => row !== null && typeof row === "object");
|
|
72
|
+
}
|
|
73
|
+
/** Speed lines from surge to choke, slowest first: lower speeds pass less flow. */
|
|
74
|
+
lines() {
|
|
75
|
+
const { x, y, series, efficiency } = this.options;
|
|
76
|
+
const points = this.rows.flatMap((row) => {
|
|
77
|
+
const flow = MachineChart.number(row[x]), ratio = MachineChart.number(row[y]);
|
|
78
|
+
return flow === null || ratio === null ? [] : [{
|
|
79
|
+
speed: String(row[series] ?? "Speed"),
|
|
80
|
+
x: flow,
|
|
81
|
+
y: ratio,
|
|
82
|
+
z: MachineChart.number(row[efficiency]) ?? NaN
|
|
83
|
+
}];
|
|
84
|
+
});
|
|
85
|
+
return [...group(points, (d) => d.speed)].map(([speed, own]) => ({
|
|
86
|
+
speed,
|
|
87
|
+
points: own.sort((a, b) => a.x - b.x)
|
|
88
|
+
})).sort((a, b) => mean(a.points, (d) => d.x) - mean(b.points, (d) => d.x));
|
|
89
|
+
}
|
|
90
|
+
table() {
|
|
91
|
+
const { x, y, series, efficiency } = this.options;
|
|
92
|
+
return {
|
|
93
|
+
columns: [
|
|
94
|
+
series,
|
|
95
|
+
x,
|
|
96
|
+
y,
|
|
97
|
+
efficiency
|
|
98
|
+
],
|
|
99
|
+
rows: this.lines().flatMap((line) => line.points.map((d) => [
|
|
100
|
+
d.speed,
|
|
101
|
+
d.x,
|
|
102
|
+
d.y,
|
|
103
|
+
Number.isFinite(d.z) ? d.z : null
|
|
104
|
+
]))
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
build(ctx) {
|
|
108
|
+
const { surge, ridge, markers, floorMap } = this.options, { theme, palette } = ctx;
|
|
109
|
+
const lines = this.lines().map((line) => line.points.every((d) => Number.isFinite(d.z)) ? line : {
|
|
110
|
+
...line,
|
|
111
|
+
points: line.points.map((d) => ({
|
|
112
|
+
...d,
|
|
113
|
+
z: NaN
|
|
114
|
+
}))
|
|
115
|
+
});
|
|
116
|
+
const all = lines.flatMap((line) => line.points), measured = lines.filter((line) => line.points.length > 1 && Number.isFinite(line.points[0].z));
|
|
117
|
+
const mesh = new MachineMesh(measured.map((line) => line.points), 24, BETWEEN), sheet = new MapSheet(mesh, all, false, [FLOOR + (floorMap ? .42 : .3), CEIL]);
|
|
118
|
+
const percent = (max(all, (d) => d.z) ?? 0) <= 1.5;
|
|
119
|
+
const efficiency = (value) => percent ? `${MachineChart.short(value * 100)}%` : MachineChart.short(value);
|
|
120
|
+
const ramp = new ColorRamp(palette.sequential), [low, high] = sheet.z;
|
|
121
|
+
const paint = (value) => ramp.at(.08 + .9 * (value - low) / (high - low));
|
|
122
|
+
palette.color(EFFICIENCY);
|
|
123
|
+
const ink = palette.color(SPEED), dark = ctx.color(theme.ink);
|
|
124
|
+
this.box(ctx, {
|
|
125
|
+
x: this.ticks(sheet.x, sheet.across),
|
|
126
|
+
y: this.ticks(sheet.y, sheet.depth),
|
|
127
|
+
z: mesh.rows ? scaleLinear(sheet.z, [0, 1]).ticks(4).map((value) => [sheet.level(value), efficiency(value)]) : []
|
|
128
|
+
});
|
|
129
|
+
if (!all.length) return;
|
|
130
|
+
const lift = this.rise(ctx);
|
|
131
|
+
const tip = (d) => ({
|
|
132
|
+
title: `Speed ${d.speed}`,
|
|
133
|
+
color: ink,
|
|
134
|
+
rows: [
|
|
135
|
+
["Flow", MachineChart.short(d.x, 4)],
|
|
136
|
+
["Pressure ratio", MachineChart.short(d.y, 4)],
|
|
137
|
+
...Number.isFinite(d.z) ? [["Efficiency", efficiency(d.z)]] : []
|
|
138
|
+
]
|
|
139
|
+
});
|
|
140
|
+
if (mesh.rows) this.solid(ctx, sheet.geometry(paint), lift, (k) => {
|
|
141
|
+
const node = mesh.nodes[Math.floor(k / mesh.columns)][k % mesh.columns];
|
|
142
|
+
return { rows: [
|
|
143
|
+
["Efficiency", efficiency(node.z)],
|
|
144
|
+
["Flow", MachineChart.short(node.x, 4)],
|
|
145
|
+
["Pressure ratio", MachineChart.short(node.y, 4)]
|
|
146
|
+
] };
|
|
147
|
+
});
|
|
148
|
+
const traced = lines.filter((line) => line.points.length > 1).map((line) => {
|
|
149
|
+
const row = measured.indexOf(line);
|
|
150
|
+
return {
|
|
151
|
+
line,
|
|
152
|
+
nodes: row < 0 || !mesh.rows ? line.points : mesh.nodes[row * BETWEEN]
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
for (const { line, nodes } of traced) {
|
|
156
|
+
const tube = this.tubes(new TubeBatch(.007).add(nodes.map((node) => sheet.place(node, .008))), lift, ink);
|
|
157
|
+
if (tube) ctx.pick(tube, () => ({
|
|
158
|
+
title: `Speed ${line.speed}`,
|
|
159
|
+
color: ink,
|
|
160
|
+
rows: [["Flow at surge", MachineChart.short(line.points[0].x)], ["Flow at choke", MachineChart.short(line.points.at(-1).x)]]
|
|
161
|
+
}));
|
|
162
|
+
const [x, y, z] = nodes.map((node) => sheet.place(node)).reduce((crest, point) => point[1] > crest[1] ? point : crest);
|
|
163
|
+
ctx.label(line.speed, [
|
|
164
|
+
x,
|
|
165
|
+
y + .085,
|
|
166
|
+
z
|
|
167
|
+
], "secondary", 10, lift);
|
|
168
|
+
}
|
|
169
|
+
const shown = markers ? all : [];
|
|
170
|
+
this.dots(ctx, shown.map((d) => sheet.place(d, .008)), .014, () => ctx.color(ink), lift, (k) => tip(shown[k]));
|
|
171
|
+
const ends = (pick) => (mesh.rows && measured.length === traced.length ? mesh.nodes : traced.map(({ nodes }) => nodes)).map(pick);
|
|
172
|
+
const surged = traced.length > 1 ? ends((nodes) => nodes[0]) : [], choked = traced.length > 1 ? ends((nodes) => nodes[nodes.length - 1]) : [];
|
|
173
|
+
if (surge !== "no-surge" && surged.length) {
|
|
174
|
+
const edge = surged.map((node) => sheet.place(node, .01));
|
|
175
|
+
this.tubes(new TubeBatch(.011).add(edge), lift, theme.ink);
|
|
176
|
+
const wall = surge === "surge-wall";
|
|
177
|
+
const band = wall ? SurfaceGeometry.curtain(edge.map(([x, , z]) => [
|
|
178
|
+
x,
|
|
179
|
+
CEIL + .06,
|
|
180
|
+
z
|
|
181
|
+
]), FLOOR + .002, () => dark, () => dark) : SurfaceGeometry.curtain(edge.map(([x, y, z]) => [
|
|
182
|
+
x,
|
|
183
|
+
y + .07,
|
|
184
|
+
z
|
|
185
|
+
]), (k) => edge[k][1] - .07, () => dark, () => dark);
|
|
186
|
+
this.solid(ctx, band, lift, void 0, wall ? {
|
|
187
|
+
transparent: true,
|
|
188
|
+
opacity: .16,
|
|
189
|
+
depthWrite: false
|
|
190
|
+
} : {}).castShadow = !wall;
|
|
191
|
+
const [x, y, z] = edge[Math.floor(edge.length / 2)];
|
|
192
|
+
this.tag(ctx, "Surge line", [
|
|
193
|
+
x,
|
|
194
|
+
wall ? CEIL + .14 : y + .21,
|
|
195
|
+
z
|
|
196
|
+
], lift);
|
|
197
|
+
}
|
|
198
|
+
const peaks = ridge ? measured.map((line) => line.points.reduce((best, d) => d.z > best.z ? d : best)) : [];
|
|
199
|
+
const crest = peaks.length ? palette.color(PEAK) : "";
|
|
200
|
+
if (peaks.length > 1) this.tubes(new TubeBatch(.006).add(peaks.map((d) => sheet.place(d, .012))), lift, crest);
|
|
201
|
+
this.dots(ctx, peaks.map((d) => sheet.place(d, .012)), .024, () => ctx.color(crest), lift, (k) => ({
|
|
202
|
+
...tip(peaks[k]),
|
|
203
|
+
title: `Peak efficiency at ${peaks[k].speed}`,
|
|
204
|
+
color: crest
|
|
205
|
+
}));
|
|
206
|
+
if (!floorMap) return;
|
|
207
|
+
const flat = new TubeBatch(.004);
|
|
208
|
+
for (const { nodes } of traced) flat.add(nodes.map((node) => sheet.floor(node, .006)));
|
|
209
|
+
this.tubes(flat, ctx.root, ink);
|
|
210
|
+
const islands = new TubeBatch(.004);
|
|
211
|
+
for (const { value, paths } of sheet.contours(mesh.levels())) for (const path of paths) islands.add(path.map((node) => sheet.floor(node, .006)), paint(value));
|
|
212
|
+
this.tubes(islands, ctx.root);
|
|
213
|
+
this.tubes(new TubeBatch(.007).add(surged.map((node) => sheet.floor(node, .007))), ctx.root, theme.ink);
|
|
214
|
+
this.tubes(new TubeBatch(.004).add(choked.map((node) => sheet.floor(node, .007))), ctx.root, theme.inkMuted);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
export { CompressorMap3DChart, CompressorMap3DSurge };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// src/charts/threefields/FieldGrid.ts
|
|
2
|
+
/** Reads a gridded surface by data position, so paths given in the grid's own units can be laid on it. */
|
|
3
|
+
var FieldGrid = class FieldGrid {
|
|
4
|
+
field;
|
|
5
|
+
space;
|
|
6
|
+
constructor(field, space) {
|
|
7
|
+
this.field = field;
|
|
8
|
+
this.space = space;
|
|
9
|
+
}
|
|
10
|
+
/** Fractional index of `value` along an axis that runs one way, held inside it. */
|
|
11
|
+
static index(axis, value) {
|
|
12
|
+
const last = axis.length - 1, falling = axis[0] > axis[last];
|
|
13
|
+
let i = 0;
|
|
14
|
+
while (i < last - 1 && (falling ? axis[i + 1] > value : axis[i + 1] < value)) i++;
|
|
15
|
+
const span = axis[i + 1] - axis[i];
|
|
16
|
+
return i + (span ? Math.max(0, Math.min(1, (value - axis[i]) / span)) : 0);
|
|
17
|
+
}
|
|
18
|
+
index(x, y) {
|
|
19
|
+
return [FieldGrid.index(this.field.x, x), FieldGrid.index(this.field.y, y)];
|
|
20
|
+
}
|
|
21
|
+
/** The gridded value under a point; a point past the grid reads its nearest edge. */
|
|
22
|
+
value(x, y) {
|
|
23
|
+
return this.field.sample(...this.index(x, y));
|
|
24
|
+
}
|
|
25
|
+
/** World position on the surface under a point. */
|
|
26
|
+
at(x, y, lift = 0) {
|
|
27
|
+
return this.space.at(...this.index(x, y), lift);
|
|
28
|
+
}
|
|
29
|
+
/** 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. */
|
|
30
|
+
drape(track, lift = 0, cell = .5) {
|
|
31
|
+
const path = [], knots = [];
|
|
32
|
+
let before = null;
|
|
33
|
+
for (const [x, y] of track) {
|
|
34
|
+
const here = this.index(x, y);
|
|
35
|
+
const pieces = before ? Math.max(1, Math.ceil(Math.max(Math.abs(here[0] - before[0]), Math.abs(here[1] - before[1])) / cell)) : 1;
|
|
36
|
+
for (let piece = 1; piece < pieces; piece++) {
|
|
37
|
+
const t = piece / pieces;
|
|
38
|
+
path.push(this.space.at(before[0] + (here[0] - before[0]) * t, before[1] + (here[1] - before[1]) * t, lift));
|
|
39
|
+
}
|
|
40
|
+
knots.push(path.length);
|
|
41
|
+
path.push(this.space.at(here[0], here[1], lift));
|
|
42
|
+
before = here;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
path,
|
|
46
|
+
knots
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
export { FieldGrid };
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { ChartFamily, DataShape, Dimension, ScaleKind } from "../../core/enums.js";
|
|
2
|
+
import { Scales } from "../../core/Scales.js";
|
|
3
|
+
import { ModelSamples } from "../modelcharts/ModelSamples.js";
|
|
4
|
+
import { ColorRamp } from "../threesurfaces/ColorRamp.js";
|
|
5
|
+
import { SurfaceOverlay } from "../threesurfaces/SurfaceBase.js";
|
|
6
|
+
import { SurfaceField } from "../threesurfaces/SurfaceField.js";
|
|
7
|
+
import { SurfaceGeometry } from "../threesurfaces/SurfaceGeometry.js";
|
|
8
|
+
import { CEIL, FLOOR, SurfaceSpace } from "../threesurfaces/SurfaceSpace.js";
|
|
9
|
+
import { TubeBatch } from "../threesurfaces/TubeBatch.js";
|
|
10
|
+
import { FieldGrid } from "./FieldGrid.js";
|
|
11
|
+
import { ThreeFieldBase } from "./ThreeFieldBase.js";
|
|
12
|
+
import { format, mean, scaleLinear, scaleLog } from "d3";
|
|
13
|
+
// src/charts/threefields/LossLandscape3DChart.ts
|
|
14
|
+
/** How an optimiser's run is drawn: a tube with a ball on each step, the tube alone, or not at all. */
|
|
15
|
+
var LossLandscape3DTrail = /* @__PURE__ */ function(LossLandscape3DTrail) {
|
|
16
|
+
LossLandscape3DTrail["Steps"] = "step-markers";
|
|
17
|
+
LossLandscape3DTrail["Path"] = "path-only";
|
|
18
|
+
LossLandscape3DTrail["None"] = "no-path";
|
|
19
|
+
return LossLandscape3DTrail;
|
|
20
|
+
}({});
|
|
21
|
+
var MOST_DOTS = 90;
|
|
22
|
+
var figure = format(".3~g");
|
|
23
|
+
/**
|
|
24
|
+
* The loss over two directions in parameter space as a height surface, with each optimiser's run laid on it as a tube.
|
|
25
|
+
* Use it when the depth of a valley or the sharpness of a minimum matters more than exact levels; the 2D loss
|
|
26
|
+
* landscape takes the same data and reads levels more precisely.
|
|
27
|
+
*/
|
|
28
|
+
var LossLandscape3DChart = class LossLandscape3DChart extends ThreeFieldBase {
|
|
29
|
+
static meta = {
|
|
30
|
+
id: "loss-landscape-3d",
|
|
31
|
+
name: "3D loss landscape",
|
|
32
|
+
family: ChartFamily.Scientific,
|
|
33
|
+
dimension: Dimension.Three,
|
|
34
|
+
shape: DataShape.Grid,
|
|
35
|
+
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.",
|
|
36
|
+
tags: [
|
|
37
|
+
"machine learning",
|
|
38
|
+
"optimisation",
|
|
39
|
+
"gradient descent",
|
|
40
|
+
"loss surface",
|
|
41
|
+
"training",
|
|
42
|
+
"optimizer path"
|
|
43
|
+
],
|
|
44
|
+
sample: (seed) => ModelSamples.lossSurface(seed),
|
|
45
|
+
variants: {
|
|
46
|
+
scale: [ScaleKind.Linear, ScaleKind.Log],
|
|
47
|
+
trail: Object.values(LossLandscape3DTrail),
|
|
48
|
+
overlay: [
|
|
49
|
+
SurfaceOverlay.Contours,
|
|
50
|
+
SurfaceOverlay.Mesh,
|
|
51
|
+
SurfaceOverlay.None
|
|
52
|
+
],
|
|
53
|
+
floorMap: [false, true]
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
defaults() {
|
|
57
|
+
return {
|
|
58
|
+
...super.defaults(),
|
|
59
|
+
scale: ScaleKind.Linear,
|
|
60
|
+
trail: "step-markers",
|
|
61
|
+
overlay: SurfaceOverlay.Contours,
|
|
62
|
+
floorMap: false,
|
|
63
|
+
levels: 9,
|
|
64
|
+
xLabel: "Direction 1",
|
|
65
|
+
yLabel: "Direction 2",
|
|
66
|
+
zLabel: "Loss"
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** 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. */
|
|
70
|
+
get losses() {
|
|
71
|
+
const field = SurfaceField.from(this.options.data).capped(90), { nx, ny } = field;
|
|
72
|
+
const end = (j) => mean(Array.from({ length: nx }, (_, i) => field.at(i, j)).filter(Number.isFinite)) ?? 0;
|
|
73
|
+
if (!(end(ny - 1) < end(0))) return field;
|
|
74
|
+
const turned = new Float64Array(nx * ny);
|
|
75
|
+
for (let j = 0; j < ny; j++) turned.set(field.z.subarray((ny - 1 - j) * nx, (ny - j) * nx), j * nx);
|
|
76
|
+
return new SurfaceField(field.x, [...field.y].reverse(), turned);
|
|
77
|
+
}
|
|
78
|
+
table() {
|
|
79
|
+
return this.gridTable(SurfaceField.from(this.options.data));
|
|
80
|
+
}
|
|
81
|
+
/** `Log` is log10 while every loss is positive, and asinh around zero otherwise, so zero and negative losses stay on the chart. */
|
|
82
|
+
static warp({ zMin, zMax, zDomain }, scale = ScaleKind.Linear) {
|
|
83
|
+
if (scale !== ScaleKind.Log) return {
|
|
84
|
+
to: (loss) => loss,
|
|
85
|
+
ticks: scaleLinear(zDomain, [0, 1]).ticks(4)
|
|
86
|
+
};
|
|
87
|
+
if (!(zMin > 0)) {
|
|
88
|
+
const bend = Math.max(zMax - zMin, 1e-9) / 50;
|
|
89
|
+
return {
|
|
90
|
+
to: (loss) => Math.asinh(loss / bend),
|
|
91
|
+
ticks: scaleLinear(zDomain, [0, 1]).ticks(4)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const all = scaleLog([zMin, Math.max(zMax, zMin * 1.0001)], [0, 1]).ticks(), tens = all.filter((tick) => Math.abs(Math.log10(tick) % 1) < 1e-9);
|
|
95
|
+
return {
|
|
96
|
+
to: Math.log10,
|
|
97
|
+
ticks: tens.length >= 2 ? tens : all.filter((_, i) => i % Math.ceil(all.length / 4) === 0)
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/** 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. */
|
|
101
|
+
runs(ctx, losses, grid) {
|
|
102
|
+
const paths = this.options.data?.paths;
|
|
103
|
+
if (this.options.trail === "no-path" || !Array.isArray(paths)) return [];
|
|
104
|
+
return paths.filter((path) => path && Array.isArray(path.points)).map((path, i) => {
|
|
105
|
+
const name = String(path.name ?? `Path ${i + 1}`);
|
|
106
|
+
const read = path.points.map((point, index) => ({
|
|
107
|
+
index,
|
|
108
|
+
x: +point?.[0],
|
|
109
|
+
y: +point?.[1]
|
|
110
|
+
})).filter((step) => Number.isFinite(step.x) && Number.isFinite(step.y)).map((step) => ({
|
|
111
|
+
...step,
|
|
112
|
+
loss: losses.sample(...grid.index(step.x, step.y))
|
|
113
|
+
})).filter((step) => Number.isFinite(step.loss));
|
|
114
|
+
const { path: laid, knots } = grid.drape(read.map((step) => [step.x, step.y]), .012);
|
|
115
|
+
return {
|
|
116
|
+
name,
|
|
117
|
+
color: "",
|
|
118
|
+
steps: read.map((step, k) => ({
|
|
119
|
+
...step,
|
|
120
|
+
at: laid[knots[k]]
|
|
121
|
+
})),
|
|
122
|
+
path: laid
|
|
123
|
+
};
|
|
124
|
+
}).filter((run) => run.steps.length).map((run) => ({
|
|
125
|
+
...run,
|
|
126
|
+
color: ctx.palette.color(run.name)
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
build(ctx) {
|
|
130
|
+
const { scale, trail, overlay, floorMap, levels, xLabel, yLabel, zLabel } = this.options;
|
|
131
|
+
const losses = this.losses, warp = LossLandscape3DChart.warp(losses, scale);
|
|
132
|
+
const field = losses.map(warp.to);
|
|
133
|
+
const space = new SurfaceSpace(field, ScaleKind.Linear, [FLOOR + (floorMap ? .42 : .04), CEIL]), grid = new FieldGrid(field, space);
|
|
134
|
+
const [low, high] = field.zDomain, ramp = new ColorRamp(ctx.palette.sequential);
|
|
135
|
+
const paint = (value) => ramp.at(.05 + .95 * (value - low) / (high - low));
|
|
136
|
+
const [x0, x1] = field.xDomain, [y0, y1] = field.yDomain, reach = losses.zDomain[1] - losses.zDomain[0];
|
|
137
|
+
this.box(ctx, {
|
|
138
|
+
x: this.ticks(field.xDomain, (v) => (v - x0) / (x1 - x0 || 1) * 2 - 1),
|
|
139
|
+
y: this.ticks(field.yDomain, (v) => 1 - (v - y0) / (y1 - y0 || 1) * 2),
|
|
140
|
+
z: warp.ticks.map((loss) => [space.level(warp.to(loss)), scale === ScaleKind.Log ? figure(loss) : Scales.format(loss, reach)])
|
|
141
|
+
});
|
|
142
|
+
const lift = this.rise(ctx);
|
|
143
|
+
this.solid(ctx, SurfaceGeometry.grid(field.nx, field.ny, (i, j) => space.at(i, j), (i, j) => paint(field.at(i, j))), lift, (k) => {
|
|
144
|
+
const i = k % field.nx, j = Math.floor(k / field.nx);
|
|
145
|
+
return { rows: [
|
|
146
|
+
[zLabel, figure(losses.at(i, j))],
|
|
147
|
+
[xLabel, figure(field.x[i])],
|
|
148
|
+
[yLabel, figure(field.y[j])]
|
|
149
|
+
] };
|
|
150
|
+
});
|
|
151
|
+
if (!field.hasData) return;
|
|
152
|
+
if (overlay === SurfaceOverlay.Mesh) this.meshLines(ctx, space, lift);
|
|
153
|
+
if (overlay === SurfaceOverlay.Contours) this.contourLines(ctx, space, lift, levels);
|
|
154
|
+
if (floorMap) this.floorContours(ctx, space, paint, levels);
|
|
155
|
+
const runs = this.runs(ctx, losses, grid);
|
|
156
|
+
const tip = (run, step, title = `${run.name}, step ${step.index}`) => ({
|
|
157
|
+
title,
|
|
158
|
+
color: run.color,
|
|
159
|
+
rows: [
|
|
160
|
+
[zLabel, figure(step.loss)],
|
|
161
|
+
[xLabel, figure(step.x)],
|
|
162
|
+
[yLabel, figure(step.y)]
|
|
163
|
+
]
|
|
164
|
+
});
|
|
165
|
+
for (const run of runs) {
|
|
166
|
+
this.casing(run.path, .008, ctx.theme.surface, lift);
|
|
167
|
+
const tube = this.tubes(new TubeBatch(.008).add(run.path), lift, run.color);
|
|
168
|
+
if (tube) ctx.pick(tube, () => ({
|
|
169
|
+
title: run.name,
|
|
170
|
+
color: run.color,
|
|
171
|
+
rows: [
|
|
172
|
+
["Steps", run.steps.length - 1],
|
|
173
|
+
["Loss at start", figure(run.steps[0].loss)],
|
|
174
|
+
["Loss at end", figure(run.steps.at(-1).loss)]
|
|
175
|
+
]
|
|
176
|
+
}));
|
|
177
|
+
if (floorMap) this.tubes(new TubeBatch(.004).add(run.path.map(([x, , z]) => [
|
|
178
|
+
x,
|
|
179
|
+
FLOOR + .008,
|
|
180
|
+
z
|
|
181
|
+
])), ctx.root, run.color);
|
|
182
|
+
}
|
|
183
|
+
const between = trail === "step-markers" ? runs.flatMap((run) => {
|
|
184
|
+
const every = Math.max(1, Math.ceil(run.steps.length / MOST_DOTS));
|
|
185
|
+
return run.steps.slice(1, -1).filter((_, k) => (k + 1) % every === 0).map((step) => ({
|
|
186
|
+
run,
|
|
187
|
+
step
|
|
188
|
+
}));
|
|
189
|
+
}) : [];
|
|
190
|
+
this.dots(ctx, between.map(({ step }) => step.at), .014, (k) => ctx.color(between[k].run.color), lift, (k) => tip(between[k].run, between[k].step));
|
|
191
|
+
this.rings(ctx, runs.map((run) => run.steps[0].at), .03, (k) => ctx.color(runs[k].color), lift, (k) => tip(runs[k], runs[k].steps[0], `${runs[k].name}, start`));
|
|
192
|
+
const ended = runs.filter((run) => run.steps.length > 1);
|
|
193
|
+
this.dots(ctx, ended.map((run) => run.steps.at(-1).at), .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`));
|
|
194
|
+
const lowest = losses.z.indexOf(losses.zMin);
|
|
195
|
+
if (lowest >= 0) this.pins(ctx, [{
|
|
196
|
+
at: space.at(lowest % field.nx, Math.floor(lowest / field.nx)),
|
|
197
|
+
text: `Lowest loss ${figure(losses.zMin)}`
|
|
198
|
+
}], lift);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
export { LossLandscape3DChart, LossLandscape3DTrail };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { SurfaceField } from "../threesurfaces/SurfaceField.js";
|
|
2
|
+
import { SurfaceGeometry } from "../threesurfaces/SurfaceGeometry.js";
|
|
3
|
+
import { CEIL, FLOOR } from "../threesurfaces/SurfaceSpace.js";
|
|
4
|
+
import { extent } from "d3";
|
|
5
|
+
// src/charts/threefields/MapSheet.ts
|
|
6
|
+
var span = (values) => {
|
|
7
|
+
const [low, high] = extent(values.filter(Number.isFinite));
|
|
8
|
+
return low === void 0 || high === void 0 ? [0, 1] : high > low ? [low, high] : [low - 1, low + 1];
|
|
9
|
+
};
|
|
10
|
+
var mix = (a, b, t) => ({
|
|
11
|
+
x: a.x + (b.x - a.x) * t,
|
|
12
|
+
y: a.y + (b.y - a.y) * t,
|
|
13
|
+
z: a.z + (b.z - a.z) * t
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* A test-bench map as a sheet in the cube: the map's x runs across, its y runs into the depth and the mapped quantity is
|
|
17
|
+
* the height. The sheet follows the mesh of the sweeps, so its outline is the operating range and not a rectangle.
|
|
18
|
+
*/
|
|
19
|
+
var MapSheet = class {
|
|
20
|
+
mesh;
|
|
21
|
+
near;
|
|
22
|
+
range;
|
|
23
|
+
x;
|
|
24
|
+
y;
|
|
25
|
+
z;
|
|
26
|
+
/** `points` are the measured nodes the axes must hold. `near` brings the high end of y to the front. */
|
|
27
|
+
constructor(mesh, points, near = false, range = [FLOOR + .3, CEIL]) {
|
|
28
|
+
this.mesh = mesh;
|
|
29
|
+
this.near = near;
|
|
30
|
+
this.range = range;
|
|
31
|
+
const all = [...mesh.nodes.flat(), ...points];
|
|
32
|
+
this.x = span(all.map((node) => node.x));
|
|
33
|
+
this.y = span(all.map((node) => node.y));
|
|
34
|
+
this.z = span(all.map((node) => node.z));
|
|
35
|
+
}
|
|
36
|
+
across = (x) => (x - this.x[0]) / (this.x[1] - this.x[0]) * 2 - 1;
|
|
37
|
+
depth = (y) => (1 - (y - this.y[0]) / (this.y[1] - this.y[0]) * 2) * (this.near ? -1 : 1);
|
|
38
|
+
level = (z) => this.range[0] + (z - this.z[0]) / (this.z[1] - this.z[0]) * (this.range[1] - this.range[0]);
|
|
39
|
+
/** A node on the sheet. One with no height lies on the floor. */
|
|
40
|
+
place(node, lift = 0) {
|
|
41
|
+
return [
|
|
42
|
+
this.across(node.x),
|
|
43
|
+
(Number.isFinite(node.z) ? this.level(node.z) : FLOOR) + lift,
|
|
44
|
+
this.depth(node.y)
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
floor(node, lift = 0) {
|
|
48
|
+
return [
|
|
49
|
+
this.across(node.x),
|
|
50
|
+
FLOOR + lift,
|
|
51
|
+
this.depth(node.y)
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
/** The map at fractional mesh coordinates. */
|
|
55
|
+
node(column, row) {
|
|
56
|
+
const { nodes, columns, rows } = this.mesh;
|
|
57
|
+
const i = Math.max(0, Math.min(columns - 2, Math.floor(column))), j = Math.max(0, Math.min(rows - 2, Math.floor(row)));
|
|
58
|
+
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);
|
|
59
|
+
}
|
|
60
|
+
geometry(paint) {
|
|
61
|
+
const { nodes, columns, rows } = this.mesh;
|
|
62
|
+
return SurfaceGeometry.grid(columns, rows, (i, j) => this.place(nodes[j][i]), (i, j) => paint(nodes[j][i].z));
|
|
63
|
+
}
|
|
64
|
+
/** Lines of equal height across the sheet, each node carrying the level as its z. */
|
|
65
|
+
contours(levels) {
|
|
66
|
+
const { columns, rows, values } = this.mesh;
|
|
67
|
+
if (!rows || !levels.length) return [];
|
|
68
|
+
return new SurfaceField(Array.from({ length: columns }, (_, i) => i), Array.from({ length: rows }, (_, j) => j), Float64Array.from(values)).lines(levels).map(({ value, paths }) => ({
|
|
69
|
+
value,
|
|
70
|
+
paths: paths.map((path) => path.map(([i, j]) => ({
|
|
71
|
+
...this.node(i, j),
|
|
72
|
+
z: value
|
|
73
|
+
})))
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
export { MapSheet };
|