From f6b2c0b2ba203a69d546869ea27d06e3507d9082 Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Mon, 17 Jun 2019 16:44:38 -0700 Subject: [PATCH] wip, sequence view --- src/mol-plugin/ui/sequence.tsx | 55 +++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/src/mol-plugin/ui/sequence.tsx b/src/mol-plugin/ui/sequence.tsx index a4375eaad..63ae9ebf7 100644 --- a/src/mol-plugin/ui/sequence.tsx +++ b/src/mol-plugin/ui/sequence.tsx @@ -16,9 +16,15 @@ import { Loci } from '../../mol-model/loci'; import { applyMarkerAction, MarkerAction } from '../../mol-geo/geometry/marker-data'; import { ButtonsType, ModifiersKeys, getButtons, getModifiers } from '../../mol-util/input/input-observer'; +function getStructureSeqKey(structureSeq: StructureSeq) { + const { structure, seq } = structureSeq + const strucHash = structure.parent ? structure.parent.hashCode : structure.hashCode + return `${strucHash}|${seq.entityId}` +} export class SequenceView extends PluginUIComponent<{ }, { }> { private spine: StateTreeSpine.Impl + private markerArrays = new Map<string, Uint8Array>() componentDidMount() { this.spine = new StateTreeSpine.Impl(this.plugin.state.dataState.cells); @@ -42,14 +48,23 @@ export class SequenceView extends PluginUIComponent<{ }, { }> { } render() { - const s = this.getStructure(); - if (!s) return <div className='msp-sequence'> + const structure = this.getStructure(); + if (!structure) return <div className='msp-sequence'> <div className='msp-sequence-entity'>No structure available</div> </div>; - const seqs = s.models[0].sequence.sequences; + const seqs = structure.models[0].sequence.sequences; return <div className='msp-sequence'> - {seqs.map((seq, i) => <EntitySequence key={i} seq={seq} structure={s} /> )} + {seqs.map((seq, i) => { + const structureSeq = { structure, seq } + const key = getStructureSeqKey(structureSeq) + let markerArray = this.markerArrays.get(key) + if (!markerArray) { + markerArray = new Uint8Array(seq.sequence.sequence.length) + this.markerArrays.set(key, markerArray) + } + return <EntitySequence key={i} structureSeq={structureSeq} markerArray={markerArray} /> + })} </div>; } } @@ -127,26 +142,36 @@ function markResidue(loci: Loci, structureSeq: StructureSeq, array: Uint8Array, }) } +type EntitySequenceProps = { structureSeq: StructureSeq, markerArray: Uint8Array } +type EntitySequenceState = { markerData: { array: Uint8Array } } + // TODO: this is really inefficient and should be done using a canvas. -class EntitySequence extends PluginUIComponent<{ seq: StructureSequence.Entity, structure: Structure }, { markerData: { array: Uint8Array } }> { +class EntitySequence extends PluginUIComponent<EntitySequenceProps, EntitySequenceState> { state = { - markerData: { array: new Uint8Array(this.props.seq.sequence.sequence.length) } + markerData: { array: new Uint8Array(this.props.markerArray) } } private lociHighlightProvider = (loci: Interaction.Loci, action: MarkerAction) => { const { array } = this.state.markerData; - const { structure, seq } = this.props - const changed = markResidue(loci.loci, { structure , seq }, array, action) + const { structureSeq } = this.props + const changed = markResidue(loci.loci, structureSeq, array, action) if (changed) this.setState({ markerData: { array } }) } private lociSelectionProvider = (loci: Interaction.Loci, action: MarkerAction) => { const { array } = this.state.markerData; - const { structure, seq } = this.props - const changed = markResidue(loci.loci, { structure , seq }, array, action) + const { structureSeq } = this.props + const changed = markResidue(loci.loci, structureSeq, array, action) if (changed) this.setState({ markerData: { array } }) } + static getDerivedStateFromProps(nextProps: EntitySequenceProps, prevState: EntitySequenceState) { + if (prevState.markerData.array !== nextProps.markerArray) { + return { markerData: { array: nextProps.markerArray } } + } + return null + } + componentDidMount() { this.plugin.lociHighlights.addProvider(this.lociHighlightProvider) this.plugin.lociSelections.addProvider(this.lociSelectionProvider) @@ -158,8 +183,9 @@ class EntitySequence extends PluginUIComponent<{ seq: StructureSequence.Entity, } getLoci(seqId: number) { - const query = createQuery(this.props.seq.entityId, seqId); - return StructureSelection.toLoci2(StructureQuery.run(query, this.props.structure)); + const { structure, seq } = this.props.structureSeq + const query = createQuery(seq.entityId, seqId); + return StructureSelection.toLoci2(StructureQuery.run(query, structure)); } highlight(seqId?: number, modifiers?: ModifiersKeys) { @@ -192,7 +218,7 @@ class EntitySequence extends PluginUIComponent<{ seq: StructureSequence.Entity, render() { const { markerData } = this.state; - const { seq } = this.props; + const { seq } = this.props.structureSeq; const { offset, sequence } = seq.sequence; const elems: JSX.Element[] = []; @@ -205,7 +231,7 @@ class EntitySequence extends PluginUIComponent<{ seq: StructureSequence.Entity, onContextMenu={this.contextMenu} onMouseDown={this.mouseDown} > - <span style={{ fontWeight: 'bold' }}>{this.props.seq.entityId}:{offset} </span> + <span style={{ fontWeight: 'bold' }}>{seq.entityId}:{offset} </span> {elems} </div>; } @@ -233,6 +259,7 @@ class Residue extends PluginUIComponent<{ seqId: number, letter: string, parent: // TODO make marker color configurable if (this.props.marker === 0) return '' if (this.props.marker % 2 === 0) return 'rgb(51, 255, 25)' // selected + if (this.props.marker === undefined) console.error('unexpected marker value') return 'rgb(255, 102, 153)' // highlighted } -- GitLab