Skip to content
Snippets Groups Projects
Commit e301eca9 authored by Alexander Rose's avatar Alexander Rose
Browse files

wip, sequence view, select options

parent 8a4ef015
No related branches found
No related tags found
No related merge requests found
......@@ -8,42 +8,42 @@
select, button, input[type=text] {
@extend .msp-form-control;
}
button {
@extend .msp-btn;
@extend .msp-btn-block;
}
}
.msp-control-row {
.msp-control-row {
position: relative;
height: $row-height;
background: $default-background;
margin-top: 1px;
> span {
line-height: $row-height;
display: block;
width: $control-label-width + $control-spacing;
text-align: right;
padding: 0 $control-spacing;
padding: 0 $control-spacing;
color: color-lower-contrast($font-color, 15%);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@include non-selectable;
@include non-selectable;
}
select, button, input[type=text] {
@extend .msp-form-control;
}
button {
@extend .msp-btn;
@extend .msp-btn-block;
}
> div:nth-child(2) {
background: $msp-form-control-background;
position: absolute;
......@@ -58,21 +58,21 @@
position: relative;
}
.msp-toggle-button {
.msp-toggle-button {
.msp-icon {
display: inline-block;
margin-right: 6px;
}
> div > button:hover {
border-color: color-increase-contrast($msp-form-control-background, 5%) !important;
border: none;
outline-offset: -1px !important;
outline: 1px solid color-increase-contrast($msp-form-control-background, 20%) !important;
outline: 1px solid color-increase-contrast($msp-form-control-background, 20%) !important;
}
}
.msp-slider {
.msp-slider {
> div:first-child {
position: absolute;
top: 0;
......@@ -91,14 +91,14 @@
top: 0;
bottom: 0;
}
input[type=text] {
padding-right: 6px;
padding-left: 4px;
font-size: 80%;
text-align: right;
}
// input[type=range] {
// width: 100%;
// }
......@@ -135,14 +135,14 @@
bottom: 0;
font-size: 80%;
}
input[type=text] {
padding-right: 4px;
padding-left: 4px;
font-size: 80%;
text-align: center;
}
// input[type=range] {
// width: 100%;
// }
......@@ -154,24 +154,24 @@
margin: 0;
text-align: center;
padding-right: $control-spacing;
padding-left: $control-spacing;
padding-left: $control-spacing;
&:hover {
border-color: color-increase-contrast($msp-form-control-background, 5%) !important;
border: none;
outline-offset: -1px !important;
outline: 1px solid color-increase-contrast($msp-form-control-background, 20%) !important;
outline: 1px solid color-increase-contrast($msp-form-control-background, 20%) !important;
}
}
.msp-color-picker {
position: absolute;
z-index: 100000;
background: $default-background;
border-top: 1px solid $default-background;
padding-bottom: $control-spacing / 2;
width: 100%;
width: 100%;
// input[type=text] {
// background: $msp-form-control-background !important;
// }
......@@ -217,7 +217,7 @@
height: 2 * $row-height / 3 !important;
line-height: 2 * $row-height / 3 !important;
font-size: 70% !important;
background: $default-background !important;
background: $default-background !important;
color: color-lower-contrast($font-color, 15%) !important;
}
}
......@@ -231,13 +231,13 @@
.msp-control-subgroup {
margin-top: 1px;
.msp-control-row {
margin-left: $control-spacing !important;
> span {
width: $control-label-width !important;
}
> div:nth-child(2) {
left: $control-label-width !important;
}
......@@ -254,7 +254,7 @@
width: $control-label-width + $control-spacing;
text-align: left;
background: transparent;
.msp-icon {
line-height: $row-height - 3;
width: $row-height - 1;
......
......@@ -4,19 +4,26 @@
top: 0;
left: 0;
bottom: 0;
overflow-y: scroll;
overflow-x: hidden;
font-size: 90%;
background: $sequence-background;
}
.msp-sequence-entity {
.msp-sequence-select {
float: left;
width: $sequence-select-width;
}
.msp-sequence-wrapper {
word-break: break-word;
padding: $info-vertical-padding $control-spacing $info-vertical-padding $control-spacing;
user-select: none;
height: 100%;
overflow-y: auto;
overflow-x: hidden;
font-size: 90%;
}
.msp-sequence-entity {
.msp-sequence-wrapper {
span {
cursor: pointer;
}
......
......@@ -78,4 +78,5 @@ $entity-color-partialy-visible: color-lower-contrast($font-color, 33%);
$entity-tag-color: color-lower-contrast($font-color, 20%);
// sequence
$sequence-background: $default-background;
\ No newline at end of file
$sequence-background: $default-background;
$sequence-select-width: 300px;
\ No newline at end of file
......@@ -18,92 +18,183 @@ import { MarkerAction } from '../../mol-util/marker-action';
import { ParameterControls } from './controls/parameters';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
function getSequenceWrapperForStructure(index: number, structure: Structure, structureSelection: StructureElementSelectionManager): SequenceWrapper.Any | undefined {
let j = 0
function opKey(ids: string[]) {
return ids.sort().join(',')
}
function getSequenceWrapper(state: SequenceViewState, structureSelection: StructureElementSelectionManager): SequenceWrapper.Any | undefined {
const { structure, entity, chain, operator } = state
const l = StructureElement.create()
for (let i = 0, il = structure.units.length; i < il; ++i) {
const unit = structure.units[i]
if (unit.polymerElements.length === 0) continue
if (j === index) {
const sw = new PolymerSequenceWrapper({ structure, unit })
sw.markResidue(structureSelection.get(structure), MarkerAction.Select)
return sw
}
j += 1
StructureElement.set(l, unit, unit.elements[0])
if (SP.entity.id(l) !== entity) continue
if (SP.chain.label_asym_id(l) !== chain) continue
if (opKey(SP.unit.pdbx_struct_oper_list_ids(l)) !== operator) continue
// console.log('new PolymerSequenceWrapper', structureSelection.get(structure))
const sw = new PolymerSequenceWrapper({ structure, unit })
sw.markResidue(structureSelection.get(structure), MarkerAction.Select)
return sw
}
}
function getPolymerOptionsForStructure(structure: Structure) {
const options: [number, string][] = []
function getEntityOptions(structure: Structure) {
const options: [string, string][] = []
const l = StructureElement.create()
const seen = new Set<string>()
structure.units.forEach(unit => {
if (unit.polymerElements.length === 0) return
StructureElement.set(l, unit, unit.elements[0])
const id = SP.entity.id(l)
if (seen.has(id)) return
const label = `${id}: ${SP.entity.pdbx_description(l).join(', ')}`
options.push([ id, label ])
seen.add(id)
})
if (options.length === 0) options.push(['', 'No entities'])
return options
}
function getChainOptions(structure: Structure, entityId: string) {
const options: [string, string][] = []
const l = StructureElement.create()
const seen = new Set<string>()
let i = 0
structure.units.forEach(unit => {
if (unit.polymerElements.length === 0) return
const l = StructureElement.create(unit, unit.elements[0])
const entityDescription = SP.entity.pdbx_description(l)
const label_asym_id = SP.chain.label_asym_id(l)
const label = `${label_asym_id}: ${entityDescription}`
StructureElement.set(l, unit, unit.elements[0])
if (SP.entity.id(l) !== entityId) return
const id = SP.chain.label_asym_id(l)
if (seen.has(id)) return
options.push([ i, label ])
i += 1
const label = `${id}: ${SP.chain.auth_asym_id(l)}`
options.push([ id, label ])
seen.add(id)
})
if (options.length === 0) options.push(['', 'No chains'])
return options
}
export class SequenceView extends PluginUIComponent<{ }, { polymer: number }> {
function getOperatorOptions(structure: Structure, entityId: string, label_asym_id: string) {
const options: [string, string][] = []
const l = StructureElement.create()
const seen = new Set<string>()
structure.units.forEach(unit => {
if (unit.polymerElements.length === 0) return
StructureElement.set(l, unit, unit.elements[0])
if (SP.entity.id(l) !== entityId) return
if (SP.chain.label_asym_id(l) !== label_asym_id) return
const id = opKey(SP.unit.pdbx_struct_oper_list_ids(l))
if (seen.has(id)) return
const label = `${SP.unit.pdbx_struct_oper_list_ids(l).join(', ')}`
options.push([ id, label ])
seen.add(id)
})
if (options.length === 0) options.push(['', 'No operators'])
return options
}
type SequenceViewState = { structure: Structure, entity: string, chain: string, operator: string }
export class SequenceView extends PluginUIComponent<{ }, SequenceViewState> {
private spine: StateTreeSpine.Impl
state = { polymer: 0 }
state = { structure: Structure.Empty, entity: '', chain: '', operator: '' }
componentDidMount() {
constructor(props: {}, context?: any) {
super(props, context);
this.spine = new StateTreeSpine.Impl(this.plugin.state.dataState.cells);
}
componentDidMount() {
this.subscribe(this.plugin.state.behavior.currentObject, o => {
const current = this.plugin.state.dataState.cells.get(o.ref)!;
this.spine.current = current
this.forceUpdate();
this.setState(this.getInitialState())
});
this.subscribe(this.plugin.events.state.object.updated, ({ ref, state }) => {
const current = this.spine.current;
if (!current || current.sourceRef !== ref || current.state !== state) return;
this.forceUpdate();
this.setState(this.getInitialState())
});
}
private getStructure() {
const so = this.spine && this.spine.getRootOfType(SO.Molecule.Structure)
return so && so.data
const so = this.spine.getRootOfType(SO.Molecule.Structure)
return (so && so.data) || Structure.Empty
}
private getSequenceWrapper() {
return getSequenceWrapper(this.state, this.plugin.helpers.structureSelection)
}
private getInitialState(): SequenceViewState {
const structure = this.getStructure()
const entity = getEntityOptions(structure)[0][0]
const chain = getChainOptions(structure, entity)[0][0]
const operator = getOperatorOptions(structure, entity, chain)[0][0]
return { structure, entity, chain, operator }
}
private getParams(structure: Structure) {
private get params() {
const { structure, entity, chain } = this.state
const entityOptions = getEntityOptions(structure)
const chainOptions = getChainOptions(structure, entity)
const operatorOptions = getOperatorOptions(structure, entity, chain)
return {
polymer: PD.Select(0, getPolymerOptionsForStructure(structure))
entity: PD.Select(entityOptions[0][0], entityOptions),
chain: PD.Select(chainOptions[0][0], chainOptions),
operator: PD.Select(operatorOptions[0][0], operatorOptions)
}
}
private setParamProps = (p: { param: PD.Base<any>, name: string, value: any }) => {
if (p.name === 'polymer') {
this.setState({ polymer: p.value })
const state = { ...this.state }
switch (p.name) {
case 'entity':
state.entity = p.value
state.chain = getChainOptions(state.structure, state.entity)[0][0]
state.operator = getOperatorOptions(state.structure, state.entity, state.chain)[0][0]
break
case 'chain':
state.chain = p.value
state.operator = getOperatorOptions(state.structure, state.entity, state.chain)[0][0]
break
case 'operator':
state.operator = p.value
break
}
this.setState(state)
}
render() {
const structure = this.getStructure();
if (!structure) return <div className='msp-sequence'>
<div className='msp-sequence-entity'>No structure available</div>
if (this.state.structure === Structure.Empty) return <div className='msp-sequence'>
<div className='msp-sequence-wrapper'>No structure available</div>
</div>;
const { structureSelection } = this.plugin.helpers
const params = this.getParams(structure)
const sequenceWrapper = getSequenceWrapperForStructure(this.state.polymer, structure, structureSelection)
const sequenceWrapper = this.getSequenceWrapper()
return <div className='msp-sequence'>
<ParameterControls params={params} values={this.state} onChange={this.setParamProps} />
<div className='msp-sequence-select'>
<ParameterControls params={this.params} values={this.state} onChange={this.setParamProps} />
</div>
{sequenceWrapper !== undefined
? <Sequence sequenceWrapper={sequenceWrapper} />
: <div className='msp-sequence-entity'>No sequence available</div>}
: <div className='msp-sequence-wrapper'>No sequence available</div>}
</div>;
}
}
\ No newline at end of file
......@@ -19,11 +19,11 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> {
eachResidue(loci: Loci, apply: (interval: Interval) => boolean) {
let changed = false
const { structure } = this.data
const { structure, unit } = this.data
if (!StructureElement.isLoci(loci)) return false
if (!Structure.areParentsEquivalent(loci.structure, structure)) return false
const { location, entityId, label_asym_id } = this
const { location, label_asym_id } = this
for (const e of loci.elements) {
let rIprev = -1
location.unit = e.unit
......@@ -35,7 +35,7 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> {
const rI = residueIndex[location.element]
// avoid checking for the same residue multiple times
if (rI !== rIprev) {
if (SP.entity.id(location) !== entityId) return
if (SP.unit.id(location) !== unit.id) return
if (SP.chain.label_asym_id(location) !== label_asym_id) return
if (apply(getSeqIdInterval(location))) changed = true
......@@ -47,7 +47,7 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> {
}
getLoci(seqId: number) {
const query = createResidueQuery(this.entityId, seqId, this.label_asym_id);
const query = createResidueQuery(this.data.unit.id, seqId);
return StructureSelection.toLoci2(StructureQuery.run(query, this.data.structure));
}
......@@ -67,13 +67,10 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> {
}
}
function createResidueQuery(entityId: string, label_seq_id: number, label_asym_id: string) {
function createResidueQuery(unitId: number, label_seq_id: number) {
return Queries.generators.atoms({
entityTest: ctx => {
return SP.entity.id(ctx.element) === entityId
},
chainTest: ctx => {
return SP.chain.label_asym_id(ctx.element) === label_asym_id
unitTest: ctx => {
return SP.unit.id(ctx.element) === unitId
},
residueTest: ctx => {
if (ctx.element.unit.kind === Unit.Kind.Atomic) {
......
......@@ -96,7 +96,7 @@ export class Sequence<P extends SequenceProps> extends PluginUIComponent<P, Sequ
}
return <div
className='msp-sequence-entity'
className='msp-sequence-wrapper'
onContextMenu={this.contextMenu}
onMouseDown={this.mouseDown}
>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment