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

wip, measurements ui

parent ad379e7a
No related branches found
No related tags found
No related merge requests found
/** /**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* *
* @author David Sehnal <david.sehnal@gmail.com> * @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Alexander Rose <alexander.rose@weirdbyte.de>
...@@ -18,6 +18,7 @@ import { ModelFromTrajectory } from '../mol-plugin-state/transforms/model'; ...@@ -18,6 +18,7 @@ import { ModelFromTrajectory } from '../mol-plugin-state/transforms/model';
import { AnimationControls } from './state/animation'; import { AnimationControls } from './state/animation';
import { StructureRepresentationControls } from './structure/representation'; import { StructureRepresentationControls } from './structure/representation';
import { StructureSelectionControls } from './structure/selection'; import { StructureSelectionControls } from './structure/selection';
import { StructureMeasurementsControls } from './structure/measurements';
export class TrajectoryViewportControls extends PluginUIComponent<{}, { show: boolean, label: string }> { export class TrajectoryViewportControls extends PluginUIComponent<{}, { show: boolean, label: string }> {
state = { show: false, label: '' } state = { show: false, label: '' }
...@@ -266,6 +267,7 @@ export class StructureToolsWrapper extends PluginUIComponent { ...@@ -266,6 +267,7 @@ export class StructureToolsWrapper extends PluginUIComponent {
<StructureSelectionControls /> <StructureSelectionControls />
<StructureRepresentationControls /> <StructureRepresentationControls />
<StructureMeasurementsControls />
</div>; </div>;
} }
} }
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as React from 'react';
import { CollapsableControls, CollapsableState } from '../base';
import { lociLabel } from '../../mol-theme/label';
import { StructureElement } from '../../mol-model/structure';
// TODO hide/show, delete, details, options (e.g. change text for labels)
// TODO better labels: shorter, include measure
// TODO better updates on state changes
interface StructureMeasurementsControlsState extends CollapsableState {
minRadius: number,
extraRadius: number,
durationMs: number,
isDisabled: boolean,
}
export class StructureMeasurementsControls<P, S extends StructureMeasurementsControlsState> extends CollapsableControls<P, S> {
componentDidMount() {
this.subscribe(this.plugin.events.state.object.updated, ({ }) => {
// TODO
this.forceUpdate()
})
this.subscribe(this.plugin.events.state.object.created, ({ }) => {
// TODO
this.forceUpdate()
})
this.subscribe(this.plugin.events.state.object.removed, ({ }) => {
// TODO
this.forceUpdate()
})
this.subscribe(this.plugin.state.dataState.events.isUpdating, v => {
this.setState({ isDisabled: v })
})
}
focusLoci(loci: StructureElement.Loci) {
return () => {
const { extraRadius, minRadius, durationMs } = this.state
if (this.plugin.helpers.structureSelectionManager.stats.elementCount === 0) return
const { sphere } = StructureElement.Loci.getBoundary(loci)
const radius = Math.max(sphere.radius + extraRadius, minRadius);
this.plugin.canvas3d?.camera.focus(sphere.center, radius, this.plugin.canvas3d.boundingSphere.radius, durationMs);
}
}
defaultState() {
return {
isCollapsed: false,
header: 'Measurements',
minRadius: 8,
extraRadius: 4,
durationMs: 250,
isDisabled: false
} as S
}
renderControls() {
const labels: JSX.Element[] = [];
const distances: JSX.Element[] = [];
const angles: JSX.Element[] = [];
const dihedrals: JSX.Element[] = [];
const measurements = this.plugin.helpers.measurement.getMeasurements();
for (const d of measurements.labels) {
const source = d.obj?.data.source
if (source) {
const lA = simpleLabel(source.data[0].loci)
labels.push(<li key={source.id}>
<button className='msp-btn msp-btn-block msp-form-control' style={{ borderRight: '6px solid transparent', overflow: 'hidden' }}
title='Click to focus.' onClick={this.focusLoci(source.data[0].loci)}>
<span dangerouslySetInnerHTML={{ __html: `${lA}` }} />
</button>
</li>)
}
}
for (const d of measurements.distances) {
const source = d.obj?.data.source
if (source) {
const lA = simpleLabel(source.data[0].loci)
const lB = simpleLabel(source.data[1].loci)
distances.push(<li key={source.id}>
<button className='msp-btn msp-btn-block msp-form-control' style={{ borderRight: '6px solid transparent', overflow: 'hidden' }}
title='Click to focus.' onClick={this.focusLoci(source.data[0].loci)}>
<span dangerouslySetInnerHTML={{ __html: `${lA} \u2014 ${lB}` }} />
</button>
</li>)
}
}
for (const d of measurements.angles) {
const source = d.obj?.data.source
if (source) {
const lA = simpleLabel(source.data[0].loci)
const lB = simpleLabel(source.data[1].loci)
const lC = simpleLabel(source.data[2].loci)
angles.push(<li key={source.id}>
<button className='msp-btn msp-btn-block msp-form-control' style={{ borderRight: '6px solid transparent', overflow: 'hidden' }}
title='Click to focus.' onClick={this.focusLoci(source.data[0].loci)}>
<span dangerouslySetInnerHTML={{ __html: `${lA} \u2014 ${lB} \u2014 ${lC}` }} />
</button>
</li>)
}
}
for (const d of measurements.dihedrals) {
const source = d.obj?.data.source
if (source) {
const lA = simpleLabel(source.data[0].loci)
const lB = simpleLabel(source.data[1].loci)
const lC = simpleLabel(source.data[2].loci)
const lD = simpleLabel(source.data[3].loci)
dihedrals.push(<li key={source.id}>
<button className='msp-btn msp-btn-block msp-form-control' style={{ borderRight: '6px solid transparent', overflow: 'hidden' }}
title='Click to focus.' onClick={this.focusLoci(source.data[0].loci)}>
<span dangerouslySetInnerHTML={{ __html: `${lA} \u2014 ${lB} \u2014 ${lC} \u2014 ${lD}` }} />
</button>
</li>)
}
}
return <div>
{labels.length > 0 && <div>
<div className='msp-control-group-header' style={{ marginTop: '1px' }}><span>Labels</span></div>
<ul style={{ listStyle: 'none', marginTop: '1px', marginBottom: '0' }} className='msp-state-list'>
{labels}
</ul>
</div>}
{distances.length > 0 && <div>
<div className='msp-control-group-header' style={{ marginTop: '1px' }}><span>Distances</span></div>
<ul style={{ listStyle: 'none', marginTop: '1px', marginBottom: '0' }} className='msp-state-list'>
{distances}
</ul>
</div>}
{angles.length > 0 && <div>
<div className='msp-control-group-header' style={{ marginTop: '1px' }}><span>Angles</span></div>
<ul style={{ listStyle: 'none', marginTop: '1px', marginBottom: '0' }} className='msp-state-list'>
{angles}
</ul>
</div>}
{dihedrals.length > 0 && <div>
<div className='msp-control-group-header' style={{ marginTop: '1px' }}><span>Dihedrals</span></div>
<ul style={{ listStyle: 'none', marginTop: '1px', marginBottom: '0' }} className='msp-state-list'>
{dihedrals}
</ul>
</div>}
</div>
}
}
function simpleLabel(loci: StructureElement.Loci) {
return lociLabel(loci, { htmlStyling: false })
.split('|')
.reverse()[0]
.replace(/\[.*\]/g, '')
.trim()
}
\ No newline at end of file
...@@ -180,7 +180,7 @@ export class StructureSelectionControls<P, S extends StructureSelectionControlsS ...@@ -180,7 +180,7 @@ export class StructureSelectionControls<P, S extends StructureSelectionControlsS
latest.push(<li key={e!.label}> latest.push(<li key={e!.label}>
<button className='msp-btn msp-btn-block msp-form-control' style={{ borderRight: '6px solid transparent', overflow: 'hidden' }} <button className='msp-btn msp-btn-block msp-form-control' style={{ borderRight: '6px solid transparent', overflow: 'hidden' }}
title='Click to focus.' onClick={this.focusLoci(e.loci)}> title='Click to focus.' onClick={this.focusLoci(e.loci)}>
<span dangerouslySetInnerHTML={{ __html: e.label }} /> <span dangerouslySetInnerHTML={{ __html: e.label.split('|').reverse().join(' | ') }} />
</button> </button>
{/* <div> {/* <div>
<IconButton icon='remove' title='Remove' onClick={() => {}} /> <IconButton icon='remove' title='Remove' onClick={() => {}} />
......
...@@ -6,14 +6,14 @@ ...@@ -6,14 +6,14 @@
import { StructureElement } from '../../mol-model/structure'; import { StructureElement } from '../../mol-model/structure';
import { PluginContext } from '../context'; import { PluginContext } from '../context';
import { StateSelection, StateTransform } from '../../mol-state'; import { StateSelection, StateTransform, StateTransformer } from '../../mol-state';
import { StateTransforms } from '../../mol-plugin-state/transforms'; import { StateTransforms } from '../../mol-plugin-state/transforms';
import { PluginCommands } from '../commands'; import { PluginCommands } from '../commands';
import { arraySetAdd } from '../../mol-util/array'; import { arraySetAdd } from '../../mol-util/array';
export { StructureMeasurementManager } export { StructureMeasurementManager }
const MeasurementGroupTag = 'measurement-group'; export const MeasurementGroupTag = 'measurement-group';
class StructureMeasurementManager { class StructureMeasurementManager {
private getGroup() { private getGroup() {
...@@ -25,6 +25,37 @@ class StructureMeasurementManager { ...@@ -25,6 +25,37 @@ class StructureMeasurementManager {
return builder.toRoot().group(StateTransforms.Misc.CreateGroup, { label: `Measurements` }, { tags: MeasurementGroupTag }); return builder.toRoot().group(StateTransforms.Misc.CreateGroup, { label: `Measurements` }, { tags: MeasurementGroupTag });
} }
private getTransforms(transformer: StateTransformer) {
const state = this.context.state.dataState;
const groupRef = StateSelection.findTagInSubtree(state.tree, StateTransform.RootRef, MeasurementGroupTag);
return groupRef ? state.select(StateSelection.Generators.ofTransformer(transformer, groupRef)) : []
}
getLabels() {
return this.getTransforms(StateTransforms.Representation.StructureSelectionsLabel3D)
}
getDistances() {
return this.getTransforms(StateTransforms.Representation.StructureSelectionsDistance3D)
}
getAngles() {
return this.getTransforms(StateTransforms.Representation.StructureSelectionsAngle3D)
}
getDihedrals() {
return this.getTransforms(StateTransforms.Representation.StructureSelectionsDihedral3D)
}
getMeasurements() {
return {
labels: this.getLabels(),
distances: this.getDistances(),
angles: this.getAngles(),
dihedrals: this.getDihedrals(),
}
}
async addDistance(a: StructureElement.Loci, b: StructureElement.Loci) { async addDistance(a: StructureElement.Loci, b: StructureElement.Loci) {
const cellA = this.context.helpers.substructureParent.get(a.structure); const cellA = this.context.helpers.substructureParent.get(a.structure);
const cellB = this.context.helpers.substructureParent.get(b.structure); const cellB = this.context.helpers.substructureParent.get(b.structure);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment