@macrostrat/column-views 3.8.0 → 3.9.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 (68) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/dist/animated-age-window.cjs +51 -0
  3. package/dist/animated-age-window.cjs.map +1 -0
  4. package/dist/animated-age-window.d.ts +51 -0
  5. package/dist/animated-age-window.js +51 -0
  6. package/dist/animated-age-window.js.map +1 -0
  7. package/dist/column.cjs +11 -1
  8. package/dist/column.cjs.map +1 -1
  9. package/dist/column.d.ts +11 -0
  10. package/dist/column.js +11 -1
  11. package/dist/column.js.map +1 -1
  12. package/dist/correlation-chart/main.cjs +24 -12
  13. package/dist/correlation-chart/main.cjs.map +1 -1
  14. package/dist/correlation-chart/main.d.ts +8 -1
  15. package/dist/correlation-chart/main.js +24 -12
  16. package/dist/correlation-chart/main.js.map +1 -1
  17. package/dist/data-provider/store.cjs +17 -4
  18. package/dist/data-provider/store.cjs.map +1 -1
  19. package/dist/data-provider/store.d.ts +6 -1
  20. package/dist/data-provider/store.js +17 -4
  21. package/dist/data-provider/store.js.map +1 -1
  22. package/dist/index.cjs +2 -0
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.js +2 -0
  26. package/dist/index.js.map +1 -1
  27. package/dist/prepare-units/composite-scale.cjs +94 -8
  28. package/dist/prepare-units/composite-scale.cjs.map +1 -1
  29. package/dist/prepare-units/composite-scale.d.ts +39 -0
  30. package/dist/prepare-units/composite-scale.js +96 -10
  31. package/dist/prepare-units/composite-scale.js.map +1 -1
  32. package/dist/prepare-units/helpers.cjs +0 -1
  33. package/dist/prepare-units/helpers.cjs.map +1 -1
  34. package/dist/prepare-units/helpers.js +0 -1
  35. package/dist/prepare-units/helpers.js.map +1 -1
  36. package/dist/prepare-units/index.cjs +33 -12
  37. package/dist/prepare-units/index.cjs.map +1 -1
  38. package/dist/prepare-units/index.js +34 -13
  39. package/dist/prepare-units/index.js.map +1 -1
  40. package/dist/prepare-units/types.cjs.map +1 -1
  41. package/dist/prepare-units/types.d.ts +13 -0
  42. package/dist/prepare-units/types.js.map +1 -1
  43. package/dist/section.cjs +4 -2
  44. package/dist/section.cjs.map +1 -1
  45. package/dist/section.d.ts +4 -1
  46. package/dist/section.js +4 -2
  47. package/dist/section.js.map +1 -1
  48. package/dist/units/boxes.cjs +19 -36
  49. package/dist/units/boxes.cjs.map +1 -1
  50. package/dist/units/boxes.js +21 -38
  51. package/dist/units/boxes.js.map +1 -1
  52. package/dist/units/composite.cjs +12 -12
  53. package/dist/units/composite.cjs.map +1 -1
  54. package/dist/units/composite.js +12 -12
  55. package/dist/units/composite.js.map +1 -1
  56. package/package.json +6 -6
  57. package/src/animated-age-window.ts +120 -0
  58. package/src/column.ts +24 -0
  59. package/src/correlation-chart/main.ts +30 -8
  60. package/src/data-provider/store.ts +21 -2
  61. package/src/index.ts +1 -0
  62. package/src/prepare-units/composite-scale.ts +210 -10
  63. package/src/prepare-units/helpers.ts +0 -2
  64. package/src/prepare-units/index.ts +68 -14
  65. package/src/prepare-units/types.ts +13 -0
  66. package/src/section.ts +6 -0
  67. package/src/units/boxes.ts +30 -52
  68. package/src/units/composite.ts +21 -12
package/dist/index.cjs CHANGED
@@ -3,6 +3,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const index = require("./units/index.cjs");
4
4
  const ageAxis = require("./age-axis.cjs");
5
5
  const index$6 = require("./prepare-units/index.cjs");
6
+ const animatedAgeWindow = require("./animated-age-window.cjs");
6
7
  const column = require("./column.cjs");
7
8
  const ageModelOverlay = require("./age-model-overlay.cjs");
8
9
  const notes = require("./notes.cjs");
@@ -53,6 +54,7 @@ exports.CompositeAgeAxisCore = ageAxis.CompositeAgeAxisCore;
53
54
  exports.VerticalAxisLabel = ageAxis.VerticalAxisLabel;
54
55
  exports.prepareColumnUnits = index$6.prepareColumnUnits;
55
56
  exports.usePreparedColumnUnits = index$6.usePreparedColumnUnits;
57
+ exports.useAnimatedAgeWindow = animatedAgeWindow.useAnimatedAgeWindow;
56
58
  exports.Column = column.Column;
57
59
  exports.ColumnBasicInfo = column.ColumnBasicInfo;
58
60
  exports.ColumnContainer = column.ColumnContainer;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export * from './units';
2
2
  export * from './data-provider';
3
3
  export * from './age-axis';
4
4
  export * from './prepare-units';
5
+ export * from './animated-age-window';
5
6
  export * from './column';
6
7
  export * from './unit-details';
7
8
  export * from './maps';
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { BasicUnitComponent, ColoredUnitComponent, EnvironmentColoredUnitComponent, SimpleUnitsColumn, UnitComponent, UnitsColumn, useUnitColor, useUnitColorByEnvironment } from "./units/index.js";
2
2
  import { AgeCursor, ApproximateHeightAxis, CompositeAgeAxis, CompositeAgeAxisCore, VerticalAxisLabel } from "./age-axis.js";
3
3
  import { prepareColumnUnits, usePreparedColumnUnits } from "./prepare-units/index.js";
4
+ import { useAnimatedAgeWindow } from "./animated-age-window.js";
4
5
  import { Column, ColumnBasicInfo, ColumnContainer } from "./column.js";
5
6
  import { BoundaryAgeModelOverlay, ComputedSurfacesOverlay } from "./age-model-overlay.js";
6
7
  import { ColumnNotes } from "./notes.js";
@@ -147,6 +148,7 @@ export {
147
148
  standardizeMeasurementHeight,
148
149
  stringifyLine,
149
150
  unitsOverlap,
151
+ useAnimatedAgeWindow,
150
152
  useAtomOverlayPosition,
151
153
  useColumnMapLink,
152
154
  useColumnNav,
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -9,10 +9,11 @@ function buildCompositeScaleInfo(inputScales, unconformityHeight) {
9
9
  let totalHeight = unconformityHeight / 2;
10
10
  let lastSectionTopHeight = 0;
11
11
  const packages2 = [];
12
- for (const group of inputScales) {
12
+ for (let i = 0; i < inputScales.length; i++) {
13
+ const group = inputScales[i];
13
14
  const { domain, scale } = group;
14
15
  const [b_age, t_age] = domain;
15
- const key = `package-${b_age}-${t_age}`;
16
+ const key = `package-${i}`;
16
17
  packages2.push({
17
18
  ...createPackageScale(group, totalHeight),
18
19
  key,
@@ -79,6 +80,7 @@ function buildSectionScale(data, opts) {
79
80
  minPixelScale = 0.2,
80
81
  axisType,
81
82
  minSectionHeight,
83
+ visibleWindow,
82
84
  scale,
83
85
  hybridScale
84
86
  } = opts;
@@ -97,7 +99,7 @@ function buildSectionScale(data, opts) {
97
99
  }
98
100
  if (scale == null) {
99
101
  if (_pixelScale == null) {
100
- const avgAgeRange = findAverageUnitHeight(data, axisType);
102
+ const avgAgeRange = findAverageUnitHeight(data, axisType, visibleWindow);
101
103
  _pixelScale = Math.max(targetUnitHeight / avgAgeRange, minPixelScale);
102
104
  }
103
105
  let height = dAge * _pixelScale;
@@ -112,6 +114,78 @@ function buildSectionScale(data, opts) {
112
114
  0
113
115
  );
114
116
  }
117
+ function resolveWindowScales(sections, focalWindow, floor) {
118
+ const [b_focal, t_focal] = focalWindow;
119
+ const scales = /* @__PURE__ */ new Map();
120
+ for (const section of sections) {
121
+ const t_age = Math.max(section.t_age, t_focal);
122
+ const b_age = Math.min(section.b_age, b_focal);
123
+ if (b_age <= t_age) continue;
124
+ const natural = section.scaleInfo.pixelScale;
125
+ if (!(natural > 0)) continue;
126
+ const isClipped = b_age < section.b_age || t_age > section.t_age;
127
+ const needed = isClipped && floor > 0 ? floor / (b_age - t_age) : 0;
128
+ scales.set(section, Math.max(natural, needed));
129
+ }
130
+ return scales;
131
+ }
132
+ function padWindowByPixels(sections, window, padding, scaleFor) {
133
+ if (!(padding > 0) || sections.length === 0) return window;
134
+ const [b_age, t_age] = window;
135
+ return [
136
+ spendOutward(sections, b_age, 1, padding, scaleFor),
137
+ spendOutward(sections, t_age, -1, padding, scaleFor)
138
+ ];
139
+ }
140
+ function spendOutward(sections, edge, direction, padding, scaleFor) {
141
+ const beyond = sections.filter(
142
+ (s) => direction * (s.b_age - edge) > 0 || direction * (s.t_age - edge) > 0
143
+ ).sort((a, b) => direction * (nearEdge(a, direction) - nearEdge(b, direction)));
144
+ let bound = edge;
145
+ let budget = padding;
146
+ for (const section of beyond) {
147
+ const pixelScale = scaleFor(section);
148
+ if (!(pixelScale > 0)) continue;
149
+ const from = direction > 0 ? Math.max(section.t_age, bound) : Math.min(section.b_age, bound);
150
+ const available = direction > 0 ? section.b_age - from : from - section.t_age;
151
+ if (available <= 0) continue;
152
+ const cost = available * pixelScale;
153
+ if (budget <= cost) return from + direction * (budget / pixelScale);
154
+ bound = direction > 0 ? section.b_age : section.t_age;
155
+ budget -= cost;
156
+ }
157
+ return bound;
158
+ }
159
+ function nearEdge(section, direction) {
160
+ return direction > 0 ? section.t_age : section.b_age;
161
+ }
162
+ function trimSectionsToWindow(sections, window, scaleFor) {
163
+ const [b_win, t_win] = window;
164
+ const out = [];
165
+ for (const section of sections) {
166
+ const t_age = Math.max(section.t_age, t_win);
167
+ const b_age = Math.min(section.b_age, b_win);
168
+ if (b_age <= t_age) continue;
169
+ const pixelScale = scaleFor(section);
170
+ if (b_age === section.b_age && t_age === section.t_age && pixelScale === section.scaleInfo.pixelScale) {
171
+ out.push(section);
172
+ continue;
173
+ }
174
+ out.push({
175
+ ...section,
176
+ t_age,
177
+ b_age,
178
+ units: section.units.filter((u) => utils.agesOverlap(u, { t_age, b_age })),
179
+ scaleInfo: createPackageScale({
180
+ domain: [b_age, t_age],
181
+ pixelScale,
182
+ pixelHeight: (b_age - t_age) * pixelScale,
183
+ scale: null
184
+ })
185
+ });
186
+ }
187
+ return out;
188
+ }
115
189
  function createPackageScale(def, offset = 0) {
116
190
  const { domain, pixelScale, pixelHeight, scale, heightScale } = def;
117
191
  if (scale == null && pixelScale == null) {
@@ -159,12 +233,21 @@ function findSectionHeightRange(data, axisType) {
159
233
  return [b_pos, t_pos];
160
234
  }
161
235
  }
162
- function findAverageUnitHeight(data, axisType) {
163
- const unitHeights = data.map((d) => {
236
+ function findAverageUnitHeight(data, axisType, visibleWindow) {
237
+ const durations = (clip) => data.map((d) => {
164
238
  const [b_pos, t_pos] = utils.getUnitHeightRange(d, axisType);
165
- return Math.abs(b_pos - t_pos);
166
- });
167
- return unitHeights.reduce((a, b) => a + b, 0) / unitHeights.length;
239
+ if (!clip) return Math.abs(b_pos - t_pos);
240
+ const [b_win, t_win] = visibleWindow;
241
+ return Math.max(
242
+ 0,
243
+ Math.min(b_pos, b_win) - Math.max(t_pos, t_win)
244
+ );
245
+ }).filter((d) => d > 0);
246
+ const useWindow = visibleWindow != null && axisType === columnComponents.ColumnAxisType.AGE;
247
+ let heights = useWindow ? durations(true) : [];
248
+ 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;
168
251
  }
169
252
  function createCompositeScale(sections, interpolateUnconformities = false) {
170
253
  const scales = [];
@@ -280,4 +363,7 @@ exports.computeSectionHeights = computeSectionHeights;
280
363
  exports.createCompositeScale = createCompositeScale;
281
364
  exports.createPackageScale = createPackageScale;
282
365
  exports.finalizeSectionHeights = finalizeSectionHeights;
366
+ exports.padWindowByPixels = padWindowByPixels;
367
+ exports.resolveWindowScales = resolveWindowScales;
368
+ exports.trimSectionsToWindow = trimSectionsToWindow;
283
369
  //# sourceMappingURL=composite-scale.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"composite-scale.cjs","sources":["../../src/prepare-units/composite-scale.ts"],"sourcesContent":["import { ColumnAxisType } from \"@macrostrat/column-components\";\nimport { 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 (const group of inputScales) {\n const { domain, scale } = group;\n const [b_age, t_age] = domain;\n const key = `package-${b_age}-${t_age}`;\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 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);\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 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\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): number {\n const unitHeights = data.map((d) => {\n const [b_pos, t_pos] = getUnitHeightRange(d, axisType);\n return Math.abs(b_pos - t_pos);\n });\n return unitHeights.reduce((a, b) => a + b, 0) / unitHeights.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","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 { 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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"}
@@ -6,6 +6,45 @@ export interface SectionInfoWithScale<T extends UnitLong = ExtUnit> extends Sect
6
6
  scaleInfo: PackageScaleInfo;
7
7
  }
8
8
  export declare function computeSectionHeights<T extends UnitLong>(sections: SectionInfo<T>[], opts: ColumnScaleOptions): SectionInfoWithScale<T>[];
9
+ /**
10
+ * The density each section will actually render at once the window is applied.
11
+ *
12
+ * A section the window cuts short is still being *looked at*, so
13
+ * `minSectionHeight` applies to what remains — it's honored by expanding the
14
+ * section's scale, not by showing more of it. Resolving this **before** padding
15
+ * matters: padding is a pixel margin measured at the density it will render at,
16
+ * and a section stretched to the floor afterwards would multiply the margin
17
+ * along with everything else (a 20 px margin in a band stretched 4× renders at
18
+ * 80 px, and asymmetrically so whenever one side is truncated).
19
+ *
20
+ * Sections the window leaves whole keep their laid-out density — they already
21
+ * met the floor when they were sized.
22
+ */
23
+ export declare function resolveWindowScales<T extends UnitLong>(sections: SectionInfoWithScale<T>[], focalWindow: [number, number], floor: number): Map<SectionInfoWithScale<T>, number>;
24
+ /**
25
+ * Expand an age window outward by `padding` **pixels** of column, spending the
26
+ * budget against sections that are already laid out at full extent, at the
27
+ * densities they will finally render at (`scaleFor`).
28
+ *
29
+ * Because the layout is done, this is exact rather than predicted: each section
30
+ * contributes its own real pixel height. Padding collapses when the column runs
31
+ * out on that side.
32
+ *
33
+ * Unconformities between sections cost nothing. They render at a fixed height
34
+ * that's typically larger than the padding itself (30–60 px), so charging them
35
+ * would mean padding could never reach across a gap — which is the case it
36
+ * exists for: revealing the neighboring section keeps its timescale intervals
37
+ * navigable. The padding is a budget of neighboring *content*.
38
+ */
39
+ export declare function padWindowByPixels<T extends UnitLong>(sections: SectionInfoWithScale<T>[], window: [number, number], padding: number, scaleFor: (section: SectionInfoWithScale<T>) => number): [number, number];
40
+ /**
41
+ * Clip laid-out sections to the rendered window — the last step of layout, so
42
+ * that every section has already been sized (and floored) at its full extent.
43
+ * Each section renders at the density `scaleFor` resolved for it, which is what
44
+ * keeps a padding margin the same number of pixels as the band it borders is
45
+ * stretched to meet `minSectionHeight`.
46
+ */
47
+ export declare function trimSectionsToWindow<T extends UnitLong>(sections: SectionInfoWithScale<T>[], window: [number, number], scaleFor: (section: SectionInfoWithScale<T>) => number): SectionInfoWithScale<T>[];
9
48
  export declare function createPackageScale(def: PackageScaleInfo, offset?: number): PackageScaleInfo;
10
49
  export interface CompositeColumnScale {
11
50
  (age: number): number | null;
@@ -1,5 +1,5 @@
1
1
  import { ColumnAxisType } from "@macrostrat/column-components";
2
- import { ensureArray, getUnitHeightRange } from "./utils.js";
2
+ import { ensureArray, agesOverlap, getUnitHeightRange } from "./utils.js";
3
3
  import { scaleLinear } from "d3-scale";
4
4
  import { buildHybridScale } from "./dynamic-scales.js";
5
5
  import { HybridScaleType } from "./types.js";
@@ -7,10 +7,11 @@ function buildCompositeScaleInfo(inputScales, unconformityHeight) {
7
7
  let totalHeight = unconformityHeight / 2;
8
8
  let lastSectionTopHeight = 0;
9
9
  const packages2 = [];
10
- for (const group of inputScales) {
10
+ for (let i = 0; i < inputScales.length; i++) {
11
+ const group = inputScales[i];
11
12
  const { domain, scale } = group;
12
13
  const [b_age, t_age] = domain;
13
- const key = `package-${b_age}-${t_age}`;
14
+ const key = `package-${i}`;
14
15
  packages2.push({
15
16
  ...createPackageScale(group, totalHeight),
16
17
  key,
@@ -77,6 +78,7 @@ function buildSectionScale(data, opts) {
77
78
  minPixelScale = 0.2,
78
79
  axisType,
79
80
  minSectionHeight,
81
+ visibleWindow,
80
82
  scale,
81
83
  hybridScale
82
84
  } = opts;
@@ -95,7 +97,7 @@ function buildSectionScale(data, opts) {
95
97
  }
96
98
  if (scale == null) {
97
99
  if (_pixelScale == null) {
98
- const avgAgeRange = findAverageUnitHeight(data, axisType);
100
+ const avgAgeRange = findAverageUnitHeight(data, axisType, visibleWindow);
99
101
  _pixelScale = Math.max(targetUnitHeight / avgAgeRange, minPixelScale);
100
102
  }
101
103
  let height = dAge * _pixelScale;
@@ -110,6 +112,78 @@ function buildSectionScale(data, opts) {
110
112
  0
111
113
  );
112
114
  }
115
+ function resolveWindowScales(sections, focalWindow, floor) {
116
+ const [b_focal, t_focal] = focalWindow;
117
+ const scales = /* @__PURE__ */ new Map();
118
+ for (const section of sections) {
119
+ const t_age = Math.max(section.t_age, t_focal);
120
+ const b_age = Math.min(section.b_age, b_focal);
121
+ if (b_age <= t_age) continue;
122
+ const natural = section.scaleInfo.pixelScale;
123
+ if (!(natural > 0)) continue;
124
+ const isClipped = b_age < section.b_age || t_age > section.t_age;
125
+ const needed = isClipped && floor > 0 ? floor / (b_age - t_age) : 0;
126
+ scales.set(section, Math.max(natural, needed));
127
+ }
128
+ return scales;
129
+ }
130
+ function padWindowByPixels(sections, window, padding, scaleFor) {
131
+ if (!(padding > 0) || sections.length === 0) return window;
132
+ const [b_age, t_age] = window;
133
+ return [
134
+ spendOutward(sections, b_age, 1, padding, scaleFor),
135
+ spendOutward(sections, t_age, -1, padding, scaleFor)
136
+ ];
137
+ }
138
+ function spendOutward(sections, edge, direction, padding, scaleFor) {
139
+ const beyond = sections.filter(
140
+ (s) => direction * (s.b_age - edge) > 0 || direction * (s.t_age - edge) > 0
141
+ ).sort((a, b) => direction * (nearEdge(a, direction) - nearEdge(b, direction)));
142
+ let bound = edge;
143
+ let budget = padding;
144
+ for (const section of beyond) {
145
+ const pixelScale = scaleFor(section);
146
+ if (!(pixelScale > 0)) continue;
147
+ const from = direction > 0 ? Math.max(section.t_age, bound) : Math.min(section.b_age, bound);
148
+ const available = direction > 0 ? section.b_age - from : from - section.t_age;
149
+ if (available <= 0) continue;
150
+ const cost = available * pixelScale;
151
+ if (budget <= cost) return from + direction * (budget / pixelScale);
152
+ bound = direction > 0 ? section.b_age : section.t_age;
153
+ budget -= cost;
154
+ }
155
+ return bound;
156
+ }
157
+ function nearEdge(section, direction) {
158
+ return direction > 0 ? section.t_age : section.b_age;
159
+ }
160
+ function trimSectionsToWindow(sections, window, scaleFor) {
161
+ const [b_win, t_win] = window;
162
+ const out = [];
163
+ for (const section of sections) {
164
+ const t_age = Math.max(section.t_age, t_win);
165
+ const b_age = Math.min(section.b_age, b_win);
166
+ if (b_age <= t_age) continue;
167
+ const pixelScale = scaleFor(section);
168
+ if (b_age === section.b_age && t_age === section.t_age && pixelScale === section.scaleInfo.pixelScale) {
169
+ out.push(section);
170
+ continue;
171
+ }
172
+ out.push({
173
+ ...section,
174
+ t_age,
175
+ b_age,
176
+ units: section.units.filter((u) => agesOverlap(u, { t_age, b_age })),
177
+ scaleInfo: createPackageScale({
178
+ domain: [b_age, t_age],
179
+ pixelScale,
180
+ pixelHeight: (b_age - t_age) * pixelScale,
181
+ scale: null
182
+ })
183
+ });
184
+ }
185
+ return out;
186
+ }
113
187
  function createPackageScale(def, offset = 0) {
114
188
  const { domain, pixelScale, pixelHeight, scale, heightScale } = def;
115
189
  if (scale == null && pixelScale == null) {
@@ -157,12 +231,21 @@ function findSectionHeightRange(data, axisType) {
157
231
  return [b_pos, t_pos];
158
232
  }
159
233
  }
160
- function findAverageUnitHeight(data, axisType) {
161
- const unitHeights = data.map((d) => {
234
+ function findAverageUnitHeight(data, axisType, visibleWindow) {
235
+ const durations = (clip) => data.map((d) => {
162
236
  const [b_pos, t_pos] = getUnitHeightRange(d, axisType);
163
- return Math.abs(b_pos - t_pos);
164
- });
165
- return unitHeights.reduce((a, b) => a + b, 0) / unitHeights.length;
237
+ if (!clip) return Math.abs(b_pos - t_pos);
238
+ const [b_win, t_win] = visibleWindow;
239
+ return Math.max(
240
+ 0,
241
+ Math.min(b_pos, b_win) - Math.max(t_pos, t_win)
242
+ );
243
+ }).filter((d) => d > 0);
244
+ const useWindow = visibleWindow != null && axisType === ColumnAxisType.AGE;
245
+ let heights = useWindow ? durations(true) : [];
246
+ 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;
166
249
  }
167
250
  function createCompositeScale(sections, interpolateUnconformities = false) {
168
251
  const scales = [];
@@ -278,6 +361,9 @@ export {
278
361
  computeSectionHeights,
279
362
  createCompositeScale,
280
363
  createPackageScale,
281
- finalizeSectionHeights
364
+ finalizeSectionHeights,
365
+ padWindowByPixels,
366
+ resolveWindowScales,
367
+ trimSectionsToWindow
282
368
  };
283
369
  //# sourceMappingURL=composite-scale.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"composite-scale.js","sources":["../../src/prepare-units/composite-scale.ts"],"sourcesContent":["import { ColumnAxisType } from \"@macrostrat/column-components\";\nimport { 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 (const group of inputScales) {\n const { domain, scale } = group;\n const [b_age, t_age] = domain;\n const key = `package-${b_age}-${t_age}`;\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 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);\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 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\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): number {\n const unitHeights = data.map((d) => {\n const [b_pos, t_pos] = getUnitHeightRange(d, axisType);\n return Math.abs(b_pos - t_pos);\n });\n return unitHeights.reduce((a, b) => a + b, 0) / unitHeights.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":["scale"],"mappings":"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"}
1
+ {"version":3,"file":"composite-scale.js","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":["scale"],"mappings":"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"}