Browse Source

added bundleLabel, improved measurement labels

Alexander Rose 5 years ago
parent
commit
aca91cf18f

+ 16 - 13
src/mol-repr/shape/loci/angle.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -21,7 +21,8 @@ import { MeshBuilder } from '../../../mol-geo/geometry/mesh/mesh-builder';
 import { radToDeg, arcLength } from '../../../mol-math/misc';
 import { Circle } from '../../../mol-geo/primitive/circle';
 import { transformPrimitive } from '../../../mol-geo/primitive/primitive';
-import { MarkerActions } from '../../../mol-util/marker-action';
+import { MarkerActions, MarkerAction } from '../../../mol-util/marker-action';
+import { bundleLabel } from '../../../mol-theme/label';
 
 export interface AngleData {
     triples: Loci.Bundle<3>[]
@@ -70,7 +71,7 @@ const AngleVisuals = {
     'vectors': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<AngleData, VectorsParams>) => ShapeRepresentation(getVectorsShape, Lines.Utils, { modifyState: s => ({ ...s, pickable: false }) }),
     'arc': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<AngleData, ArcParams>) => ShapeRepresentation(getArcShape, Lines.Utils, { modifyState: s => ({ ...s, pickable: false }) }),
     'sector': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<AngleData, SectorParams>) => ShapeRepresentation(getSectorShape, Mesh.Utils, { modifyProps: p => ({ ...p, alpha: p.sectorOpacity }), modifyState: s => ({ ...s, markerActions: MarkerActions.Highlighting }) }),
-    'text': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<AngleData, TextParams>) => ShapeRepresentation(getTextShape, Text.Utils),
+    'text': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<AngleData, TextParams>) => ShapeRepresentation(getTextShape, Text.Utils, { modifyState: s => ({ ...s, markerActions: MarkerAction.None }) }),
 }
 
 export const AngleParams = {
@@ -165,10 +166,7 @@ function buildVectorsLines(data: AngleData, props: AngleProps, lines?: Lines): L
 function getVectorsShape(ctx: RuntimeContext, data: AngleData, props: AngleProps, shape?: Shape<Lines>) {
     const lines = buildVectorsLines(data, props, shape && shape.geometry);
     const name = getAngleName(data)
-    const getLabel = function (groupId: number ) {
-        return angleLabel(data.triples[groupId])
-    }
-    return Shape.create(name, data, lines, () => props.color, () => props.linesSize, getLabel)
+    return Shape.create(name, data, lines, () => props.color, () => props.linesSize, () => '')
 }
 
 //
@@ -198,10 +196,7 @@ function buildArcLines(data: AngleData, props: AngleProps, lines?: Lines): Lines
 function getArcShape(ctx: RuntimeContext, data: AngleData, props: AngleProps, shape?: Shape<Lines>) {
     const lines = buildArcLines(data, props, shape && shape.geometry);
     const name = getAngleName(data)
-    const getLabel = function (groupId: number ) {
-        return angleLabel(data.triples[groupId])
-    }
-    return Shape.create(name, data, lines, () => props.color, () => props.linesSize, getLabel)
+    return Shape.create(name, data, lines, () => props.color, () => props.linesSize, () => '')
 }
 
 //
@@ -222,7 +217,11 @@ function getSectorShape(ctx: RuntimeContext, data: AngleData, props: AngleProps,
     const mesh = buildSectorMesh(data, props, shape && shape.geometry);
     const name = getAngleName(data)
     const getLabel = function (groupId: number ) {
-        return angleLabel(data.triples[groupId])
+        const triple = data.triples[groupId]
+        return [
+            angleLabel(triple),
+            bundleLabel(triple)
+        ].join('</br>')
     }
     return Shape.create(name, data, mesh, () => props.color, () => 1, getLabel)
 }
@@ -249,7 +248,11 @@ function getTextShape(ctx: RuntimeContext, data: AngleData, props: AngleProps, s
     const text = buildText(data, props, shape && shape.geometry);
     const name = getAngleName(data)
     const getLabel = function (groupId: number ) {
-        return angleLabel(data.triples[groupId])
+        const triple = data.triples[groupId]
+        return [
+            angleLabel(triple),
+            bundleLabel(triple)
+        ].join('</br>')
     }
     return Shape.create(name, data, text, () => props.textColor, () => props.textSize, getLabel)
 }

+ 17 - 17
src/mol-repr/shape/loci/dihedral.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -21,7 +21,8 @@ import { MeshBuilder } from '../../../mol-geo/geometry/mesh/mesh-builder';
 import { arcLength, halfPI, radToDeg } from '../../../mol-math/misc';
 import { Circle } from '../../../mol-geo/primitive/circle';
 import { transformPrimitive } from '../../../mol-geo/primitive/primitive';
-import { MarkerActions } from '../../../mol-util/marker-action';
+import { MarkerActions, MarkerAction } from '../../../mol-util/marker-action';
+import { bundleLabel } from '../../../mol-theme/label';
 
 export interface DihedralData {
     quads: Loci.Bundle<4>[]
@@ -76,7 +77,7 @@ const DihedralVisuals = {
     'extenders': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<DihedralData, ExtendersParams>) => ShapeRepresentation(getExtendersShape, Lines.Utils, { modifyState: s => ({ ...s, pickable: false }) }),
     'arc': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<DihedralData, ArcParams>) => ShapeRepresentation(getArcShape, Lines.Utils, { modifyState: s => ({ ...s, pickable: false }) }),
     'sector': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<DihedralData, SectorParams>) => ShapeRepresentation(getSectorShape, Mesh.Utils, { modifyProps: p => ({ ...p, alpha: p.sectorOpacity }), modifyState: s => ({ ...s, markerActions: MarkerActions.Highlighting }) }),
-    'text': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<DihedralData, TextParams>) => ShapeRepresentation(getTextShape, Text.Utils),
+    'text': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<DihedralData, TextParams>) => ShapeRepresentation(getTextShape, Text.Utils, { modifyState: s => ({ ...s, markerActions: MarkerAction.None }) }),
 }
 
 export const DihedralParams = {
@@ -207,10 +208,7 @@ function buildVectorsLines(data: DihedralData, props: DihedralProps, lines?: Lin
 function getVectorsShape(ctx: RuntimeContext, data: DihedralData, props: DihedralProps, shape?: Shape<Lines>) {
     const lines = buildVectorsLines(data, props, shape && shape.geometry);
     const name = getDihedralName(data)
-    const getLabel = function (groupId: number ) {
-        return dihedralLabel(data.quads[groupId])
-    }
-    return Shape.create(name, data, lines, () => props.color, () => props.linesSize, getLabel)
+    return Shape.create(name, data, lines, () => props.color, () => props.linesSize, () => '')
 }
 
 //
@@ -228,10 +226,7 @@ function buildExtendersLines(data: DihedralData, props: DihedralProps, lines?: L
 function getExtendersShape(ctx: RuntimeContext, data: DihedralData, props: DihedralProps, shape?: Shape<Lines>) {
     const lines = buildExtendersLines(data, props, shape && shape.geometry);
     const name = getDihedralName(data)
-    const getLabel = function (groupId: number ) {
-        return dihedralLabel(data.quads[groupId])
-    }
-    return Shape.create(name, data, lines, () => props.color, () => props.linesSize, getLabel)
+    return Shape.create(name, data, lines, () => props.color, () => props.linesSize, () => '')
 }
 
 //
@@ -261,10 +256,7 @@ function buildArcLines(data: DihedralData, props: DihedralProps, lines?: Lines):
 function getArcShape(ctx: RuntimeContext, data: DihedralData, props: DihedralProps, shape?: Shape<Lines>) {
     const lines = buildArcLines(data, props, shape && shape.geometry);
     const name = getDihedralName(data)
-    const getLabel = function (groupId: number ) {
-        return dihedralLabel(data.quads[groupId])
-    }
-    return Shape.create(name, data, lines, () => props.color, () => props.linesSize, getLabel)
+    return Shape.create(name, data, lines, () => props.color, () => props.linesSize, () => '')
 }
 
 //
@@ -285,7 +277,11 @@ function getSectorShape(ctx: RuntimeContext, data: DihedralData, props: Dihedral
     const mesh = buildSectorMesh(data, props, shape && shape.geometry);
     const name = getDihedralName(data)
     const getLabel = function (groupId: number ) {
-        return dihedralLabel(data.quads[groupId])
+        const quad = data.quads[groupId]
+        return [
+            dihedralLabel(quad),
+            bundleLabel(quad)
+        ].join('</br>')
     }
     return Shape.create(name, data, mesh, () => props.color, () => 1, getLabel)
 }
@@ -312,7 +308,11 @@ function getTextShape(ctx: RuntimeContext, data: DihedralData, props: DihedralPr
     const text = buildText(data, props, shape && shape.geometry);
     const name = getDihedralName(data)
     const getLabel = function (groupId: number ) {
-        return dihedralLabel(data.quads[groupId])
+        const quad = data.quads[groupId]
+        return [
+            dihedralLabel(quad),
+            bundleLabel(quad)
+        ].join('</br>')
     }
     return Shape.create(name, data, text, () => props.textColor, () => props.textSize, getLabel)
 }

+ 14 - 5
src/mol-repr/shape/loci/distance.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -16,7 +16,8 @@ import { Shape } from '../../../mol-model/shape';
 import { LinesBuilder } from '../../../mol-geo/geometry/lines/lines-builder';
 import { TextBuilder } from '../../../mol-geo/geometry/text/text-builder';
 import { Vec3 } from '../../../mol-math/linear-algebra';
-import { MarkerActions } from '../../../mol-util/marker-action';
+import { MarkerActions, MarkerAction } from '../../../mol-util/marker-action';
+import { bundleLabel } from '../../../mol-theme/label';
 
 export interface DistanceData {
     pairs: Loci.Bundle<2>[]
@@ -47,7 +48,7 @@ type TextParams = typeof TextParams
 
 const DistanceVisuals = {
     'lines': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<DistanceData, LineParams>) => ShapeRepresentation(getLinesShape, Lines.Utils, { modifyState: s => ({ ...s, markerActions: MarkerActions.Highlighting }) }),
-    'text': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<DistanceData, TextParams>) => ShapeRepresentation(getTextShape, Text.Utils, { modifyState: s => ({ ...s, pickable: false }) }),
+    'text': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<DistanceData, TextParams>) => ShapeRepresentation(getTextShape, Text.Utils, { modifyState: s => ({ ...s, markerActions: MarkerAction.None }) }),
 }
 
 export const DistanceParams = {
@@ -111,7 +112,11 @@ function getLinesShape(ctx: RuntimeContext, data: DistanceData, props: DistanceP
     const lines = buildLines(data, props, shape && shape.geometry);
     const name = getDistanceName(data, props.unitLabel)
     const getLabel = function (groupId: number ) {
-        return distanceLabel(data.pairs[groupId], props.unitLabel)
+        const pair = data.pairs[groupId]
+        return [
+            distanceLabel(pair, props.unitLabel),
+            bundleLabel(pair)
+        ].join('</br>')
     }
     return Shape.create(name, data, lines, () => props.linesColor, () => props.linesSize, getLabel)
 }
@@ -133,7 +138,11 @@ function getTextShape(ctx: RuntimeContext, data: DistanceData, props: DistancePr
     const text = buildText(data, props, shape && shape.geometry);
     const name = getDistanceName(data, props.unitLabel)
     const getLabel = function (groupId: number ) {
-        return distanceLabel(data.pairs[groupId], props.unitLabel)
+        const pair = data.pairs[groupId]
+        return [
+            distanceLabel(pair, props.unitLabel),
+            bundleLabel(pair)
+        ].join('</br>')
     }
     return Shape.create(name, data, text, () => props.textColor, () => props.textSize, getLabel)
 }

+ 44 - 0
src/mol-theme/label.ts

@@ -124,6 +124,50 @@ export function bondLabel(bond: Bond.Location, options: Partial<LabelOptions> =
     return `${labelA.join(' | ')} \u2014 ${labelB.slice(offset).join(' | ')}`
 }
 
+export function bundleLabel(bundle: Loci.Bundle<any>, options: Partial<LabelOptions> = {}) {
+    let isSingleElements = true
+    for (const l of bundle.loci) {
+        if (!StructureElement.Loci.is(l) || StructureElement.Loci.size(l) !== 1) {
+            isSingleElements = false
+            break
+        }
+    }
+
+    if (isSingleElements) {
+        const o = { ...DefaultLabelOptions, ...options }
+        const locations = (bundle.loci as StructureElement.Loci[]).map(l => {
+            const { unit, indices } = l.elements[0]
+            return StructureElement.Location.create(unit, unit.elements[OrderedSet.start(indices)])
+        })
+        const labels = locations.map(l => _elementLabel(l, o.granularity, o.hidePrefix))
+
+        let offset = 0
+        for (let i = 0, il = Math.min(...labels.map(l => l.length)); i < il; ++i) {
+            let areIdentical = true
+            for (let j = 1, jl = labels.length; j < jl; ++j) {
+                if (labels[0][i] !== labels[j][i]) {
+                    areIdentical = false
+                    break
+                }
+            }
+            if (areIdentical) offset += 1
+            else break
+        }
+
+        if (offset > 0) {
+            const offsetLabels = [labels[0].join(' | ')]
+            for (let j = 1, jl = labels.length; j < jl; ++j) {
+                offsetLabels.push(labels[j].slice(offset).join(' | '))
+            }
+            return offsetLabels.join(' \u2014 ')
+        } else {
+            return labels.map(l => l.join(' | ')).join('</br>')
+        }
+    } else {
+        return bundle.loci.map(l => lociLabel(l)).join('</br>')
+    }
+}
+
 export function elementLabel(location: StructureElement.Location, options: Partial<LabelOptions> = {}): string {
     const o = { ...DefaultLabelOptions, ...options }
     const label = _elementLabel(location, o.granularity, o.hidePrefix).join(' | ')