@macrostrat/column-views 3.10.0 → 3.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/CHANGELOG.md +188 -0
  2. package/dist/age-model/data.cjs +57 -0
  3. package/dist/age-model/data.cjs.map +1 -0
  4. package/dist/age-model/data.d.ts +29 -0
  5. package/dist/age-model/data.js +57 -0
  6. package/dist/age-model/data.js.map +1 -0
  7. package/dist/age-model/details.cjs +261 -0
  8. package/dist/age-model/details.cjs.map +1 -0
  9. package/dist/age-model/details.d.ts +39 -0
  10. package/dist/age-model/details.js +258 -0
  11. package/dist/age-model/details.js.map +1 -0
  12. package/dist/age-model/details.module.sass.cjs +42 -0
  13. package/dist/age-model/details.module.sass.cjs.map +1 -0
  14. package/dist/age-model/details.module.sass.js +40 -0
  15. package/dist/age-model/details.module.sass.js.map +1 -0
  16. package/dist/age-model/index.d.ts +5 -0
  17. package/dist/age-model/legacy.cjs +28 -0
  18. package/dist/age-model/legacy.cjs.map +1 -0
  19. package/dist/age-model/legacy.d.ts +6 -0
  20. package/dist/age-model/legacy.js +26 -0
  21. package/dist/age-model/legacy.js.map +1 -0
  22. package/dist/age-model/surfaces.cjs +355 -0
  23. package/dist/age-model/surfaces.cjs.map +1 -0
  24. package/dist/age-model/surfaces.d.ts +68 -0
  25. package/dist/age-model/surfaces.js +352 -0
  26. package/dist/age-model/surfaces.js.map +1 -0
  27. package/dist/age-model/surfaces.module.sass.cjs +45 -0
  28. package/dist/age-model/surfaces.module.sass.cjs.map +1 -0
  29. package/dist/age-model/surfaces.module.sass.js +43 -0
  30. package/dist/age-model/surfaces.module.sass.js.map +1 -0
  31. package/dist/age-model/types.cjs +229 -0
  32. package/dist/age-model/types.cjs.map +1 -0
  33. package/dist/age-model/types.d.ts +112 -0
  34. package/dist/age-model/types.js +229 -0
  35. package/dist/age-model/types.js.map +1 -0
  36. package/dist/column-views.css +398 -56
  37. package/dist/column.cjs +20 -13
  38. package/dist/column.cjs.map +1 -1
  39. package/dist/column.d.ts +21 -6
  40. package/dist/column.js +21 -14
  41. package/dist/column.js.map +1 -1
  42. package/dist/column.module.sass.cjs +19 -15
  43. package/dist/column.module.sass.cjs.map +1 -1
  44. package/dist/column.module.sass.js +19 -15
  45. package/dist/column.module.sass.js.map +1 -1
  46. package/dist/correlation-chart/main.cjs +11 -3
  47. package/dist/correlation-chart/main.cjs.map +1 -1
  48. package/dist/correlation-chart/main.d.ts +1 -1
  49. package/dist/correlation-chart/main.js +11 -3
  50. package/dist/correlation-chart/main.js.map +1 -1
  51. package/dist/data-provider/store.cjs +34 -1
  52. package/dist/data-provider/store.cjs.map +1 -1
  53. package/dist/data-provider/store.d.ts +5 -0
  54. package/dist/data-provider/store.js +35 -2
  55. package/dist/data-provider/store.js.map +1 -1
  56. package/dist/index.cjs +57 -3
  57. package/dist/index.cjs.map +1 -1
  58. package/dist/index.d.ts +3 -1
  59. package/dist/index.js +57 -3
  60. package/dist/index.js.map +1 -1
  61. package/dist/notes.cjs +11 -2
  62. package/dist/notes.cjs.map +1 -1
  63. package/dist/notes.d.ts +11 -1
  64. package/dist/notes.js +11 -2
  65. package/dist/notes.js.map +1 -1
  66. package/dist/prepare-units/composite-scale.cjs +18 -16
  67. package/dist/prepare-units/composite-scale.cjs.map +1 -1
  68. package/dist/prepare-units/composite-scale.js +18 -16
  69. package/dist/prepare-units/composite-scale.js.map +1 -1
  70. package/dist/prepare-units/density.cjs +83 -0
  71. package/dist/prepare-units/density.cjs.map +1 -0
  72. package/dist/prepare-units/density.d.ts +92 -0
  73. package/dist/prepare-units/density.js +83 -0
  74. package/dist/prepare-units/density.js.map +1 -0
  75. package/dist/prepare-units/index.cjs +15 -7
  76. package/dist/prepare-units/index.cjs.map +1 -1
  77. package/dist/prepare-units/index.d.ts +1 -0
  78. package/dist/prepare-units/index.js +15 -7
  79. package/dist/prepare-units/index.js.map +1 -1
  80. package/dist/prepare-units/types.cjs.map +1 -1
  81. package/dist/prepare-units/types.d.ts +34 -13
  82. package/dist/prepare-units/types.js.map +1 -1
  83. package/dist/section.cjs +108 -24
  84. package/dist/section.cjs.map +1 -1
  85. package/dist/section.d.ts +66 -6
  86. package/dist/section.js +109 -25
  87. package/dist/section.js.map +1 -1
  88. package/dist/timescale-zoom.cjs +191 -0
  89. package/dist/timescale-zoom.cjs.map +1 -0
  90. package/dist/timescale-zoom.d.ts +74 -0
  91. package/dist/timescale-zoom.js +191 -0
  92. package/dist/timescale-zoom.js.map +1 -0
  93. package/dist/unit-details/age-range.cjs +1 -0
  94. package/dist/unit-details/age-range.cjs.map +1 -1
  95. package/dist/unit-details/age-range.d.ts +5 -1
  96. package/dist/unit-details/age-range.js +1 -0
  97. package/dist/unit-details/age-range.js.map +1 -1
  98. package/dist/unit-details/age-window-tag.cjs +68 -0
  99. package/dist/unit-details/age-window-tag.cjs.map +1 -0
  100. package/dist/unit-details/age-window-tag.d.ts +26 -0
  101. package/dist/unit-details/age-window-tag.js +66 -0
  102. package/dist/unit-details/age-window-tag.js.map +1 -0
  103. package/dist/{age-model-overlay.module.sass.cjs → unit-details/age-window-tag.module.sass.cjs} +3 -3
  104. package/dist/unit-details/age-window-tag.module.sass.cjs.map +1 -0
  105. package/dist/unit-details/age-window-tag.module.sass.js +11 -0
  106. package/dist/unit-details/age-window-tag.module.sass.js.map +1 -0
  107. package/dist/unit-details/index.d.ts +1 -0
  108. package/dist/units/composite.cjs +4 -1
  109. package/dist/units/composite.cjs.map +1 -1
  110. package/dist/units/composite.d.ts +2 -0
  111. package/dist/units/composite.js +4 -1
  112. package/dist/units/composite.js.map +1 -1
  113. package/package.json +7 -7
  114. package/src/age-model/data.ts +98 -0
  115. package/src/age-model/details.module.sass +108 -0
  116. package/src/age-model/details.ts +333 -0
  117. package/src/age-model/index.ts +5 -0
  118. package/src/age-model/legacy.ts +30 -0
  119. package/src/age-model/surfaces.module.sass +168 -0
  120. package/src/age-model/surfaces.ts +568 -0
  121. package/src/age-model/types.ts +392 -0
  122. package/src/column.module.sass +59 -8
  123. package/src/column.ts +49 -23
  124. package/src/correlation-chart/main.ts +14 -2
  125. package/src/data-provider/store.ts +50 -1
  126. package/src/index.ts +5 -1
  127. package/src/notes.ts +20 -2
  128. package/src/prepare-units/composite-scale.ts +34 -31
  129. package/src/prepare-units/density.ts +197 -0
  130. package/src/prepare-units/index.ts +25 -6
  131. package/src/prepare-units/types.ts +34 -13
  132. package/src/section.ts +219 -33
  133. package/src/timescale-zoom.ts +341 -0
  134. package/src/unit-details/age-range.ts +3 -3
  135. package/src/unit-details/age-window-tag.module.sass +10 -0
  136. package/src/unit-details/age-window-tag.ts +102 -0
  137. package/src/unit-details/index.ts +1 -0
  138. package/src/units/composite.ts +11 -0
  139. package/dist/age-model-overlay.cjs +0 -50
  140. package/dist/age-model-overlay.cjs.map +0 -1
  141. package/dist/age-model-overlay.d.ts +0 -24
  142. package/dist/age-model-overlay.js +0 -48
  143. package/dist/age-model-overlay.js.map +0 -1
  144. package/dist/age-model-overlay.module.sass.cjs.map +0 -1
  145. package/dist/age-model-overlay.module.sass.js +0 -11
  146. package/dist/age-model-overlay.module.sass.js.map +0 -1
  147. package/src/age-model-overlay.module.sass +0 -11
  148. package/src/age-model-overlay.ts +0 -96
package/dist/index.js CHANGED
@@ -2,38 +2,48 @@ import { BasicUnitComponent, ColoredUnitComponent, EnvironmentColoredUnitCompone
2
2
  import { AgeCursor, ApproximateHeightAxis, CompositeAgeAxis, CompositeAgeAxisCore, VerticalAxisLabel } from "./age-axis.js";
3
3
  import { prepareColumnUnits, usePreparedColumnUnits } from "./prepare-units/index.js";
4
4
  import { useAnimatedAgeWindow } from "./animated-age-window.js";
5
+ import { levelsForSelected, unitsAgeExtent, useTimescaleZoom } from "./timescale-zoom.js";
5
6
  import { Column, ColumnBasicInfo, ColumnContainer } from "./column.js";
6
- import { BoundaryAgeModelOverlay, ComputedSurfacesOverlay } from "./age-model-overlay.js";
7
+ import { CompositeTimescale, CompositeTimescaleCore } from "./section.js";
7
8
  import { ColumnNotes } from "./notes.js";
8
9
  export * from "@macrostrat/map-views";
9
- import { AgeField, AgeLabel, AgeRange, Duration, IntervalProportions, getAge, getAgeRange } from "./unit-details/age-range.js";
10
+ import { AGE_MODEL_ROUTE, useColumnAgeModel, useColumnSurfaces } from "./age-model/data.js";
11
+ import { AgeField, AgeLabel, AgeRange, Duration, IntervalProportions, Proportion, getAge, getAgeRange } from "./unit-details/age-range.js";
12
+ import { AgeWindowTag, intervalShortFromTimescale } from "./unit-details/age-window-tag.js";
10
13
  import { AnnotatedUnitsColumn, ColumnNotesProvider, CompositeUnitComponent, CompositeUnitsColumn, LabelTrackerProvider, SectionLabelsColumn, TrackedLabeledUnit } from "./units/composite.js";
11
14
  import { BaseMeasurementsColumn, TruncatedList, groupNotesByPixelDistance, mergeHeightRanges, standardizeMeasurementHeight } from "./facets/measurements/base.js";
15
+ import { BoundaryAgeModelOverlay, ComputedSurfacesOverlay } from "./age-model/legacy.js";
12
16
  import { ClickableText, Identifier, UnitIdentifier } from "@macrostrat/data-components";
13
17
  import { ColumnNavProvider, useColumnNav } from "./data-provider/column-nav.js";
18
+ import { ColumnSurfaceLabels, ColumnSurfaceLines, ColumnSurfaces, surfaceClasses } from "./age-model/surfaces.js";
14
19
  import { CorrelationChart } from "./correlation-chart/main.js";
20
+ import { DEFAULT_TARGET_UNIT_HEIGHT, atLeast, fixedDensity, resolveDensity, sectionDensity, sectionHeight, statedDensity, unitHeight } from "./prepare-units/density.js";
15
21
  import { DetritalColumn, DetritalGroup } from "./facets/detrital-zircon/index.js";
16
22
  import { FossilDataType, useFossilData } from "./facets/fossils/provider.js";
17
23
  import { HeightMethod, HybridScaleType } from "./prepare-units/types.js";
24
+ import { INTERVAL_BOUND_TOLERANCE, TIE_POINT_STATUSES, boundaryToSurface, compareAlongAxis, formatAge, formatProportion, inferTiePointStatuses, isPositionAxis, nullifyUnitID, surfaceLabel, surfacePosition, surfaceStatusDescriptions, surfaceStatusLabels, surfaceStatuses, surfaceToken, surfacesFromBoundaries, surfacesFromUnits } from "./age-model/types.js";
18
25
  import { IsotopesColumn, IsotopesDataset } from "./facets/carbon-isotopes/isotopes-column.js";
19
26
  import { LegendPanelHeader, ReferencesField, ThicknessField, UnitDetailsFeature, UnitDetailsPanel } from "./unit-details/panel.js";
20
- import { LithologiesProvider, MacrostratColumnDataProvider, MacrostratColumnProvider, MacrostratColumnStateProvider, UnitSelectionProvider, columnUnitsAtom, columnUnitsMapAtom, scope, useColumnUnitsMap, useCompositeScale, useMacrostratColumnData, useMacrostratUnits } from "./data-provider/store.js";
27
+ import { LithologiesProvider, MacrostratColumnDataProvider, MacrostratColumnProvider, MacrostratColumnStateProvider, UnitSelectionProvider, columnUnitsAtom, columnUnitsMapAtom, scope, useClaimLabelColumn, useColumnUnitsMap, useCompositeScale, useLabelColumnClaimed, useMacrostratColumnData, useMacrostratUnits } from "./data-provider/store.js";
21
28
  import { MeasurementDataContext, MeasurementDataProvider, useMeasurementData } from "./facets/carbon-isotopes/data-provider.js";
22
29
  import { MergeSectionsMode, agesOverlap, createUnitSorter, ensureArray, ensureRealFloat, getPositionWithinUnit, getUnitHeightRange, unitsOverlap } from "./prepare-units/utils.js";
23
30
  import { PBDBFossilsColumn } from "./facets/fossils/index.js";
24
31
  import { PBDBOccurrencesMatrix } from "./facets/fossils/taxon-ranges.js";
25
32
  import { SGPMeasurementsColumn } from "./facets/measurements/sgp.js";
26
33
  import { StratNameField } from "./unit-details/strat-names.js";
34
+ import { SurfaceDetailsPanel, SurfaceStatusLegend, SurfaceStatusTag, SurfaceTypeTag } from "./age-model/details.js";
27
35
  import { UnitDetailsPanelWithNavigation } from "./unit-details/modal-panel.js";
28
36
  import { UnitDetailsPopover, UnitSelectionPopover } from "./unit-details/popover.js";
29
37
  import { UnitKeyboardNavigation, UnitSelectionCallbackManager, allowUnitSelectionAtom, selectedUnitElementAtom, selectedUnitIDAtom, useAtomOverlayPosition, useColumnRef, useSelectedUnit, useUnitSelection, useUnitSelectionDispatch, useUnitSelectionTarget } from "./data-provider/unit-selection.js";
30
38
  import { parseLineFromString, stringifyLine } from "./correlation-chart/hash-string.js";
31
39
  import { preprocessUnits } from "./prepare-units/helpers.js";
32
40
  export {
41
+ AGE_MODEL_ROUTE,
33
42
  AgeCursor,
34
43
  AgeField,
35
44
  AgeLabel,
36
45
  AgeRange,
46
+ AgeWindowTag,
37
47
  AnnotatedUnitsColumn,
38
48
  ApproximateHeightAxis,
39
49
  BaseMeasurementsColumn,
@@ -47,12 +57,18 @@ export {
47
57
  ColumnNavProvider,
48
58
  ColumnNotes,
49
59
  ColumnNotesProvider,
60
+ ColumnSurfaceLabels,
61
+ ColumnSurfaceLines,
62
+ ColumnSurfaces,
50
63
  CompositeAgeAxis,
51
64
  CompositeAgeAxisCore,
65
+ CompositeTimescale,
66
+ CompositeTimescaleCore,
52
67
  CompositeUnitComponent,
53
68
  CompositeUnitsColumn,
54
69
  ComputedSurfacesOverlay,
55
70
  CorrelationChart,
71
+ DEFAULT_TARGET_UNIT_HEIGHT,
56
72
  DetritalColumn,
57
73
  DetritalGroup,
58
74
  Duration,
@@ -60,6 +76,7 @@ export {
60
76
  FossilDataType,
61
77
  HeightMethod,
62
78
  HybridScaleType,
79
+ INTERVAL_BOUND_TOLERANCE,
63
80
  Identifier,
64
81
  IntervalProportions,
65
82
  IsotopesColumn,
@@ -75,11 +92,17 @@ export {
75
92
  MergeSectionsMode,
76
93
  PBDBFossilsColumn,
77
94
  PBDBOccurrencesMatrix,
95
+ Proportion,
78
96
  ReferencesField,
79
97
  SGPMeasurementsColumn,
80
98
  SectionLabelsColumn,
81
99
  SimpleUnitsColumn,
82
100
  StratNameField,
101
+ SurfaceDetailsPanel,
102
+ SurfaceStatusLegend,
103
+ SurfaceStatusTag,
104
+ SurfaceTypeTag,
105
+ TIE_POINT_STATUSES,
83
106
  ThicknessField,
84
107
  TrackedLabeledUnit,
85
108
  TruncatedList,
@@ -97,38 +120,69 @@ export {
97
120
  VerticalAxisLabel,
98
121
  agesOverlap,
99
122
  allowUnitSelectionAtom,
123
+ atLeast,
124
+ boundaryToSurface,
100
125
  columnUnitsAtom,
101
126
  columnUnitsMapAtom,
127
+ compareAlongAxis,
102
128
  createUnitSorter,
103
129
  ensureArray,
104
130
  ensureRealFloat,
131
+ fixedDensity,
132
+ formatAge,
133
+ formatProportion,
105
134
  getAge,
106
135
  getAgeRange,
107
136
  getPositionWithinUnit,
108
137
  getUnitHeightRange,
109
138
  groupNotesByPixelDistance,
139
+ inferTiePointStatuses,
140
+ intervalShortFromTimescale,
141
+ isPositionAxis,
142
+ levelsForSelected,
110
143
  mergeHeightRanges,
144
+ nullifyUnitID,
111
145
  parseLineFromString,
112
146
  prepareColumnUnits,
113
147
  preprocessUnits,
148
+ resolveDensity,
114
149
  scope,
150
+ sectionDensity,
151
+ sectionHeight,
115
152
  selectedUnitElementAtom,
116
153
  selectedUnitIDAtom,
117
154
  standardizeMeasurementHeight,
155
+ statedDensity,
118
156
  stringifyLine,
157
+ surfaceClasses,
158
+ surfaceLabel,
159
+ surfacePosition,
160
+ surfaceStatusDescriptions,
161
+ surfaceStatusLabels,
162
+ surfaceStatuses,
163
+ surfaceToken,
164
+ surfacesFromBoundaries,
165
+ surfacesFromUnits,
166
+ unitHeight,
167
+ unitsAgeExtent,
119
168
  unitsOverlap,
120
169
  useAnimatedAgeWindow,
121
170
  useAtomOverlayPosition,
171
+ useClaimLabelColumn,
172
+ useColumnAgeModel,
122
173
  useColumnNav,
123
174
  useColumnRef,
175
+ useColumnSurfaces,
124
176
  useColumnUnitsMap,
125
177
  useCompositeScale,
126
178
  useFossilData,
179
+ useLabelColumnClaimed,
127
180
  useMacrostratColumnData,
128
181
  useMacrostratUnits,
129
182
  useMeasurementData,
130
183
  usePreparedColumnUnits,
131
184
  useSelectedUnit,
185
+ useTimescaleZoom,
132
186
  useUnitColor,
133
187
  useUnitColorByEnvironment,
134
188
  useUnitSelection,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/notes.cjs CHANGED
@@ -13,6 +13,11 @@ function ColumnNotes({
13
13
  paddingLeft = 60,
14
14
  deltaConnectorAttachment,
15
15
  focusedNoteComponent,
16
+ onClickNote,
17
+ forceOptions,
18
+ connectorOverhang,
19
+ showPointMarker,
20
+ className,
16
21
  children
17
22
  }) {
18
23
  const { totalHeight } = store.useMacrostratColumnData();
@@ -25,14 +30,18 @@ function ColumnNotes({
25
30
  pixelScale: -1
26
31
  },
27
32
  [
28
- h__default.default(columnComponents.SVG, { width, height: totalHeight, paddingH: 4 }, [
33
+ h__default.default(columnComponents.SVG, { width, height: totalHeight, paddingH: 4, className }, [
29
34
  h__default.default(columnComponents.NotesColumn, {
30
35
  width,
31
36
  notes,
32
37
  noteComponent,
33
38
  paddingLeft,
34
39
  deltaConnectorAttachment,
35
- focusedNoteComponent
40
+ focusedNoteComponent,
41
+ onClickNote,
42
+ forceOptions,
43
+ connectorOverhang,
44
+ showPointMarker
36
45
  })
37
46
  ]),
38
47
  children
@@ -1 +1 @@
1
- {"version":3,"file":"notes.cjs","sources":["../src/notes.ts"],"sourcesContent":["import h from \"@macrostrat/hyper\";\n\nimport { ColumnNotesProvider } from \"./units\";\n\nimport { NotesColumn, SVG } from \"@macrostrat/column-components\";\nimport { useCompositeScale, useMacrostratColumnData } from \"./data-provider\";\nimport type { ComponentType, ReactNode } from \"react\";\n\ninterface ColumnNotesProps {\n notes: any[];\n width?: number;\n noteComponent?: any;\n paddingLeft?: number;\n deltaConnectorAttachment?: number;\n children?: ReactNode;\n focusedNoteComponent?: ComponentType<any> | null;\n}\n\nexport function ColumnNotes({\n notes,\n width = 200,\n noteComponent,\n paddingLeft = 60,\n deltaConnectorAttachment,\n focusedNoteComponent,\n children,\n}: ColumnNotesProps) {\n const { totalHeight } = useMacrostratColumnData();\n const scale = useCompositeScale();\n\n return h(\n ColumnNotesProvider,\n {\n scale,\n totalHeight,\n pixelScale: -1,\n },\n [\n h(SVG, { width, height: totalHeight, paddingH: 4 }, [\n h(NotesColumn, {\n width,\n notes,\n noteComponent,\n paddingLeft,\n deltaConnectorAttachment,\n focusedNoteComponent,\n }),\n ]),\n children,\n ],\n );\n}\n"],"names":["useMacrostratColumnData","useCompositeScale","h","ColumnNotesProvider","SVG","NotesColumn"],"mappings":";;;;;;;;AAkBO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,EAAE,YAAA,IAAgBA,8BAAA;AACxB,QAAM,QAAQC,MAAAA,kBAAA;AAEd,SAAOC,WAAAA;AAAAA,IACLC,UAAAA;AAAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA,YAAY;AAAA,IAAA;AAAA,IAEd;AAAA,MACED,WAAAA,QAAEE,iBAAAA,KAAK,EAAE,OAAO,QAAQ,aAAa,UAAU,KAAK;AAAA,QAClDF,WAAAA,QAAEG,iBAAAA,aAAa;AAAA,UACb;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QAAA,CACD;AAAA,MAAA,CACF;AAAA,MACD;AAAA,IAAA;AAAA,EACF;AAEJ;;"}
1
+ {"version":3,"file":"notes.cjs","sources":["../src/notes.ts"],"sourcesContent":["import h from \"@macrostrat/hyper\";\n\nimport { ColumnNotesProvider } from \"./units\";\n\nimport { NotesColumn, SVG, type NoteData } from \"@macrostrat/column-components\";\nimport { useCompositeScale, useMacrostratColumnData } from \"./data-provider\";\nimport type { ComponentType, ReactNode } from \"react\";\n\ninterface ColumnNotesProps {\n notes: any[];\n width?: number;\n noteComponent?: any;\n paddingLeft?: number;\n deltaConnectorAttachment?: number;\n children?: ReactNode;\n focusedNoteComponent?: ComponentType<any> | null;\n /** Called when a note is clicked */\n onClickNote?: (note: NoteData) => void;\n /** Px the connector continues past each end (see `NodeConnectorOptions`) */\n connectorOverhang?: number | [number, number];\n /** Draw the marker at a point note's height (default true) */\n showPointMarker?: boolean;\n /** Options for the label force layout (e.g. `nodeSpacing`) */\n forceOptions?: object;\n className?: string;\n}\n\nexport function ColumnNotes({\n notes,\n width = 200,\n noteComponent,\n paddingLeft = 60,\n deltaConnectorAttachment,\n focusedNoteComponent,\n onClickNote,\n forceOptions,\n connectorOverhang,\n showPointMarker,\n className,\n children,\n}: ColumnNotesProps) {\n const { totalHeight } = useMacrostratColumnData();\n const scale = useCompositeScale();\n\n return h(\n ColumnNotesProvider,\n {\n scale,\n totalHeight,\n pixelScale: -1,\n },\n [\n h(SVG, { width, height: totalHeight, paddingH: 4, className }, [\n h(NotesColumn, {\n width,\n notes,\n noteComponent,\n paddingLeft,\n deltaConnectorAttachment,\n focusedNoteComponent,\n onClickNote,\n forceOptions,\n connectorOverhang,\n showPointMarker,\n }),\n ]),\n children,\n ],\n );\n}\n"],"names":["useMacrostratColumnData","useCompositeScale","h","ColumnNotesProvider","SVG","NotesColumn"],"mappings":";;;;;;;;AA2BO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,EAAE,YAAA,IAAgBA,8BAAA;AACxB,QAAM,QAAQC,MAAAA,kBAAA;AAEd,SAAOC,WAAAA;AAAAA,IACLC,UAAAA;AAAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA,YAAY;AAAA,IAAA;AAAA,IAEd;AAAA,MACED,mBAAEE,iBAAAA,KAAK,EAAE,OAAO,QAAQ,aAAa,UAAU,GAAG,aAAa;AAAA,QAC7DF,WAAAA,QAAEG,iBAAAA,aAAa;AAAA,UACb;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QAAA,CACD;AAAA,MAAA,CACF;AAAA,MACD;AAAA,IAAA;AAAA,EACF;AAEJ;;"}
package/dist/notes.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { NoteData } from '@macrostrat/column-components';
1
2
  import { ComponentType, ReactNode } from 'react';
2
3
  interface ColumnNotesProps {
3
4
  notes: any[];
@@ -7,6 +8,15 @@ interface ColumnNotesProps {
7
8
  deltaConnectorAttachment?: number;
8
9
  children?: ReactNode;
9
10
  focusedNoteComponent?: ComponentType<any> | null;
11
+ /** Called when a note is clicked */
12
+ onClickNote?: (note: NoteData) => void;
13
+ /** Px the connector continues past each end (see `NodeConnectorOptions`) */
14
+ connectorOverhang?: number | [number, number];
15
+ /** Draw the marker at a point note's height (default true) */
16
+ showPointMarker?: boolean;
17
+ /** Options for the label force layout (e.g. `nodeSpacing`) */
18
+ forceOptions?: object;
19
+ className?: string;
10
20
  }
11
- export declare function ColumnNotes({ notes, width, noteComponent, paddingLeft, deltaConnectorAttachment, focusedNoteComponent, children, }: ColumnNotesProps): import('react').FunctionComponentElement<any>;
21
+ export declare function ColumnNotes({ notes, width, noteComponent, paddingLeft, deltaConnectorAttachment, focusedNoteComponent, onClickNote, forceOptions, connectorOverhang, showPointMarker, className, children, }: ColumnNotesProps): import('react').FunctionComponentElement<any>;
12
22
  export {};
package/dist/notes.js CHANGED
@@ -9,6 +9,11 @@ function ColumnNotes({
9
9
  paddingLeft = 60,
10
10
  deltaConnectorAttachment,
11
11
  focusedNoteComponent,
12
+ onClickNote,
13
+ forceOptions,
14
+ connectorOverhang,
15
+ showPointMarker,
16
+ className,
12
17
  children
13
18
  }) {
14
19
  const { totalHeight } = useMacrostratColumnData();
@@ -21,14 +26,18 @@ function ColumnNotes({
21
26
  pixelScale: -1
22
27
  },
23
28
  [
24
- h(SVG, { width, height: totalHeight, paddingH: 4 }, [
29
+ h(SVG, { width, height: totalHeight, paddingH: 4, className }, [
25
30
  h(NotesColumn, {
26
31
  width,
27
32
  notes,
28
33
  noteComponent,
29
34
  paddingLeft,
30
35
  deltaConnectorAttachment,
31
- focusedNoteComponent
36
+ focusedNoteComponent,
37
+ onClickNote,
38
+ forceOptions,
39
+ connectorOverhang,
40
+ showPointMarker
32
41
  })
33
42
  ]),
34
43
  children
package/dist/notes.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"notes.js","sources":["../src/notes.ts"],"sourcesContent":["import h from \"@macrostrat/hyper\";\n\nimport { ColumnNotesProvider } from \"./units\";\n\nimport { NotesColumn, SVG } from \"@macrostrat/column-components\";\nimport { useCompositeScale, useMacrostratColumnData } from \"./data-provider\";\nimport type { ComponentType, ReactNode } from \"react\";\n\ninterface ColumnNotesProps {\n notes: any[];\n width?: number;\n noteComponent?: any;\n paddingLeft?: number;\n deltaConnectorAttachment?: number;\n children?: ReactNode;\n focusedNoteComponent?: ComponentType<any> | null;\n}\n\nexport function ColumnNotes({\n notes,\n width = 200,\n noteComponent,\n paddingLeft = 60,\n deltaConnectorAttachment,\n focusedNoteComponent,\n children,\n}: ColumnNotesProps) {\n const { totalHeight } = useMacrostratColumnData();\n const scale = useCompositeScale();\n\n return h(\n ColumnNotesProvider,\n {\n scale,\n totalHeight,\n pixelScale: -1,\n },\n [\n h(SVG, { width, height: totalHeight, paddingH: 4 }, [\n h(NotesColumn, {\n width,\n notes,\n noteComponent,\n paddingLeft,\n deltaConnectorAttachment,\n focusedNoteComponent,\n }),\n ]),\n children,\n ],\n );\n}\n"],"names":[],"mappings":";;;;AAkBO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,EAAE,YAAA,IAAgB,wBAAA;AACxB,QAAM,QAAQ,kBAAA;AAEd,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA,YAAY;AAAA,IAAA;AAAA,IAEd;AAAA,MACE,EAAE,KAAK,EAAE,OAAO,QAAQ,aAAa,UAAU,KAAK;AAAA,QAClD,EAAE,aAAa;AAAA,UACb;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QAAA,CACD;AAAA,MAAA,CACF;AAAA,MACD;AAAA,IAAA;AAAA,EACF;AAEJ;"}
1
+ {"version":3,"file":"notes.js","sources":["../src/notes.ts"],"sourcesContent":["import h from \"@macrostrat/hyper\";\n\nimport { ColumnNotesProvider } from \"./units\";\n\nimport { NotesColumn, SVG, type NoteData } from \"@macrostrat/column-components\";\nimport { useCompositeScale, useMacrostratColumnData } from \"./data-provider\";\nimport type { ComponentType, ReactNode } from \"react\";\n\ninterface ColumnNotesProps {\n notes: any[];\n width?: number;\n noteComponent?: any;\n paddingLeft?: number;\n deltaConnectorAttachment?: number;\n children?: ReactNode;\n focusedNoteComponent?: ComponentType<any> | null;\n /** Called when a note is clicked */\n onClickNote?: (note: NoteData) => void;\n /** Px the connector continues past each end (see `NodeConnectorOptions`) */\n connectorOverhang?: number | [number, number];\n /** Draw the marker at a point note's height (default true) */\n showPointMarker?: boolean;\n /** Options for the label force layout (e.g. `nodeSpacing`) */\n forceOptions?: object;\n className?: string;\n}\n\nexport function ColumnNotes({\n notes,\n width = 200,\n noteComponent,\n paddingLeft = 60,\n deltaConnectorAttachment,\n focusedNoteComponent,\n onClickNote,\n forceOptions,\n connectorOverhang,\n showPointMarker,\n className,\n children,\n}: ColumnNotesProps) {\n const { totalHeight } = useMacrostratColumnData();\n const scale = useCompositeScale();\n\n return h(\n ColumnNotesProvider,\n {\n scale,\n totalHeight,\n pixelScale: -1,\n },\n [\n h(SVG, { width, height: totalHeight, paddingH: 4, className }, [\n h(NotesColumn, {\n width,\n notes,\n noteComponent,\n paddingLeft,\n deltaConnectorAttachment,\n focusedNoteComponent,\n onClickNote,\n forceOptions,\n connectorOverhang,\n showPointMarker,\n }),\n ]),\n children,\n ],\n );\n}\n"],"names":[],"mappings":";;;;AA2BO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,EAAE,YAAA,IAAgB,wBAAA;AACxB,QAAM,QAAQ,kBAAA;AAEd,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA,YAAY;AAAA,IAAA;AAAA,IAEd;AAAA,MACE,EAAE,KAAK,EAAE,OAAO,QAAQ,aAAa,UAAU,GAAG,aAAa;AAAA,QAC7D,EAAE,aAAa;AAAA,UACb;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QAAA,CACD;AAAA,MAAA,CACF;AAAA,MACD;AAAA,IAAA;AAAA,EACF;AAEJ;"}
@@ -4,6 +4,7 @@ const columnComponents = require("@macrostrat/column-components");
4
4
  const utils = require("./utils.cjs");
5
5
  const d3Scale = require("d3-scale");
6
6
  const dynamicScales = require("./dynamic-scales.cjs");
7
+ const density = require("./density.cjs");
7
8
  const types = require("./types.cjs");
8
9
  function buildCompositeScaleInfo(inputScales, unconformityHeight) {
9
10
  let totalHeight = unconformityHeight / 2;
@@ -67,7 +68,8 @@ function addScaleToSection(group, opts) {
67
68
  }
68
69
  const scaleInfo = buildSectionScale(units, {
69
70
  ...opts,
70
- domain: _range
71
+ domain: _range,
72
+ sectionID: group.section_id
71
73
  });
72
74
  return {
73
75
  ...group,
@@ -77,12 +79,11 @@ function addScaleToSection(group, opts) {
77
79
  function buildSectionScale(data, opts) {
78
80
  const {
79
81
  targetUnitHeight = 20,
80
- minPixelScale = 0.2,
81
82
  axisType,
82
- minSectionHeight,
83
83
  visibleWindow,
84
84
  scale,
85
- hybridScale
85
+ hybridScale,
86
+ sectionID
86
87
  } = opts;
87
88
  const domain = opts.domain ?? findSectionHeightRange(data, axisType);
88
89
  const dAge = Math.abs(domain[0] - domain[1]);
@@ -91,6 +92,8 @@ function buildSectionScale(data, opts) {
91
92
  if (hybridScale != null) {
92
93
  if (hybridScale.type === types.HybridScaleType.EquidistantSurfaces) {
93
94
  _pixelScale ??= targetUnitHeight;
95
+ } else {
96
+ _pixelScale = opts.pixelsPerMeter ?? _pixelScale;
94
97
  }
95
98
  return dynamicScales.buildHybridScale(hybridScale, data, domain, {
96
99
  pixelOffset: 0,
@@ -98,14 +101,14 @@ function buildSectionScale(data, opts) {
98
101
  });
99
102
  }
100
103
  if (scale == null) {
101
- if (_pixelScale == null) {
102
- const avgAgeRange = findAverageUnitHeight(data, axisType, visibleWindow);
103
- _pixelScale = Math.max(targetUnitHeight / avgAgeRange, minPixelScale);
104
- }
105
- let height = dAge * _pixelScale;
106
- const _minSectionHeight = minSectionHeight ?? targetUnitHeight ?? 0;
107
- pixelHeight = Math.max(height, _minSectionHeight);
108
- _pixelScale = pixelHeight / dAge;
104
+ _pixelScale = density.sectionDensity(opts)({
105
+ extent: dAge,
106
+ unitExtents: visibleUnitExtents(data, axisType, visibleWindow),
107
+ units: data,
108
+ sectionID,
109
+ axisType
110
+ });
111
+ pixelHeight = dAge * _pixelScale;
109
112
  } else {
110
113
  pixelHeight = Math.abs(scale(domain[0]) - scale(domain[1]));
111
114
  }
@@ -233,7 +236,7 @@ function findSectionHeightRange(data, axisType) {
233
236
  return [b_pos, t_pos];
234
237
  }
235
238
  }
236
- function findAverageUnitHeight(data, axisType, visibleWindow) {
239
+ function visibleUnitExtents(data, axisType, visibleWindow) {
237
240
  const durations = (clip) => data.map((d) => {
238
241
  const [b_pos, t_pos] = utils.getUnitHeightRange(d, axisType);
239
242
  if (!clip) return Math.abs(b_pos - t_pos);
@@ -246,8 +249,7 @@ function findAverageUnitHeight(data, axisType, visibleWindow) {
246
249
  const useWindow = visibleWindow != null && axisType === columnComponents.ColumnAxisType.AGE;
247
250
  let heights = useWindow ? durations(true) : [];
248
251
  if (heights.length === 0) heights = durations(false);
249
- if (heights.length === 0) return 1;
250
- return heights.reduce((a, b) => a + b, 0) / heights.length;
252
+ return heights;
251
253
  }
252
254
  function createCompositeScale(sections, interpolateUnconformities = false) {
253
255
  const scales = [];
@@ -324,7 +326,7 @@ function collapseUnconformitiesByPixelHeight(sections, threshold, opts) {
324
326
  _diff(heights.map(nextSection.scaleInfo.scale)),
325
327
  _diff(heights.map(currentSection.scaleInfo.scale))
326
328
  ];
327
- const pxHeight = Math.min(...pxHeights);
329
+ const pxHeight = Math.max(...pxHeights);
328
330
  if (pxHeight < threshold) {
329
331
  let t_pos;
330
332
  let b_pos;
@@ -1 +1 @@
1
- {"version":3,"file":"composite-scale.cjs","sources":["../../src/prepare-units/composite-scale.ts"],"sourcesContent":["import { ColumnAxisType } from \"@macrostrat/column-components\";\nimport { agesOverlap, ensureArray, getUnitHeightRange } from \"./utils\";\nimport { ScaleContinuousNumeric, scaleLinear } from \"d3-scale\";\nimport { UnitLong } from \"@macrostrat/api-types\";\nimport { buildHybridScale } from \"./dynamic-scales\";\nimport { ExtUnit, HybridScaleType, SectionInfo } from \"./types\";\nimport type {\n ColumnScaleOptions,\n CompositeColumnData,\n CompositeScaleData,\n PackageLayoutData,\n PackageScaleInfo,\n PackageScaleLayoutData,\n SectionScaleOptions,\n} from \"./types\";\n\n// Composite scale information augmented with units in each package\n\nexport function buildCompositeScaleInfo(\n inputScales: PackageScaleInfo[],\n unconformityHeight: number,\n): CompositeScaleData {\n /** Finalize the heights of sections, including the heights of unconformities\n * between them.\n */\n\n let totalHeight = unconformityHeight / 2;\n let lastSectionTopHeight = 0;\n\n const packages2: PackageScaleLayoutData[] = [];\n for (let i = 0; i < inputScales.length; i++) {\n const group = inputScales[i];\n const { domain, scale } = group;\n const [b_age, t_age] = domain;\n // Positionally stable key: packages are always emitted in age order, so the\n // index is a stable identity. An age-based key (`package-${b_age}-${t_age}`)\n // changes every frame while the age window animates, forcing every package,\n // column, and timescale slice in the correlation chart to remount each frame.\n const key = `package-${i}`;\n\n packages2.push({\n ...createPackageScale(group, totalHeight),\n key,\n offset: totalHeight,\n // Unconformity height above this particular section\n paddingTop: totalHeight - lastSectionTopHeight,\n });\n\n const pixelHeight = Math.abs(scale(b_age) - scale(t_age));\n lastSectionTopHeight = totalHeight + pixelHeight;\n totalHeight = lastSectionTopHeight + unconformityHeight;\n }\n totalHeight += unconformityHeight / 2;\n return {\n totalHeight,\n sections: packages2,\n };\n}\n\nexport function finalizeSectionHeights<T extends UnitLong>(\n sections: SectionInfoWithScale<T>[],\n unconformityHeight: number,\n): CompositeColumnData<T> {\n /** Finalize the heights of sections, including the heights of unconformities\n * between them.\n */\n\n const sectionScales = sections.map((d) => d.scaleInfo);\n\n const { totalHeight, sections: packages } = buildCompositeScaleInfo(\n sectionScales,\n unconformityHeight,\n );\n\n // This could perhaps be simplified.\n const sections1: PackageLayoutData<T>[] = [];\n for (const i in sections) {\n const group = sections[i];\n const scaleInfo = packages[i];\n sections1.push({\n ...group,\n key: scaleInfo.key,\n scaleInfo,\n });\n }\n return {\n totalHeight,\n sections: sections1,\n };\n}\n\nexport interface SectionInfoWithScale<\n T extends UnitLong = ExtUnit,\n> extends SectionInfo<T> {\n scaleInfo: PackageScaleInfo;\n}\n\nexport function computeSectionHeights<T extends UnitLong>(\n sections: SectionInfo<T>[],\n opts: ColumnScaleOptions,\n): SectionInfoWithScale<T>[] {\n return sections.map((group) => {\n return addScaleToSection<T>(group, opts);\n });\n}\n\nfunction addScaleToSection<T extends UnitLong = ExtUnit>(\n group: SectionInfo<T>,\n opts: ColumnScaleOptions,\n): SectionInfoWithScale<T> {\n const { t_age, b_age, t_pos, b_pos, units } = group;\n let _range = null;\n // if t_age and b_age are set for a group, use them to define the range...\n if (opts.axisType == ColumnAxisType.AGE) {\n _range = [b_age, t_age];\n } else {\n _range = [b_pos, t_pos];\n }\n\n const scaleInfo = buildSectionScale<T>(units, {\n ...opts,\n domain: _range,\n });\n\n return {\n ...group,\n scaleInfo,\n };\n}\n\nfunction buildSectionScale<T extends UnitLong>(\n data: T[],\n opts: SectionScaleOptions,\n): PackageScaleInfo {\n const {\n targetUnitHeight = 20,\n minPixelScale = 0.2,\n axisType,\n minSectionHeight,\n visibleWindow,\n scale,\n hybridScale,\n } = opts;\n const domain = opts.domain ?? findSectionHeightRange(data, axisType);\n\n const dAge = Math.abs(domain[0] - domain[1]);\n\n let _pixelScale = opts.pixelScale;\n let pixelHeight: number;\n\n if (hybridScale != null) {\n /** In an equidistant surfaces scale, we want to determine the heights of surfaces\n * and then distribute units evenly between them.\n * This is somewhat like an ordinal scale\n */\n if (hybridScale.type === HybridScaleType.EquidistantSurfaces) {\n _pixelScale ??= targetUnitHeight;\n }\n\n return buildHybridScale(hybridScale, data, domain, {\n pixelOffset: 0,\n pixelScale: _pixelScale,\n });\n }\n\n if (scale == null) {\n if (_pixelScale == null) {\n const avgAgeRange = findAverageUnitHeight(data, axisType, visibleWindow);\n // Get pixel height necessary to render average unit at target height\n _pixelScale = Math.max(targetUnitHeight / avgAgeRange, minPixelScale);\n\n // OLD METHOD that cares about overall section height vs. individual unit height\n // 0.2 pixel per myr is the floor scale\n //const targetHeight = targetUnitHeight * data.length;\n // 1 pixel per myr is the floor scale\n //_pixelScale = Math.max(targetHeight / dAge, minPixelScale);\n }\n\n let height = dAge * _pixelScale;\n // If height is less than minSectionHeight, set it to minSectionHeight.\n // Sections reach here at their *full* extent (the rendered window is applied\n // afterwards, by `trimSectionsToWindow`), so this floor only ever inflates a\n // genuinely small section — which is what it's for — and never a sliver that\n // the window happens to cut.\n const _minSectionHeight = minSectionHeight ?? targetUnitHeight ?? 0;\n pixelHeight = Math.max(height, _minSectionHeight);\n _pixelScale = pixelHeight / dAge;\n } else {\n // If a scale is provided, use it to compute pixel height\n pixelHeight = Math.abs(scale(domain[0]) - scale(domain[1]));\n }\n\n return createPackageScale(\n { scale, domain, pixelHeight, pixelScale: _pixelScale },\n 0,\n );\n}\n\n/**\n * The density each section will actually render at once the window is applied.\n *\n * A section the window cuts short is still being *looked at*, so\n * `minSectionHeight` applies to what remains — it's honored by expanding the\n * section's scale, not by showing more of it. Resolving this **before** padding\n * matters: padding is a pixel margin measured at the density it will render at,\n * and a section stretched to the floor afterwards would multiply the margin\n * along with everything else (a 20 px margin in a band stretched 4× renders at\n * 80 px, and asymmetrically so whenever one side is truncated).\n *\n * Sections the window leaves whole keep their laid-out density — they already\n * met the floor when they were sized.\n */\nexport function resolveWindowScales<T extends UnitLong>(\n sections: SectionInfoWithScale<T>[],\n focalWindow: [number, number],\n floor: number,\n): Map<SectionInfoWithScale<T>, number> {\n const [b_focal, t_focal] = focalWindow;\n const scales = new Map<SectionInfoWithScale<T>, number>();\n\n for (const section of sections) {\n const t_age = Math.max(section.t_age, t_focal);\n const b_age = Math.min(section.b_age, b_focal);\n // Sections outside the focal window are visible only as padding; they keep\n // their own density and render exactly the pixels asked for.\n if (b_age <= t_age) continue;\n\n const natural = section.scaleInfo.pixelScale;\n if (!(natural > 0)) continue;\n\n const isClipped = b_age < section.b_age || t_age > section.t_age;\n const needed = isClipped && floor > 0 ? floor / (b_age - t_age) : 0;\n scales.set(section, Math.max(natural, needed));\n }\n return scales;\n}\n\n/**\n * Expand an age window outward by `padding` **pixels** of column, spending the\n * budget against sections that are already laid out at full extent, at the\n * densities they will finally render at (`scaleFor`).\n *\n * Because the layout is done, this is exact rather than predicted: each section\n * contributes its own real pixel height. Padding collapses when the column runs\n * out on that side.\n *\n * Unconformities between sections cost nothing. They render at a fixed height\n * that's typically larger than the padding itself (30–60 px), so charging them\n * would mean padding could never reach across a gap — which is the case it\n * exists for: revealing the neighboring section keeps its timescale intervals\n * navigable. The padding is a budget of neighboring *content*.\n */\nexport function padWindowByPixels<T extends UnitLong>(\n sections: SectionInfoWithScale<T>[],\n window: [number, number],\n padding: number,\n scaleFor: (section: SectionInfoWithScale<T>) => number,\n): [number, number] {\n if (!(padding > 0) || sections.length === 0) return window;\n const [b_age, t_age] = window;\n return [\n spendOutward(sections, b_age, 1, padding, scaleFor),\n spendOutward(sections, t_age, -1, padding, scaleFor),\n ];\n}\n\n/** Walk out from one edge of the window through laid-out sections, spending a\n * pixel budget, and return the age it lands on. `direction` is +1 toward older\n * ages, -1 toward younger. */\nfunction spendOutward<T extends UnitLong>(\n sections: SectionInfoWithScale<T>[],\n edge: number,\n direction: 1 | -1,\n padding: number,\n scaleFor: (section: SectionInfoWithScale<T>) => number,\n): number {\n // Sections ordered outward from the edge, keeping only what lies beyond it.\n const beyond = sections\n .filter(\n (s) => direction * (s.b_age - edge) > 0 || direction * (s.t_age - edge) > 0,\n )\n .sort((a, b) => direction * (nearEdge(a, direction) - nearEdge(b, direction)));\n\n let bound = edge;\n let budget = padding;\n\n for (const section of beyond) {\n const pixelScale = scaleFor(section);\n if (!(pixelScale > 0)) continue;\n\n const from =\n direction > 0\n ? Math.max(section.t_age, bound)\n : Math.min(section.b_age, bound);\n const available = direction > 0 ? section.b_age - from : from - section.t_age;\n if (available <= 0) continue;\n\n const cost = available * pixelScale;\n if (budget <= cost) return from + direction * (budget / pixelScale);\n\n bound = direction > 0 ? section.b_age : section.t_age;\n budget -= cost;\n }\n return bound;\n}\n\n/** The age at which a section starts, measured from the window edge outward. */\nfunction nearEdge<T extends UnitLong>(\n section: SectionInfoWithScale<T>,\n direction: 1 | -1,\n): number {\n return direction > 0 ? section.t_age : section.b_age;\n}\n\n/**\n * Clip laid-out sections to the rendered window — the last step of layout, so\n * that every section has already been sized (and floored) at its full extent.\n * Each section renders at the density `scaleFor` resolved for it, which is what\n * keeps a padding margin the same number of pixels as the band it borders is\n * stretched to meet `minSectionHeight`.\n */\nexport function trimSectionsToWindow<T extends UnitLong>(\n sections: SectionInfoWithScale<T>[],\n window: [number, number],\n scaleFor: (section: SectionInfoWithScale<T>) => number,\n): SectionInfoWithScale<T>[] {\n const [b_win, t_win] = window;\n const out: SectionInfoWithScale<T>[] = [];\n\n for (const section of sections) {\n const t_age = Math.max(section.t_age, t_win);\n const b_age = Math.min(section.b_age, b_win);\n if (b_age <= t_age) continue;\n\n const pixelScale = scaleFor(section);\n if (\n b_age === section.b_age &&\n t_age === section.t_age &&\n pixelScale === section.scaleInfo.pixelScale\n ) {\n out.push(section);\n continue;\n }\n\n out.push({\n ...section,\n t_age,\n b_age,\n units: section.units.filter((u) => agesOverlap(u, { t_age, b_age })),\n scaleInfo: createPackageScale({\n domain: [b_age, t_age],\n pixelScale,\n pixelHeight: (b_age - t_age) * pixelScale,\n scale: null,\n }),\n });\n }\n return out;\n}\n\nexport function createPackageScale(\n def: PackageScaleInfo,\n offset: number = 0,\n): PackageScaleInfo {\n /** Build a section scale */\n // Domain should be oriented from bottom to top, but scale is oriented from top to bottom\n const { domain, pixelScale, pixelHeight, scale, heightScale } = def;\n\n if (scale == null && pixelScale == null) {\n throw new Error(\"Either scale or pixelScale must be provided\");\n }\n\n let _scale: ScaleContinuousNumeric<number, number>;\n if (scale == null) {\n _scale = scaleLinear()\n .domain([domain[1], domain[0]])\n .range([offset, pixelHeight + offset]);\n } else {\n const domain0 = scale.domain();\n const range0 = scale.range();\n _scale = scale\n .copy()\n .domain(domain0)\n .range(range0.map((d) => d + offset));\n }\n\n _scale.clamp();\n\n let _heightScale = null;\n if (heightScale != null) {\n // Adjust height scale as well\n const range0 = scale.range();\n _heightScale = heightScale.copy().range(range0.map((d) => d + offset));\n _heightScale.clamp();\n }\n\n return {\n domain,\n pixelScale,\n pixelHeight,\n scale: _scale,\n // Internal details for hybrid scales. TODO: improve this\n heightScale: _heightScale,\n };\n}\n\nfunction findSectionHeightRange(\n data: UnitLong[],\n axisType: ColumnAxisType,\n): [number, number] {\n if (axisType == null) {\n throw new Error(\"Axis type is not set\");\n }\n if (axisType === ColumnAxisType.AGE) {\n const t_age = Math.min(...data.map((d) => d.t_age));\n const b_age = Math.max(...data.map((d) => d.b_age));\n return [b_age, t_age];\n } else if (\n axisType == ColumnAxisType.DEPTH ||\n axisType == ColumnAxisType.ORDINAL\n ) {\n const t_pos = Math.min(...data.map((d) => d.t_pos));\n const b_pos = Math.max(...data.map((d) => d.b_pos));\n return [b_pos, t_pos];\n } else if (axisType == ColumnAxisType.HEIGHT) {\n const t_pos = Math.max(...data.map((d) => d.t_pos));\n const b_pos = Math.min(...data.map((d) => d.b_pos));\n return [b_pos, t_pos];\n }\n}\n\nfunction findAverageUnitHeight(\n data: UnitLong[],\n axisType: ColumnAxisType,\n visibleWindow?: [number, number] | null,\n): number {\n /** The typical duration of a unit, which `targetUnitHeight` sizes.\n *\n * Measured over what the render window actually *shows* — units outside it\n * are ignored and a unit it cuts through counts only for its visible part.\n * That's what makes `targetUnitHeight` mean the same thing at every zoom\n * depth: the units on screen are drawn at the target height whether they're\n * whole or slivers of something much longer.\n *\n * A section the window doesn't reach has nothing visible to measure, so it\n * falls back to its own units at full duration — it's drawn at its own scale.\n * (Deriving it from the padded window instead would be circular: padding is\n * measured in pixels, which is what we're trying to establish.)\n */\n const durations = (clip: boolean) =>\n data\n .map((d) => {\n const [b_pos, t_pos] = getUnitHeightRange(d, axisType);\n if (!clip) return Math.abs(b_pos - t_pos);\n const [b_win, t_win] = visibleWindow;\n return Math.max(\n 0,\n Math.min(b_pos, b_win) - Math.max(t_pos, t_win),\n );\n })\n .filter((d) => d > 0);\n\n const useWindow = visibleWindow != null && axisType === ColumnAxisType.AGE;\n let heights = useWindow ? durations(true) : [];\n if (heights.length === 0) heights = durations(false);\n if (heights.length === 0) return 1;\n\n return heights.reduce((a, b) => a + b, 0) / heights.length;\n}\n\nexport interface CompositeColumnScale {\n (age: number): number | null;\n copy(): CompositeColumnScale;\n domain(): [number, number];\n invert(pixelHeight: number): number | null;\n clamp(clamp: boolean): void;\n}\n\nexport function createCompositeScale(\n sections: PackageLayoutData[],\n interpolateUnconformities: boolean = false,\n): CompositeColumnScale {\n /** Create a scale that works across multiple packages */\n\n const scales: ScaleContinuousNumeric<number, number>[] = [];\n\n let lastScale: ScaleContinuousNumeric<number, number> | null = null;\n for (const section of sections) {\n const _scale = section.scaleInfo.scale.copy().clamp(true);\n scales.push(_scale);\n if (lastScale != null && interpolateUnconformities) {\n // Add a new scale that interpolates between lastScale and _scale\n const lastDomain = lastScale.domain();\n const lastRange = lastScale.range();\n const currentDomain = _scale.domain();\n const currentRange = _scale.range();\n\n const interpScale = scaleLinear()\n .domain([lastDomain[lastDomain.length - 1], currentDomain[0]])\n .range([lastRange[lastRange.length - 1], currentRange[0]])\n .clamp(true);\n scales.push(interpScale);\n }\n lastScale = _scale;\n }\n\n const scale: CompositeColumnScale = (age) => {\n for (const s of scales) {\n const domain = s.domain();\n if (\n domain[0] < domain[domain.length - 1] &&\n age >= domain[0] &&\n age <= domain[domain.length - 1]\n ) {\n // Age axes\n return s(age);\n } else if (age <= domain[0] && age >= domain[domain.length - 1]) {\n // Normal axes like height\n return s(age);\n }\n }\n return null;\n };\n\n scale.copy = () => {\n return createCompositeScale(sections, interpolateUnconformities);\n };\n\n scale.domain = () => {\n /** Return the domain of the scale */\n const vals = sections.flatMap((d) => d.scaleInfo.domain);\n if (vals[0] < vals[vals.length - 1]) {\n // age axes\n return [Math.min(...vals), Math.max(...vals)];\n }\n // Normal axes like height\n return [Math.max(...vals), Math.min(...vals)];\n };\n\n scale.invert = (pixelHeight) => {\n /** Invert the scale to get the age at a given pixel height */\n // Iterate through the sections to find the correct one\n for (const scale of scales) {\n const range = scale.range();\n if (\n pixelHeight > Math.min(...range) &&\n pixelHeight <= Math.max(...range)\n ) {\n //console.log(\"Inverting scale at pixel height\", pixelHeight);\n return scale.invert(pixelHeight);\n }\n }\n return null;\n };\n\n scale.clamp = (clamp: boolean) => {\n /** Clamp all constituent scales */\n for (const s of scales) {\n s.clamp(clamp);\n }\n };\n\n return scale;\n}\n\n/** Collapse sections separated by unconformities that are smaller than a given pixel height. */\nexport function collapseUnconformitiesByPixelHeight<T extends UnitLong>(\n sections: SectionInfoWithScale<T>[],\n threshold: number,\n opts: ColumnScaleOptions,\n): SectionInfoWithScale<T>[] {\n const newSections = [];\n let currentSection: SectionInfoWithScale<T> | null = null;\n for (const nextSection of sections) {\n if (currentSection == null) {\n currentSection = nextSection;\n continue;\n }\n let heights: [number, number];\n let pxHeights: [number, number];\n if (opts.axisType !== ColumnAxisType.AGE) {\n heights = [nextSection.t_pos, currentSection.b_pos];\n } else {\n heights = [nextSection.t_age, currentSection.b_age];\n }\n\n const _diff = (vals: number[]) => {\n return Math.abs(vals[0] - vals[1]);\n };\n\n pxHeights = [\n _diff(heights.map(nextSection.scaleInfo.scale)),\n _diff(heights.map(currentSection.scaleInfo.scale)),\n ];\n\n const pxHeight = Math.min(...pxHeights);\n\n if (pxHeight < threshold) {\n let t_pos: number;\n let b_pos: number;\n if (\n opts.axisType == ColumnAxisType.AGE ||\n opts.axisType == ColumnAxisType.DEPTH\n ) {\n t_pos = Math.min(currentSection.t_pos, nextSection.t_pos);\n b_pos = Math.max(currentSection.b_pos, nextSection.b_pos);\n } else {\n t_pos = Math.max(currentSection.t_pos, nextSection.t_pos);\n b_pos = Math.min(currentSection.b_pos, nextSection.b_pos);\n }\n\n // We need to merge the sections\n const compositeSection0: SectionInfo<T> = {\n units: [...currentSection.units, ...nextSection.units],\n section_id: [\n ...ensureArray(currentSection.section_id),\n ...ensureArray(nextSection.section_id),\n ],\n t_age: Math.min(currentSection.t_age, nextSection.t_age),\n b_age: Math.max(currentSection.b_age, nextSection.b_age),\n t_pos,\n b_pos,\n };\n\n currentSection = addScaleToSection(compositeSection0, opts);\n } else {\n // We need to keep the section\n newSections.push(currentSection);\n currentSection = nextSection;\n }\n }\n if (currentSection != null) {\n newSections.push(currentSection);\n }\n\n return newSections;\n}\n"],"names":["ColumnAxisType","HybridScaleType","buildHybridScale","agesOverlap","scaleLinear","getUnitHeightRange","scale","ensureArray"],"mappings":"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"}
1
+ {"version":3,"file":"composite-scale.cjs","sources":["../../src/prepare-units/composite-scale.ts"],"sourcesContent":["import { ColumnAxisType } from \"@macrostrat/column-components\";\nimport { agesOverlap, ensureArray, getUnitHeightRange } from \"./utils\";\nimport { ScaleContinuousNumeric, scaleLinear } from \"d3-scale\";\nimport { UnitLong } from \"@macrostrat/api-types\";\nimport { buildHybridScale } from \"./dynamic-scales\";\nimport { sectionDensity } from \"./density\";\nimport { ExtUnit, HybridScaleType, SectionInfo } from \"./types\";\nimport type {\n ColumnScaleOptions,\n CompositeColumnData,\n CompositeScaleData,\n PackageLayoutData,\n PackageScaleInfo,\n PackageScaleLayoutData,\n SectionScaleOptions,\n} from \"./types\";\n\n// Composite scale information augmented with units in each package\n\nexport function buildCompositeScaleInfo(\n inputScales: PackageScaleInfo[],\n unconformityHeight: number,\n): CompositeScaleData {\n /** Finalize the heights of sections, including the heights of unconformities\n * between them.\n */\n\n let totalHeight = unconformityHeight / 2;\n let lastSectionTopHeight = 0;\n\n const packages2: PackageScaleLayoutData[] = [];\n for (let i = 0; i < inputScales.length; i++) {\n const group = inputScales[i];\n const { domain, scale } = group;\n const [b_age, t_age] = domain;\n // Positionally stable key: packages are always emitted in age order, so the\n // index is a stable identity. An age-based key (`package-${b_age}-${t_age}`)\n // changes every frame while the age window animates, forcing every package,\n // column, and timescale slice in the correlation chart to remount each frame.\n const key = `package-${i}`;\n\n packages2.push({\n ...createPackageScale(group, totalHeight),\n key,\n offset: totalHeight,\n // Unconformity height above this particular section\n paddingTop: totalHeight - lastSectionTopHeight,\n });\n\n const pixelHeight = Math.abs(scale(b_age) - scale(t_age));\n lastSectionTopHeight = totalHeight + pixelHeight;\n totalHeight = lastSectionTopHeight + unconformityHeight;\n }\n totalHeight += unconformityHeight / 2;\n return {\n totalHeight,\n sections: packages2,\n };\n}\n\nexport function finalizeSectionHeights<T extends UnitLong>(\n sections: SectionInfoWithScale<T>[],\n unconformityHeight: number,\n): CompositeColumnData<T> {\n /** Finalize the heights of sections, including the heights of unconformities\n * between them.\n */\n\n const sectionScales = sections.map((d) => d.scaleInfo);\n\n const { totalHeight, sections: packages } = buildCompositeScaleInfo(\n sectionScales,\n unconformityHeight,\n );\n\n // This could perhaps be simplified.\n const sections1: PackageLayoutData<T>[] = [];\n for (const i in sections) {\n const group = sections[i];\n const scaleInfo = packages[i];\n sections1.push({\n ...group,\n key: scaleInfo.key,\n scaleInfo,\n });\n }\n return {\n totalHeight,\n sections: sections1,\n };\n}\n\nexport interface SectionInfoWithScale<\n T extends UnitLong = ExtUnit,\n> extends SectionInfo<T> {\n scaleInfo: PackageScaleInfo;\n}\n\nexport function computeSectionHeights<T extends UnitLong>(\n sections: SectionInfo<T>[],\n opts: ColumnScaleOptions,\n): SectionInfoWithScale<T>[] {\n return sections.map((group) => {\n return addScaleToSection<T>(group, opts);\n });\n}\n\nfunction addScaleToSection<T extends UnitLong = ExtUnit>(\n group: SectionInfo<T>,\n opts: ColumnScaleOptions,\n): SectionInfoWithScale<T> {\n const { t_age, b_age, t_pos, b_pos, units } = group;\n let _range = null;\n // if t_age and b_age are set for a group, use them to define the range...\n if (opts.axisType == ColumnAxisType.AGE) {\n _range = [b_age, t_age];\n } else {\n _range = [b_pos, t_pos];\n }\n\n const scaleInfo = buildSectionScale<T>(units, {\n ...opts,\n domain: _range,\n sectionID: group.section_id,\n });\n\n return {\n ...group,\n scaleInfo,\n };\n}\n\nfunction buildSectionScale<T extends UnitLong>(\n data: T[],\n opts: SectionScaleOptions,\n): PackageScaleInfo {\n const {\n targetUnitHeight = 20,\n axisType,\n visibleWindow,\n scale,\n hybridScale,\n sectionID,\n } = opts;\n const domain = opts.domain ?? findSectionHeightRange(data, axisType);\n\n const dAge = Math.abs(domain[0] - domain[1]);\n\n let _pixelScale: any = opts.pixelScale;\n let pixelHeight: number;\n\n if (hybridScale != null) {\n /** In an equidistant surfaces scale, we want to determine the heights of surfaces\n * and then distribute units evenly between them.\n * This is somewhat like an ordinal scale\n */\n if (hybridScale.type === HybridScaleType.EquidistantSurfaces) {\n // Pixels between one surface and the next: neither axis's units\n _pixelScale ??= targetUnitHeight;\n } else {\n // An approximate-height column is drawn from measured thickness, so it\n // is metres per pixel whatever the axis is labeled in\n _pixelScale = opts.pixelsPerMeter ?? _pixelScale;\n }\n\n return buildHybridScale(hybridScale, data, domain, {\n pixelOffset: 0,\n pixelScale: _pixelScale,\n });\n }\n\n if (scale == null) {\n /** Every rule that sets a section's height resolves to one density (see\n * `./density`). Sections reach here at their *full* extent — the rendered\n * window is applied afterwards, by `trimSectionsToWindow` — so a floor on\n * the section's height only ever inflates a genuinely small section, which\n * is what it's for, and never a sliver the window happens to cut. */\n _pixelScale = sectionDensity(opts)({\n extent: dAge,\n unitExtents: visibleUnitExtents(data, axisType, visibleWindow),\n units: data,\n sectionID,\n axisType,\n });\n pixelHeight = dAge * _pixelScale;\n } else {\n // If a scale is provided, use it to compute pixel height\n pixelHeight = Math.abs(scale(domain[0]) - scale(domain[1]));\n }\n\n return createPackageScale(\n { scale, domain, pixelHeight, pixelScale: _pixelScale },\n 0,\n );\n}\n\n/**\n * The density each section will actually render at once the window is applied.\n *\n * A section the window cuts short is still being *looked at*, so\n * `minSectionHeight` applies to what remains — it's honored by expanding the\n * section's scale, not by showing more of it. Resolving this **before** padding\n * matters: padding is a pixel margin measured at the density it will render at,\n * and a section stretched to the floor afterwards would multiply the margin\n * along with everything else (a 20 px margin in a band stretched 4× renders at\n * 80 px, and asymmetrically so whenever one side is truncated).\n *\n * Sections the window leaves whole keep their laid-out density — they already\n * met the floor when they were sized.\n */\nexport function resolveWindowScales<T extends UnitLong>(\n sections: SectionInfoWithScale<T>[],\n focalWindow: [number, number],\n floor: number,\n): Map<SectionInfoWithScale<T>, number> {\n const [b_focal, t_focal] = focalWindow;\n const scales = new Map<SectionInfoWithScale<T>, number>();\n\n for (const section of sections) {\n const t_age = Math.max(section.t_age, t_focal);\n const b_age = Math.min(section.b_age, b_focal);\n // Sections outside the focal window are visible only as padding; they keep\n // their own density and render exactly the pixels asked for.\n if (b_age <= t_age) continue;\n\n const natural = section.scaleInfo.pixelScale;\n if (!(natural > 0)) continue;\n\n const isClipped = b_age < section.b_age || t_age > section.t_age;\n const needed = isClipped && floor > 0 ? floor / (b_age - t_age) : 0;\n scales.set(section, Math.max(natural, needed));\n }\n return scales;\n}\n\n/**\n * Expand an age window outward by `padding` **pixels** of column, spending the\n * budget against sections that are already laid out at full extent, at the\n * densities they will finally render at (`scaleFor`).\n *\n * Because the layout is done, this is exact rather than predicted: each section\n * contributes its own real pixel height. Padding collapses when the column runs\n * out on that side.\n *\n * Unconformities between sections cost nothing. They render at a fixed height\n * that's typically larger than the padding itself (30–60 px), so charging them\n * would mean padding could never reach across a gap — which is the case it\n * exists for: revealing the neighboring section keeps its timescale intervals\n * navigable. The padding is a budget of neighboring *content*.\n */\nexport function padWindowByPixels<T extends UnitLong>(\n sections: SectionInfoWithScale<T>[],\n window: [number, number],\n padding: number,\n scaleFor: (section: SectionInfoWithScale<T>) => number,\n): [number, number] {\n if (!(padding > 0) || sections.length === 0) return window;\n const [b_age, t_age] = window;\n return [\n spendOutward(sections, b_age, 1, padding, scaleFor),\n spendOutward(sections, t_age, -1, padding, scaleFor),\n ];\n}\n\n/** Walk out from one edge of the window through laid-out sections, spending a\n * pixel budget, and return the age it lands on. `direction` is +1 toward older\n * ages, -1 toward younger. */\nfunction spendOutward<T extends UnitLong>(\n sections: SectionInfoWithScale<T>[],\n edge: number,\n direction: 1 | -1,\n padding: number,\n scaleFor: (section: SectionInfoWithScale<T>) => number,\n): number {\n // Sections ordered outward from the edge, keeping only what lies beyond it.\n const beyond = sections\n .filter(\n (s) => direction * (s.b_age - edge) > 0 || direction * (s.t_age - edge) > 0,\n )\n .sort((a, b) => direction * (nearEdge(a, direction) - nearEdge(b, direction)));\n\n let bound = edge;\n let budget = padding;\n\n for (const section of beyond) {\n const pixelScale = scaleFor(section);\n if (!(pixelScale > 0)) continue;\n\n const from =\n direction > 0\n ? Math.max(section.t_age, bound)\n : Math.min(section.b_age, bound);\n const available = direction > 0 ? section.b_age - from : from - section.t_age;\n if (available <= 0) continue;\n\n const cost = available * pixelScale;\n if (budget <= cost) return from + direction * (budget / pixelScale);\n\n bound = direction > 0 ? section.b_age : section.t_age;\n budget -= cost;\n }\n return bound;\n}\n\n/** The age at which a section starts, measured from the window edge outward. */\nfunction nearEdge<T extends UnitLong>(\n section: SectionInfoWithScale<T>,\n direction: 1 | -1,\n): number {\n return direction > 0 ? section.t_age : section.b_age;\n}\n\n/**\n * Clip laid-out sections to the rendered window — the last step of layout, so\n * that every section has already been sized (and floored) at its full extent.\n * Each section renders at the density `scaleFor` resolved for it, which is what\n * keeps a padding margin the same number of pixels as the band it borders is\n * stretched to meet `minSectionHeight`.\n */\nexport function trimSectionsToWindow<T extends UnitLong>(\n sections: SectionInfoWithScale<T>[],\n window: [number, number],\n scaleFor: (section: SectionInfoWithScale<T>) => number,\n): SectionInfoWithScale<T>[] {\n const [b_win, t_win] = window;\n const out: SectionInfoWithScale<T>[] = [];\n\n for (const section of sections) {\n const t_age = Math.max(section.t_age, t_win);\n const b_age = Math.min(section.b_age, b_win);\n if (b_age <= t_age) continue;\n\n const pixelScale = scaleFor(section);\n if (\n b_age === section.b_age &&\n t_age === section.t_age &&\n pixelScale === section.scaleInfo.pixelScale\n ) {\n out.push(section);\n continue;\n }\n\n out.push({\n ...section,\n t_age,\n b_age,\n units: section.units.filter((u) => agesOverlap(u, { t_age, b_age })),\n scaleInfo: createPackageScale({\n domain: [b_age, t_age],\n pixelScale,\n pixelHeight: (b_age - t_age) * pixelScale,\n scale: null,\n }),\n });\n }\n return out;\n}\n\nexport function createPackageScale(\n def: PackageScaleInfo,\n offset: number = 0,\n): PackageScaleInfo {\n /** Build a section scale */\n // Domain should be oriented from bottom to top, but scale is oriented from top to bottom\n const { domain, pixelScale, pixelHeight, scale, heightScale } = def;\n\n if (scale == null && pixelScale == null) {\n throw new Error(\"Either scale or pixelScale must be provided\");\n }\n\n let _scale: ScaleContinuousNumeric<number, number>;\n if (scale == null) {\n _scale = scaleLinear()\n .domain([domain[1], domain[0]])\n .range([offset, pixelHeight + offset]);\n } else {\n const domain0 = scale.domain();\n const range0 = scale.range();\n _scale = scale\n .copy()\n .domain(domain0)\n .range(range0.map((d) => d + offset));\n }\n\n _scale.clamp();\n\n let _heightScale = null;\n if (heightScale != null) {\n // Adjust height scale as well\n const range0 = scale.range();\n _heightScale = heightScale.copy().range(range0.map((d) => d + offset));\n _heightScale.clamp();\n }\n\n return {\n domain,\n pixelScale,\n pixelHeight,\n scale: _scale,\n // Internal details for hybrid scales. TODO: improve this\n heightScale: _heightScale,\n };\n}\n\nfunction findSectionHeightRange(\n data: UnitLong[],\n axisType: ColumnAxisType,\n): [number, number] {\n if (axisType == null) {\n throw new Error(\"Axis type is not set\");\n }\n if (axisType === ColumnAxisType.AGE) {\n const t_age = Math.min(...data.map((d) => d.t_age));\n const b_age = Math.max(...data.map((d) => d.b_age));\n return [b_age, t_age];\n } else if (\n axisType == ColumnAxisType.DEPTH ||\n axisType == ColumnAxisType.ORDINAL\n ) {\n const t_pos = Math.min(...data.map((d) => d.t_pos));\n const b_pos = Math.max(...data.map((d) => d.b_pos));\n return [b_pos, t_pos];\n } else if (axisType == ColumnAxisType.HEIGHT) {\n const t_pos = Math.max(...data.map((d) => d.t_pos));\n const b_pos = Math.min(...data.map((d) => d.b_pos));\n return [b_pos, t_pos];\n }\n}\n\nfunction visibleUnitExtents(\n data: UnitLong[],\n axisType: ColumnAxisType,\n visibleWindow?: [number, number] | null,\n): number[] {\n /** The durations of the units a density rule gets to size.\n *\n * Measured over what the render window actually *shows* — units outside it\n * are ignored and a unit it cuts through counts only for its visible part.\n * That's what makes `targetUnitHeight` mean the same thing at every zoom\n * depth: the units on screen are drawn at the target height whether they're\n * whole or slivers of something much longer.\n *\n * A section the window doesn't reach has nothing visible to measure, so it\n * falls back to its own units at full duration — it's drawn at its own scale.\n * (Deriving it from the padded window instead would be circular: padding is\n * measured in pixels, which is what we're trying to establish.)\n */\n const durations = (clip: boolean) =>\n data\n .map((d) => {\n const [b_pos, t_pos] = getUnitHeightRange(d, axisType);\n if (!clip) return Math.abs(b_pos - t_pos);\n const [b_win, t_win] = visibleWindow;\n return Math.max(\n 0,\n Math.min(b_pos, b_win) - Math.max(t_pos, t_win),\n );\n })\n .filter((d) => d > 0);\n\n const useWindow = visibleWindow != null && axisType === ColumnAxisType.AGE;\n let heights = useWindow ? durations(true) : [];\n if (heights.length === 0) heights = durations(false);\n return heights;\n}\n\nexport interface CompositeColumnScale {\n (age: number): number | null;\n copy(): CompositeColumnScale;\n domain(): [number, number];\n invert(pixelHeight: number): number | null;\n clamp(clamp: boolean): void;\n}\n\nexport function createCompositeScale(\n sections: PackageLayoutData[],\n interpolateUnconformities: boolean = false,\n): CompositeColumnScale {\n /** Create a scale that works across multiple packages */\n\n const scales: ScaleContinuousNumeric<number, number>[] = [];\n\n let lastScale: ScaleContinuousNumeric<number, number> | null = null;\n for (const section of sections) {\n const _scale = section.scaleInfo.scale.copy().clamp(true);\n scales.push(_scale);\n if (lastScale != null && interpolateUnconformities) {\n // Add a new scale that interpolates between lastScale and _scale\n const lastDomain = lastScale.domain();\n const lastRange = lastScale.range();\n const currentDomain = _scale.domain();\n const currentRange = _scale.range();\n\n const interpScale = scaleLinear()\n .domain([lastDomain[lastDomain.length - 1], currentDomain[0]])\n .range([lastRange[lastRange.length - 1], currentRange[0]])\n .clamp(true);\n scales.push(interpScale);\n }\n lastScale = _scale;\n }\n\n const scale: CompositeColumnScale = (age) => {\n for (const s of scales) {\n const domain = s.domain();\n if (\n domain[0] < domain[domain.length - 1] &&\n age >= domain[0] &&\n age <= domain[domain.length - 1]\n ) {\n // Age axes\n return s(age);\n } else if (age <= domain[0] && age >= domain[domain.length - 1]) {\n // Normal axes like height\n return s(age);\n }\n }\n return null;\n };\n\n scale.copy = () => {\n return createCompositeScale(sections, interpolateUnconformities);\n };\n\n scale.domain = () => {\n /** Return the domain of the scale */\n const vals = sections.flatMap((d) => d.scaleInfo.domain);\n if (vals[0] < vals[vals.length - 1]) {\n // age axes\n return [Math.min(...vals), Math.max(...vals)];\n }\n // Normal axes like height\n return [Math.max(...vals), Math.min(...vals)];\n };\n\n scale.invert = (pixelHeight) => {\n /** Invert the scale to get the age at a given pixel height */\n // Iterate through the sections to find the correct one\n for (const scale of scales) {\n const range = scale.range();\n if (\n pixelHeight > Math.min(...range) &&\n pixelHeight <= Math.max(...range)\n ) {\n //console.log(\"Inverting scale at pixel height\", pixelHeight);\n return scale.invert(pixelHeight);\n }\n }\n return null;\n };\n\n scale.clamp = (clamp: boolean) => {\n /** Clamp all constituent scales */\n for (const s of scales) {\n s.clamp(clamp);\n }\n };\n\n return scale;\n}\n\n/** Collapse sections separated by unconformities that are smaller than a given pixel height. */\nexport function collapseUnconformitiesByPixelHeight<T extends UnitLong>(\n sections: SectionInfoWithScale<T>[],\n threshold: number,\n opts: ColumnScaleOptions,\n): SectionInfoWithScale<T>[] {\n const newSections = [];\n let currentSection: SectionInfoWithScale<T> | null = null;\n for (const nextSection of sections) {\n if (currentSection == null) {\n currentSection = nextSection;\n continue;\n }\n let heights: [number, number];\n let pxHeights: [number, number];\n if (opts.axisType !== ColumnAxisType.AGE) {\n heights = [nextSection.t_pos, currentSection.b_pos];\n } else {\n heights = [nextSection.t_age, currentSection.b_age];\n }\n\n const _diff = (vals: number[]) => {\n return Math.abs(vals[0] - vals[1]);\n };\n\n pxHeights = [\n _diff(heights.map(nextSection.scaleInfo.scale)),\n _diff(heights.map(currentSection.scaleInfo.scale)),\n ];\n\n /** The gap has no density of its own — it falls between two sections that\n * may be drawn at very different ones — so it is judged at the finer of\n * the two. Taking the smaller estimate let a sparse neighbor speak for a\n * gap the other neighbor would have drawn many times larger: a 16 Myr\n * hiatus in column 22 read as 26px against one section and 166px against\n * the other, and collapsed. A gap is only worth hiding when neither scale\n * would give it more room than the break that replaces it. */\n const pxHeight = Math.max(...pxHeights);\n\n if (pxHeight < threshold) {\n let t_pos: number;\n let b_pos: number;\n if (\n opts.axisType == ColumnAxisType.AGE ||\n opts.axisType == ColumnAxisType.DEPTH\n ) {\n t_pos = Math.min(currentSection.t_pos, nextSection.t_pos);\n b_pos = Math.max(currentSection.b_pos, nextSection.b_pos);\n } else {\n t_pos = Math.max(currentSection.t_pos, nextSection.t_pos);\n b_pos = Math.min(currentSection.b_pos, nextSection.b_pos);\n }\n\n // We need to merge the sections\n const compositeSection0: SectionInfo<T> = {\n units: [...currentSection.units, ...nextSection.units],\n section_id: [\n ...ensureArray(currentSection.section_id),\n ...ensureArray(nextSection.section_id),\n ],\n t_age: Math.min(currentSection.t_age, nextSection.t_age),\n b_age: Math.max(currentSection.b_age, nextSection.b_age),\n t_pos,\n b_pos,\n };\n\n currentSection = addScaleToSection(compositeSection0, opts);\n } else {\n // We need to keep the section\n newSections.push(currentSection);\n currentSection = nextSection;\n }\n }\n if (currentSection != null) {\n newSections.push(currentSection);\n }\n\n return newSections;\n}\n"],"names":["ColumnAxisType","HybridScaleType","buildHybridScale","sectionDensity","agesOverlap","scaleLinear","getUnitHeightRange","scale","ensureArray"],"mappings":"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"}
@@ -2,6 +2,7 @@ import { ColumnAxisType } from "@macrostrat/column-components";
2
2
  import { ensureArray, agesOverlap, getUnitHeightRange } from "./utils.js";
3
3
  import { scaleLinear } from "d3-scale";
4
4
  import { buildHybridScale } from "./dynamic-scales.js";
5
+ import { sectionDensity } from "./density.js";
5
6
  import { HybridScaleType } from "./types.js";
6
7
  function buildCompositeScaleInfo(inputScales, unconformityHeight) {
7
8
  let totalHeight = unconformityHeight / 2;
@@ -65,7 +66,8 @@ function addScaleToSection(group, opts) {
65
66
  }
66
67
  const scaleInfo = buildSectionScale(units, {
67
68
  ...opts,
68
- domain: _range
69
+ domain: _range,
70
+ sectionID: group.section_id
69
71
  });
70
72
  return {
71
73
  ...group,
@@ -75,12 +77,11 @@ function addScaleToSection(group, opts) {
75
77
  function buildSectionScale(data, opts) {
76
78
  const {
77
79
  targetUnitHeight = 20,
78
- minPixelScale = 0.2,
79
80
  axisType,
80
- minSectionHeight,
81
81
  visibleWindow,
82
82
  scale,
83
- hybridScale
83
+ hybridScale,
84
+ sectionID
84
85
  } = opts;
85
86
  const domain = opts.domain ?? findSectionHeightRange(data, axisType);
86
87
  const dAge = Math.abs(domain[0] - domain[1]);
@@ -89,6 +90,8 @@ function buildSectionScale(data, opts) {
89
90
  if (hybridScale != null) {
90
91
  if (hybridScale.type === HybridScaleType.EquidistantSurfaces) {
91
92
  _pixelScale ??= targetUnitHeight;
93
+ } else {
94
+ _pixelScale = opts.pixelsPerMeter ?? _pixelScale;
92
95
  }
93
96
  return buildHybridScale(hybridScale, data, domain, {
94
97
  pixelOffset: 0,
@@ -96,14 +99,14 @@ function buildSectionScale(data, opts) {
96
99
  });
97
100
  }
98
101
  if (scale == null) {
99
- if (_pixelScale == null) {
100
- const avgAgeRange = findAverageUnitHeight(data, axisType, visibleWindow);
101
- _pixelScale = Math.max(targetUnitHeight / avgAgeRange, minPixelScale);
102
- }
103
- let height = dAge * _pixelScale;
104
- const _minSectionHeight = minSectionHeight ?? targetUnitHeight ?? 0;
105
- pixelHeight = Math.max(height, _minSectionHeight);
106
- _pixelScale = pixelHeight / dAge;
102
+ _pixelScale = sectionDensity(opts)({
103
+ extent: dAge,
104
+ unitExtents: visibleUnitExtents(data, axisType, visibleWindow),
105
+ units: data,
106
+ sectionID,
107
+ axisType
108
+ });
109
+ pixelHeight = dAge * _pixelScale;
107
110
  } else {
108
111
  pixelHeight = Math.abs(scale(domain[0]) - scale(domain[1]));
109
112
  }
@@ -231,7 +234,7 @@ function findSectionHeightRange(data, axisType) {
231
234
  return [b_pos, t_pos];
232
235
  }
233
236
  }
234
- function findAverageUnitHeight(data, axisType, visibleWindow) {
237
+ function visibleUnitExtents(data, axisType, visibleWindow) {
235
238
  const durations = (clip) => data.map((d) => {
236
239
  const [b_pos, t_pos] = getUnitHeightRange(d, axisType);
237
240
  if (!clip) return Math.abs(b_pos - t_pos);
@@ -244,8 +247,7 @@ function findAverageUnitHeight(data, axisType, visibleWindow) {
244
247
  const useWindow = visibleWindow != null && axisType === ColumnAxisType.AGE;
245
248
  let heights = useWindow ? durations(true) : [];
246
249
  if (heights.length === 0) heights = durations(false);
247
- if (heights.length === 0) return 1;
248
- return heights.reduce((a, b) => a + b, 0) / heights.length;
250
+ return heights;
249
251
  }
250
252
  function createCompositeScale(sections, interpolateUnconformities = false) {
251
253
  const scales = [];
@@ -322,7 +324,7 @@ function collapseUnconformitiesByPixelHeight(sections, threshold, opts) {
322
324
  _diff(heights.map(nextSection.scaleInfo.scale)),
323
325
  _diff(heights.map(currentSection.scaleInfo.scale))
324
326
  ];
325
- const pxHeight = Math.min(...pxHeights);
327
+ const pxHeight = Math.max(...pxHeights);
326
328
  if (pxHeight < threshold) {
327
329
  let t_pos;
328
330
  let b_pos;