pdbe-molstar 3.6.0 → 3.7.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.
@@ -10,6 +10,7 @@ exports.transform = transform;
10
10
  const tslib_1 = require("tslib");
11
11
  const linear_algebra_1 = require("molstar/lib/mol-math/linear-algebra");
12
12
  const structure_1 = require("molstar/lib/mol-model/structure");
13
+ const superposition_sifts_mapping_1 = require("molstar/lib/mol-model/structure/structure/util/superposition-sifts-mapping");
13
14
  const transforms_1 = require("molstar/lib/mol-plugin-state/transforms");
14
15
  const builder_1 = require("molstar/lib/mol-script/language/builder");
15
16
  const script_1 = require("molstar/lib/mol-script/script");
@@ -20,7 +21,6 @@ const lists_1 = require("molstar/lib/mol-util/color/lists");
20
21
  const alphafold_transparency_1 = require("./alphafold-transparency");
21
22
  const helpers_1 = require("./helpers");
22
23
  const plugin_custom_state_1 = require("./plugin-custom-state");
23
- const superposition_sifts_mapping_1 = require("./superposition-sifts-mapping");
24
24
  function combinedColorPalette(palettes) {
25
25
  return palettes.flatMap(paletteName => lists_1.ColorLists[paletteName].list);
26
26
  }
@@ -67,7 +67,6 @@ function initSuperposition(plugin, completeSubject) {
67
67
  visibility: [],
68
68
  transforms: [],
69
69
  rmsds: [],
70
- coordinateSystems: [],
71
70
  },
72
71
  };
73
72
  // Get segment and cluster information for the given uniprot accession
@@ -158,7 +157,7 @@ function loadAfStructure(plugin) {
158
157
  const { structure } = yield loadStructure(plugin, url, 'mmcif', isBinary);
159
158
  const strInstance = structure;
160
159
  if (!strInstance)
161
- return false;
160
+ return undefined;
162
161
  // Store Refs in state
163
162
  const spState = customState.superpositionState;
164
163
  spState.alphafold.ref = strInstance === null || strInstance === void 0 ? void 0 : strInstance.ref;
@@ -170,79 +169,65 @@ function loadAfStructure(plugin) {
170
169
  yield plugin.builders.structure.representation.addRepresentation(chainSel, { type: 'putty', color: 'plddt-confidence', size: 'uniform', sizeParams: { value: 1.5 } }, { tag: `af-superposition-visual` });
171
170
  return strInstance === null || strInstance === void 0 ? void 0 : strInstance.ref;
172
171
  }
173
- return false;
172
+ return undefined;
174
173
  });
175
174
  }
176
- function superposeAf(plugin, traceOnly, segmentIndex) {
175
+ function superposeAf(plugin, traceOnly) {
177
176
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
178
177
  const customState = (0, plugin_custom_state_1.PluginCustomState)(plugin);
179
178
  const spState = customState.superpositionState;
180
179
  if (!(spState === null || spState === void 0 ? void 0 : spState.segmentData))
181
- return;
180
+ return false;
182
181
  // Load AF structure
183
- const afStrRef = spState.alphafold.ref || (yield loadAfStructure(plugin));
184
- if (!afStrRef)
185
- return;
186
- const afStr = plugin.managers.structure.hierarchy.current.refs.get(afStrRef);
187
- const segmentNum = segmentIndex ? segmentIndex : spState.activeSegment - 1;
182
+ const afStructRefString = spState.alphafold.ref || (yield loadAfStructure(plugin));
183
+ if (!afStructRefString)
184
+ return false;
185
+ const afStructRef = plugin.managers.structure.hierarchy.current.refs.get(afStructRefString);
186
+ if ((afStructRef === null || afStructRef === void 0 ? void 0 : afStructRef.kind) !== 'structure')
187
+ return false;
188
+ const segmentNum = spState.activeSegment - 1;
188
189
  if (!spState.alphafold.transforms[segmentNum]) {
189
190
  // Create representative list
190
191
  const mappingResult = [];
191
- const coordinateSystems = [];
192
- const failedPairsResult = [];
193
- const zeroOverlapPairsResult = [];
194
- let minRmsd = 0;
192
+ let minRmsd = Infinity;
195
193
  let minIndex = 0;
196
194
  const rmsdList = [];
197
195
  const segmentClusters = spState.segmentData[segmentNum].clusters;
198
- segmentClusters.forEach((cluster) => {
199
- var _a, _b, _c;
200
- const modelRef = spState.models[`${cluster[0].pdb_id}_${cluster[0].struct_asym_id}`];
201
- if (modelRef) {
202
- const structHierarchy = plugin.managers.structure.hierarchy.current.refs.get(modelRef);
203
- if (structHierarchy) {
204
- const input = [structHierarchy.components[0], afStr];
205
- const structures = input.map(s => { var _a; return (_a = s.cell.obj) === null || _a === void 0 ? void 0 : _a.data; });
206
- let { entries, failedPairs, zeroOverlapPairs } = (0, superposition_sifts_mapping_1.alignAndSuperposeWithSIFTSMapping)(structures, {
207
- traceOnly,
208
- includeResidueTest: loc => structure_1.StructureProperties.atom.B_iso_or_equiv(loc) > 70,
209
- applyTestIndex: [1],
210
- });
211
- if (entries.length === 0 || (entries && entries[0] && entries[0].transform.rmsd.toFixed(1) === '0.0')) {
212
- const alignWithoutPlddt = (0, superposition_sifts_mapping_1.alignAndSuperposeWithSIFTSMapping)(structures, { traceOnly });
213
- entries = alignWithoutPlddt.entries;
214
- }
215
- if (entries && entries[0]) {
216
- mappingResult.push(entries[0]);
217
- coordinateSystems.push((_c = (_b = (_a = input[0]) === null || _a === void 0 ? void 0 : _a.transform) === null || _b === void 0 ? void 0 : _b.cell.obj) === null || _c === void 0 ? void 0 : _c.data.coordinateSystem);
218
- const totalMappings = mappingResult.length;
219
- if (totalMappings === 1 || entries[0].transform.rmsd < minRmsd) {
220
- minRmsd = entries[0].transform.rmsd;
221
- minIndex = totalMappings === 1 ? 0 : mappingResult.length - 1;
222
- }
223
- rmsdList.push(`${cluster[0].pdb_id} chain ${cluster[0].struct_asym_id}:${entries[0].transform.rmsd.toFixed(2)}`);
224
- }
225
- else {
226
- if (failedPairs.length > 0)
227
- failedPairsResult.push(failedPairs);
228
- if (zeroOverlapPairs.length > 0)
229
- zeroOverlapPairsResult.push(zeroOverlapPairs);
230
- // rmsdList.push(`${cluster[0].pdb_id} ${cluster[0].struct_asym_id}:-`)
231
- }
196
+ for (const cluster of segmentClusters) {
197
+ const representative = cluster[0];
198
+ const structRefString = spState.models[`${representative.pdb_id}_${representative.struct_asym_id}`];
199
+ if (!structRefString)
200
+ continue;
201
+ const structRef = plugin.managers.structure.hierarchy.current.refs.get(structRefString);
202
+ if ((structRef === null || structRef === void 0 ? void 0 : structRef.kind) !== 'structure')
203
+ continue;
204
+ const structComponentRef = structRef.components[0];
205
+ const structures = [structComponentRef.cell.obj.data, afStructRef.cell.obj.data];
206
+ let { entries } = (0, superposition_sifts_mapping_1.alignAndSuperposeWithSIFTSMapping)(structures, {
207
+ traceOnly,
208
+ includeResidueTest: loc => !(loc.structure === structures[1] && structure_1.StructureProperties.atom.B_iso_or_equiv(loc) <= 70), // exclude AlphaFold residues with pLDDT <= 70
209
+ });
210
+ if (entries.length === 0 || (entries[0] && entries[0].transform.rmsd.toFixed(1) === '0.0')) {
211
+ const alignWithoutPlddt = (0, superposition_sifts_mapping_1.alignAndSuperposeWithSIFTSMapping)(structures, { traceOnly });
212
+ entries = alignWithoutPlddt.entries;
213
+ }
214
+ if (entries[0]) {
215
+ mappingResult.push(entries[0]);
216
+ if (entries[0].transform.rmsd < minRmsd) {
217
+ minRmsd = entries[0].transform.rmsd;
218
+ minIndex = mappingResult.length - 1;
232
219
  }
220
+ rmsdList.push(`${representative.pdb_id} chain ${representative.struct_asym_id}:${entries[0].transform.rmsd.toFixed(2)}`);
233
221
  }
234
- });
235
- // console.log(failedPairsResult);
236
- // console.log(zeroOverlapPairsResult);
222
+ }
237
223
  if (mappingResult.length > 0) {
238
224
  spState.alphafold.visibility[segmentNum] = true;
239
225
  spState.alphafold.transforms[segmentNum] = mappingResult[minIndex].transform.bTransform;
240
- spState.alphafold.coordinateSystems[segmentNum] = coordinateSystems[minIndex];
241
226
  spState.alphafold.rmsds[segmentNum] = rmsdList.sort((a, b) => parseFloat(a.split(':')[1]) - parseFloat(b.split(':')[1]));
242
227
  }
243
228
  }
244
- yield afTransform(plugin, afStr.cell, spState.alphafold.transforms[segmentNum], spState.alphafold.coordinateSystems[segmentNum]);
245
- (0, alphafold_transparency_1.applyAFTransparency)(plugin, afStr, 0.8, 70);
229
+ yield afTransform(plugin, afStructRef.cell, spState.alphafold.transforms[segmentNum]);
230
+ yield (0, alphafold_transparency_1.applyAFTransparency)(plugin, afStructRef, 0.8, 70);
246
231
  return true;
247
232
  });
248
233
  }
@@ -325,10 +310,6 @@ function renderSuperposition(plugin, segmentIndex, entryList) {
325
310
  yield plugin.builders.structure.representation.addRepresentation(chainSel, { type: 'putty', color: 'uniform', colorParams: { value: uniformColor2 }, size: 'uniform' }, { tag: `superposition-visual` });
326
311
  spState.refMaps[chainSel.ref] = `${s.pdb_id}_${s.struct_asym_id}`;
327
312
  }
328
- // // const addTooltipUpdate = plugin.state.behaviors.build().to(BestDatabaseSequenceMapping.id).update(BestDatabaseSequenceMapping, (old: any) => { old.showTooltip = true; });
329
- // // await plugin.runTask(plugin.state.behaviors.updateTree(addTooltipUpdate));
330
- // BestDatabaseSequenceMapping
331
- // console.log(plugin.state.data.select(modelRef)[0])
332
313
  }
333
314
  let invalidStruct = chainSel ? false : true;
334
315
  if (superpositionParams && superpositionParams.ligandView) {
@@ -391,7 +372,7 @@ function renderSuperposition(plugin, segmentIndex, entryList) {
391
372
  0: polymerChainWithSurroundings,
392
373
  by: carbEntityChain,
393
374
  });
394
- const data = (plugin.state.data.select(strInstance.ref)[0].obj).data;
375
+ const data = plugin.state.data.select(strInstance.ref)[0].obj.data;
395
376
  const carbChainSel = script_1.Script.getStructureSelection(carbEntityChainInVicinity, data);
396
377
  if (carbChainSel && carbChainSel.kind === 'sequence') {
397
378
  // console.log(carbEntityChainId + ' chain present in 5 A radius');
@@ -148,10 +148,10 @@ class AfSuperpositionControls extends base_1.PurePluginUIComponent {
148
148
  return (0, plugin_custom_state_1.PluginCustomState)(this.plugin);
149
149
  }
150
150
  superposeByDbMapping() {
151
- return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)(common_1.Button, { icon: icons_1.SuperposeChainsSvg, title: 'Superpose AlphaFold structure using intersection of residues from SIFTS UNIPROT mapping.', className: 'msp-btn msp-btn-block', onClick: this.superposeDb, style: { marginTop: '1px', textAlign: 'left' }, disabled: this.state.isBusy, children: "Load AlphaFold structure" }) });
151
+ return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)(common_1.Button, { icon: icons_1.AddSvg, title: 'Superpose AlphaFold structure using intersection of residues from SIFTS UNIPROT mapping.', className: 'msp-btn msp-btn-block', onClick: this.superposeDb, style: { marginTop: '1px', textAlign: 'left' }, disabled: this.state.isBusy, children: "Load AlphaFold structure" }) });
152
152
  }
153
153
  render() {
154
- return (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { style: { backgroundColor: '#dce54e', fontWeight: 500, padding: '5px 12px' }, children: "New Feature!" }), (0, jsx_runtime_1.jsx)("div", { className: 'msp-help-text', style: { margin: '2px 0' }, children: (0, jsx_runtime_1.jsxs)("div", { className: 'msp-help-description', children: [(0, jsx_runtime_1.jsx)(icons_1.Icon, { svg: icons_2.InfoIconSvg, inline: true }), "Load and superpose AlphaFold structure against representative chains."] }) }), (0, jsx_runtime_1.jsxs)("div", { className: 'msp-flex-row', children: [this.state.canUseDb && this.superposeByDbMapping(), (0, jsx_runtime_1.jsx)(common_1.ToggleButton, { icon: icons_1.TuneSvg, label: '', title: 'Options', toggle: this.toggleOptions, isSelected: this.state.action === 'options', disabled: this.state.isBusy, style: { flex: '0 0 40px', padding: 0 } })] }), this.state.action === 'options' && (0, jsx_runtime_1.jsx)("div", { className: 'msp-control-offset', children: (0, jsx_runtime_1.jsx)(parameters_1.ParameterControls, { params: exports.AlphafoldSuperpositionParams, values: this.state.options, onChangeValues: this.setOptions, isDisabled: this.state.isBusy }) })] });
154
+ return (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: 'msp-help-text', style: { margin: '2px 0' }, children: (0, jsx_runtime_1.jsxs)("div", { className: 'msp-help-description', children: [(0, jsx_runtime_1.jsx)(icons_1.Icon, { svg: icons_2.InfoIconSvg, inline: true }), "Load and superpose AlphaFold structure against representative chains."] }) }), (0, jsx_runtime_1.jsxs)("div", { className: 'msp-flex-row', children: [this.state.canUseDb && this.superposeByDbMapping(), (0, jsx_runtime_1.jsx)(common_1.ToggleButton, { icon: icons_1.TuneSvg, label: '', title: 'Options', toggle: this.toggleOptions, isSelected: this.state.action === 'options', disabled: this.state.isBusy, style: { flex: '0 0 40px', padding: 0 } })] }), this.state.action === 'options' && (0, jsx_runtime_1.jsx)("div", { className: 'msp-control-offset', children: (0, jsx_runtime_1.jsx)(parameters_1.ParameterControls, { params: exports.AlphafoldSuperpositionParams, values: this.state.options, onChangeValues: this.setOptions, isDisabled: this.state.isBusy }) })] });
155
155
  }
156
156
  }
157
157
  exports.AfSuperpositionControls = AfSuperpositionControls;
package/lib/viewer.js CHANGED
@@ -776,7 +776,7 @@ class PDBeMolstarPlugin {
776
776
  return;
777
777
  const asm = this.state.select(this.assemblyRef)[0].obj;
778
778
  const defaultMapParams = transformers_1.InitVolumeStreaming.createDefaultParams(asm, this.plugin);
779
- const pdbeMapParams = helpers_1.PDBeVolumes.mapParams(defaultMapParams, this.initParams.mapSettings, '');
779
+ const pdbeMapParams = helpers_1.PDBeVolumes.mapParams(defaultMapParams, this.initParams.mapSettings);
780
780
  if (pdbeMapParams) {
781
781
  yield this.plugin.runTask(this.state.applyAction(transformers_1.InitVolumeStreaming, pdbeMapParams, this.assemblyRef));
782
782
  if (pdbeMapParams.method !== 'em' && !this.initParams.ligandView)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pdbe-molstar",
3
- "version": "3.6.0",
3
+ "version": "3.7.0",
4
4
  "description": "Molstar implementation for PDBe",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,22 +0,0 @@
1
- import { MinimizeRmsd } from 'molstar/lib/mol-math/linear-algebra/3d/minimize-rmsd';
2
- import { Structure } from 'molstar/lib/mol-model/structure';
3
- import { ElementIndex, ResidueIndex } from 'molstar/lib/mol-model/structure/model/indexing';
4
- import { StructureElement } from 'molstar/lib/mol-model/structure/structure/element';
5
- import { Unit } from 'molstar/lib/mol-model/structure/structure/unit';
6
- export interface AlignmentResultEntry {
7
- transform: MinimizeRmsd.Result;
8
- pivot: number;
9
- other: number;
10
- }
11
- export interface AlignmentResult {
12
- entries: AlignmentResultEntry[];
13
- zeroOverlapPairs: [number, number][];
14
- failedPairs: [number, number][];
15
- }
16
- type IncludeResidueTest = (traceElementOrFirstAtom: StructureElement.Location<Unit.Atomic>, residueIndex: ResidueIndex, startIndex: ElementIndex, endIndex: ElementIndex) => boolean;
17
- export declare function alignAndSuperposeWithSIFTSMapping(structures: Structure[], options?: {
18
- traceOnly?: boolean;
19
- includeResidueTest?: IncludeResidueTest;
20
- applyTestIndex?: number[];
21
- }): AlignmentResult;
22
- export {};
@@ -1,153 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.alignAndSuperposeWithSIFTSMapping = alignAndSuperposeWithSIFTSMapping;
4
- const int_1 = require("molstar/lib/mol-data/int");
5
- const minimize_rmsd_1 = require("molstar/lib/mol-math/linear-algebra/3d/minimize-rmsd");
6
- const element_1 = require("molstar/lib/mol-model/structure/structure/element");
7
- const sifts_mapping_1 = require("./sifts-mapping");
8
- function alignAndSuperposeWithSIFTSMapping(structures, options) {
9
- var _a, _b;
10
- const indexMap = new Map();
11
- for (let i = 0; i < structures.length; i++) {
12
- let includeResidueTest = (_a = options === null || options === void 0 ? void 0 : options.includeResidueTest) !== null && _a !== void 0 ? _a : _includeAllResidues;
13
- if ((options === null || options === void 0 ? void 0 : options.applyTestIndex) && !options.applyTestIndex.includes(i))
14
- includeResidueTest = _includeAllResidues;
15
- buildIndex(structures[i], indexMap, i, (_b = options === null || options === void 0 ? void 0 : options.traceOnly) !== null && _b !== void 0 ? _b : true, includeResidueTest);
16
- }
17
- const index = Array.from(indexMap.values());
18
- // TODO: support non-first structure pivots
19
- const pairs = findPairs(structures.length, index);
20
- const zeroOverlapPairs = [];
21
- const failedPairs = [];
22
- const entries = [];
23
- for (const p of pairs) {
24
- if (p.count === 0) {
25
- zeroOverlapPairs.push([p.i, p.j]);
26
- }
27
- else {
28
- const [a, b] = getPositionTables(index, p.i, p.j, p.count);
29
- const transform = minimize_rmsd_1.MinimizeRmsd.compute({ a, b });
30
- if (Number.isNaN(transform.rmsd)) {
31
- failedPairs.push([p.i, p.j]);
32
- }
33
- else {
34
- entries.push({ transform, pivot: p.i, other: p.j });
35
- }
36
- }
37
- }
38
- return { entries, zeroOverlapPairs, failedPairs };
39
- }
40
- function getPositionTables(index, pivot, other, N) {
41
- const xs = minimize_rmsd_1.MinimizeRmsd.Positions.empty(N);
42
- const ys = minimize_rmsd_1.MinimizeRmsd.Positions.empty(N);
43
- let o = 0;
44
- for (const { pivots } of index) {
45
- const a = pivots[pivot];
46
- const b = pivots[other];
47
- if (!a || !b)
48
- continue;
49
- const l = Math.min(a[2] - a[1], b[2] - b[1]);
50
- // TODO: check if residue types match?
51
- for (let i = 0; i < l; i++) {
52
- let eI = (a[1] + i);
53
- xs.x[o] = a[0].conformation.x(eI);
54
- xs.y[o] = a[0].conformation.y(eI);
55
- xs.z[o] = a[0].conformation.z(eI);
56
- eI = (b[1] + i);
57
- ys.x[o] = b[0].conformation.x(eI);
58
- ys.y[o] = b[0].conformation.y(eI);
59
- ys.z[o] = b[0].conformation.z(eI);
60
- o++;
61
- }
62
- }
63
- return [xs, ys];
64
- }
65
- function findPairs(N, index) {
66
- const pairwiseCounts = [];
67
- for (let i = 0; i < N; i++) {
68
- pairwiseCounts[i] = [];
69
- for (let j = 0; j < N; j++)
70
- pairwiseCounts[i][j] = 0;
71
- }
72
- for (const { pivots } of index) {
73
- for (let i = 0; i < N; i++) {
74
- if (!pivots[i])
75
- continue;
76
- const lI = pivots[i][2] - pivots[i][1];
77
- for (let j = i + 1; j < N; j++) {
78
- if (!pivots[j])
79
- continue;
80
- const lJ = pivots[j][2] - pivots[j][1];
81
- pairwiseCounts[i][j] = pairwiseCounts[i][j] + Math.min(lI, lJ);
82
- }
83
- }
84
- }
85
- const ret = [];
86
- for (let j = 1; j < N; j++) {
87
- ret[j - 1] = { i: 0, j, count: pairwiseCounts[0][j] };
88
- }
89
- // TODO: support non-first structure pivots
90
- // for (let i = 0; i < N - 1; i++) {
91
- // let max = 0, maxJ = i;
92
- // for (let j = i + 1; j < N; j++) {
93
- // if (pairwiseCounts[i][j] > max) {
94
- // maxJ = j;
95
- // max = pairwiseCounts[i][j];
96
- // }
97
- // }
98
- // ret[i] = { i, j: maxJ, count: max };
99
- // }
100
- return ret;
101
- }
102
- function _includeAllResidues() { return true; }
103
- function buildIndex(structure, index, sI, traceOnly, includeTest) {
104
- const loc = element_1.StructureElement.Location.create(structure);
105
- for (const unit of structure.units) {
106
- if (unit.kind !== 0 /* Unit.Kind.Atomic */)
107
- continue;
108
- const { elements, model } = unit;
109
- loc.unit = unit;
110
- const map = sifts_mapping_1.SIFTSMapping.Provider.get(model).value;
111
- if (!map)
112
- return;
113
- const { dbName, accession, num } = map;
114
- const chainsIt = int_1.Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, elements);
115
- const residuesIt = int_1.Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, elements);
116
- const traceElementIndex = unit.model.atomicHierarchy.derived.residue.traceElementIndex;
117
- while (chainsIt.hasNext) {
118
- const chainSegment = chainsIt.move();
119
- residuesIt.setSegment(chainSegment);
120
- while (residuesIt.hasNext) {
121
- const residueSegment = residuesIt.move();
122
- const rI = residueSegment.index;
123
- if (!dbName[rI])
124
- continue;
125
- const traceElement = traceElementIndex[rI];
126
- let start, end;
127
- if (traceOnly) {
128
- start = traceElement;
129
- if (start === -1)
130
- continue;
131
- end = start + 1;
132
- }
133
- else {
134
- start = elements[residueSegment.start];
135
- end = elements[residueSegment.end - 1] + 1;
136
- }
137
- loc.element = (traceElement >= 0 ? traceElement : start);
138
- if (!includeTest(loc, rI, start, end))
139
- continue;
140
- const key = `${dbName[rI]}-${accession[rI].split('-')[0]}-${num[rI]}`;
141
- if (!index.has(key)) {
142
- index.set(key, { key, pivots: { [sI]: [unit, start, end] } });
143
- }
144
- else {
145
- const entry = index.get(key);
146
- if (!entry.pivots[sI]) {
147
- entry.pivots[sI] = [unit, start, end];
148
- }
149
- }
150
- }
151
- }
152
- }
153
- }