diff --git a/src/mol-model/structure/structure/element/loci.ts b/src/mol-model/structure/structure/element/loci.ts index 64ed577f291a445a739ba15932c212f7f14dd374..8f6d7f8eb03a35ad45a89b6f6480daf7ba19c510 100644 --- a/src/mol-model/structure/structure/element/loci.ts +++ b/src/mol-model/structure/structure/element/loci.ts @@ -17,6 +17,7 @@ import { sortArray, hashFnv32a, hash2 } from '../../../../mol-data/util'; import Expression from '../../../../mol-script/language/expression'; import { ElementIndex } from '../../model'; import { UnitIndex } from './element'; +import { Location } from './location'; /** Represents multiple element index locations */ export interface Loci { @@ -78,6 +79,18 @@ export namespace Loci { return Loci(structure, []); } + export function getFirstLocation(loci: Loci, e?: Location): Location | undefined { + if (isEmpty(loci)) return void 0; + const unit = loci.elements[0].unit; + const element = unit.elements[OrderedSet.getAt(loci.elements[0].indices, 0)]; + if (e) { + e.unit = loci.elements[0].unit; + e.element = element; + return e; + } + return Location.create(unit, element); + } + export function toStructure(loci: Loci): Structure { const units: Unit[] = [] for (const e of loci.elements) { diff --git a/src/mol-plugin/skin/base/components/sequence.scss b/src/mol-plugin/skin/base/components/sequence.scss index a5587b3b0aa81df5d713e8aba660a7c9300c68aa..89f1656406c52841174086fc3f117242c5b6ed67 100644 --- a/src/mol-plugin/skin/base/components/sequence.scss +++ b/src/mol-plugin/skin/base/components/sequence.scss @@ -30,4 +30,13 @@ span { cursor: pointer; } + + .msp-sequence-marker { + color: $sequence-marker-color; + // vertical-align: middle; + text-decoration: underline; + padding: 0 0 0 1ch; + word-break: keep-all; + cursor: default; + } } \ No newline at end of file diff --git a/src/mol-plugin/skin/base/variables.scss b/src/mol-plugin/skin/base/variables.scss index bf88c3345a2bc9a865b003b710881dfa939ec945..dcb6a902162327704aa45ce6ac936f2fd714299d 100644 --- a/src/mol-plugin/skin/base/variables.scss +++ b/src/mol-plugin/skin/base/variables.scss @@ -80,4 +80,5 @@ $entity-tag-color: color-lower-contrast($font-color, 20%); // sequence $sequence-background: $default-background; +$sequence-marker-color: $hover-font-color; $sequence-select-width: 300px; \ No newline at end of file diff --git a/src/mol-plugin/ui/sequence/sequence.tsx b/src/mol-plugin/ui/sequence/sequence.tsx index 1c69e850185ee835efe09d8122d5390496963b37..20b96b80243f8718598c6f14e7d19487f4324c0d 100644 --- a/src/mol-plugin/ui/sequence/sequence.tsx +++ b/src/mol-plugin/ui/sequence/sequence.tsx @@ -11,12 +11,14 @@ import { Interactivity } from '../../util/interactivity'; import { MarkerAction } from '../../../mol-util/marker-action'; import { ButtonsType, ModifiersKeys, getButtons, getModifiers } from '../../../mol-util/input/input-observer'; import { SequenceWrapper } from './wrapper'; -import { StructureElement } from '../../../mol-model/structure'; +import { StructureElement, StructureProperties } from '../../../mol-model/structure'; import { Subject } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; import { Color } from '../../../mol-util/color'; -type SequenceProps = { sequenceWrapper: SequenceWrapper.Any } +type SequenceProps = { sequenceWrapper: SequenceWrapper.Any, hideMarkers?: boolean } + +const SequenceMarkerPeriod = 50 // TODO: this is somewhat inefficient and should be done using a canvas. export class Sequence<P extends SequenceProps> extends PluginUIComponent<P> { @@ -101,10 +103,16 @@ export class Sequence<P extends SequenceProps> extends PluginUIComponent<P> { if (!this.parentDiv.current) return; const xs = this.parentDiv.current.children; const { markerArray } = this.props.sequenceWrapper; + const markers = !this.props.hideMarkers; + let o = 0; for (let i = 0, _i = markerArray.length; i < _i; i++) { - const span = xs[i] as HTMLSpanElement; - if (!span) continue; + const span = xs[o] as HTMLSpanElement; + if (!span) return; + o++; + if (markers && i > 0 && i % SequenceMarkerPeriod === 0) { + o++; + } const backgroundColor = this.getBackgroundColor(markerArray[i]); if (span.style.backgroundColor !== backgroundColor) span.style.backgroundColor = backgroundColor; @@ -147,9 +155,21 @@ export class Sequence<P extends SequenceProps> extends PluginUIComponent<P> { const sw = this.props.sequenceWrapper const elems: JSX.Element[] = []; + const markers = !this.props.hideMarkers; + const location = StructureElement.Location.create(); for (let i = 0, il = sw.length; i < il; ++i) { elems[elems.length] = this.residue(i, sw.residueLabel(i), sw.markerArray[i], sw.residueColor(i)); - // TODO: add seq idx markers every N residues? Would need to modify "updateMarker" + if (markers && i > 0 && i % SequenceMarkerPeriod === 0) { + if (i === sw.length - 1) break; + // TODO: is this correct way to show the offset? + const l = StructureElement.Loci.getFirstLocation(sw.getLoci(i + 1), location); + if (l) { + elems[elems.length] = <span key={`marker-${i}`} className='msp-sequence-marker'>{StructureProperties.residue.auth_seq_id(l)}</span> + } else { + // do not show the marker if the seq id is not known. + elems[elems.length] = <span key={`marker-${i}`} className='msp-sequence-marker'></span> + } + } } // calling .updateMarker here is neccesary to ensure existing