@macrostrat/column-views 3.3.5 → 3.4.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 (62) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/column.cjs +2 -1
  3. package/dist/column.cjs.map +1 -1
  4. package/dist/column.js +2 -1
  5. package/dist/column.js.map +1 -1
  6. package/dist/data-provider/unit-selection.cjs +7 -3
  7. package/dist/data-provider/unit-selection.cjs.map +1 -1
  8. package/dist/data-provider/unit-selection.d.ts +1 -0
  9. package/dist/data-provider/unit-selection.js +7 -3
  10. package/dist/data-provider/unit-selection.js.map +1 -1
  11. package/dist/index.cjs +13 -8
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.js +7 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/prepare-units/helpers.cjs +83 -49
  16. package/dist/prepare-units/helpers.cjs.map +1 -1
  17. package/dist/prepare-units/helpers.d.ts +19 -2
  18. package/dist/prepare-units/helpers.js +84 -50
  19. package/dist/prepare-units/helpers.js.map +1 -1
  20. package/dist/prepare-units/index.cjs.map +1 -1
  21. package/dist/prepare-units/index.js.map +1 -1
  22. package/dist/section.cjs +1 -2
  23. package/dist/section.cjs.map +1 -1
  24. package/dist/section.js +1 -2
  25. package/dist/section.js.map +1 -1
  26. package/dist/unit-details/identifiers.cjs +56 -0
  27. package/dist/unit-details/identifiers.cjs.map +1 -0
  28. package/dist/unit-details/identifiers.d.ts +31 -0
  29. package/dist/unit-details/identifiers.js +54 -0
  30. package/dist/unit-details/identifiers.js.map +1 -0
  31. package/dist/unit-details/index.d.ts +2 -0
  32. package/dist/unit-details/panel.cjs +11 -88
  33. package/dist/unit-details/panel.cjs.map +1 -1
  34. package/dist/unit-details/panel.d.ts +3 -19
  35. package/dist/unit-details/panel.js +10 -87
  36. package/dist/unit-details/panel.js.map +1 -1
  37. package/dist/unit-details/popover.cjs +1 -2
  38. package/dist/unit-details/popover.cjs.map +1 -1
  39. package/dist/unit-details/popover.js +2 -3
  40. package/dist/unit-details/popover.js.map +1 -1
  41. package/dist/unit-details/strat-names.cjs +44 -0
  42. package/dist/unit-details/strat-names.cjs.map +1 -0
  43. package/dist/unit-details/strat-names.d.ts +14 -0
  44. package/dist/unit-details/strat-names.js +42 -0
  45. package/dist/unit-details/strat-names.js.map +1 -0
  46. package/dist/units/index.cjs +20 -6
  47. package/dist/units/index.cjs.map +1 -1
  48. package/dist/units/index.d.ts +11 -20
  49. package/dist/units/index.js +20 -6
  50. package/dist/units/index.js.map +1 -1
  51. package/package.json +14 -14
  52. package/src/column.ts +1 -1
  53. package/src/data-provider/unit-selection.ts +8 -4
  54. package/src/prepare-units/helpers.ts +121 -72
  55. package/src/prepare-units/index.ts +1 -1
  56. package/src/section.ts +0 -1
  57. package/src/unit-details/identifiers.ts +67 -0
  58. package/src/unit-details/index.ts +2 -0
  59. package/src/unit-details/panel.ts +13 -126
  60. package/src/unit-details/popover.ts +2 -7
  61. package/src/unit-details/strat-names.ts +57 -0
  62. package/src/units/index.ts +31 -8
@@ -5,64 +5,97 @@ const columnComponents = require("@macrostrat/column-components");
5
5
  const utils = require("./utils.cjs");
6
6
  const stratigraphyUtils = require("@macrostrat/stratigraphy-utils");
7
7
  const dt = 1e-3;
8
- function preprocessUnits(section, axisType = columnComponents.ColumnAxisType.AGE) {
9
- let units = section.units;
10
- let divisions = units.map((...args) => extendDivision(...args, axisType));
11
- for (let d of divisions) {
12
- const overlappingUnits = divisions.filter(
13
- (u) => d.overlappingUnits.includes(u.unit_id)
14
- );
15
- const columns = overlappingUnits.map((d2) => d2.column);
16
- if (columns.includes(d.column)) {
17
- let col = 0;
18
- while (columns.includes(col)) {
19
- col++;
20
- }
21
- d.column ??= col;
22
- }
23
- const [b_pos, t_pos] = utils.getUnitHeightRange(d, axisType);
24
- if (axisType == columnComponents.ColumnAxisType.AGE) {
25
- if (b_pos > section.b_age) {
26
- d.b_clip_pos = section.b_age;
27
- }
28
- if (t_pos < section.t_age) {
29
- d.t_clip_pos = section.t_age;
8
+ class UnitGroup {
9
+ units;
10
+ constructor(units) {
11
+ this.units = units;
12
+ }
13
+ get t_age() {
14
+ return Math.min(...this.units.map((d) => d.t_age));
15
+ }
16
+ get b_age() {
17
+ return Math.max(...this.units.map((d) => d.b_age));
18
+ }
19
+ getUnits() {
20
+ return assignColumnsToUnitsWithinOverlappingGroup(this);
21
+ }
22
+ }
23
+ function groupUnitsByMutualOverlap(units, tolerance = dt) {
24
+ const sections = groupUnitsIntoSectionsByOverlap(
25
+ units,
26
+ columnComponents.ColumnAxisType.AGE,
27
+ tolerance
28
+ );
29
+ return sections.map((d) => {
30
+ return new UnitGroup(d.units);
31
+ });
32
+ }
33
+ const sortByAge = utils.createUnitSorter(columnComponents.ColumnAxisType.AGE);
34
+ function assignColumnsToUnitsWithinOverlappingGroup(group2) {
35
+ const sortedUnits = [...group2.units].sort((a, b) => b.b_age - a.b_age);
36
+ const columns = [];
37
+ for (const unit of sortedUnits) {
38
+ let didAddToExistingColumn = false;
39
+ for (const column of columns) {
40
+ const lastUnit = column[column.length - 1];
41
+ if (!utils.unitsOverlap(unit, lastUnit, columnComponents.ColumnAxisType.AGE, dt)) {
42
+ column.push(unit);
43
+ didAddToExistingColumn = true;
44
+ break;
30
45
  }
31
46
  }
32
- if (axisType == columnComponents.ColumnAxisType.DEPTH) {
33
- if (b_pos > section.b_pos) {
34
- d.b_clip_pos = section.b_pos;
35
- }
36
- if (t_pos < section.t_pos) {
37
- d.t_clip_pos = section.t_pos;
38
- }
47
+ if (!didAddToExistingColumn) {
48
+ columns.push([unit]);
39
49
  }
40
50
  }
41
- return divisions;
51
+ const units = columns.flatMap(
52
+ (column, columnIndex) => column.map((unit) => {
53
+ return {
54
+ ...unit,
55
+ overlap: {
56
+ column: columnIndex,
57
+ nColumns: columns.length,
58
+ unitGroup: group2
59
+ }
60
+ };
61
+ })
62
+ );
63
+ return units.toSorted(sortByAge);
42
64
  }
43
- function extendDivision(unit, i, divisions, axisType = columnComponents.ColumnAxisType.AGE) {
65
+ function preprocessUnits(section, axisType = columnComponents.ColumnAxisType.AGE) {
66
+ let units = section.units;
44
67
  let tolerance = 1e-3;
45
68
  if (axisType != columnComponents.ColumnAxisType.AGE) {
46
69
  tolerance = 0.01;
47
70
  }
48
- const overlappingUnits = divisions.filter(
49
- (d) => d.unit_id != unit.unit_id && utils.unitsOverlap(unit, d, axisType, tolerance)
71
+ const unitGroups = groupUnitsByMutualOverlap(units, tolerance);
72
+ const divisions = [];
73
+ for (const group2 of unitGroups) {
74
+ divisions.push(...group2.getUnits());
75
+ }
76
+ return divisions.map(
77
+ (d) => enhanceUnitWithClippingPosition(section, d, axisType)
50
78
  );
51
- const u_pos = utils.getUnitHeightRange(unit, axisType);
52
- const bottomOverlap = overlappingUnits.some((d) => {
53
- const d_pos = utils.getUnitHeightRange(d, axisType);
54
- return d_pos[0] < u_pos[0] + dt;
55
- });
56
- let column = 0;
57
- if (overlappingUnits.length == 1) {
58
- column = 1;
79
+ }
80
+ function enhanceUnitWithClippingPosition(section, d, axisType) {
81
+ const [b_pos, t_pos] = utils.getUnitHeightRange(d, axisType);
82
+ if (axisType == columnComponents.ColumnAxisType.AGE) {
83
+ if (b_pos > section.b_age) {
84
+ d.b_clip_pos = section.b_age;
85
+ }
86
+ if (t_pos < section.t_age) {
87
+ d.t_clip_pos = section.t_age;
88
+ }
59
89
  }
60
- return {
61
- ...unit,
62
- bottomOverlap,
63
- column: unit.column ?? column,
64
- overlappingUnits: overlappingUnits.map((d) => d.unit_id)
65
- };
90
+ if (axisType == columnComponents.ColumnAxisType.DEPTH) {
91
+ if (b_pos > section.b_pos) {
92
+ d.b_clip_pos = section.b_pos;
93
+ }
94
+ if (t_pos < section.t_pos) {
95
+ d.t_clip_pos = section.t_pos;
96
+ }
97
+ }
98
+ return d;
66
99
  }
67
100
  function groupUnitsIntoSectionsBySectionID(units, axisType = columnComponents.ColumnAxisType.AGE) {
68
101
  let groups = Array.from(d3Array.group(units, (d) => d.section_id));
@@ -77,12 +110,12 @@ function groupUnitsIntoSectionsBySectionID(units, axisType = columnComponents.Co
77
110
  groups1.sort(compareSections);
78
111
  return groups1;
79
112
  }
80
- function groupUnitsIntoSectionsByOverlap(units, axisType = columnComponents.ColumnAxisType.AGE) {
113
+ function groupUnitsIntoSectionsByOverlap(units, axisType = columnComponents.ColumnAxisType.AGE, tolerance = dt) {
81
114
  const sectionList = [];
82
115
  for (const unit of units) {
83
116
  const heightRange = utils.getUnitHeightRange(unit, axisType);
84
117
  let section = sectionList.find(
85
- (s) => stratigraphyUtils.compareAgeRanges(heightRange, s.heightRange) !== stratigraphyUtils.AgeRangeRelationship.Disjoint
118
+ (s) => stratigraphyUtils.compareAgeRanges(heightRange, s.heightRange, tolerance) !== stratigraphyUtils.AgeRangeRelationship.Disjoint
86
119
  );
87
120
  if (section == null) {
88
121
  sectionList.push({
@@ -188,6 +221,7 @@ function preprocessSectionUnit(unit) {
188
221
  unit_name
189
222
  };
190
223
  }
224
+ exports.enhanceUnitWithClippingPosition = enhanceUnitWithClippingPosition;
191
225
  exports.getSectionAgeRange = getSectionAgeRange;
192
226
  exports.getSectionPosRange = getSectionPosRange;
193
227
  exports.groupUnitsIntoSectionsByOverlap = groupUnitsIntoSectionsByOverlap;
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.cjs","sources":["../../src/prepare-units/helpers.ts"],"sourcesContent":["import type { BaseUnit, UnitLong } from \"@macrostrat/api-types\";\nimport { group } from \"d3-array\";\nimport { ColumnAxisType } from \"@macrostrat/column-components\";\nimport {\n createUnitSorter,\n ensureArray,\n ensureRealFloat,\n getUnitHeightRange,\n unitsOverlap,\n} from \"./utils\";\nimport {\n AgeRangeRelationship,\n compareAgeRanges,\n} from \"@macrostrat/stratigraphy-utils\";\nimport type { ExtUnit, SectionInfo, StratigraphicPackage } from \"./types\";\n\nconst dt = 0.001;\n\nexport function preprocessUnits<T extends UnitLong = UnitLong>(\n section: SectionInfo<T>,\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): ExtUnit[] {\n /** Preprocess units to add overlapping units and columns. */\n let units = section.units;\n\n let divisions = units.map((...args) => extendDivision(...args, axisType));\n for (let d of divisions) {\n const overlappingUnits = divisions.filter((u) =>\n d.overlappingUnits.includes(u.unit_id),\n );\n\n // Overlapping columns\n const columns = overlappingUnits.map((d) => d.column);\n\n if (columns.includes(d.column)) {\n let col = 0;\n // Go through columns to find a better index\n while (columns.includes(col)) {\n col++;\n }\n d.column ??= col;\n }\n\n // If unit overlaps the edges of a section, set the clip positions\n const [b_pos, t_pos] = getUnitHeightRange(d, axisType);\n if (axisType == ColumnAxisType.AGE) {\n if (b_pos > section.b_age) {\n d.b_clip_pos = section.b_age;\n }\n if (t_pos < section.t_age) {\n d.t_clip_pos = section.t_age;\n }\n }\n if (axisType == ColumnAxisType.DEPTH) {\n if (b_pos > section.b_pos) {\n d.b_clip_pos = section.b_pos;\n }\n if (t_pos < section.t_pos) {\n d.t_clip_pos = section.t_pos;\n }\n }\n // if (axisType == ColumnAxisType.HEIGHT) {\n // if (b_pos < section.b_pos) {\n // d.b_clip_pos = section.b_pos;\n // }\n // if (t_pos > section.t_pos) {\n // d.t_clip_pos = section.t_pos;\n // }\n // }\n }\n\n return divisions;\n}\n\nfunction extendDivision(\n unit: UnitLong,\n i: number,\n divisions: UnitLong[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): ExtUnit {\n // TODO: make this configurable\n let tolerance = 0.001; // 1 kyr tolerance for age columns\n if (axisType != ColumnAxisType.AGE) {\n tolerance = 0.01; // 1cm tolerance for height/depth columns\n }\n\n const overlappingUnits = divisions.filter(\n (d) =>\n d.unit_id != unit.unit_id && unitsOverlap(unit, d, axisType, tolerance),\n );\n const u_pos = getUnitHeightRange(unit, axisType);\n const bottomOverlap = overlappingUnits.some((d) => {\n const d_pos = getUnitHeightRange(d, axisType);\n // Check if the unit is below the current unit\n return d_pos[0] < u_pos[0] + dt;\n });\n let column = 0;\n if (overlappingUnits.length == 1) {\n column = 1;\n }\n\n return {\n ...unit,\n bottomOverlap,\n column: unit.column ?? column,\n overlappingUnits: overlappingUnits.map((d) => d.unit_id),\n };\n}\n\nexport function groupUnitsIntoSectionsBySectionID<T extends UnitLong>(\n units: T[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): SectionInfo<T>[] {\n /** Group units into sections by section_id.\n * This works for large-scale Macrostrat columns, where units are grouped by section_id.\n * */\n let groups = Array.from(group(units, (d) => d.section_id));\n const unitComparator = createUnitSorter(axisType);\n\n const groups1 = groups.map(([section_id, sectionUnits]) => {\n const [b_age, t_age] = getSectionAgeRange(sectionUnits);\n const [b_pos, t_pos] = getSectionPosRange(sectionUnits, axisType);\n\n // sort units by position\n sectionUnits.sort(unitComparator);\n return { section_id, t_age, b_age, b_pos, t_pos, units: sectionUnits };\n });\n // Sort sections by increasing top age, then increasing bottom age.\n // Sections have no relative ordinal position other than age...\n const compareSections = createUnitSorter(axisType) as (\n a: StratigraphicPackage,\n b: StratigraphicPackage,\n ) => number;\n groups1.sort(compareSections);\n return groups1;\n}\n\ninterface WorkingSection {\n units: UnitLong[];\n // Position or age\n heightRange?: [number, number];\n}\n\nexport function groupUnitsIntoSectionsByOverlap<T extends UnitLong>(\n units: T[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): SectionInfo<T>[] {\n /** Group units into sections by overlap.\n * This creates \"synthetic\" sections that correspond to packages bound by scale gaps.\n * This is most useful in the height and depth domains, where gaps (e.g., missing core) are\n * common.\n * */\n // Start with each unit as its own \"section\", and progressively merge...\n const sectionList: WorkingSection[] = [];\n for (const unit of units) {\n // Check if the unit overlaps with any existing section\n const heightRange = getUnitHeightRange(unit, axisType);\n let section: WorkingSection | undefined = sectionList.find(\n (s) =>\n compareAgeRanges(heightRange, s.heightRange) !==\n AgeRangeRelationship.Disjoint,\n );\n if (section == null) {\n // No overlap, create a new section\n sectionList.push({\n heightRange,\n units: [unit],\n });\n } else {\n // Overlap, merge the unit into the section\n section.units.push(unit);\n // Update the height range\n if (axisType == ColumnAxisType.DEPTH || axisType == ColumnAxisType.AGE) {\n section.heightRange = [\n Math.max(section.heightRange[0], heightRange[0]),\n Math.min(section.heightRange[1], heightRange[1]),\n ];\n } else {\n section.heightRange = [\n Math.min(section.heightRange[0], heightRange[0]),\n Math.max(section.heightRange[1], heightRange[1]),\n ];\n }\n }\n }\n // We should have a section for each unit, now we can convert to SectionInfo\n // Ages have to be really actually ages, not heights\n\n return sectionList.map((section, i) => {\n const [b_age, t_age] = getSectionAgeRange(section.units);\n const [b_pos, t_pos] = getSectionPosRange(section.units, axisType);\n return {\n // Negative section IDs are used to indicate that these are synthetic sections\n section_id: -i,\n t_age,\n b_age,\n t_pos,\n b_pos,\n units: section.units as T[],\n };\n });\n}\n\nexport function getSectionPosRange(\n units: BaseUnit[],\n axisType: ColumnAxisType,\n): [number, number] {\n /** Get the overall position range of a set of units. */\n const t_positions = units.map((d) => {\n switch (axisType) {\n case ColumnAxisType.AGE:\n return d.t_age;\n case ColumnAxisType.DEPTH:\n case ColumnAxisType.HEIGHT:\n case ColumnAxisType.ORDINAL:\n return d.t_pos;\n default:\n throw new Error(`Unknown axis type: ${axisType}`);\n }\n });\n const b_positions = units.map((d) => {\n switch (axisType) {\n case ColumnAxisType.AGE:\n return d.b_age;\n case ColumnAxisType.DEPTH:\n case ColumnAxisType.HEIGHT:\n case ColumnAxisType.ORDINAL:\n return d.b_pos;\n default:\n throw new Error(`Unknown axis type: ${axisType}`);\n }\n });\n if (axisType == ColumnAxisType.AGE || axisType == ColumnAxisType.DEPTH) {\n return [Math.max(...b_positions), Math.min(...t_positions)];\n } else {\n return [Math.min(...b_positions), Math.max(...t_positions)];\n }\n}\n\nexport function getSectionAgeRange(units: BaseUnit[]): [number, number] {\n /** Get the overall age range of a set of units. */\n return getSectionPosRange(units, ColumnAxisType.AGE);\n}\n\nexport function mergeOverlappingSections<T extends UnitLong>(\n sections: SectionInfo<T>[],\n): SectionInfo<T>[] {\n /** Columns can have sections that overlap in time. Here, we merge overlapping\n * sections into a single section to correctly render gap-bound packages.\n */\n const [firstSection, ...rest] = sections;\n const newSections = [firstSection];\n for (const section of rest) {\n const lastSection = newSections[newSections.length - 1];\n if (\n lastSection.b_age < section.t_age ||\n lastSection.t_age > section.b_age\n ) {\n // No overlap, add the section as normal\n newSections.push(section);\n continue;\n }\n\n // Overlap, merge the sections\n lastSection.section_id = [\n ...ensureArray(lastSection.section_id),\n ...ensureArray(section.section_id),\n ];\n lastSection.units.push(...section.units);\n lastSection.b_age = Math.max(lastSection.b_age, section.b_age);\n lastSection.t_age = Math.min(lastSection.t_age, section.t_age);\n }\n return newSections;\n}\n\nexport function preprocessSectionUnit(unit: UnitLong): UnitLong {\n /** Preprocess a single unit for a \"section\" column type.\n * This mostly handles vagaries of eODP-style columns.\n * */\n\n let b_pos = unit.b_pos;\n let t_pos = unit.t_pos;\n\n let unit_name = unit.unit_name;\n\n // eODP columns sometimes have overlapping core sections, which are encoded in the name field\n // Match eODP section names\n const match = unit.unit_name.match(/^(\\d+(\\.\\d+)?)-(\\d+(\\.\\d+)?): (.+)/);\n\n if (match) {\n // These values should already be set if we've used the show_positions flag\n t_pos ??= ensureRealFloat(match[1]);\n b_pos ??= ensureRealFloat(match[3]);\n unit_name = match[5];\n }\n\n return {\n ...unit,\n b_pos: ensureRealFloat(b_pos),\n t_pos: ensureRealFloat(t_pos),\n unit_name,\n };\n}\n"],"names":["ColumnAxisType","d","getUnitHeightRange","unitsOverlap","group","createUnitSorter","compareAgeRanges","AgeRangeRelationship","ensureArray","ensureRealFloat"],"mappings":"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"}
1
+ {"version":3,"file":"helpers.cjs","sources":["../../src/prepare-units/helpers.ts"],"sourcesContent":["import type { BaseUnit, UnitLong } from \"@macrostrat/api-types\";\nimport { group } from \"d3-array\";\nimport { ColumnAxisType } from \"@macrostrat/column-components\";\nimport {\n createUnitSorter,\n ensureArray,\n ensureRealFloat,\n getUnitHeightRange,\n unitsOverlap,\n} from \"./utils\";\nimport {\n AgeRangeRelationship,\n compareAgeRanges,\n} from \"@macrostrat/stratigraphy-utils\";\nimport type { ExtUnit, SectionInfo, StratigraphicPackage } from \"./types\";\n\nconst dt = 0.001;\n\nexport type UnitWithDefinedOverlap<T extends BaseUnit> = T & {\n overlap: UnitOverlapInfo<T>;\n};\n\ntype UnitOverlapInfo<T extends BaseUnit> = {\n column: number;\n nColumns: number;\n unitGroup: UnitGroup<T>;\n};\n\nclass UnitGroup<T extends BaseUnit> implements StratigraphicPackage {\n units: T[];\n\n constructor(units: T[]) {\n this.units = units;\n }\n\n get t_age(): number {\n return Math.min(...this.units.map((d) => d.t_age));\n }\n\n get b_age(): number {\n return Math.max(...this.units.map((d) => d.b_age));\n }\n\n getUnits(): UnitWithDefinedOverlap<T>[] {\n return assignColumnsToUnitsWithinOverlappingGroup(this);\n }\n}\n\nfunction groupUnitsByMutualOverlap<T extends BaseUnit>(\n units: T[],\n tolerance: number = dt,\n): UnitGroup<T>[] {\n /** Group units within a section that have mutual overlap. */\n const sections = groupUnitsIntoSectionsByOverlap(\n units,\n ColumnAxisType.AGE,\n tolerance,\n );\n return sections.map((d) => {\n return new UnitGroup(d.units);\n });\n}\n\nconst sortByAge = createUnitSorter(ColumnAxisType.AGE);\n\nfunction assignColumnsToUnitsWithinOverlappingGroup<T extends BaseUnit>(\n group: UnitGroup<T>,\n): UnitWithDefinedOverlap<T>[] {\n /** A relatively naïve function to assign columns to units within a mutually overlapping group.\n * This gets the number of columns that would be needed to accommodate the units in the group.\n * It then assigns columns to units in the group based on the number of columns needed, with\n * lower units further to the left.\n */\n\n const sortedUnits = [...group.units].sort((a, b) => b.b_age - a.b_age);\n\n const columns: T[][] = [];\n\n for (const unit of sortedUnits) {\n // Find a column that is not already occupied by an overlapping unit\n let didAddToExistingColumn = false;\n for (const column of columns) {\n const lastUnit = column[column.length - 1];\n if (!unitsOverlap(unit, lastUnit, ColumnAxisType.AGE, dt)) {\n column.push(unit);\n didAddToExistingColumn = true;\n break;\n }\n }\n if (!didAddToExistingColumn) {\n columns.push([unit]);\n }\n }\n\n const units: UnitWithDefinedOverlap<T>[] = columns.flatMap(\n (column, columnIndex) =>\n column.map((unit) => {\n return {\n ...unit,\n overlap: {\n column: columnIndex,\n nColumns: columns.length,\n unitGroup: group,\n },\n };\n }),\n );\n\n return units.toSorted(sortByAge);\n}\n\nexport function preprocessUnits<T extends UnitLong = UnitLong>(\n section: SectionInfo<T>,\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): ExtUnit[] {\n /** Preprocess units to add overlapping units and columns. */\n let units = section.units;\n\n let tolerance = 0.001; // 1 kyr tolerance for age columns\n if (axisType != ColumnAxisType.AGE) {\n tolerance = 0.01; // 1cm tolerance for height/depth columns\n }\n\n const unitGroups = groupUnitsByMutualOverlap(units, tolerance);\n\n const divisions: any[] = [];\n // Serialize all groups' units to a single array\n for (const group of unitGroups) {\n divisions.push(...group.getUnits());\n }\n\n return divisions.map((d) =>\n enhanceUnitWithClippingPosition(section, d, axisType),\n );\n}\n\nexport function enhanceUnitWithClippingPosition(section, d, axisType) {\n const [b_pos, t_pos] = getUnitHeightRange(d, axisType);\n if (axisType == ColumnAxisType.AGE) {\n if (b_pos > section.b_age) {\n d.b_clip_pos = section.b_age;\n }\n if (t_pos < section.t_age) {\n d.t_clip_pos = section.t_age;\n }\n }\n if (axisType == ColumnAxisType.DEPTH) {\n if (b_pos > section.b_pos) {\n d.b_clip_pos = section.b_pos;\n }\n if (t_pos < section.t_pos) {\n d.t_clip_pos = section.t_pos;\n }\n }\n return d;\n}\n\nexport function groupUnitsIntoSectionsBySectionID<T extends UnitLong>(\n units: T[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): SectionInfo<T>[] {\n /** Group units into sections by section_id.\n * This works for large-scale Macrostrat columns, where units are grouped by section_id.\n * */\n let groups = Array.from(group(units, (d) => d.section_id));\n const unitComparator = createUnitSorter(axisType);\n\n const groups1 = groups.map(([section_id, sectionUnits]) => {\n const [b_age, t_age] = getSectionAgeRange(sectionUnits);\n const [b_pos, t_pos] = getSectionPosRange(sectionUnits, axisType);\n\n // sort units by position\n sectionUnits.sort(unitComparator);\n return { section_id, t_age, b_age, b_pos, t_pos, units: sectionUnits };\n });\n // Sort sections by increasing top age, then increasing bottom age.\n // Sections have no relative ordinal position other than age...\n const compareSections = createUnitSorter(axisType) as (\n a: StratigraphicPackage,\n b: StratigraphicPackage,\n ) => number;\n groups1.sort(compareSections);\n return groups1;\n}\n\ninterface WorkingSection {\n units: UnitLong[];\n // Position or age\n heightRange?: [number, number];\n}\n\nexport function groupUnitsIntoSectionsByOverlap<T extends BaseUnit>(\n units: T[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n tolerance: number = dt,\n): SectionInfo<T>[] {\n /** Group units into sections by overlap.\n * This creates \"synthetic\" sections that correspond to packages bound by scale gaps.\n * This is most useful in the height and depth domains, where gaps (e.g., missing core) are\n * common.\n * */\n // Start with each unit as its own \"section\", and progressively merge...\n const sectionList: WorkingSection[] = [];\n for (const unit of units) {\n // Check if the unit overlaps with any existing section\n const heightRange = getUnitHeightRange(unit, axisType);\n let section: WorkingSection | undefined = sectionList.find(\n (s) =>\n compareAgeRanges(heightRange, s.heightRange, tolerance) !==\n AgeRangeRelationship.Disjoint,\n );\n if (section == null) {\n // No overlap, create a new section\n sectionList.push({\n heightRange,\n units: [unit],\n });\n } else {\n // Overlap, merge the unit into the section\n section.units.push(unit);\n // Update the height range\n if (axisType == ColumnAxisType.DEPTH || axisType == ColumnAxisType.AGE) {\n section.heightRange = [\n Math.max(section.heightRange[0], heightRange[0]),\n Math.min(section.heightRange[1], heightRange[1]),\n ];\n } else {\n section.heightRange = [\n Math.min(section.heightRange[0], heightRange[0]),\n Math.max(section.heightRange[1], heightRange[1]),\n ];\n }\n }\n }\n // We should have a section for each unit, now we can convert to SectionInfo\n // Ages have to be really actually ages, not heights\n\n return sectionList.map((section, i) => {\n const [b_age, t_age] = getSectionAgeRange(section.units);\n const [b_pos, t_pos] = getSectionPosRange(section.units, axisType);\n return {\n // Negative section IDs are used to indicate that these are synthetic sections\n section_id: -i,\n t_age,\n b_age,\n t_pos,\n b_pos,\n units: section.units as T[],\n };\n });\n}\n\nexport function getSectionPosRange(\n units: BaseUnit[],\n axisType: ColumnAxisType,\n): [number, number] {\n /** Get the overall position range of a set of units. */\n const t_positions = units.map((d) => {\n switch (axisType) {\n case ColumnAxisType.AGE:\n return d.t_age;\n case ColumnAxisType.DEPTH:\n case ColumnAxisType.HEIGHT:\n case ColumnAxisType.ORDINAL:\n return d.t_pos;\n default:\n throw new Error(`Unknown axis type: ${axisType}`);\n }\n });\n const b_positions = units.map((d) => {\n switch (axisType) {\n case ColumnAxisType.AGE:\n return d.b_age;\n case ColumnAxisType.DEPTH:\n case ColumnAxisType.HEIGHT:\n case ColumnAxisType.ORDINAL:\n return d.b_pos;\n default:\n throw new Error(`Unknown axis type: ${axisType}`);\n }\n });\n if (axisType == ColumnAxisType.AGE || axisType == ColumnAxisType.DEPTH) {\n return [Math.max(...b_positions), Math.min(...t_positions)];\n } else {\n return [Math.min(...b_positions), Math.max(...t_positions)];\n }\n}\n\nexport function getSectionAgeRange(units: BaseUnit[]): [number, number] {\n /** Get the overall age range of a set of units. */\n return getSectionPosRange(units, ColumnAxisType.AGE);\n}\n\nexport function mergeOverlappingSections<T extends UnitLong>(\n sections: SectionInfo<T>[],\n): SectionInfo<T>[] {\n /** Columns can have sections that overlap in time. Here, we merge overlapping\n * sections into a single section to correctly render gap-bound packages.\n */\n const [firstSection, ...rest] = sections;\n const newSections = [firstSection];\n for (const section of rest) {\n const lastSection = newSections[newSections.length - 1];\n if (\n lastSection.b_age < section.t_age ||\n lastSection.t_age > section.b_age\n ) {\n // No overlap, add the section as normal\n newSections.push(section);\n continue;\n }\n\n // Overlap, merge the sections\n lastSection.section_id = [\n ...ensureArray(lastSection.section_id),\n ...ensureArray(section.section_id),\n ];\n lastSection.units.push(...section.units);\n lastSection.b_age = Math.max(lastSection.b_age, section.b_age);\n lastSection.t_age = Math.min(lastSection.t_age, section.t_age);\n }\n return newSections;\n}\n\nexport function preprocessSectionUnit(unit: UnitLong): UnitLong {\n /** Preprocess a single unit for a \"section\" column type.\n * This mostly handles vagaries of eODP-style columns.\n * */\n\n let b_pos = unit.b_pos;\n let t_pos = unit.t_pos;\n\n let unit_name = unit.unit_name;\n\n // eODP columns sometimes have overlapping core sections, which are encoded in the name field\n // Match eODP section names\n const match = unit.unit_name.match(/^(\\d+(\\.\\d+)?)-(\\d+(\\.\\d+)?): (.+)/);\n\n if (match) {\n // These values should already be set if we've used the show_positions flag\n t_pos ??= ensureRealFloat(match[1]);\n b_pos ??= ensureRealFloat(match[3]);\n unit_name = match[5];\n }\n\n return {\n ...unit,\n b_pos: ensureRealFloat(b_pos),\n t_pos: ensureRealFloat(t_pos),\n unit_name,\n };\n}\n"],"names":["ColumnAxisType","createUnitSorter","group","unitsOverlap","getUnitHeightRange","compareAgeRanges","AgeRangeRelationship","ensureArray","ensureRealFloat"],"mappings":"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"}
@@ -1,10 +1,27 @@
1
1
  import { BaseUnit, UnitLong } from '@macrostrat/api-types';
2
2
  import { ColumnAxisType } from '@macrostrat/column-components';
3
- import { ExtUnit, SectionInfo } from './types';
3
+ import { ExtUnit, SectionInfo, StratigraphicPackage } from './types';
4
+ export type UnitWithDefinedOverlap<T extends BaseUnit> = T & {
5
+ overlap: UnitOverlapInfo<T>;
6
+ };
7
+ type UnitOverlapInfo<T extends BaseUnit> = {
8
+ column: number;
9
+ nColumns: number;
10
+ unitGroup: UnitGroup<T>;
11
+ };
12
+ declare class UnitGroup<T extends BaseUnit> implements StratigraphicPackage {
13
+ units: T[];
14
+ constructor(units: T[]);
15
+ get t_age(): number;
16
+ get b_age(): number;
17
+ getUnits(): UnitWithDefinedOverlap<T>[];
18
+ }
4
19
  export declare function preprocessUnits<T extends UnitLong = UnitLong>(section: SectionInfo<T>, axisType?: ColumnAxisType): ExtUnit[];
20
+ export declare function enhanceUnitWithClippingPosition(section: any, d: any, axisType: any): any;
5
21
  export declare function groupUnitsIntoSectionsBySectionID<T extends UnitLong>(units: T[], axisType?: ColumnAxisType): SectionInfo<T>[];
6
- export declare function groupUnitsIntoSectionsByOverlap<T extends UnitLong>(units: T[], axisType?: ColumnAxisType): SectionInfo<T>[];
22
+ export declare function groupUnitsIntoSectionsByOverlap<T extends BaseUnit>(units: T[], axisType?: ColumnAxisType, tolerance?: number): SectionInfo<T>[];
7
23
  export declare function getSectionPosRange(units: BaseUnit[], axisType: ColumnAxisType): [number, number];
8
24
  export declare function getSectionAgeRange(units: BaseUnit[]): [number, number];
9
25
  export declare function mergeOverlappingSections<T extends UnitLong>(sections: SectionInfo<T>[]): SectionInfo<T>[];
10
26
  export declare function preprocessSectionUnit(unit: UnitLong): UnitLong;
27
+ export {};
@@ -1,66 +1,99 @@
1
1
  import { group } from "d3-array";
2
2
  import { ColumnAxisType } from "@macrostrat/column-components";
3
- import { getUnitHeightRange, ensureRealFloat, ensureArray, unitsOverlap, createUnitSorter } from "./utils.js";
3
+ import { ensureRealFloat, getUnitHeightRange, ensureArray, createUnitSorter, unitsOverlap } from "./utils.js";
4
4
  import { compareAgeRanges, AgeRangeRelationship } from "@macrostrat/stratigraphy-utils";
5
5
  const dt = 1e-3;
6
- function preprocessUnits(section, axisType = ColumnAxisType.AGE) {
7
- let units = section.units;
8
- let divisions = units.map((...args) => extendDivision(...args, axisType));
9
- for (let d of divisions) {
10
- const overlappingUnits = divisions.filter(
11
- (u) => d.overlappingUnits.includes(u.unit_id)
12
- );
13
- const columns = overlappingUnits.map((d2) => d2.column);
14
- if (columns.includes(d.column)) {
15
- let col = 0;
16
- while (columns.includes(col)) {
17
- col++;
18
- }
19
- d.column ??= col;
20
- }
21
- const [b_pos, t_pos] = getUnitHeightRange(d, axisType);
22
- if (axisType == ColumnAxisType.AGE) {
23
- if (b_pos > section.b_age) {
24
- d.b_clip_pos = section.b_age;
25
- }
26
- if (t_pos < section.t_age) {
27
- d.t_clip_pos = section.t_age;
6
+ class UnitGroup {
7
+ units;
8
+ constructor(units) {
9
+ this.units = units;
10
+ }
11
+ get t_age() {
12
+ return Math.min(...this.units.map((d) => d.t_age));
13
+ }
14
+ get b_age() {
15
+ return Math.max(...this.units.map((d) => d.b_age));
16
+ }
17
+ getUnits() {
18
+ return assignColumnsToUnitsWithinOverlappingGroup(this);
19
+ }
20
+ }
21
+ function groupUnitsByMutualOverlap(units, tolerance = dt) {
22
+ const sections = groupUnitsIntoSectionsByOverlap(
23
+ units,
24
+ ColumnAxisType.AGE,
25
+ tolerance
26
+ );
27
+ return sections.map((d) => {
28
+ return new UnitGroup(d.units);
29
+ });
30
+ }
31
+ const sortByAge = createUnitSorter(ColumnAxisType.AGE);
32
+ function assignColumnsToUnitsWithinOverlappingGroup(group2) {
33
+ const sortedUnits = [...group2.units].sort((a, b) => b.b_age - a.b_age);
34
+ const columns = [];
35
+ for (const unit of sortedUnits) {
36
+ let didAddToExistingColumn = false;
37
+ for (const column of columns) {
38
+ const lastUnit = column[column.length - 1];
39
+ if (!unitsOverlap(unit, lastUnit, ColumnAxisType.AGE, dt)) {
40
+ column.push(unit);
41
+ didAddToExistingColumn = true;
42
+ break;
28
43
  }
29
44
  }
30
- if (axisType == ColumnAxisType.DEPTH) {
31
- if (b_pos > section.b_pos) {
32
- d.b_clip_pos = section.b_pos;
33
- }
34
- if (t_pos < section.t_pos) {
35
- d.t_clip_pos = section.t_pos;
36
- }
45
+ if (!didAddToExistingColumn) {
46
+ columns.push([unit]);
37
47
  }
38
48
  }
39
- return divisions;
49
+ const units = columns.flatMap(
50
+ (column, columnIndex) => column.map((unit) => {
51
+ return {
52
+ ...unit,
53
+ overlap: {
54
+ column: columnIndex,
55
+ nColumns: columns.length,
56
+ unitGroup: group2
57
+ }
58
+ };
59
+ })
60
+ );
61
+ return units.toSorted(sortByAge);
40
62
  }
41
- function extendDivision(unit, i, divisions, axisType = ColumnAxisType.AGE) {
63
+ function preprocessUnits(section, axisType = ColumnAxisType.AGE) {
64
+ let units = section.units;
42
65
  let tolerance = 1e-3;
43
66
  if (axisType != ColumnAxisType.AGE) {
44
67
  tolerance = 0.01;
45
68
  }
46
- const overlappingUnits = divisions.filter(
47
- (d) => d.unit_id != unit.unit_id && unitsOverlap(unit, d, axisType, tolerance)
69
+ const unitGroups = groupUnitsByMutualOverlap(units, tolerance);
70
+ const divisions = [];
71
+ for (const group2 of unitGroups) {
72
+ divisions.push(...group2.getUnits());
73
+ }
74
+ return divisions.map(
75
+ (d) => enhanceUnitWithClippingPosition(section, d, axisType)
48
76
  );
49
- const u_pos = getUnitHeightRange(unit, axisType);
50
- const bottomOverlap = overlappingUnits.some((d) => {
51
- const d_pos = getUnitHeightRange(d, axisType);
52
- return d_pos[0] < u_pos[0] + dt;
53
- });
54
- let column = 0;
55
- if (overlappingUnits.length == 1) {
56
- column = 1;
77
+ }
78
+ function enhanceUnitWithClippingPosition(section, d, axisType) {
79
+ const [b_pos, t_pos] = getUnitHeightRange(d, axisType);
80
+ if (axisType == ColumnAxisType.AGE) {
81
+ if (b_pos > section.b_age) {
82
+ d.b_clip_pos = section.b_age;
83
+ }
84
+ if (t_pos < section.t_age) {
85
+ d.t_clip_pos = section.t_age;
86
+ }
57
87
  }
58
- return {
59
- ...unit,
60
- bottomOverlap,
61
- column: unit.column ?? column,
62
- overlappingUnits: overlappingUnits.map((d) => d.unit_id)
63
- };
88
+ if (axisType == ColumnAxisType.DEPTH) {
89
+ if (b_pos > section.b_pos) {
90
+ d.b_clip_pos = section.b_pos;
91
+ }
92
+ if (t_pos < section.t_pos) {
93
+ d.t_clip_pos = section.t_pos;
94
+ }
95
+ }
96
+ return d;
64
97
  }
65
98
  function groupUnitsIntoSectionsBySectionID(units, axisType = ColumnAxisType.AGE) {
66
99
  let groups = Array.from(group(units, (d) => d.section_id));
@@ -75,12 +108,12 @@ function groupUnitsIntoSectionsBySectionID(units, axisType = ColumnAxisType.AGE)
75
108
  groups1.sort(compareSections);
76
109
  return groups1;
77
110
  }
78
- function groupUnitsIntoSectionsByOverlap(units, axisType = ColumnAxisType.AGE) {
111
+ function groupUnitsIntoSectionsByOverlap(units, axisType = ColumnAxisType.AGE, tolerance = dt) {
79
112
  const sectionList = [];
80
113
  for (const unit of units) {
81
114
  const heightRange = getUnitHeightRange(unit, axisType);
82
115
  let section = sectionList.find(
83
- (s) => compareAgeRanges(heightRange, s.heightRange) !== AgeRangeRelationship.Disjoint
116
+ (s) => compareAgeRanges(heightRange, s.heightRange, tolerance) !== AgeRangeRelationship.Disjoint
84
117
  );
85
118
  if (section == null) {
86
119
  sectionList.push({
@@ -187,6 +220,7 @@ function preprocessSectionUnit(unit) {
187
220
  };
188
221
  }
189
222
  export {
223
+ enhanceUnitWithClippingPosition,
190
224
  getSectionAgeRange,
191
225
  getSectionPosRange,
192
226
  groupUnitsIntoSectionsByOverlap,
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","sources":["../../src/prepare-units/helpers.ts"],"sourcesContent":["import type { BaseUnit, UnitLong } from \"@macrostrat/api-types\";\nimport { group } from \"d3-array\";\nimport { ColumnAxisType } from \"@macrostrat/column-components\";\nimport {\n createUnitSorter,\n ensureArray,\n ensureRealFloat,\n getUnitHeightRange,\n unitsOverlap,\n} from \"./utils\";\nimport {\n AgeRangeRelationship,\n compareAgeRanges,\n} from \"@macrostrat/stratigraphy-utils\";\nimport type { ExtUnit, SectionInfo, StratigraphicPackage } from \"./types\";\n\nconst dt = 0.001;\n\nexport function preprocessUnits<T extends UnitLong = UnitLong>(\n section: SectionInfo<T>,\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): ExtUnit[] {\n /** Preprocess units to add overlapping units and columns. */\n let units = section.units;\n\n let divisions = units.map((...args) => extendDivision(...args, axisType));\n for (let d of divisions) {\n const overlappingUnits = divisions.filter((u) =>\n d.overlappingUnits.includes(u.unit_id),\n );\n\n // Overlapping columns\n const columns = overlappingUnits.map((d) => d.column);\n\n if (columns.includes(d.column)) {\n let col = 0;\n // Go through columns to find a better index\n while (columns.includes(col)) {\n col++;\n }\n d.column ??= col;\n }\n\n // If unit overlaps the edges of a section, set the clip positions\n const [b_pos, t_pos] = getUnitHeightRange(d, axisType);\n if (axisType == ColumnAxisType.AGE) {\n if (b_pos > section.b_age) {\n d.b_clip_pos = section.b_age;\n }\n if (t_pos < section.t_age) {\n d.t_clip_pos = section.t_age;\n }\n }\n if (axisType == ColumnAxisType.DEPTH) {\n if (b_pos > section.b_pos) {\n d.b_clip_pos = section.b_pos;\n }\n if (t_pos < section.t_pos) {\n d.t_clip_pos = section.t_pos;\n }\n }\n // if (axisType == ColumnAxisType.HEIGHT) {\n // if (b_pos < section.b_pos) {\n // d.b_clip_pos = section.b_pos;\n // }\n // if (t_pos > section.t_pos) {\n // d.t_clip_pos = section.t_pos;\n // }\n // }\n }\n\n return divisions;\n}\n\nfunction extendDivision(\n unit: UnitLong,\n i: number,\n divisions: UnitLong[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): ExtUnit {\n // TODO: make this configurable\n let tolerance = 0.001; // 1 kyr tolerance for age columns\n if (axisType != ColumnAxisType.AGE) {\n tolerance = 0.01; // 1cm tolerance for height/depth columns\n }\n\n const overlappingUnits = divisions.filter(\n (d) =>\n d.unit_id != unit.unit_id && unitsOverlap(unit, d, axisType, tolerance),\n );\n const u_pos = getUnitHeightRange(unit, axisType);\n const bottomOverlap = overlappingUnits.some((d) => {\n const d_pos = getUnitHeightRange(d, axisType);\n // Check if the unit is below the current unit\n return d_pos[0] < u_pos[0] + dt;\n });\n let column = 0;\n if (overlappingUnits.length == 1) {\n column = 1;\n }\n\n return {\n ...unit,\n bottomOverlap,\n column: unit.column ?? column,\n overlappingUnits: overlappingUnits.map((d) => d.unit_id),\n };\n}\n\nexport function groupUnitsIntoSectionsBySectionID<T extends UnitLong>(\n units: T[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): SectionInfo<T>[] {\n /** Group units into sections by section_id.\n * This works for large-scale Macrostrat columns, where units are grouped by section_id.\n * */\n let groups = Array.from(group(units, (d) => d.section_id));\n const unitComparator = createUnitSorter(axisType);\n\n const groups1 = groups.map(([section_id, sectionUnits]) => {\n const [b_age, t_age] = getSectionAgeRange(sectionUnits);\n const [b_pos, t_pos] = getSectionPosRange(sectionUnits, axisType);\n\n // sort units by position\n sectionUnits.sort(unitComparator);\n return { section_id, t_age, b_age, b_pos, t_pos, units: sectionUnits };\n });\n // Sort sections by increasing top age, then increasing bottom age.\n // Sections have no relative ordinal position other than age...\n const compareSections = createUnitSorter(axisType) as (\n a: StratigraphicPackage,\n b: StratigraphicPackage,\n ) => number;\n groups1.sort(compareSections);\n return groups1;\n}\n\ninterface WorkingSection {\n units: UnitLong[];\n // Position or age\n heightRange?: [number, number];\n}\n\nexport function groupUnitsIntoSectionsByOverlap<T extends UnitLong>(\n units: T[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): SectionInfo<T>[] {\n /** Group units into sections by overlap.\n * This creates \"synthetic\" sections that correspond to packages bound by scale gaps.\n * This is most useful in the height and depth domains, where gaps (e.g., missing core) are\n * common.\n * */\n // Start with each unit as its own \"section\", and progressively merge...\n const sectionList: WorkingSection[] = [];\n for (const unit of units) {\n // Check if the unit overlaps with any existing section\n const heightRange = getUnitHeightRange(unit, axisType);\n let section: WorkingSection | undefined = sectionList.find(\n (s) =>\n compareAgeRanges(heightRange, s.heightRange) !==\n AgeRangeRelationship.Disjoint,\n );\n if (section == null) {\n // No overlap, create a new section\n sectionList.push({\n heightRange,\n units: [unit],\n });\n } else {\n // Overlap, merge the unit into the section\n section.units.push(unit);\n // Update the height range\n if (axisType == ColumnAxisType.DEPTH || axisType == ColumnAxisType.AGE) {\n section.heightRange = [\n Math.max(section.heightRange[0], heightRange[0]),\n Math.min(section.heightRange[1], heightRange[1]),\n ];\n } else {\n section.heightRange = [\n Math.min(section.heightRange[0], heightRange[0]),\n Math.max(section.heightRange[1], heightRange[1]),\n ];\n }\n }\n }\n // We should have a section for each unit, now we can convert to SectionInfo\n // Ages have to be really actually ages, not heights\n\n return sectionList.map((section, i) => {\n const [b_age, t_age] = getSectionAgeRange(section.units);\n const [b_pos, t_pos] = getSectionPosRange(section.units, axisType);\n return {\n // Negative section IDs are used to indicate that these are synthetic sections\n section_id: -i,\n t_age,\n b_age,\n t_pos,\n b_pos,\n units: section.units as T[],\n };\n });\n}\n\nexport function getSectionPosRange(\n units: BaseUnit[],\n axisType: ColumnAxisType,\n): [number, number] {\n /** Get the overall position range of a set of units. */\n const t_positions = units.map((d) => {\n switch (axisType) {\n case ColumnAxisType.AGE:\n return d.t_age;\n case ColumnAxisType.DEPTH:\n case ColumnAxisType.HEIGHT:\n case ColumnAxisType.ORDINAL:\n return d.t_pos;\n default:\n throw new Error(`Unknown axis type: ${axisType}`);\n }\n });\n const b_positions = units.map((d) => {\n switch (axisType) {\n case ColumnAxisType.AGE:\n return d.b_age;\n case ColumnAxisType.DEPTH:\n case ColumnAxisType.HEIGHT:\n case ColumnAxisType.ORDINAL:\n return d.b_pos;\n default:\n throw new Error(`Unknown axis type: ${axisType}`);\n }\n });\n if (axisType == ColumnAxisType.AGE || axisType == ColumnAxisType.DEPTH) {\n return [Math.max(...b_positions), Math.min(...t_positions)];\n } else {\n return [Math.min(...b_positions), Math.max(...t_positions)];\n }\n}\n\nexport function getSectionAgeRange(units: BaseUnit[]): [number, number] {\n /** Get the overall age range of a set of units. */\n return getSectionPosRange(units, ColumnAxisType.AGE);\n}\n\nexport function mergeOverlappingSections<T extends UnitLong>(\n sections: SectionInfo<T>[],\n): SectionInfo<T>[] {\n /** Columns can have sections that overlap in time. Here, we merge overlapping\n * sections into a single section to correctly render gap-bound packages.\n */\n const [firstSection, ...rest] = sections;\n const newSections = [firstSection];\n for (const section of rest) {\n const lastSection = newSections[newSections.length - 1];\n if (\n lastSection.b_age < section.t_age ||\n lastSection.t_age > section.b_age\n ) {\n // No overlap, add the section as normal\n newSections.push(section);\n continue;\n }\n\n // Overlap, merge the sections\n lastSection.section_id = [\n ...ensureArray(lastSection.section_id),\n ...ensureArray(section.section_id),\n ];\n lastSection.units.push(...section.units);\n lastSection.b_age = Math.max(lastSection.b_age, section.b_age);\n lastSection.t_age = Math.min(lastSection.t_age, section.t_age);\n }\n return newSections;\n}\n\nexport function preprocessSectionUnit(unit: UnitLong): UnitLong {\n /** Preprocess a single unit for a \"section\" column type.\n * This mostly handles vagaries of eODP-style columns.\n * */\n\n let b_pos = unit.b_pos;\n let t_pos = unit.t_pos;\n\n let unit_name = unit.unit_name;\n\n // eODP columns sometimes have overlapping core sections, which are encoded in the name field\n // Match eODP section names\n const match = unit.unit_name.match(/^(\\d+(\\.\\d+)?)-(\\d+(\\.\\d+)?): (.+)/);\n\n if (match) {\n // These values should already be set if we've used the show_positions flag\n t_pos ??= ensureRealFloat(match[1]);\n b_pos ??= ensureRealFloat(match[3]);\n unit_name = match[5];\n }\n\n return {\n ...unit,\n b_pos: ensureRealFloat(b_pos),\n t_pos: ensureRealFloat(t_pos),\n unit_name,\n };\n}\n"],"names":["d"],"mappings":"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"}
1
+ {"version":3,"file":"helpers.js","sources":["../../src/prepare-units/helpers.ts"],"sourcesContent":["import type { BaseUnit, UnitLong } from \"@macrostrat/api-types\";\nimport { group } from \"d3-array\";\nimport { ColumnAxisType } from \"@macrostrat/column-components\";\nimport {\n createUnitSorter,\n ensureArray,\n ensureRealFloat,\n getUnitHeightRange,\n unitsOverlap,\n} from \"./utils\";\nimport {\n AgeRangeRelationship,\n compareAgeRanges,\n} from \"@macrostrat/stratigraphy-utils\";\nimport type { ExtUnit, SectionInfo, StratigraphicPackage } from \"./types\";\n\nconst dt = 0.001;\n\nexport type UnitWithDefinedOverlap<T extends BaseUnit> = T & {\n overlap: UnitOverlapInfo<T>;\n};\n\ntype UnitOverlapInfo<T extends BaseUnit> = {\n column: number;\n nColumns: number;\n unitGroup: UnitGroup<T>;\n};\n\nclass UnitGroup<T extends BaseUnit> implements StratigraphicPackage {\n units: T[];\n\n constructor(units: T[]) {\n this.units = units;\n }\n\n get t_age(): number {\n return Math.min(...this.units.map((d) => d.t_age));\n }\n\n get b_age(): number {\n return Math.max(...this.units.map((d) => d.b_age));\n }\n\n getUnits(): UnitWithDefinedOverlap<T>[] {\n return assignColumnsToUnitsWithinOverlappingGroup(this);\n }\n}\n\nfunction groupUnitsByMutualOverlap<T extends BaseUnit>(\n units: T[],\n tolerance: number = dt,\n): UnitGroup<T>[] {\n /** Group units within a section that have mutual overlap. */\n const sections = groupUnitsIntoSectionsByOverlap(\n units,\n ColumnAxisType.AGE,\n tolerance,\n );\n return sections.map((d) => {\n return new UnitGroup(d.units);\n });\n}\n\nconst sortByAge = createUnitSorter(ColumnAxisType.AGE);\n\nfunction assignColumnsToUnitsWithinOverlappingGroup<T extends BaseUnit>(\n group: UnitGroup<T>,\n): UnitWithDefinedOverlap<T>[] {\n /** A relatively naïve function to assign columns to units within a mutually overlapping group.\n * This gets the number of columns that would be needed to accommodate the units in the group.\n * It then assigns columns to units in the group based on the number of columns needed, with\n * lower units further to the left.\n */\n\n const sortedUnits = [...group.units].sort((a, b) => b.b_age - a.b_age);\n\n const columns: T[][] = [];\n\n for (const unit of sortedUnits) {\n // Find a column that is not already occupied by an overlapping unit\n let didAddToExistingColumn = false;\n for (const column of columns) {\n const lastUnit = column[column.length - 1];\n if (!unitsOverlap(unit, lastUnit, ColumnAxisType.AGE, dt)) {\n column.push(unit);\n didAddToExistingColumn = true;\n break;\n }\n }\n if (!didAddToExistingColumn) {\n columns.push([unit]);\n }\n }\n\n const units: UnitWithDefinedOverlap<T>[] = columns.flatMap(\n (column, columnIndex) =>\n column.map((unit) => {\n return {\n ...unit,\n overlap: {\n column: columnIndex,\n nColumns: columns.length,\n unitGroup: group,\n },\n };\n }),\n );\n\n return units.toSorted(sortByAge);\n}\n\nexport function preprocessUnits<T extends UnitLong = UnitLong>(\n section: SectionInfo<T>,\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): ExtUnit[] {\n /** Preprocess units to add overlapping units and columns. */\n let units = section.units;\n\n let tolerance = 0.001; // 1 kyr tolerance for age columns\n if (axisType != ColumnAxisType.AGE) {\n tolerance = 0.01; // 1cm tolerance for height/depth columns\n }\n\n const unitGroups = groupUnitsByMutualOverlap(units, tolerance);\n\n const divisions: any[] = [];\n // Serialize all groups' units to a single array\n for (const group of unitGroups) {\n divisions.push(...group.getUnits());\n }\n\n return divisions.map((d) =>\n enhanceUnitWithClippingPosition(section, d, axisType),\n );\n}\n\nexport function enhanceUnitWithClippingPosition(section, d, axisType) {\n const [b_pos, t_pos] = getUnitHeightRange(d, axisType);\n if (axisType == ColumnAxisType.AGE) {\n if (b_pos > section.b_age) {\n d.b_clip_pos = section.b_age;\n }\n if (t_pos < section.t_age) {\n d.t_clip_pos = section.t_age;\n }\n }\n if (axisType == ColumnAxisType.DEPTH) {\n if (b_pos > section.b_pos) {\n d.b_clip_pos = section.b_pos;\n }\n if (t_pos < section.t_pos) {\n d.t_clip_pos = section.t_pos;\n }\n }\n return d;\n}\n\nexport function groupUnitsIntoSectionsBySectionID<T extends UnitLong>(\n units: T[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): SectionInfo<T>[] {\n /** Group units into sections by section_id.\n * This works for large-scale Macrostrat columns, where units are grouped by section_id.\n * */\n let groups = Array.from(group(units, (d) => d.section_id));\n const unitComparator = createUnitSorter(axisType);\n\n const groups1 = groups.map(([section_id, sectionUnits]) => {\n const [b_age, t_age] = getSectionAgeRange(sectionUnits);\n const [b_pos, t_pos] = getSectionPosRange(sectionUnits, axisType);\n\n // sort units by position\n sectionUnits.sort(unitComparator);\n return { section_id, t_age, b_age, b_pos, t_pos, units: sectionUnits };\n });\n // Sort sections by increasing top age, then increasing bottom age.\n // Sections have no relative ordinal position other than age...\n const compareSections = createUnitSorter(axisType) as (\n a: StratigraphicPackage,\n b: StratigraphicPackage,\n ) => number;\n groups1.sort(compareSections);\n return groups1;\n}\n\ninterface WorkingSection {\n units: UnitLong[];\n // Position or age\n heightRange?: [number, number];\n}\n\nexport function groupUnitsIntoSectionsByOverlap<T extends BaseUnit>(\n units: T[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n tolerance: number = dt,\n): SectionInfo<T>[] {\n /** Group units into sections by overlap.\n * This creates \"synthetic\" sections that correspond to packages bound by scale gaps.\n * This is most useful in the height and depth domains, where gaps (e.g., missing core) are\n * common.\n * */\n // Start with each unit as its own \"section\", and progressively merge...\n const sectionList: WorkingSection[] = [];\n for (const unit of units) {\n // Check if the unit overlaps with any existing section\n const heightRange = getUnitHeightRange(unit, axisType);\n let section: WorkingSection | undefined = sectionList.find(\n (s) =>\n compareAgeRanges(heightRange, s.heightRange, tolerance) !==\n AgeRangeRelationship.Disjoint,\n );\n if (section == null) {\n // No overlap, create a new section\n sectionList.push({\n heightRange,\n units: [unit],\n });\n } else {\n // Overlap, merge the unit into the section\n section.units.push(unit);\n // Update the height range\n if (axisType == ColumnAxisType.DEPTH || axisType == ColumnAxisType.AGE) {\n section.heightRange = [\n Math.max(section.heightRange[0], heightRange[0]),\n Math.min(section.heightRange[1], heightRange[1]),\n ];\n } else {\n section.heightRange = [\n Math.min(section.heightRange[0], heightRange[0]),\n Math.max(section.heightRange[1], heightRange[1]),\n ];\n }\n }\n }\n // We should have a section for each unit, now we can convert to SectionInfo\n // Ages have to be really actually ages, not heights\n\n return sectionList.map((section, i) => {\n const [b_age, t_age] = getSectionAgeRange(section.units);\n const [b_pos, t_pos] = getSectionPosRange(section.units, axisType);\n return {\n // Negative section IDs are used to indicate that these are synthetic sections\n section_id: -i,\n t_age,\n b_age,\n t_pos,\n b_pos,\n units: section.units as T[],\n };\n });\n}\n\nexport function getSectionPosRange(\n units: BaseUnit[],\n axisType: ColumnAxisType,\n): [number, number] {\n /** Get the overall position range of a set of units. */\n const t_positions = units.map((d) => {\n switch (axisType) {\n case ColumnAxisType.AGE:\n return d.t_age;\n case ColumnAxisType.DEPTH:\n case ColumnAxisType.HEIGHT:\n case ColumnAxisType.ORDINAL:\n return d.t_pos;\n default:\n throw new Error(`Unknown axis type: ${axisType}`);\n }\n });\n const b_positions = units.map((d) => {\n switch (axisType) {\n case ColumnAxisType.AGE:\n return d.b_age;\n case ColumnAxisType.DEPTH:\n case ColumnAxisType.HEIGHT:\n case ColumnAxisType.ORDINAL:\n return d.b_pos;\n default:\n throw new Error(`Unknown axis type: ${axisType}`);\n }\n });\n if (axisType == ColumnAxisType.AGE || axisType == ColumnAxisType.DEPTH) {\n return [Math.max(...b_positions), Math.min(...t_positions)];\n } else {\n return [Math.min(...b_positions), Math.max(...t_positions)];\n }\n}\n\nexport function getSectionAgeRange(units: BaseUnit[]): [number, number] {\n /** Get the overall age range of a set of units. */\n return getSectionPosRange(units, ColumnAxisType.AGE);\n}\n\nexport function mergeOverlappingSections<T extends UnitLong>(\n sections: SectionInfo<T>[],\n): SectionInfo<T>[] {\n /** Columns can have sections that overlap in time. Here, we merge overlapping\n * sections into a single section to correctly render gap-bound packages.\n */\n const [firstSection, ...rest] = sections;\n const newSections = [firstSection];\n for (const section of rest) {\n const lastSection = newSections[newSections.length - 1];\n if (\n lastSection.b_age < section.t_age ||\n lastSection.t_age > section.b_age\n ) {\n // No overlap, add the section as normal\n newSections.push(section);\n continue;\n }\n\n // Overlap, merge the sections\n lastSection.section_id = [\n ...ensureArray(lastSection.section_id),\n ...ensureArray(section.section_id),\n ];\n lastSection.units.push(...section.units);\n lastSection.b_age = Math.max(lastSection.b_age, section.b_age);\n lastSection.t_age = Math.min(lastSection.t_age, section.t_age);\n }\n return newSections;\n}\n\nexport function preprocessSectionUnit(unit: UnitLong): UnitLong {\n /** Preprocess a single unit for a \"section\" column type.\n * This mostly handles vagaries of eODP-style columns.\n * */\n\n let b_pos = unit.b_pos;\n let t_pos = unit.t_pos;\n\n let unit_name = unit.unit_name;\n\n // eODP columns sometimes have overlapping core sections, which are encoded in the name field\n // Match eODP section names\n const match = unit.unit_name.match(/^(\\d+(\\.\\d+)?)-(\\d+(\\.\\d+)?): (.+)/);\n\n if (match) {\n // These values should already be set if we've used the show_positions flag\n t_pos ??= ensureRealFloat(match[1]);\n b_pos ??= ensureRealFloat(match[3]);\n unit_name = match[5];\n }\n\n return {\n ...unit,\n b_pos: ensureRealFloat(b_pos),\n t_pos: ensureRealFloat(t_pos),\n unit_name,\n };\n}\n"],"names":["group"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/prepare-units/index.ts"],"sourcesContent":["import {\n getSectionAgeRange,\n getSectionPosRange,\n groupUnitsIntoSectionsByOverlap,\n groupUnitsIntoSectionsBySectionID,\n mergeOverlappingSections,\n preprocessSectionUnit,\n preprocessUnits,\n} from \"./helpers\";\nimport { ColumnAxisType } from \"@macrostrat/column-components\";\nimport { useMemo } from \"react\";\nimport { UnitLong } from \"@macrostrat/api-types\";\nimport {\n collapseUnconformitiesByPixelHeight,\n computeSectionHeights,\n finalizeSectionHeights,\n type CompositeColumnScale,\n} from \"./composite-scale\";\nimport {\n agesOverlap,\n MergeSectionsMode,\n PrepareColumnOptions,\n PreparedColumnData,\n unitsOverlap,\n} from \"./utils\";\nimport { SectionInfo } from \"./types\";\n\nexport * from \"./utils\";\nexport * from \"./types\";\nexport { preprocessUnits };\nexport type { CompositeColumnScale };\n\nexport function usePreparedColumnUnits(\n data: UnitLong[],\n options: PrepareColumnOptions,\n): PreparedColumnData {\n /** This function wraps and memoizes all preparation steps for converting\n * an array of units from the /units route to a form ready for usage.\n */\n return useMemo(() => {\n return prepareColumnUnits(data, options);\n }, [data, ...Object.values(options)]);\n}\n\nexport function prepareColumnUnits(\n units: UnitLong[],\n options: PrepareColumnOptions,\n): PreparedColumnData {\n /** Prepare units for rendering into Macrostrat columns */\n\n let { t_age, b_age, t_pos, b_pos } = options;\n\n const {\n mergeSections = MergeSectionsMode.OVERLAPPING,\n axisType,\n unconformityHeight,\n collapseSmallUnconformities = false,\n hybridScale,\n scale,\n } = options;\n\n let _totalHeight = null;\n\n if (scale != null) {\n // Set t_age and b_age based on scale domain if not already set\n const domain = scale.domain();\n if (axisType == ColumnAxisType.AGE) {\n if (t_age == null) t_age = Math.min(...domain);\n if (b_age == null) b_age = Math.max(...domain);\n _totalHeight = Math.abs(scale(b_age) - scale(t_age));\n } else {\n if (t_pos == null) t_pos = Math.min(...domain);\n if (b_pos == null) b_pos = Math.max(...domain);\n _totalHeight = Math.abs(scale(b_pos) - scale(t_pos));\n }\n }\n\n // Start by ensuring that ages and positions are numbers\n // also set up some values for eODP-style columns\n let units1 = units.map(preprocessSectionUnit);\n\n /** Prototype filtering to age range */\n units1 = units1.filter((d) => {\n // Filter units by t_age and b_age, inclusive\n if (axisType == ColumnAxisType.AGE) {\n return agesOverlap(d, { t_age, b_age });\n } else {\n return unitsOverlap(d, { t_pos, b_pos } as any, axisType);\n }\n });\n\n let mergeMode = mergeSections;\n // if (axisType != ColumnAxisType.AGE) {\n // // For non-age columns, we always merge sections.\n // // This is because the \"groupUnitsIntoSections\" function is not well-defined\n // // for non-age columns.\n // mergeMode = MergeSectionsMode.ALL;\n // }\n\n let sections0: SectionInfo<UnitLong>[];\n if (mergeMode == MergeSectionsMode.ALL) {\n // For the \"merge sections\" mode, we need to create a single section\n const [b_unit_pos, t_unit_pos] = getSectionPosRange(units1, axisType);\n const [b_unit_age, t_unit_age] = getSectionAgeRange(units1);\n sections0 = [\n {\n section_id: 0,\n /**\n * If ages limits are directly specified, use them to define the section bounds.\n * */\n t_pos: t_unit_pos,\n b_pos: b_unit_pos,\n t_age: t_unit_age,\n b_age: b_unit_age,\n units: units1,\n },\n ];\n } else if (axisType == ColumnAxisType.AGE) {\n sections0 = groupUnitsIntoSectionsBySectionID(units1, axisType);\n } else {\n sections0 = groupUnitsIntoSectionsByOverlap(units1, axisType);\n }\n\n // Limit sections to the range specified by t_age/b_age or t_pos/b_pos global options\n for (let section of sections0) {\n if (axisType == ColumnAxisType.AGE) {\n section.t_age = Math.max(section.t_age, t_age ?? -Infinity);\n section.b_age = Math.min(section.b_age, b_age ?? Infinity);\n } else if (axisType == ColumnAxisType.DEPTH) {\n section.t_pos = Math.max(section.t_pos, t_pos ?? -Infinity);\n section.b_pos = Math.min(section.b_pos, b_pos ?? Infinity);\n } else if (axisType == ColumnAxisType.HEIGHT) {\n section.t_pos = Math.max(section.t_pos, t_pos ?? -Infinity);\n section.b_pos = Math.min(section.b_pos, b_pos ?? Infinity);\n }\n }\n\n /** Merging overlapping sections really only makes sense for age/height/depth\n * columns. Ordinal columns are numbered by section so merging them\n * results in collisions.\n */\n let sections = sections0;\n if (\n mergeSections == MergeSectionsMode.OVERLAPPING &&\n axisType == ColumnAxisType.AGE\n ) {\n sections = mergeOverlappingSections(sections);\n }\n // Filter out undefined sections just in case\n sections = sections.filter((d) => d != null);\n\n // SCALES\n\n /* Compute pixel scales etc. for sections\n * We need to do this now to determine which unconformities\n * are small enough to collapse.\n */\n let sectionsWithScales = computeSectionHeights(sections, options);\n\n if (collapseSmallUnconformities && hybridScale == null) {\n // Collapse small unconformities in pixel height space\n // TODO: this doesn't seem to work properly for non-age columns?\n let threshold = unconformityHeight ?? 30;\n if (typeof collapseSmallUnconformities == \"number\") {\n threshold = collapseSmallUnconformities;\n }\n\n sectionsWithScales = collapseUnconformitiesByPixelHeight(\n sectionsWithScales,\n threshold,\n options,\n );\n }\n\n /** Prepare section scale information using groups.\n * Total height is computed from section scales.\n * */\n let { totalHeight, sections: sections2 } = finalizeSectionHeights(\n sectionsWithScales,\n unconformityHeight,\n );\n\n /** For each section, find units that are overlapping.\n * We do this after merging sections so that we can\n * handle cases where there are overlapping units across sections\n * */\n const sectionsOut = sections2.map((section) => {\n return {\n ...section,\n units: preprocessUnits(section, axisType),\n };\n });\n\n /** Reconstitute the units so that they are sorted by section and properly enhanced.\n * This is mostly important so that unit keyboard navigation\n * predictably selects adjacent units.\n */\n const units2 = sectionsOut.reduce((acc, group) => {\n const { units } = group;\n for (const unit of units) {\n acc.push(unit);\n }\n return acc;\n }, []);\n\n return {\n units: units2,\n totalHeight: _totalHeight ?? totalHeight,\n sections: sectionsOut,\n };\n}\n"],"names":["useMemo","MergeSectionsMode","ColumnAxisType","preprocessSectionUnit","agesOverlap","unitsOverlap","getSectionPosRange","getSectionAgeRange","groupUnitsIntoSectionsBySectionID","groupUnitsIntoSectionsByOverlap","mergeOverlappingSections","computeSectionHeights","collapseUnconformitiesByPixelHeight","finalizeSectionHeights","preprocessUnits","units"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/prepare-units/index.ts"],"sourcesContent":["import {\n getSectionAgeRange,\n getSectionPosRange,\n groupUnitsIntoSectionsByOverlap,\n groupUnitsIntoSectionsBySectionID,\n mergeOverlappingSections,\n preprocessSectionUnit,\n preprocessUnits,\n} from \"./helpers\";\nimport { ColumnAxisType } from \"@macrostrat/column-components\";\nimport { useMemo } from \"react\";\nimport { UnitLong } from \"@macrostrat/api-types\";\nimport {\n collapseUnconformitiesByPixelHeight,\n computeSectionHeights,\n finalizeSectionHeights,\n type CompositeColumnScale,\n} from \"./composite-scale\";\nimport {\n agesOverlap,\n MergeSectionsMode,\n PrepareColumnOptions,\n PreparedColumnData,\n unitsOverlap,\n} from \"./utils\";\nimport { SectionInfo } from \"./types\";\n\nexport * from \"./utils\";\nexport * from \"./types\";\nexport { preprocessUnits };\nexport type { CompositeColumnScale };\n\nexport function usePreparedColumnUnits(\n data: UnitLong[],\n options: PrepareColumnOptions,\n): PreparedColumnData {\n /** This function wraps and memoizes all preparation steps for converting\n * an array of units from the /units route to a form ready for usage.\n */\n return useMemo(() => {\n return prepareColumnUnits(data, options);\n }, [data, ...Object.values(options)]);\n}\n\nexport function prepareColumnUnits(\n units: UnitLong[],\n options: PrepareColumnOptions,\n): PreparedColumnData {\n /** Prepare units for rendering into Macrostrat columns */\n\n let { t_age, b_age, t_pos, b_pos } = options;\n\n const {\n mergeSections = MergeSectionsMode.OVERLAPPING,\n axisType,\n unconformityHeight,\n collapseSmallUnconformities = false,\n hybridScale,\n scale,\n } = options;\n\n let _totalHeight: number | null = null;\n\n if (scale != null) {\n // Set t_age and b_age based on scale domain if not already set\n const domain = scale.domain();\n if (axisType == ColumnAxisType.AGE) {\n if (t_age == null) t_age = Math.min(...domain);\n if (b_age == null) b_age = Math.max(...domain);\n _totalHeight = Math.abs(scale(b_age) - scale(t_age));\n } else {\n if (t_pos == null) t_pos = Math.min(...domain);\n if (b_pos == null) b_pos = Math.max(...domain);\n _totalHeight = Math.abs(scale(b_pos) - scale(t_pos));\n }\n }\n\n // Start by ensuring that ages and positions are numbers\n // also set up some values for eODP-style columns\n let units1 = units.map(preprocessSectionUnit);\n\n /** Prototype filtering to age range */\n units1 = units1.filter((d) => {\n // Filter units by t_age and b_age, inclusive\n if (axisType == ColumnAxisType.AGE) {\n return agesOverlap(d, { t_age, b_age });\n } else {\n return unitsOverlap(d, { t_pos, b_pos } as any, axisType);\n }\n });\n\n let mergeMode = mergeSections;\n // if (axisType != ColumnAxisType.AGE) {\n // // For non-age columns, we always merge sections.\n // // This is because the \"groupUnitsIntoSections\" function is not well-defined\n // // for non-age columns.\n // mergeMode = MergeSectionsMode.ALL;\n // }\n\n let sections0: SectionInfo<UnitLong>[];\n if (mergeMode == MergeSectionsMode.ALL) {\n // For the \"merge sections\" mode, we need to create a single section\n const [b_unit_pos, t_unit_pos] = getSectionPosRange(units1, axisType);\n const [b_unit_age, t_unit_age] = getSectionAgeRange(units1);\n sections0 = [\n {\n section_id: 0,\n /**\n * If ages limits are directly specified, use them to define the section bounds.\n * */\n t_pos: t_unit_pos,\n b_pos: b_unit_pos,\n t_age: t_unit_age,\n b_age: b_unit_age,\n units: units1,\n },\n ];\n } else if (axisType == ColumnAxisType.AGE) {\n sections0 = groupUnitsIntoSectionsBySectionID(units1, axisType);\n } else {\n sections0 = groupUnitsIntoSectionsByOverlap(units1, axisType);\n }\n\n // Limit sections to the range specified by t_age/b_age or t_pos/b_pos global options\n for (let section of sections0) {\n if (axisType == ColumnAxisType.AGE) {\n section.t_age = Math.max(section.t_age, t_age ?? -Infinity);\n section.b_age = Math.min(section.b_age, b_age ?? Infinity);\n } else if (axisType == ColumnAxisType.DEPTH) {\n section.t_pos = Math.max(section.t_pos, t_pos ?? -Infinity);\n section.b_pos = Math.min(section.b_pos, b_pos ?? Infinity);\n } else if (axisType == ColumnAxisType.HEIGHT) {\n section.t_pos = Math.max(section.t_pos, t_pos ?? -Infinity);\n section.b_pos = Math.min(section.b_pos, b_pos ?? Infinity);\n }\n }\n\n /** Merging overlapping sections really only makes sense for age/height/depth\n * columns. Ordinal columns are numbered by section so merging them\n * results in collisions.\n */\n let sections = sections0;\n if (\n mergeSections == MergeSectionsMode.OVERLAPPING &&\n axisType == ColumnAxisType.AGE\n ) {\n sections = mergeOverlappingSections(sections);\n }\n // Filter out undefined sections just in case\n sections = sections.filter((d) => d != null);\n\n // SCALES\n\n /* Compute pixel scales etc. for sections\n * We need to do this now to determine which unconformities\n * are small enough to collapse.\n */\n let sectionsWithScales = computeSectionHeights(sections, options);\n\n if (collapseSmallUnconformities && hybridScale == null) {\n // Collapse small unconformities in pixel height space\n // TODO: this doesn't seem to work properly for non-age columns?\n let threshold = unconformityHeight ?? 30;\n if (typeof collapseSmallUnconformities == \"number\") {\n threshold = collapseSmallUnconformities;\n }\n\n sectionsWithScales = collapseUnconformitiesByPixelHeight(\n sectionsWithScales,\n threshold,\n options,\n );\n }\n\n /** Prepare section scale information using groups.\n * Total height is computed from section scales.\n * */\n let { totalHeight, sections: sections2 } = finalizeSectionHeights(\n sectionsWithScales,\n unconformityHeight,\n );\n\n /** For each section, find units that are overlapping.\n * We do this after merging sections so that we can\n * handle cases where there are overlapping units across sections\n * */\n const sectionsOut = sections2.map((section) => {\n return {\n ...section,\n units: preprocessUnits(section, axisType),\n };\n });\n\n /** Reconstitute the units so that they are sorted by section and properly enhanced.\n * This is mostly important so that unit keyboard navigation\n * predictably selects adjacent units.\n */\n const units2 = sectionsOut.reduce((acc, group) => {\n const { units } = group;\n for (const unit of units) {\n acc.push(unit);\n }\n return acc;\n }, []);\n\n return {\n units: units2,\n totalHeight: _totalHeight ?? totalHeight,\n sections: sectionsOut,\n };\n}\n"],"names":["useMemo","MergeSectionsMode","ColumnAxisType","preprocessSectionUnit","agesOverlap","unitsOverlap","getSectionPosRange","getSectionAgeRange","groupUnitsIntoSectionsBySectionID","groupUnitsIntoSectionsByOverlap","mergeOverlappingSections","computeSectionHeights","collapseUnconformitiesByPixelHeight","finalizeSectionHeights","preprocessUnits","units"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/prepare-units/index.ts"],"sourcesContent":["import {\n getSectionAgeRange,\n getSectionPosRange,\n groupUnitsIntoSectionsByOverlap,\n groupUnitsIntoSectionsBySectionID,\n mergeOverlappingSections,\n preprocessSectionUnit,\n preprocessUnits,\n} from \"./helpers\";\nimport { ColumnAxisType } from \"@macrostrat/column-components\";\nimport { useMemo } from \"react\";\nimport { UnitLong } from \"@macrostrat/api-types\";\nimport {\n collapseUnconformitiesByPixelHeight,\n computeSectionHeights,\n finalizeSectionHeights,\n type CompositeColumnScale,\n} from \"./composite-scale\";\nimport {\n agesOverlap,\n MergeSectionsMode,\n PrepareColumnOptions,\n PreparedColumnData,\n unitsOverlap,\n} from \"./utils\";\nimport { SectionInfo } from \"./types\";\n\nexport * from \"./utils\";\nexport * from \"./types\";\nexport { preprocessUnits };\nexport type { CompositeColumnScale };\n\nexport function usePreparedColumnUnits(\n data: UnitLong[],\n options: PrepareColumnOptions,\n): PreparedColumnData {\n /** This function wraps and memoizes all preparation steps for converting\n * an array of units from the /units route to a form ready for usage.\n */\n return useMemo(() => {\n return prepareColumnUnits(data, options);\n }, [data, ...Object.values(options)]);\n}\n\nexport function prepareColumnUnits(\n units: UnitLong[],\n options: PrepareColumnOptions,\n): PreparedColumnData {\n /** Prepare units for rendering into Macrostrat columns */\n\n let { t_age, b_age, t_pos, b_pos } = options;\n\n const {\n mergeSections = MergeSectionsMode.OVERLAPPING,\n axisType,\n unconformityHeight,\n collapseSmallUnconformities = false,\n hybridScale,\n scale,\n } = options;\n\n let _totalHeight = null;\n\n if (scale != null) {\n // Set t_age and b_age based on scale domain if not already set\n const domain = scale.domain();\n if (axisType == ColumnAxisType.AGE) {\n if (t_age == null) t_age = Math.min(...domain);\n if (b_age == null) b_age = Math.max(...domain);\n _totalHeight = Math.abs(scale(b_age) - scale(t_age));\n } else {\n if (t_pos == null) t_pos = Math.min(...domain);\n if (b_pos == null) b_pos = Math.max(...domain);\n _totalHeight = Math.abs(scale(b_pos) - scale(t_pos));\n }\n }\n\n // Start by ensuring that ages and positions are numbers\n // also set up some values for eODP-style columns\n let units1 = units.map(preprocessSectionUnit);\n\n /** Prototype filtering to age range */\n units1 = units1.filter((d) => {\n // Filter units by t_age and b_age, inclusive\n if (axisType == ColumnAxisType.AGE) {\n return agesOverlap(d, { t_age, b_age });\n } else {\n return unitsOverlap(d, { t_pos, b_pos } as any, axisType);\n }\n });\n\n let mergeMode = mergeSections;\n // if (axisType != ColumnAxisType.AGE) {\n // // For non-age columns, we always merge sections.\n // // This is because the \"groupUnitsIntoSections\" function is not well-defined\n // // for non-age columns.\n // mergeMode = MergeSectionsMode.ALL;\n // }\n\n let sections0: SectionInfo<UnitLong>[];\n if (mergeMode == MergeSectionsMode.ALL) {\n // For the \"merge sections\" mode, we need to create a single section\n const [b_unit_pos, t_unit_pos] = getSectionPosRange(units1, axisType);\n const [b_unit_age, t_unit_age] = getSectionAgeRange(units1);\n sections0 = [\n {\n section_id: 0,\n /**\n * If ages limits are directly specified, use them to define the section bounds.\n * */\n t_pos: t_unit_pos,\n b_pos: b_unit_pos,\n t_age: t_unit_age,\n b_age: b_unit_age,\n units: units1,\n },\n ];\n } else if (axisType == ColumnAxisType.AGE) {\n sections0 = groupUnitsIntoSectionsBySectionID(units1, axisType);\n } else {\n sections0 = groupUnitsIntoSectionsByOverlap(units1, axisType);\n }\n\n // Limit sections to the range specified by t_age/b_age or t_pos/b_pos global options\n for (let section of sections0) {\n if (axisType == ColumnAxisType.AGE) {\n section.t_age = Math.max(section.t_age, t_age ?? -Infinity);\n section.b_age = Math.min(section.b_age, b_age ?? Infinity);\n } else if (axisType == ColumnAxisType.DEPTH) {\n section.t_pos = Math.max(section.t_pos, t_pos ?? -Infinity);\n section.b_pos = Math.min(section.b_pos, b_pos ?? Infinity);\n } else if (axisType == ColumnAxisType.HEIGHT) {\n section.t_pos = Math.max(section.t_pos, t_pos ?? -Infinity);\n section.b_pos = Math.min(section.b_pos, b_pos ?? Infinity);\n }\n }\n\n /** Merging overlapping sections really only makes sense for age/height/depth\n * columns. Ordinal columns are numbered by section so merging them\n * results in collisions.\n */\n let sections = sections0;\n if (\n mergeSections == MergeSectionsMode.OVERLAPPING &&\n axisType == ColumnAxisType.AGE\n ) {\n sections = mergeOverlappingSections(sections);\n }\n // Filter out undefined sections just in case\n sections = sections.filter((d) => d != null);\n\n // SCALES\n\n /* Compute pixel scales etc. for sections\n * We need to do this now to determine which unconformities\n * are small enough to collapse.\n */\n let sectionsWithScales = computeSectionHeights(sections, options);\n\n if (collapseSmallUnconformities && hybridScale == null) {\n // Collapse small unconformities in pixel height space\n // TODO: this doesn't seem to work properly for non-age columns?\n let threshold = unconformityHeight ?? 30;\n if (typeof collapseSmallUnconformities == \"number\") {\n threshold = collapseSmallUnconformities;\n }\n\n sectionsWithScales = collapseUnconformitiesByPixelHeight(\n sectionsWithScales,\n threshold,\n options,\n );\n }\n\n /** Prepare section scale information using groups.\n * Total height is computed from section scales.\n * */\n let { totalHeight, sections: sections2 } = finalizeSectionHeights(\n sectionsWithScales,\n unconformityHeight,\n );\n\n /** For each section, find units that are overlapping.\n * We do this after merging sections so that we can\n * handle cases where there are overlapping units across sections\n * */\n const sectionsOut = sections2.map((section) => {\n return {\n ...section,\n units: preprocessUnits(section, axisType),\n };\n });\n\n /** Reconstitute the units so that they are sorted by section and properly enhanced.\n * This is mostly important so that unit keyboard navigation\n * predictably selects adjacent units.\n */\n const units2 = sectionsOut.reduce((acc, group) => {\n const { units } = group;\n for (const unit of units) {\n acc.push(unit);\n }\n return acc;\n }, []);\n\n return {\n units: units2,\n totalHeight: _totalHeight ?? totalHeight,\n sections: sectionsOut,\n };\n}\n"],"names":["units"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/prepare-units/index.ts"],"sourcesContent":["import {\n getSectionAgeRange,\n getSectionPosRange,\n groupUnitsIntoSectionsByOverlap,\n groupUnitsIntoSectionsBySectionID,\n mergeOverlappingSections,\n preprocessSectionUnit,\n preprocessUnits,\n} from \"./helpers\";\nimport { ColumnAxisType } from \"@macrostrat/column-components\";\nimport { useMemo } from \"react\";\nimport { UnitLong } from \"@macrostrat/api-types\";\nimport {\n collapseUnconformitiesByPixelHeight,\n computeSectionHeights,\n finalizeSectionHeights,\n type CompositeColumnScale,\n} from \"./composite-scale\";\nimport {\n agesOverlap,\n MergeSectionsMode,\n PrepareColumnOptions,\n PreparedColumnData,\n unitsOverlap,\n} from \"./utils\";\nimport { SectionInfo } from \"./types\";\n\nexport * from \"./utils\";\nexport * from \"./types\";\nexport { preprocessUnits };\nexport type { CompositeColumnScale };\n\nexport function usePreparedColumnUnits(\n data: UnitLong[],\n options: PrepareColumnOptions,\n): PreparedColumnData {\n /** This function wraps and memoizes all preparation steps for converting\n * an array of units from the /units route to a form ready for usage.\n */\n return useMemo(() => {\n return prepareColumnUnits(data, options);\n }, [data, ...Object.values(options)]);\n}\n\nexport function prepareColumnUnits(\n units: UnitLong[],\n options: PrepareColumnOptions,\n): PreparedColumnData {\n /** Prepare units for rendering into Macrostrat columns */\n\n let { t_age, b_age, t_pos, b_pos } = options;\n\n const {\n mergeSections = MergeSectionsMode.OVERLAPPING,\n axisType,\n unconformityHeight,\n collapseSmallUnconformities = false,\n hybridScale,\n scale,\n } = options;\n\n let _totalHeight: number | null = null;\n\n if (scale != null) {\n // Set t_age and b_age based on scale domain if not already set\n const domain = scale.domain();\n if (axisType == ColumnAxisType.AGE) {\n if (t_age == null) t_age = Math.min(...domain);\n if (b_age == null) b_age = Math.max(...domain);\n _totalHeight = Math.abs(scale(b_age) - scale(t_age));\n } else {\n if (t_pos == null) t_pos = Math.min(...domain);\n if (b_pos == null) b_pos = Math.max(...domain);\n _totalHeight = Math.abs(scale(b_pos) - scale(t_pos));\n }\n }\n\n // Start by ensuring that ages and positions are numbers\n // also set up some values for eODP-style columns\n let units1 = units.map(preprocessSectionUnit);\n\n /** Prototype filtering to age range */\n units1 = units1.filter((d) => {\n // Filter units by t_age and b_age, inclusive\n if (axisType == ColumnAxisType.AGE) {\n return agesOverlap(d, { t_age, b_age });\n } else {\n return unitsOverlap(d, { t_pos, b_pos } as any, axisType);\n }\n });\n\n let mergeMode = mergeSections;\n // if (axisType != ColumnAxisType.AGE) {\n // // For non-age columns, we always merge sections.\n // // This is because the \"groupUnitsIntoSections\" function is not well-defined\n // // for non-age columns.\n // mergeMode = MergeSectionsMode.ALL;\n // }\n\n let sections0: SectionInfo<UnitLong>[];\n if (mergeMode == MergeSectionsMode.ALL) {\n // For the \"merge sections\" mode, we need to create a single section\n const [b_unit_pos, t_unit_pos] = getSectionPosRange(units1, axisType);\n const [b_unit_age, t_unit_age] = getSectionAgeRange(units1);\n sections0 = [\n {\n section_id: 0,\n /**\n * If ages limits are directly specified, use them to define the section bounds.\n * */\n t_pos: t_unit_pos,\n b_pos: b_unit_pos,\n t_age: t_unit_age,\n b_age: b_unit_age,\n units: units1,\n },\n ];\n } else if (axisType == ColumnAxisType.AGE) {\n sections0 = groupUnitsIntoSectionsBySectionID(units1, axisType);\n } else {\n sections0 = groupUnitsIntoSectionsByOverlap(units1, axisType);\n }\n\n // Limit sections to the range specified by t_age/b_age or t_pos/b_pos global options\n for (let section of sections0) {\n if (axisType == ColumnAxisType.AGE) {\n section.t_age = Math.max(section.t_age, t_age ?? -Infinity);\n section.b_age = Math.min(section.b_age, b_age ?? Infinity);\n } else if (axisType == ColumnAxisType.DEPTH) {\n section.t_pos = Math.max(section.t_pos, t_pos ?? -Infinity);\n section.b_pos = Math.min(section.b_pos, b_pos ?? Infinity);\n } else if (axisType == ColumnAxisType.HEIGHT) {\n section.t_pos = Math.max(section.t_pos, t_pos ?? -Infinity);\n section.b_pos = Math.min(section.b_pos, b_pos ?? Infinity);\n }\n }\n\n /** Merging overlapping sections really only makes sense for age/height/depth\n * columns. Ordinal columns are numbered by section so merging them\n * results in collisions.\n */\n let sections = sections0;\n if (\n mergeSections == MergeSectionsMode.OVERLAPPING &&\n axisType == ColumnAxisType.AGE\n ) {\n sections = mergeOverlappingSections(sections);\n }\n // Filter out undefined sections just in case\n sections = sections.filter((d) => d != null);\n\n // SCALES\n\n /* Compute pixel scales etc. for sections\n * We need to do this now to determine which unconformities\n * are small enough to collapse.\n */\n let sectionsWithScales = computeSectionHeights(sections, options);\n\n if (collapseSmallUnconformities && hybridScale == null) {\n // Collapse small unconformities in pixel height space\n // TODO: this doesn't seem to work properly for non-age columns?\n let threshold = unconformityHeight ?? 30;\n if (typeof collapseSmallUnconformities == \"number\") {\n threshold = collapseSmallUnconformities;\n }\n\n sectionsWithScales = collapseUnconformitiesByPixelHeight(\n sectionsWithScales,\n threshold,\n options,\n );\n }\n\n /** Prepare section scale information using groups.\n * Total height is computed from section scales.\n * */\n let { totalHeight, sections: sections2 } = finalizeSectionHeights(\n sectionsWithScales,\n unconformityHeight,\n );\n\n /** For each section, find units that are overlapping.\n * We do this after merging sections so that we can\n * handle cases where there are overlapping units across sections\n * */\n const sectionsOut = sections2.map((section) => {\n return {\n ...section,\n units: preprocessUnits(section, axisType),\n };\n });\n\n /** Reconstitute the units so that they are sorted by section and properly enhanced.\n * This is mostly important so that unit keyboard navigation\n * predictably selects adjacent units.\n */\n const units2 = sectionsOut.reduce((acc, group) => {\n const { units } = group;\n for (const unit of units) {\n acc.push(unit);\n }\n return acc;\n }, []);\n\n return {\n units: units2,\n totalHeight: _totalHeight ?? totalHeight,\n sections: sectionsOut,\n };\n}\n"],"names":["units"],"mappings":";;;;;;AAgCO,SAAS,uBACd,MACA,SACoB;AAIpB,SAAO,QAAQ,MAAM;AACnB,WAAO,mBAAmB,MAAM,OAAO;AAAA,EACzC,GAAG,CAAC,MAAM,GAAG,OAAO,OAAO,OAAO,CAAC,CAAC;AACtC;AAEO,SAAS,mBACd,OACA,SACoB;AAGpB,MAAI,EAAE,OAAO,OAAO,OAAO,UAAU;AAErC,QAAM;AAAA,IACJ,gBAAgB,kBAAkB;AAAA,IAClC;AAAA,IACA;AAAA,IACA,8BAA8B;AAAA,IAC9B;AAAA,IACA;AAAA,EAAA,IACE;AAEJ,MAAI,eAA8B;AAElC,MAAI,SAAS,MAAM;AAEjB,UAAM,SAAS,MAAM,OAAA;AACrB,QAAI,YAAY,eAAe,KAAK;AAClC,UAAI,SAAS,KAAM,SAAQ,KAAK,IAAI,GAAG,MAAM;AAC7C,UAAI,SAAS,KAAM,SAAQ,KAAK,IAAI,GAAG,MAAM;AAC7C,qBAAe,KAAK,IAAI,MAAM,KAAK,IAAI,MAAM,KAAK,CAAC;AAAA,IACrD,OAAO;AACL,UAAI,SAAS,KAAM,SAAQ,KAAK,IAAI,GAAG,MAAM;AAC7C,UAAI,SAAS,KAAM,SAAQ,KAAK,IAAI,GAAG,MAAM;AAC7C,qBAAe,KAAK,IAAI,MAAM,KAAK,IAAI,MAAM,KAAK,CAAC;AAAA,IACrD;AAAA,EACF;AAIA,MAAI,SAAS,MAAM,IAAI,qBAAqB;AAG5C,WAAS,OAAO,OAAO,CAAC,MAAM;AAE5B,QAAI,YAAY,eAAe,KAAK;AAClC,aAAO,YAAY,GAAG,EAAE,OAAO,OAAO;AAAA,IACxC,OAAO;AACL,aAAO,aAAa,GAAG,EAAE,OAAO,MAAA,GAAgB,QAAQ;AAAA,IAC1D;AAAA,EACF,CAAC;AAED,MAAI,YAAY;AAQhB,MAAI;AACJ,MAAI,aAAa,kBAAkB,KAAK;AAEtC,UAAM,CAAC,YAAY,UAAU,IAAI,mBAAmB,QAAQ,QAAQ;AACpE,UAAM,CAAC,YAAY,UAAU,IAAI,mBAAmB,MAAM;AAC1D,gBAAY;AAAA,MACV;AAAA,QACE,YAAY;AAAA;AAAA;AAAA;AAAA,QAIZ,OAAO;AAAA,QACP,OAAO;AAAA,QACP,OAAO;AAAA,QACP,OAAO;AAAA,QACP,OAAO;AAAA,MAAA;AAAA,IACT;AAAA,EAEJ,WAAW,YAAY,eAAe,KAAK;AACzC,gBAAY,kCAAkC,QAAQ,QAAQ;AAAA,EAChE,OAAO;AACL,gBAAY,gCAAgC,QAAQ,QAAQ;AAAA,EAC9D;AAGA,WAAS,WAAW,WAAW;AAC7B,QAAI,YAAY,eAAe,KAAK;AAClC,cAAQ,QAAQ,KAAK,IAAI,QAAQ,OAAO,SAAS,SAAS;AAC1D,cAAQ,QAAQ,KAAK,IAAI,QAAQ,OAAO,SAAS,QAAQ;AAAA,IAC3D,WAAW,YAAY,eAAe,OAAO;AAC3C,cAAQ,QAAQ,KAAK,IAAI,QAAQ,OAAO,SAAS,SAAS;AAC1D,cAAQ,QAAQ,KAAK,IAAI,QAAQ,OAAO,SAAS,QAAQ;AAAA,IAC3D,WAAW,YAAY,eAAe,QAAQ;AAC5C,cAAQ,QAAQ,KAAK,IAAI,QAAQ,OAAO,SAAS,SAAS;AAC1D,cAAQ,QAAQ,KAAK,IAAI,QAAQ,OAAO,SAAS,QAAQ;AAAA,IAC3D;AAAA,EACF;AAMA,MAAI,WAAW;AACf,MACE,iBAAiB,kBAAkB,eACnC,YAAY,eAAe,KAC3B;AACA,eAAW,yBAAyB,QAAQ;AAAA,EAC9C;AAEA,aAAW,SAAS,OAAO,CAAC,MAAM,KAAK,IAAI;AAQ3C,MAAI,qBAAqB,sBAAsB,UAAU,OAAO;AAEhE,MAAI,+BAA+B,eAAe,MAAM;AAGtD,QAAI,YAAY,sBAAsB;AACtC,QAAI,OAAO,+BAA+B,UAAU;AAClD,kBAAY;AAAA,IACd;AAEA,yBAAqB;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAKA,MAAI,EAAE,aAAa,UAAU,UAAA,IAAc;AAAA,IACzC;AAAA,IACA;AAAA,EAAA;AAOF,QAAM,cAAc,UAAU,IAAI,CAAC,YAAY;AAC7C,WAAO;AAAA,MACL,GAAG;AAAA,MACH,OAAO,gBAAgB,SAAS,QAAQ;AAAA,IAAA;AAAA,EAE5C,CAAC;AAMD,QAAM,SAAS,YAAY,OAAO,CAAC,KAAK,UAAU;AAChD,UAAM,EAAE,OAAAA,OAAAA,IAAU;AAClB,eAAW,QAAQA,QAAO;AACxB,UAAI,KAAK,IAAI;AAAA,IACf;AACA,WAAO;AAAA,EACT,GAAG,CAAA,CAAE;AAEL,SAAO;AAAA,IACL,OAAO;AAAA,IACP,aAAa,gBAAgB;AAAA,IAC7B,UAAU;AAAA,EAAA;AAEd;"}