diff --git a/src/mol-app/ui/transform/backbone.tsx b/src/mol-app/ui/transform/backbone.tsx index 76bb3475c7ed50883e426c557bff7f326500518f..3586f32082a2c0a7712bba2000880c4935116528 100644 --- a/src/mol-app/ui/transform/backbone.tsx +++ b/src/mol-app/ui/transform/backbone.tsx @@ -15,21 +15,12 @@ import { Toggle } from '../controls/common'; import { BackboneEntity } from 'mol-view/state/entity'; import { BackboneUpdate } from 'mol-view/state/transform' import { StateContext } from 'mol-view/state/context'; -import { ColorTheme, SizeTheme } from 'mol-geo/theme'; +import { ColorTheme, SizeTheme, ColorThemeNames, ColorThemeName } from 'mol-geo/theme'; import { Color, ColorNames } from 'mol-util/color'; import { Slider } from '../controls/slider'; import { VisualQuality } from 'mol-geo/representation/util'; import { Unit } from 'mol-model/structure'; -export const ColorThemeInfo = { - 'atom-index': {}, - 'chain-id': {}, - 'element-symbol': {}, - 'instance-index': {}, - 'uniform': {} -} -export type ColorThemeInfo = keyof typeof ColorThemeInfo - interface BackboneState { doubleSided: boolean flipSided: boolean @@ -86,7 +77,7 @@ export class Backbone extends View<Controller<any>, BackboneState, { transform: return <option key={value} value={value}>{value.toString()}</option> }) - const colorThemeOptions = Object.keys(ColorThemeInfo).map((name, idx) => { + const colorThemeOptions = ColorThemeNames.map((name, idx) => { return <option key={name} value={name}>{name}</option> }) @@ -137,19 +128,12 @@ export class Backbone extends View<Controller<any>, BackboneState, { transform: className='molstar-form-control' value={this.state.colorTheme.name} onChange={(e) => { - const colorThemeName = e.target.value as ColorThemeInfo - if (colorThemeName === 'uniform') { - this.update({ - colorTheme: { - name: colorThemeName, - value: this.state.colorValue - } - }) - } else { - this.update({ - colorTheme: { name: colorThemeName } - }) - } + this.update({ + colorTheme: { + name: e.target.value as ColorThemeName, + value: this.state.colorValue + } + }) }} > {colorThemeOptions} diff --git a/src/mol-app/ui/transform/ball-and-stick.tsx b/src/mol-app/ui/transform/ball-and-stick.tsx index 9ed5c5e35c133e5193d4e33c0e821cbce760a607..9ccf4be39ee89a4ca40215df6071936c0b05894d 100644 --- a/src/mol-app/ui/transform/ball-and-stick.tsx +++ b/src/mol-app/ui/transform/ball-and-stick.tsx @@ -15,7 +15,7 @@ import { Toggle } from '../controls/common'; import { DistanceRestraintEntity } from 'mol-view/state/entity'; import { DistanceRestraintUpdate } from 'mol-view/state/transform' import { StateContext } from 'mol-view/state/context'; -import { ColorTheme, SizeTheme } from 'mol-geo/theme'; +import { ColorTheme, SizeTheme, ColorThemeName, ColorThemeNames } from 'mol-geo/theme'; import { Color, ColorNames } from 'mol-util/color'; import { Slider } from '../controls/slider'; import { VisualQuality } from 'mol-geo/representation/util'; @@ -23,6 +23,7 @@ import { Unit } from 'mol-model/structure'; export const ColorThemeInfo = { 'atom-index': {}, + 'carbohydrate-symbol': {}, 'chain-id': {}, 'element-symbol': {}, 'instance-index': {}, @@ -89,7 +90,7 @@ export class BallAndStick extends View<Controller<any>, BallAndStickState, { tra return <option key={name} value={name}>{name}</option> }) - const colorThemeOptions = Object.keys(ColorThemeInfo).map((name, idx) => { + const colorThemeOptions = ColorThemeNames.map((name, idx) => { return <option key={name} value={name}>{name}</option> }) @@ -128,19 +129,12 @@ export class BallAndStick extends View<Controller<any>, BallAndStickState, { tra className='molstar-form-control' value={this.state.colorTheme.name} onChange={(e) => { - const colorThemeName = e.target.value as ColorThemeInfo - if (colorThemeName === 'uniform') { - this.update({ - colorTheme: { - name: colorThemeName, - value: this.state.colorValue - } - }) - } else { - this.update({ - colorTheme: { name: colorThemeName } - }) - } + this.update({ + colorTheme: { + name: e.target.value as ColorThemeName, + value: this.state.colorValue + } + }) }} > {colorThemeOptions} diff --git a/src/mol-app/ui/transform/cartoon.tsx b/src/mol-app/ui/transform/cartoon.tsx index da5da45e7f27e5feb50f7ef2932637e82d3eb35d..eb8f356b580ab58ef91292a75a1c345701c503aa 100644 --- a/src/mol-app/ui/transform/cartoon.tsx +++ b/src/mol-app/ui/transform/cartoon.tsx @@ -15,7 +15,7 @@ import { Toggle } from '../controls/common'; import { CartoonEntity } from 'mol-view/state/entity'; import { CartoonUpdate } from 'mol-view/state/transform' import { StateContext } from 'mol-view/state/context'; -import { ColorTheme, SizeTheme } from 'mol-geo/theme'; +import { ColorTheme, SizeTheme, ColorThemeName, ColorThemeNames } from 'mol-geo/theme'; import { Color, ColorNames } from 'mol-util/color'; import { Slider } from '../controls/slider'; import { VisualQuality } from 'mol-geo/representation/util'; @@ -23,6 +23,7 @@ import { Unit } from 'mol-model/structure'; export const ColorThemeInfo = { 'atom-index': {}, + 'carbohydrate-symbol': {}, 'chain-id': {}, 'element-symbol': {}, 'instance-index': {}, @@ -86,7 +87,7 @@ export class Cartoon extends View<Controller<any>, CartoonState, { transform: Ca return <option key={value} value={value}>{value.toString()}</option> }) - const colorThemeOptions = Object.keys(ColorThemeInfo).map((name, idx) => { + const colorThemeOptions = ColorThemeNames.map((name, idx) => { return <option key={name} value={name}>{name}</option> }) @@ -137,19 +138,12 @@ export class Cartoon extends View<Controller<any>, CartoonState, { transform: Ca className='molstar-form-control' value={this.state.colorTheme.name} onChange={(e) => { - const colorThemeName = e.target.value as ColorThemeInfo - if (colorThemeName === 'uniform') { - this.update({ - colorTheme: { - name: colorThemeName, - value: this.state.colorValue - } - }) - } else { - this.update({ - colorTheme: { name: colorThemeName } - }) - } + this.update({ + colorTheme: { + name: e.target.value as ColorThemeName, + value: this.state.colorValue + } + }) }} > {colorThemeOptions} diff --git a/src/mol-app/ui/transform/distance-restraint.tsx b/src/mol-app/ui/transform/distance-restraint.tsx index cf74c3992c5b83c9c9fc57b21b1dc3743c69019f..a032eeaf2a37d6f41ee36575a841f7537167befa 100644 --- a/src/mol-app/ui/transform/distance-restraint.tsx +++ b/src/mol-app/ui/transform/distance-restraint.tsx @@ -15,7 +15,7 @@ import { Toggle } from '../controls/common'; import { DistanceRestraintEntity } from 'mol-view/state/entity'; import { DistanceRestraintUpdate } from 'mol-view/state/transform' import { StateContext } from 'mol-view/state/context'; -import { ColorTheme, SizeTheme } from 'mol-geo/theme'; +import { ColorTheme, SizeTheme, ColorThemeName, ColorThemeNames } from 'mol-geo/theme'; import { Color, ColorNames } from 'mol-util/color'; import { Slider } from '../controls/slider'; import { VisualQuality } from 'mol-geo/representation/util'; @@ -23,6 +23,7 @@ import { Unit } from 'mol-model/structure'; export const ColorThemeInfo = { 'atom-index': {}, + 'carbohydrate-symbol': {}, 'chain-id': {}, 'element-symbol': {}, 'instance-index': {}, @@ -89,7 +90,7 @@ export class DistanceRestraint extends View<Controller<any>, DistanceRestraintSt return <option key={name} value={name}>{name}</option> }) - const colorThemeOptions = Object.keys(ColorThemeInfo).map((name, idx) => { + const colorThemeOptions = ColorThemeNames.map((name, idx) => { return <option key={name} value={name}>{name}</option> }) @@ -128,19 +129,12 @@ export class DistanceRestraint extends View<Controller<any>, DistanceRestraintSt className='molstar-form-control' value={this.state.colorTheme.name} onChange={(e) => { - const colorThemeName = e.target.value as ColorThemeInfo - if (colorThemeName === 'uniform') { - this.update({ - colorTheme: { - name: colorThemeName, - value: this.state.colorValue - } - }) - } else { - this.update({ - colorTheme: { name: colorThemeName } - }) - } + this.update({ + colorTheme: { + name: e.target.value as ColorThemeName, + value: this.state.colorValue + } + }) }} > {colorThemeOptions} diff --git a/src/mol-app/ui/transform/spacefill.tsx b/src/mol-app/ui/transform/spacefill.tsx index 55377180ac8c6eaa6b29cc678649e40c47e76530..42719bd63ffca7cd02ad3bda4edff290f610fd8d 100644 --- a/src/mol-app/ui/transform/spacefill.tsx +++ b/src/mol-app/ui/transform/spacefill.tsx @@ -15,7 +15,7 @@ import { Toggle } from '../controls/common'; import { SpacefillEntity } from 'mol-view/state/entity'; import { SpacefillUpdate } from 'mol-view/state/transform' import { StateContext } from 'mol-view/state/context'; -import { ColorTheme, SizeTheme } from 'mol-geo/theme'; +import { ColorTheme, SizeTheme, ColorThemeName, ColorThemeNames } from 'mol-geo/theme'; import { Color, ColorNames } from 'mol-util/color'; import { Slider } from '../controls/slider'; import { VisualQuality } from 'mol-geo/representation/util'; @@ -23,6 +23,7 @@ import { Unit } from 'mol-model/structure'; export const ColorThemeInfo = { 'atom-index': {}, + 'carbohydrate-symbol': {}, 'chain-id': {}, 'element-symbol': {}, 'instance-index': {}, @@ -86,7 +87,7 @@ export class Spacefill extends View<Controller<any>, SpacefillState, { transform return <option key={value} value={value}>{value.toString()}</option> }) - const colorThemeOptions = Object.keys(ColorThemeInfo).map((name, idx) => { + const colorThemeOptions = ColorThemeNames.map((name, idx) => { return <option key={name} value={name}>{name}</option> }) @@ -137,19 +138,12 @@ export class Spacefill extends View<Controller<any>, SpacefillState, { transform className='molstar-form-control' value={this.state.colorTheme.name} onChange={(e) => { - const colorThemeName = e.target.value as ColorThemeInfo - if (colorThemeName === 'uniform') { - this.update({ - colorTheme: { - name: colorThemeName, - value: this.state.colorValue - } - }) - } else { - this.update({ - colorTheme: { name: colorThemeName } - }) - } + this.update({ + colorTheme: { + name: e.target.value as ColorThemeName, + value: this.state.colorValue + } + }) }} > {colorThemeOptions} diff --git a/src/mol-geo/primitive/box.ts b/src/mol-geo/primitive/box.ts index ee7e5b906485c4546c6a9bfb29e07c11b627e35c..990730641206701eda7102ee6e4fb8441398dd95 100644 --- a/src/mol-geo/primitive/box.ts +++ b/src/mol-geo/primitive/box.ts @@ -4,72 +4,55 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -// adapted from three.js, MIT License Copyright 2010-2018 three.js authors - import { Vec3 } from 'mol-math/linear-algebra' -import { Primitive } from './primitive'; - -export const DefaultBoxProps = { - width: 1, - height: 1, - depth: 1 -} -export type BoxProps = Partial<typeof DefaultBoxProps> - -const tmpVector = Vec3.zero(); - -export function Box(props?: BoxProps): Primitive { - const { width, height, depth } = { ...DefaultBoxProps, ...props } - - // buffers - const vertices = new Float32Array(72); - const normals = new Float32Array(72); - const indices = new Uint32Array(36); - - // helper variables - let vertexCount = 0; - - // build each side of the box geometry - buildPlane(2, 1, 0, -1, -1, depth, height, width); // px - buildPlane(2, 1, 0, 1, -1, depth, height, -width); // nx - buildPlane(0, 2, 1, 1, 1, width, depth, height); // py - buildPlane(0, 2, 1, 1, -1, width, depth, -height); // ny - buildPlane(0, 1, 2, 1, -1, width, height, depth); // pz - buildPlane(0, 1, 2, -1, -1, width, height, -depth); // nz +import { Primitive, PrimitiveBuilder } from './primitive'; +import { polygon } from './polygon' - return { vertices, normals, indices } +const a = Vec3.zero(), b = Vec3.zero(), c = Vec3.zero(), d = Vec3.zero() +const points = polygon(4, true) - function buildPlane(u: number, v: number, w: number, udir: number, vdir: number, width: number, height: number, depth: number) { - // generate vertices and normals - for (let iy = 0; iy < 2; ++iy) { - const y = iy * height - height / 2; - for (let ix = 0; ix < 2; ++ix) { - const x = ix * width - width / 2; - - // set values to correct vector component and add to vertex buffer - tmpVector[u] = x * udir; - tmpVector[v] = y * vdir; - tmpVector[w] = depth / 2; - Vec3.toArray(tmpVector, vertices, vertexCount * 3); +/** + * Create a box + */ +function createBox(perforated: boolean): Primitive { + const builder = PrimitiveBuilder(12) + + // create sides + for (let i = 0; i < 4; ++i) { + const ni = (i + 1) % 4 + Vec3.set(a, points[i * 2], points[i * 2 + 1], -0.5) + Vec3.set(b, points[ni * 2], points[ni * 2 + 1], -0.5) + Vec3.set(c, points[ni * 2], points[ni * 2 + 1], 0.5) + Vec3.set(d, points[i * 2], points[i * 2 + 1], 0.5) + builder.add(a, b, c) + if (!perforated) builder.add(c, d, a) + } - // set values to correct vector component and add to normal buffer - tmpVector[u] = 0; - tmpVector[v] = 0; - tmpVector[w] = depth > 0 ? 1 : -1; - Vec3.toArray(tmpVector, normals, vertexCount * 3); + // create bases + Vec3.set(a, points[0], points[1], -0.5) + Vec3.set(b, points[2], points[3], -0.5) + Vec3.set(c, points[4], points[5], -0.5) + Vec3.set(d, points[6], points[7], -0.5) + builder.add(a, b, c) + if (!perforated) builder.add(c, d, a) + Vec3.set(a, points[0], points[1], 0.5) + Vec3.set(b, points[2], points[3], 0.5) + Vec3.set(c, points[4], points[5], 0.5) + Vec3.set(d, points[6], points[7], 0.5) + builder.add(a, b, c) + if (!perforated) builder.add(c, d, a) + + return builder.getPrimitive() +} - ++vertexCount; - } - } +let box: Primitive +export function Box() { + if (!box) box = createBox(false) + return box +} - // faces - const vc = vertexCount - 4 - const iidx = (vc / 2) * 3 - indices[iidx] = vc - indices[iidx + 1] = vc + 2 - indices[iidx + 2] = vc + 1 - indices[iidx + 3] = vc + 2 - indices[iidx + 4] = vc + 3 - indices[iidx + 5] = vc + 1 - } +let perforatedBox: Primitive +export function PerforatedBox() { + if (!perforatedBox) perforatedBox = createBox(true) + return perforatedBox } \ No newline at end of file diff --git a/src/mol-geo/primitive/octahedron.ts b/src/mol-geo/primitive/octahedron.ts index 3375145fa02c4984f6121038392c6b6cd90b696b..cb23691c9cb84b24b83677026009649e1a462453 100644 --- a/src/mol-geo/primitive/octahedron.ts +++ b/src/mol-geo/primitive/octahedron.ts @@ -10,13 +10,20 @@ export const octahedronVertices: ReadonlyArray<number> = [ 0.5, 0, 0, -0.5, 0, 0, 0, 0.5, 0, 0, -0.5, 0, 0, 0, 0.5, 0, 0, -0.5 ]; - export const octahedronIndices: ReadonlyArray<number> = [ 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2 ]; +export const perforatedOctahedronIndices: ReadonlyArray<number> = [ + 0, 2, 4, 0, 4, 3, + // 0, 3, 5, 0, 5, 2, + 1, 2, 5, 1, 5, 3, + // 1, 3, 4, 1, 4, 2 +]; const octahedron = createPrimitive(octahedronVertices, octahedronIndices) +const perforatedOctahedron = createPrimitive(octahedronVertices, perforatedOctahedronIndices) -export function Octahedron(): Primitive { return octahedron } \ No newline at end of file +export function Octahedron(): Primitive { return octahedron } +export function PerforatedOctahedron(): Primitive { return perforatedOctahedron } \ No newline at end of file diff --git a/src/mol-geo/primitive/prism.ts b/src/mol-geo/primitive/prism.ts index 681bb3260cffacfa9e4f48d50bd0aafbfe54594e..d7ad601033b04cd4429ae07d725dc2ba57e5beef 100644 --- a/src/mol-geo/primitive/prism.ts +++ b/src/mol-geo/primitive/prism.ts @@ -12,12 +12,13 @@ const on = Vec3.create(0, 0, -0.5), op = Vec3.create(0, 0, 0.5) const a = Vec3.zero(), b = Vec3.zero(), c = Vec3.zero(), d = Vec3.zero() /** - * Create a prism with a poligonal base + * Create a prism with a poligonal base of 5 or more points */ export function Prism(points: ArrayLike<number>): Primitive { const sideCount = points.length / 2 - const baseCount = sideCount === 3 ? 1 : sideCount === 4 ? 2 : sideCount - const count = 2 * baseCount + 2 * sideCount + if (sideCount < 5) throw new Error('need at least 5 points to build a prism') + + const count = 4 * sideCount const builder = PrimitiveBuilder(count) // create sides @@ -32,55 +33,19 @@ export function Prism(points: ArrayLike<number>): Primitive { } // create bases - if (sideCount === 3) { - Vec3.set(a, points[0], points[1], -0.5) - Vec3.set(b, points[2], points[3], -0.5) - Vec3.set(c, points[4], points[5], -0.5) - builder.add(a, b, c) - Vec3.set(a, points[0], points[1], 0.5) - Vec3.set(b, points[2], points[3], 0.5) - Vec3.set(c, points[4], points[5], 0.5) - builder.add(c, b, a) - } else if (sideCount === 4) { - Vec3.set(a, points[0], points[1], -0.5) - Vec3.set(b, points[2], points[3], -0.5) - Vec3.set(c, points[4], points[5], -0.5) - Vec3.set(d, points[6], points[7], -0.5) - builder.add(a, b, c) - builder.add(c, d, a) - Vec3.set(a, points[0], points[1], 0.5) - Vec3.set(b, points[2], points[3], 0.5) - Vec3.set(c, points[4], points[5], 0.5) - Vec3.set(d, points[6], points[7], 0.5) - builder.add(a, b, c) - builder.add(c, d, a) - } else { - for (let i = 0; i < sideCount; ++i) { - const ni = (i + 1) % sideCount - Vec3.set(a, points[i * 2], points[i * 2 + 1], -0.5) - Vec3.set(b, points[ni * 2], points[ni * 2 + 1], -0.5) - builder.add(a, b, on) - Vec3.set(a, points[i * 2], points[i * 2 + 1], 0.5) - Vec3.set(b, points[ni * 2], points[ni * 2 + 1], 0.5) - builder.add(op, b, a) - } + for (let i = 0; i < sideCount; ++i) { + const ni = (i + 1) % sideCount + Vec3.set(a, points[i * 2], points[i * 2 + 1], -0.5) + Vec3.set(b, points[ni * 2], points[ni * 2 + 1], -0.5) + builder.add(a, b, on) + Vec3.set(a, points[i * 2], points[i * 2 + 1], 0.5) + Vec3.set(b, points[ni * 2], points[ni * 2 + 1], 0.5) + builder.add(op, b, a) } return builder.getPrimitive() } -let wedge: Primitive -export function Wedge() { - if (!wedge) wedge = Prism(polygon(3, false)) - return wedge -} - -let box: Primitive -export function Box() { - if (!box) box = Prism(polygon(4, true)) - return box -} - let diamond: Primitive export function DiamondPrism() { if (!diamond) diamond = Prism(polygon(4, false)) diff --git a/src/mol-geo/primitive/pyramid.ts b/src/mol-geo/primitive/pyramid.ts index a188f7232fbd1a2ae5dff1edc04fcdef6e0c3b6a..1a0fb1ae9bf801d9eb575f1c0e67950be5288d70 100644 --- a/src/mol-geo/primitive/pyramid.ts +++ b/src/mol-geo/primitive/pyramid.ts @@ -5,7 +5,7 @@ */ import { Vec3 } from 'mol-math/linear-algebra' -import { Primitive, PrimitiveBuilder } from './primitive'; +import { Primitive, PrimitiveBuilder, createPrimitive } from './primitive'; import { polygon } from './polygon' const on = Vec3.create(0, 0, -0.5), op = Vec3.create(0, 0, 0.5) @@ -57,4 +57,31 @@ let octagonalPyramide: Primitive export function OctagonalPyramide() { if (!octagonalPyramide) octagonalPyramide = Pyramide(polygon(8, true)) return octagonalPyramide +} + +// + +let perforatedOctagonalPyramide: Primitive +export function PerforatedOctagonalPyramide() { + if (!perforatedOctagonalPyramide) { + const points = polygon(8, true) + const vertices = new Float32Array(8 * 3 + 6) + for (let i = 0; i < 8; ++i) { + vertices[i * 3] = points[i * 2] + vertices[i * 3 + 1] = points[i * 2 + 1] + vertices[i * 3 + 2] = -0.5 + } + vertices[8 * 3] = 0 + vertices[8 * 3 + 1] = 0 + vertices[8 * 3 + 2] = -0.5 + vertices[8 * 3 + 3] = 0 + vertices[8 * 3 + 4] = 0 + vertices[8 * 3 + 5] = 0.5 + const indices: ReadonlyArray<number> = [ + 0, 1, 8, 1, 2, 8, 4, 5, 8, 5, 6, 8, + 2, 3, 9, 3, 4, 9, 6, 7, 9, 7, 0, 9 + ]; + perforatedOctagonalPyramide = createPrimitive(vertices, indices) + } + return perforatedOctagonalPyramide } \ No newline at end of file diff --git a/src/mol-geo/primitive/wedge.ts b/src/mol-geo/primitive/wedge.ts index 1b00533691b83741e101278050762b9c8db19dd9..3b2d317b9e3575e1aaf527e3697638692179c648 100644 --- a/src/mol-geo/primitive/wedge.ts +++ b/src/mol-geo/primitive/wedge.ts @@ -5,118 +5,44 @@ */ import { Vec3 } from 'mol-math/linear-algebra' -import { Primitive } from './primitive'; +import { Primitive, PrimitiveBuilder } from './primitive'; +import { polygon } from './polygon' -export const DefaultWedgeProps = { - width: 1, - height: 1, - depth: 1 -} -export type WedgeProps = Partial<typeof DefaultWedgeProps> - -const _a = Vec3.create(0, 0.5, 0.5) -const _b = Vec3.create(0.5, -0.5, 0.5) -const _c = Vec3.create(-0.5, -0.5, 0.5) -const _d = Vec3.create(0, 0.5, -0.5) -const _e = Vec3.create(0.5, -0.5, -0.5) -const _f = Vec3.create(-0.5, -0.5, -0.5) - -const a = Vec3.zero(), b = Vec3.zero(), c = Vec3.zero() -const d = Vec3.zero(), e = Vec3.zero(), f = Vec3.zero() - -const nabc = Vec3.create(0, 0, 1) -const ndef = Vec3.create(0, 0, -1) -const nabde = Vec3.zero() -const nbcef = Vec3.create(0, -1, 0) -const nacdf = Vec3.zero() - -const s = Vec3.zero() - -export function Wedge(props?: WedgeProps): Primitive { - const { width, height, depth } = { ...DefaultWedgeProps, ...props } - - const vertices = new Float32Array(54) - const normals = new Float32Array(54) - const indices = new Uint32Array(24) - - Vec3.set(s, width, height, depth) - Vec3.mul(a, _a, s); Vec3.mul(b, _b, s); Vec3.mul(c, _c, s) - Vec3.mul(d, _d, s); Vec3.mul(e, _e, s); Vec3.mul(f, _f, s) - - Vec3.sub(nabde, b, a) - Vec3.normalize(nabde, Vec3.set(nabde, -nabde[1], nabde[0], 0)) - Vec3.sub(nacdf, c, a) - Vec3.normalize(nacdf, Vec3.set(nacdf, nacdf[1], -nacdf[0], 0)) +const a = Vec3.zero(), b = Vec3.zero(), c = Vec3.zero(), d = Vec3.zero() +const points = polygon(3, false) - let vc = 0 - let ic = 0 - - // abc - Vec3.toArray(a, vertices, vc + 0) - Vec3.toArray(c, vertices, vc + 3) - Vec3.toArray(b, vertices, vc + 6) - for (let i = 0; i < 3; ++i) Vec3.toArray(nabc, normals, vc + i * 3) - indices[ic + 0] = vc / 3 + 0 - indices[ic + 1] = vc / 3 + 1 - indices[ic + 2] = vc / 3 + 2 - vc += 9 - ic += 3 - - // def - Vec3.toArray(d, vertices, vc + 0) - Vec3.toArray(e, vertices, vc + 3) - Vec3.toArray(f, vertices, vc + 6) - for (let i = 0; i < 3; ++i) Vec3.toArray(ndef, normals, vc + i * 3) - indices[ic + 0] = vc / 3 + 0 - indices[ic + 1] = vc / 3 + 1 - indices[ic + 2] = vc / 3 + 2 - vc += 9 - ic += 3 - - // abde - Vec3.toArray(a, vertices, vc + 0) - Vec3.toArray(d, vertices, vc + 3) - Vec3.toArray(e, vertices, vc + 6) - Vec3.toArray(b, vertices, vc + 9) - for (let i = 0; i < 4; ++i) Vec3.toArray(nabde, normals, vc + i * 3) - indices[ic + 0] = vc / 3 + 2 - indices[ic + 1] = vc / 3 + 1 - indices[ic + 2] = vc / 3 + 0 - indices[ic + 3] = vc / 3 + 0 - indices[ic + 4] = vc / 3 + 3 - indices[ic + 5] = vc / 3 + 2 - vc += 12 - ic += 6 - - // acdf - Vec3.toArray(d, vertices, vc + 0) - Vec3.toArray(a, vertices, vc + 3) - Vec3.toArray(c, vertices, vc + 6) - Vec3.toArray(f, vertices, vc + 9) - for (let i = 0; i < 4; ++i) Vec3.toArray(nacdf, normals, vc + i * 3) - indices[ic + 0] = vc / 3 + 2 - indices[ic + 1] = vc / 3 + 1 - indices[ic + 2] = vc / 3 + 0 - indices[ic + 3] = vc / 3 + 0 - indices[ic + 4] = vc / 3 + 3 - indices[ic + 5] = vc / 3 + 2 - vc += 12 - ic += 6 - - // bcef - Vec3.toArray(e, vertices, vc + 0) - Vec3.toArray(f, vertices, vc + 3) - Vec3.toArray(c, vertices, vc + 6) - Vec3.toArray(b, vertices, vc + 9) - for (let i = 0; i < 4; ++i) Vec3.toArray(nbcef, normals, vc + i * 3) - indices[ic + 0] = vc / 3 + 2 - indices[ic + 1] = vc / 3 + 1 - indices[ic + 2] = vc / 3 + 0 - indices[ic + 3] = vc / 3 + 0 - indices[ic + 4] = vc / 3 + 3 - indices[ic + 5] = vc / 3 + 2 - vc += 12 - ic += 6 +/** + * Create a prism with a poligonal base + */ +export function createWedge(): Primitive { + const builder = PrimitiveBuilder(8) + + // create sides + for (let i = 0; i < 3; ++i) { + const ni = (i + 1) % 3 + Vec3.set(a, points[i * 2], points[i * 2 + 1], -0.5) + Vec3.set(b, points[ni * 2], points[ni * 2 + 1], -0.5) + Vec3.set(c, points[ni * 2], points[ni * 2 + 1], 0.5) + Vec3.set(d, points[i * 2], points[i * 2 + 1], 0.5) + builder.add(a, b, c) + builder.add(c, d, a) + } + + // create bases + Vec3.set(a, points[0], points[1], -0.5) + Vec3.set(b, points[2], points[3], -0.5) + Vec3.set(c, points[4], points[5], -0.5) + builder.add(a, b, c) + Vec3.set(a, points[0], points[1], 0.5) + Vec3.set(b, points[2], points[3], 0.5) + Vec3.set(c, points[4], points[5], 0.5) + builder.add(c, b, a) + + return builder.getPrimitive() +} - return { vertices, normals, indices } +let wedge: Primitive +export function Wedge() { + if (!wedge) wedge = createWedge() + return wedge } \ No newline at end of file diff --git a/src/mol-geo/representation/structure/index.ts b/src/mol-geo/representation/structure/index.ts index bc6c343baadbcbfa74b75a8759fa680a9fedfb03..7b95c2c54e9f71eb35e7fc66fe9d3a6868c9cce0 100644 --- a/src/mol-geo/representation/structure/index.ts +++ b/src/mol-geo/representation/structure/index.ts @@ -35,6 +35,7 @@ export function StructureRepresentation<P extends StructureProps>(visualCtor: () function create(structure: Structure, props: P = {} as P) { _props = Object.assign({}, DefaultStructureProps, _props, props, getQualityProps(props, structure)) + _props.colorTheme!.structure = structure return Task.create('Creating StructureRepresentation', async ctx => { if (!_structure) { @@ -56,6 +57,7 @@ export function StructureRepresentation<P extends StructureProps>(visualCtor: () function update(props: P) { return Task.create('Updating StructureRepresentation', async ctx => { _props = Object.assign({}, DefaultStructureProps, _props, props, getQualityProps(props, _structure)) + _props.colorTheme!.structure = _structure if (!await visual.update(ctx, _props)) { await visual.create(ctx, _structure, _props) @@ -98,6 +100,7 @@ export function StructureUnitsRepresentation<P extends StructureProps>(visualCto function create(structure: Structure, props: P = {} as P) { _props = Object.assign({}, DefaultStructureProps, _props, props, getQualityProps(props, structure)) + _props.colorTheme!.structure = structure return Task.create('Creating StructureRepresentation', async ctx => { if (!_structure) { @@ -151,6 +154,7 @@ export function StructureUnitsRepresentation<P extends StructureProps>(visualCto function update(props: P) { return Task.create('Updating StructureRepresentation', async ctx => { _props = Object.assign({}, DefaultStructureProps, _props, props, getQualityProps(props, _structure)) + _props.colorTheme!.structure = _structure visuals.forEach(async ({ visual, group }) => { if (!await visual.update(ctx, _props)) { diff --git a/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts b/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts index 49676477f3217edfaf6f8897a03f80e2927481a0..0bebb4471114e9413bbce503881f0c0f89db3fe5 100644 --- a/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts +++ b/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts @@ -78,14 +78,13 @@ export function CarbohydrateLinkVisual(): StructureVisual<CarbohydrateLinkProps> currentStructure = structure const { colorTheme } = { ...DefaultCarbohydrateLinkProps, ...props } + const elementCount = structure.carbohydrates.links.length const instanceCount = 1 - const elementCount = currentStructure.elementCount mesh = await createCarbohydrateLinkCylinderMesh(ctx, currentStructure, currentProps, mesh) - // console.log(mesh) const transforms = createIdentityTransform() - const color = createColors(createCarbohydrateLinkIterator(structure), colorTheme) + const color = createColors(CarbohydrateLinkIterator(structure), colorTheme) const marker = createMarkers(instanceCount * elementCount) const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount } @@ -114,7 +113,7 @@ export function CarbohydrateLinkVisual(): StructureVisual<CarbohydrateLinkProps> } if (updateColor) { - createColors(createCarbohydrateLinkIterator(currentStructure), newProps.colorTheme, renderObject.values) + createColors(CarbohydrateLinkIterator(currentStructure), newProps.colorTheme, renderObject.values) } updateMeshValues(renderObject.values, newProps) @@ -135,7 +134,7 @@ export function CarbohydrateLinkVisual(): StructureVisual<CarbohydrateLinkProps> } } -function createCarbohydrateLinkIterator(structure: Structure): LocationIterator { +function CarbohydrateLinkIterator(structure: Structure): LocationIterator { const { elements, links } = structure.carbohydrates const elementCount = links.length const instanceCount = 1 @@ -178,7 +177,7 @@ function markLink(loci: Loci, action: MarkerAction, structure: Structure, values const tMarker = values.tMarker const { getLinkIndex } = structure.carbohydrates - const elementCount = structure.carbohydrates.elements.length + const elementCount = structure.carbohydrates.links.length let changed = false const array = tMarker.ref.value.array diff --git a/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts b/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts index e51b2049c9b18dfa6a1c4092f0a8a40dc08e1850..806964cf9474783a013146c38fa45d00c9f8e8b7 100644 --- a/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts +++ b/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts @@ -47,7 +47,7 @@ async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Stru Mat4.targetTo(t, center, pd, normal) Mat4.setTranslation(t, center) - builder.setId(i) + builder.setId(i * 2) switch (shapeType) { case SaccharideShapes.FilledSphere: @@ -58,18 +58,22 @@ async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Stru builder.addBox(t) break; case SaccharideShapes.CrossedCube: - // TODO split Mat4.scaleUniformly(t, t, side) - builder.addBox(t) + builder.addPerforatedBox(t) + Mat4.mul(t, t, Mat4.rotZ90X180) + builder.setId(i * 2 + 1) + builder.addPerforatedBox(t) break; case SaccharideShapes.FilledCone: Mat4.scaleUniformly(t, t, side * 1.2) builder.addOctagonalPyramid(t) break case SaccharideShapes.DevidedCone: - // TODO split Mat4.scaleUniformly(t, t, side * 1.2) - builder.addOctagonalPyramid(t) + builder.addPerforatedOctagonalPyramid(t) + Mat4.mul(t, t, Mat4.rotZ90) + builder.setId(i * 2 + 1) + builder.addPerforatedOctagonalPyramid(t) break case SaccharideShapes.FlatBox: Mat4.mul(t, t, Mat4.rotZY90) @@ -86,10 +90,12 @@ async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Stru builder.addOctahedron(t) break case SaccharideShapes.DividedDiamond: - // TODO split Mat4.mul(t, t, Mat4.rotZY90) Mat4.scale(t, t, Vec3.set(sVec, side * 1.4, side * 1.4, side * 1.4)) - builder.addOctahedron(t) + builder.addPerforatedOctahedron(t) + Mat4.mul(t, t, Mat4.rotY90) + builder.setId(i * 2 + 1) + builder.addPerforatedOctahedron(t) break case SaccharideShapes.FlatDiamond: Mat4.mul(t, t, Mat4.rotZY90) @@ -141,7 +147,7 @@ export function CarbohydrateSymbolVisual(): StructureVisual<CarbohydrateSymbolPr mesh = await createCarbohydrateSymbolMesh(ctx, currentStructure, mesh) const transforms = createIdentityTransform() - const color = createColors(createCarbohydrateElementIterator(structure), colorTheme) + const color = createColors(CarbohydrateElementIterator(structure), colorTheme) const marker = createMarkers(instanceCount * elementCount) const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount } @@ -170,7 +176,7 @@ export function CarbohydrateSymbolVisual(): StructureVisual<CarbohydrateSymbolPr } if (updateColor) { - createColors(createCarbohydrateElementIterator(currentStructure), newProps.colorTheme, renderObject.values) + createColors(CarbohydrateElementIterator(currentStructure), newProps.colorTheme, renderObject.values) } updateMeshValues(renderObject.values, newProps) @@ -191,24 +197,27 @@ export function CarbohydrateSymbolVisual(): StructureVisual<CarbohydrateSymbolPr } } -function createCarbohydrateElementIterator(structure: Structure): LocationIterator { +function CarbohydrateElementIterator(structure: Structure): LocationIterator { const carbElements = structure.carbohydrates.elements - const elementCount = carbElements.length + const elementCount = carbElements.length * 2 const instanceCount = 1 const location = StructureElement.create() - const getLocation = (elementIndex: number, instanceIndex: number) => { - const carb = carbElements[elementIndex] + function getLocation (elementIndex: number, instanceIndex: number) { + const carb = carbElements[Math.floor(elementIndex / 2)] location.unit = carb.unit location.element = carb.anomericCarbon return location } - return LocationIterator(elementCount, instanceCount, getLocation) + function isSecondary (elementIndex: number, instanceIndex: number) { + return (elementIndex % 2) === 1 + } + return LocationIterator(elementCount, instanceCount, getLocation, isSecondary) } function getCarbohydrateLoci(pickingId: PickingId, structure: Structure, id: number) { const { objectId, elementId } = pickingId if (id === objectId) { - const carb = structure.carbohydrates.elements[elementId] + const carb = structure.carbohydrates.elements[Math.floor(elementId / 2)] const { unit } = carb const index = OrderedSet.findPredecessorIndex(unit.elements, carb.anomericCarbon) const indices = OrderedSet.ofSingleton(index as StructureElement.UnitIndex) @@ -221,7 +230,7 @@ function markCarbohydrate(loci: Loci, action: MarkerAction, structure: Structure const tMarker = values.tMarker const { getElementIndex } = structure.carbohydrates - const elementCount = structure.carbohydrates.elements.length + const elementCount = structure.carbohydrates.elements.length * 2 let changed = false const array = tMarker.ref.value.array @@ -234,7 +243,7 @@ function markCarbohydrate(loci: Loci, action: MarkerAction, structure: Structure OrderedSet.forEach(e.indices, index => { const idx = getElementIndex(e.unit, e.unit.elements[index]) if (idx !== undefined) { - if (applyMarkerAction(array, idx, idx + 1, action) && !changed) { + if (applyMarkerAction(array, idx * 2, idx * 2 + 2, action) && !changed) { changed = true } } diff --git a/src/mol-geo/representation/structure/visual/util/common.ts b/src/mol-geo/representation/structure/visual/util/common.ts index 07bc94993cb11eafcf00208fd395fe0aed5175dd..d4264354fee2a72890d394307d57debcdd9aa364 100644 --- a/src/mol-geo/representation/structure/visual/util/common.ts +++ b/src/mol-geo/representation/structure/visual/util/common.ts @@ -39,7 +39,7 @@ export function createColors(locationIt: LocationIterator, props: ColorTheme, co case 'atom-index': return elementIndexColorData(locationIt, colorData) case 'carbohydrate-symbol': - return carbohydrateSymbolColorData(locationIt, colorData) + return carbohydrateSymbolColorData(locationIt, props, colorData) case 'chain-id': return chainIdColorData(locationIt, colorData) case 'element-symbol': @@ -47,7 +47,7 @@ export function createColors(locationIt: LocationIterator, props: ColorTheme, co case 'instance-index': return instanceIndexColorData(locationIt, colorData) case 'uniform': - return createUniformColor(locationIt, () => props.value, colorData) + return createUniformColor(locationIt, () => props.value || 0x000000, colorData) } } diff --git a/src/mol-geo/representation/structure/visual/util/location-iterator.ts b/src/mol-geo/representation/structure/visual/util/location-iterator.ts index 5dc3d76ce9380de088e6119900ea4ec895edcc6c..2fc20e011ef88f779cac6fbaaec3e84f5fa465c6 100644 --- a/src/mol-geo/representation/structure/visual/util/location-iterator.ts +++ b/src/mol-geo/representation/structure/visual/util/location-iterator.ts @@ -13,13 +13,15 @@ export interface LocationValue { index: number elementIndex: number instanceIndex: number + isSecondary: boolean } -export const NullLocationValue = { +export const NullLocationValue: LocationValue = { location: NullLocation, index: 0, elementIndex: 0, - instanceIndex: 0 + instanceIndex: 0, + isSecondary: false } export interface LocationIterator extends Iterator<LocationValue> { @@ -32,13 +34,15 @@ export interface LocationIterator extends Iterator<LocationValue> { } type LocationGetter = (elementIndex: number, instanceIndex: number) => Location +type IsSecondaryGetter = (elementIndex: number, instanceIndex: number) => boolean -export function LocationIterator(elementCount: number, instanceCount: number, getLocation: LocationGetter): LocationIterator { - const value = { +export function LocationIterator(elementCount: number, instanceCount: number, getLocation: LocationGetter, isSecondary: IsSecondaryGetter = () => false): LocationIterator { + const value: LocationValue = { location: NullLocation as Location, index: 0, elementIndex: 0, - instanceIndex: 0 + instanceIndex: 0, + isSecondary: false } let hasNext = value.elementIndex < elementCount @@ -57,6 +61,7 @@ export function LocationIterator(elementCount: number, instanceCount: number, ge value.instanceIndex = instanceIndex value.index = instanceIndex * elementCount + elementIndex value.location = getLocation(elementIndex, instanceIndex) + value.isSecondary = isSecondary(elementIndex, instanceIndex) ++elementIndex if (elementIndex === elementCount) { ++instanceIndex diff --git a/src/mol-geo/shape/mesh-builder.ts b/src/mol-geo/shape/mesh-builder.ts index 8b8404c19794dc5be1bb54f3a214d6aedbdb7269..f3dafe8a41d33153039a6af87bb1dee4bfd44e81 100644 --- a/src/mol-geo/shape/mesh-builder.ts +++ b/src/mol-geo/shape/mesh-builder.ts @@ -16,10 +16,12 @@ import { getNormalMatrix } from '../util'; import { addSheet } from '../primitive/sheet'; import { addTube } from '../primitive/tube'; import { StarProps, Star } from '../primitive/star'; -import { Octahedron } from '../primitive/octahedron'; +import { Octahedron, PerforatedOctahedron } from '../primitive/octahedron'; import { Primitive } from '../primitive/primitive'; -import { Wedge, Box, DiamondPrism, PentagonalPrism, HexagonalPrism } from '../primitive/prism'; -import { OctagonalPyramide } from '../primitive/pyramid'; +import { DiamondPrism, PentagonalPrism, HexagonalPrism } from '../primitive/prism'; +import { OctagonalPyramide, PerforatedOctagonalPyramide } from '../primitive/pyramid'; +import { PerforatedBox, Box } from '../primitive/box'; +import { Wedge } from '../primitive/wedge'; export interface MeshBuilderState { vertices: ChunkedArray<number, 3> @@ -30,14 +32,17 @@ export interface MeshBuilderState { export interface MeshBuilder { add(t: Mat4, _vertices: ArrayLike<number>, _normals: ArrayLike<number>, _indices?: ArrayLike<number>): void addBox(t: Mat4): void + addPerforatedBox(t: Mat4): void addPlane(t: Mat4, props?: PlaneProps): void addWedge(t: Mat4): void addDiamondPrism(t: Mat4): void addPentagonalPrism(t: Mat4): void addHexagonalPrism(t: Mat4): void addOctagonalPyramid(t: Mat4): void + addPerforatedOctagonalPyramid(t: Mat4): void addStar(t: Mat4, props?: StarProps): void addOctahedron(t: Mat4): void + addPerforatedOctahedron(t: Mat4): void addCylinder(start: Vec3, end: Vec3, lengthScale: number, props: CylinderProps): void addDoubleCylinder(start: Vec3, end: Vec3, lengthScale: number, shift: Vec3, props: CylinderProps): void addFixedCountDashedCylinder(start: Vec3, end: Vec3, lengthScale: number, segmentCount: number, props: CylinderProps): void @@ -140,6 +145,10 @@ export namespace MeshBuilder { const { vertices, normals, indices } = Box() add(t, vertices, normals, indices) }, + addPerforatedBox: (t: Mat4) => { + const { vertices, normals, indices } = PerforatedBox() + add(t, vertices, normals, indices) + }, addPlane: (t: Mat4, props?: PlaneProps) => { const { vertices, normals, indices } = Plane(props) add(t, vertices, normals, indices) @@ -164,6 +173,10 @@ export namespace MeshBuilder { const { vertices, normals, indices } = OctagonalPyramide() add(t, vertices, normals, indices) }, + addPerforatedOctagonalPyramid: (t: Mat4) => { + const { vertices, normals, indices } = PerforatedOctagonalPyramide() + add(t, vertices, normals, indices) + }, addStar: (t: Mat4, props?: StarProps) => { const { vertices, normals, indices } = Star(props) add(t, vertices, normals, indices) @@ -172,6 +185,10 @@ export namespace MeshBuilder { const { vertices, normals, indices } = Octahedron() add(t, vertices, normals, indices) }, + addPerforatedOctahedron: (t: Mat4) => { + const { vertices, normals, indices } = PerforatedOctahedron() + add(t, vertices, normals, indices) + }, addCylinder: (start: Vec3, end: Vec3, lengthScale: number, props: CylinderProps) => { const d = Vec3.distance(start, end) * lengthScale props.height = d diff --git a/src/mol-geo/theme/index.ts b/src/mol-geo/theme/index.ts index d34cb26a6d101a9ff03d58d9d03b136337ce69d1..0e0ed1a9c73405bcc3a52371f0d7d17b346e85ef 100644 --- a/src/mol-geo/theme/index.ts +++ b/src/mol-geo/theme/index.ts @@ -5,22 +5,25 @@ */ import { Color } from 'mol-util/color'; +import { Structure } from 'mol-model/structure'; -export interface UniformColorTheme { - name: 'uniform' - value: Color -} - -export interface ScaleColorTheme { - name: 'atom-index' | 'chain-id'| 'instance-index' +export interface ColorTheme { + name: 'atom-index' | 'chain-id'| 'instance-index' | 'uniform' | 'carbohydrate-symbol' | 'element-symbol' domain?: [number, number] + value?: Color + structure?: Structure } -export interface TableColorTheme { - name: 'carbohydrate-symbol' | 'element-symbol' +export const ColorThemeInfo = { + 'atom-index': {}, + 'carbohydrate-symbol': {}, + 'chain-id': {}, + 'element-symbol': {}, + 'instance-index': {}, + 'uniform': {} } - -export type ColorTheme = UniformColorTheme | ScaleColorTheme | TableColorTheme +export type ColorThemeName = keyof typeof ColorThemeInfo +export const ColorThemeNames = Object.keys(ColorThemeInfo) export interface UniformSizeTheme { name: 'uniform', diff --git a/src/mol-geo/theme/structure/color/carbohydrate-symbol.ts b/src/mol-geo/theme/structure/color/carbohydrate-symbol.ts index e6f8065b436ea3260b77f9aea806b1f8dead892a..1417b4e39578e8776ac4ea0ab6e36e6fcb14f212 100644 --- a/src/mol-geo/theme/structure/color/carbohydrate-symbol.ts +++ b/src/mol-geo/theme/structure/color/carbohydrate-symbol.ts @@ -4,41 +4,47 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Unit, StructureProperties, StructureElement, Link } from 'mol-model/structure'; +import { StructureElement, Link, ElementIndex, Unit } from 'mol-model/structure'; import { ColorData, createElementColor } from '../../../util/color-data'; -import { ColorScale, Color } from 'mol-util/color'; +import { Color } from 'mol-util/color'; import { LocationIterator, LocationValue } from '../../../representation/structure/visual/util/location-iterator'; +import { ColorTheme } from '../..'; +import { SaccharideColors } from 'mol-model/structure/structure/carbohydrates/constants'; -function getAsymId(unit: Unit): StructureElement.Property<string> { - switch (unit.kind) { - case Unit.Kind.Atomic: - return StructureProperties.chain.label_asym_id - case Unit.Kind.Spheres: - case Unit.Kind.Gaussians: - return StructureProperties.coarse.asym_id - } -} - -export function carbohydrateSymbolColorData(locationIt: LocationIterator, colorData?: ColorData) { - const l = StructureElement.create() - - function colorFn(locationValue: LocationValue): Color { - const { location } = locationValue - if (StructureElement.isLocation(location)) { - const map = location.unit.model.properties.asymIdSerialMap - const scale = ColorScale.create({ domain: [ 0, map.size - 1 ] }) - const asym_id = getAsymId(location.unit) - return scale.color(map.get(asym_id(location)) || 0) - } else if (Link.isLocation(location)) { - const map = location.aUnit.model.properties.asymIdSerialMap - const scale = ColorScale.create({ domain: [ 0, map.size - 1 ] }) - const asym_id = getAsymId(location.aUnit) - l.unit = location.aUnit - l.element = location.aUnit.elements[location.aIndex] - return scale.color(map.get(asym_id(l)) || 0) +const DefaultColor = 0xCCCCCC; + +export function carbohydrateSymbolColorData(locationIt: LocationIterator, props: ColorTheme, colorData?: ColorData) { + let colorFn: (locationValue: LocationValue) => Color + + if (props.structure) { + const { elements, getElementIndex, getAnomericCarbon } = props.structure.carbohydrates + + const getColor = (unit: Unit, index: ElementIndex) => { + const residueIndex = unit.model.atomicHierarchy.residueAtomSegments.index[index] + const anomericCarbon = getAnomericCarbon(unit, residueIndex) + if (anomericCarbon !== undefined) { + const idx = getElementIndex(unit, anomericCarbon) + if (idx !== undefined) return elements[idx].component.color + } + return DefaultColor + } + + colorFn = (locationValue: LocationValue) => { + const { location: l } = locationValue + if (locationValue.isSecondary) { + return SaccharideColors.Secondary + } else { + if (StructureElement.isLocation(l)) { + return getColor(l.unit, l.element) + } else if (Link.isLocation(l)) { + return getColor(l.aUnit, l.aUnit.elements[l.aIndex]) + } + } + return DefaultColor } - return 0 + } else { + colorFn = () => DefaultColor } return createElementColor(locationIt, colorFn, colorData) diff --git a/src/mol-math/linear-algebra/3d/mat4.ts b/src/mol-math/linear-algebra/3d/mat4.ts index ddd7eaafd42725f55018bfdbd35f4185b581a9af..968870259862fd95d033d32f347598ab264a04e1 100644 --- a/src/mol-math/linear-algebra/3d/mat4.ts +++ b/src/mol-math/linear-algebra/3d/mat4.ts @@ -873,18 +873,26 @@ namespace Mat4 { return out; } - /** Rotation matrix for 90deg rotation around x-axis */ + /** Rotation matrix for 90deg around x-axis */ export const rotX90: ReadonlyMat4 = Mat4.fromRotation(Mat4.identity(), degToRad(90), Vec3.create(1, 0, 0)) - /** Rotation matrix for 90deg rotation around y-axis */ + /** Rotation matrix for 180deg around x-axis */ + export const rotX180: ReadonlyMat4 = Mat4.fromRotation(Mat4.identity(), degToRad(180), Vec3.create(1, 0, 0)) + /** Rotation matrix for 90deg around y-axis */ export const rotY90: ReadonlyMat4 = Mat4.fromRotation(Mat4.identity(), degToRad(90), Vec3.create(0, 1, 0)) - /** Rotation matrix for 90deg rotation around z-axis */ + /** Rotation matrix for 180deg around y-axis */ + export const rotY180: ReadonlyMat4 = Mat4.fromRotation(Mat4.identity(), degToRad(180), Vec3.create(0, 1, 0)) + /** Rotation matrix for 90deg around z-axis */ export const rotZ90: ReadonlyMat4 = Mat4.fromRotation(Mat4.identity(), degToRad(90), Vec3.create(0, 0, 1)) - /** Rotation matrix for 90deg rotation around first x-axis and then y-axis */ + /** Rotation matrix for 180deg around z-axis */ + export const rotZ180: ReadonlyMat4 = Mat4.fromRotation(Mat4.identity(), degToRad(180), Vec3.create(0, 0, 1)) + /** Rotation matrix for 90deg around first x-axis and then y-axis */ export const rotXY90: ReadonlyMat4 = Mat4.mul(Mat4.identity(), rotX90, rotY90) - /** Rotation matrix for 90deg rotation around first z-axis and then y-axis */ + /** Rotation matrix for 90deg around first z-axis and then y-axis */ export const rotZY90: ReadonlyMat4 = Mat4.mul(Mat4.identity(), rotZ90, rotY90) - /** Rotation matrix for 90deg rotation around first z-axis and then y-axis and then z-axis */ + /** Rotation matrix for 90deg around first z-axis and then y-axis and then z-axis */ export const rotZYZ90: ReadonlyMat4 = Mat4.mul(Mat4.identity(), rotZY90, rotZ90) + /** Rotation matrix for 90deg around first z-axis and then 180deg around x-axis */ + export const rotZ90X180: ReadonlyMat4 = Mat4.mul(Mat4.identity(), rotZ90, rotX180) } export default Mat4 \ No newline at end of file diff --git a/src/mol-model/structure/structure/carbohydrates/compute.ts b/src/mol-model/structure/structure/carbohydrates/compute.ts index ebf52e14c8dee07e214c32fa59b26fe8d5a1481a..d983593c0036ca5c3107a784781e6406c984e366 100644 --- a/src/mol-model/structure/structure/carbohydrates/compute.ts +++ b/src/mol-model/structure/structure/carbohydrates/compute.ts @@ -315,5 +315,22 @@ function buildLookups (elements: CarbohydrateElement[], links: CarbohydrateLink[ return linkMap.get(linkKey(unitA, anomericCarbonA, unitB, anomericCarbonB)) } - return { getElementIndex, getLinkIndex } + // anomeric carbon lookup + + function anomericCarbonKey(unit: Unit, residueIndex: ResidueIndex) { + return `${unit.id}|${residueIndex}` + } + + const anomericCarbonMap = new Map<string, ElementIndex>() + for (let i = 0, il = elements.length; i < il; ++i) { + const { unit, anomericCarbon } = elements[i] + const residueIndex = unit.model.atomicHierarchy.residueAtomSegments.index[anomericCarbon] + anomericCarbonMap.set(anomericCarbonKey(unit, residueIndex), anomericCarbon) + } + + function getAnomericCarbon(unit: Unit, residueIndex: ResidueIndex) { + return anomericCarbonMap.get(anomericCarbonKey(unit, residueIndex)) + } + + return { getElementIndex, getLinkIndex, getAnomericCarbon } } \ No newline at end of file diff --git a/src/mol-model/structure/structure/carbohydrates/constants.ts b/src/mol-model/structure/structure/carbohydrates/constants.ts index a6c16844db26d6db7587be46f6ba822d37c3e7e2..4de9309289ece0ed1d09ea6f6bdcdd05a326875b 100644 --- a/src/mol-model/structure/structure/carbohydrates/constants.ts +++ b/src/mol-model/structure/structure/carbohydrates/constants.ts @@ -12,8 +12,7 @@ export const enum SaccharideShapes { FlatBox, FilledStar, FilledDiamond, FlatDiamond, FlatHexagon, Pentagon } -// TODO move to theme -const enum SaccharideColors { +export const enum SaccharideColors { Blue = 0x0090bc, Green = 0x00a651, Yellow = 0xffd400, diff --git a/src/mol-model/structure/structure/carbohydrates/data.ts b/src/mol-model/structure/structure/carbohydrates/data.ts index c69bde439533cd91c36936588394870fde68c83f..4afcf03e445fc2c9671ac8aee04e84ab7a1edb60 100644 --- a/src/mol-model/structure/structure/carbohydrates/data.ts +++ b/src/mol-model/structure/structure/carbohydrates/data.ts @@ -44,4 +44,5 @@ export interface Carbohydrates { partialElements: ReadonlyArray<PartialCarbohydrateElement> getElementIndex: (unit: Unit, anomericCarbon: ElementIndex) => number | undefined getLinkIndex: (unitA: Unit, anomericCarbonA: ElementIndex, unitB: Unit, anomericCarbonB: ElementIndex) => number | undefined + getAnomericCarbon: (unit: Unit, residueIndex: ResidueIndex) => ElementIndex | undefined } \ No newline at end of file diff --git a/src/mol-view/stage.ts b/src/mol-view/stage.ts index ed5ce1cc1e9c53bb933dab8bc12afa39c5c25e3c..6b0998765d054be89468eeb56d14e5f21235a57f 100644 --- a/src/mol-view/stage.ts +++ b/src/mol-view/stage.ts @@ -78,7 +78,7 @@ export class Stage { // this.loadPdbid('1hrv') // viral assembly // this.loadPdbid('1rb8') // virus // this.loadPdbid('1blu') // metal coordination - this.loadPdbid('3pqr') // inter unit bonds, two polymer chains, ligands, water, carbohydrates linked to protein + // this.loadPdbid('3pqr') // inter unit bonds, two polymer chains, ligands, water, carbohydrates linked to protein // this.loadPdbid('4v5a') // ribosome // this.loadPdbid('3j3q') // ... // this.loadPdbid('2np2') // dna @@ -98,7 +98,7 @@ export class Stage { // this.loadPdbid('3sn6') // discontinuous chains // this.loadPdbid('2zex') // contains carbohydrate polymer // this.loadPdbid('3sgj') // contains carbohydrate polymer - // this.loadPdbid('3ina') // contains GlcN and IdoA + this.loadPdbid('3ina') // contains GlcN and IdoA // this.loadPdbid('1umz') // contains Xyl (Xyloglucan) // this.loadPdbid('1mfb') // contains Abe // this.loadPdbid('2gdu') // contains sucrose