mirror of
https://github.com/molstar/molstar.git
synced 2026-06-07 23:34:23 +08:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50b615e86c | ||
|
|
5b4c6743e7 | ||
|
|
99a3906978 | ||
|
|
981db34736 | ||
|
|
c079a8c5a8 | ||
|
|
ad70adf6ce | ||
|
|
89110b52bd |
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "1.2.1",
|
||||
"version": "1.2.3",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "1.2.1",
|
||||
"version": "1.2.3",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
|
||||
@@ -34,6 +34,11 @@ uniform sampler2D tCumulativeSum;
|
||||
|
||||
uniform bool uLittleEndian;
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
// floatToRgba adapted from https://github.com/equinor/glsl-float-to-rgba
|
||||
// MIT License, Copyright (c) 2020 Equinor
|
||||
|
||||
float shiftRight (float v, float amt) {
|
||||
v = floor(v) + 0.5;
|
||||
return floor(v / exp2(amt));
|
||||
@@ -48,8 +53,7 @@ float extractBits (float num, float from, float to) {
|
||||
from = floor(from + 0.5); to = floor(to + 0.5);
|
||||
return maskLast(shiftRight(num, from), to - from);
|
||||
}
|
||||
// Adapted from https://github.com/equinor/glsl-float-to-rgba
|
||||
// MIT License, Copyright (c) 2020 Equinor
|
||||
|
||||
vec4 floatToRgba(float texelFloat) {
|
||||
if (texelFloat == 0.0) return vec4(0, 0, 0, 0);
|
||||
float sign = texelFloat > 0.0 ? 0.0 : 1.0;
|
||||
@@ -73,6 +77,23 @@ vec4 floatToRgba(float texelFloat) {
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
// rgbaToFloat adapted from https://github.com/ihmeuw/glsl-rgba-to-float
|
||||
// BSD 3-Clause License
|
||||
//
|
||||
// Copyright (c) 2019, Institute for Health Metrics and Evaluation All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
// - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
// - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
// - Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
// OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
ivec4 floatsToBytes(vec4 inputFloats) {
|
||||
ivec4 bytes = ivec4(inputFloats * 255.0);
|
||||
return (
|
||||
@@ -133,7 +154,6 @@ float bitsToFloat(bool bits[32]) {
|
||||
return signBit * mantissa * exp2(exponent - exponentBias - 23.0);
|
||||
}
|
||||
|
||||
// Decode a 32-bit float from the RGBA color channels of a texel.
|
||||
float rgbaToFloat(vec4 texelRGBA) {
|
||||
ivec4 rgbaBytes = floatsToBytes(texelRGBA);
|
||||
bool bits[32];
|
||||
|
||||
@@ -75,7 +75,7 @@ export class DrawPass {
|
||||
|
||||
this.drawTarget = createNullRenderTarget(webgl.gl);
|
||||
|
||||
this.colorTarget = webgl.createRenderTarget(width, height);
|
||||
this.colorTarget = webgl.createRenderTarget(width, height, true, 'uint8', 'linear');
|
||||
this.packedDepth = !extensions.depthTexture;
|
||||
|
||||
this.depthTarget = webgl.createRenderTarget(width, height);
|
||||
|
||||
@@ -193,7 +193,7 @@ export class MultiSamplePass {
|
||||
|
||||
const { x, y, width, height } = camera.viewport;
|
||||
const sampleWeight = 1.0 / offsetList.length;
|
||||
const postprocessingEnabled = PostprocessingPass.isEnabled(props.postprocessing);
|
||||
const postprocessingEnabled = PostprocessingPass.isEnabled(props.postprocessing) || props.postprocessing.antialiasing.name === 'on';
|
||||
|
||||
if (sampleIndex === -1) {
|
||||
drawPass.render(renderer, camera, scene, helper, false, transparentBackground);
|
||||
|
||||
@@ -48,11 +48,14 @@ const PostprocessingShaderCode = ShaderCode('postprocessing', quad_vert, postpro
|
||||
type PostprocessingRenderable = ComputeRenderable<Values<typeof PostprocessingSchema>>
|
||||
|
||||
function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, depthTexture: Texture): PostprocessingRenderable {
|
||||
const width = colorTexture.getWidth();
|
||||
const height = colorTexture.getHeight();
|
||||
|
||||
const values: Values<typeof PostprocessingSchema> = {
|
||||
...QuadValues,
|
||||
tColor: ValueCell.create(colorTexture),
|
||||
tPackedDepth: ValueCell.create(depthTexture),
|
||||
uTexSize: ValueCell.create(Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())),
|
||||
uTexSize: ValueCell.create(Vec2.create(width, height)),
|
||||
|
||||
dOrthographic: ValueCell.create(0),
|
||||
uNear: ValueCell.create(1),
|
||||
@@ -93,7 +96,15 @@ export const PostprocessingParams = {
|
||||
}),
|
||||
off: PD.Group({})
|
||||
}, { cycle: true, description: 'Draw outline around 3D objects' }),
|
||||
antialiasing: PD.Boolean(true, { description: 'Fast Approximate Anti-Aliasing (FXAA)' })
|
||||
antialiasing: PD.MappedStatic('on', {
|
||||
on: PD.Group({
|
||||
edgeThresholdMin:PD.Numeric(0.0312, { min: 0.0312, max: 0.0833, step: 0.0001 }, { description: 'Trims the algorithm from processing darks.' }),
|
||||
edgeThresholdMax: PD.Numeric(0.063, { min: 0.063, max: 0.333, step: 0.001 }, { description: 'The minimum amount of local contrast required to apply algorithm.' }),
|
||||
iterations: PD.Numeric(12, { min: 0, max: 32, step: 1 }, { description: 'Number of edge exploration steps.' }),
|
||||
subpixelQuality: PD.Numeric(1.00, { min: 0.00, max: 1.00, step: 0.01 }, { description: 'Choose the amount of sub-pixel aliasing removal.' }),
|
||||
}),
|
||||
off: PD.Group({})
|
||||
}, { cycle: true, description: 'Fast Approximate Anti-Aliasing (FXAA)' }),
|
||||
};
|
||||
export type PostprocessingProps = PD.Values<typeof PostprocessingParams>
|
||||
|
||||
@@ -114,7 +125,7 @@ export class PostprocessingPass {
|
||||
const height = colorTarget.getHeight();
|
||||
|
||||
this.target = webgl.createRenderTarget(width, height, false);
|
||||
this.tmpTarget = webgl.createRenderTarget(width, height, false);
|
||||
this.tmpTarget = webgl.createRenderTarget(width, height, false, 'uint8', 'linear');
|
||||
this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTexture);
|
||||
this.fxaa = getFxaaRenderable(webgl, this.tmpTarget.texture);
|
||||
}
|
||||
@@ -128,7 +139,7 @@ export class PostprocessingPass {
|
||||
this.target.setSize(width, height);
|
||||
this.tmpTarget.setSize(width, height);
|
||||
ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height));
|
||||
ValueCell.update(this.fxaa.values.uTexSize, Vec2.set(this.fxaa.values.uTexSize.ref.value, width, height));
|
||||
ValueCell.update(this.fxaa.values.uTexSizeInv, Vec2.set(this.fxaa.values.uTexSizeInv.ref.value, 1 / width, 1 / height));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +196,7 @@ export class PostprocessingPass {
|
||||
this.renderable.update();
|
||||
}
|
||||
|
||||
if (props.antialiasing) {
|
||||
if (props.antialiasing.name === 'on') {
|
||||
this.tmpTarget.bind();
|
||||
} else if (toDrawingBuffer) {
|
||||
this.webgl.unbindFramebuffer();
|
||||
@@ -198,10 +209,30 @@ export class PostprocessingPass {
|
||||
}
|
||||
|
||||
private _renderFxaa(camera: ICamera, toDrawingBuffer: boolean, props: PostprocessingProps) {
|
||||
if (props.antialiasing.name === 'off') return;
|
||||
|
||||
const { values } = this.fxaa;
|
||||
|
||||
let needsUpdate = false;
|
||||
|
||||
const input = (props.occlusion.name === 'on' || props.outline.name === 'on')
|
||||
? this.tmpTarget.texture : this.drawPass.colorTarget.texture;
|
||||
if (this.fxaa.values.tColor.ref.value !== input) {
|
||||
if (values.tColor.ref.value !== input) {
|
||||
ValueCell.update(this.fxaa.values.tColor, input);
|
||||
needsUpdate = true;
|
||||
}
|
||||
|
||||
const { edgeThresholdMin, edgeThresholdMax, iterations, subpixelQuality } = props.antialiasing.params;
|
||||
if (values.dEdgeThresholdMin.ref.value !== edgeThresholdMin) needsUpdate = true;
|
||||
ValueCell.updateIfChanged(values.dEdgeThresholdMin, edgeThresholdMin);
|
||||
if (values.dEdgeThresholdMax.ref.value !== edgeThresholdMax) needsUpdate = true;
|
||||
ValueCell.updateIfChanged(values.dEdgeThresholdMax, edgeThresholdMax);
|
||||
if (values.dIterations.ref.value !== iterations) needsUpdate = true;
|
||||
ValueCell.updateIfChanged(values.dIterations, iterations);
|
||||
if (values.dSubpixelQuality.ref.value !== subpixelQuality) needsUpdate = true;
|
||||
ValueCell.updateIfChanged(values.dSubpixelQuality, subpixelQuality);
|
||||
|
||||
if (needsUpdate) {
|
||||
this.fxaa.update();
|
||||
}
|
||||
|
||||
@@ -216,11 +247,11 @@ export class PostprocessingPass {
|
||||
}
|
||||
|
||||
private _render(camera: ICamera, toDrawingBuffer: boolean, props: PostprocessingProps) {
|
||||
if (props.occlusion.name === 'on' || props.outline.name === 'on' || !props.antialiasing) {
|
||||
if (props.occlusion.name === 'on' || props.outline.name === 'on' || props.antialiasing.name === 'off') {
|
||||
this._renderPostprocessing(camera, toDrawingBuffer, props);
|
||||
}
|
||||
|
||||
if (props.antialiasing) {
|
||||
if (props.antialiasing.name === 'on') {
|
||||
this._renderFxaa(camera, toDrawingBuffer, props);
|
||||
}
|
||||
}
|
||||
@@ -240,16 +271,29 @@ export class PostprocessingPass {
|
||||
const FxaaSchema = {
|
||||
...QuadSchema,
|
||||
tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
uTexSize: UniformSpec('v2'),
|
||||
uTexSizeInv: UniformSpec('v2'),
|
||||
|
||||
dEdgeThresholdMin: DefineSpec('number'),
|
||||
dEdgeThresholdMax: DefineSpec('number'),
|
||||
dIterations: DefineSpec('number'),
|
||||
dSubpixelQuality: DefineSpec('number'),
|
||||
};
|
||||
const FxaaShaderCode = ShaderCode('fxaa', quad_vert, fxaa_frag);
|
||||
type FxaaRenderable = ComputeRenderable<Values<typeof FxaaSchema>>
|
||||
|
||||
function getFxaaRenderable(ctx: WebGLContext, colorTexture: Texture): FxaaRenderable {
|
||||
const width = colorTexture.getWidth();
|
||||
const height = colorTexture.getHeight();
|
||||
|
||||
const values: Values<typeof FxaaSchema> = {
|
||||
...QuadValues,
|
||||
tColor: ValueCell.create(colorTexture),
|
||||
uTexSize: ValueCell.create(Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())),
|
||||
uTexSizeInv: ValueCell.create(Vec2.create(1 / width, 1 / height)),
|
||||
|
||||
dEdgeThresholdMin: ValueCell.create(0.0312),
|
||||
dEdgeThresholdMax: ValueCell.create(0.125),
|
||||
dIterations: ValueCell.create(12),
|
||||
dSubpixelQuality: ValueCell.create(0.75),
|
||||
};
|
||||
|
||||
const schema = { ...FxaaSchema };
|
||||
|
||||
@@ -151,7 +151,7 @@ export type ShaderDefines = {
|
||||
[k: string]: ValueCell<DefineType>
|
||||
}
|
||||
|
||||
function getDefinesCode (defines: ShaderDefines) {
|
||||
function getDefinesCode(defines: ShaderDefines) {
|
||||
if (defines === undefined) return '';
|
||||
const lines = [];
|
||||
for (const name in defines) {
|
||||
|
||||
@@ -4,104 +4,226 @@ precision highp int;
|
||||
precision highp sampler2D;
|
||||
|
||||
uniform sampler2D tColor;
|
||||
uniform vec2 uTexSize;
|
||||
uniform vec2 uTexSizeInv;
|
||||
|
||||
// Basic FXAA implementation based on the code on geeks3d.com with the
|
||||
// modification that the texture2DLod stuff was removed since it's
|
||||
// unsupported by WebGL.
|
||||
// --
|
||||
// From:
|
||||
// https://github.com/mitsuhiko/webgl-meincraft
|
||||
// Copyright (c) 2011 by Armin Ronacher.
|
||||
// Some rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * The names of the contributors may not be used to endorse or
|
||||
// promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
// */
|
||||
// adapted from https://github.com/kosua20/Rendu
|
||||
// MIT License Copyright (c) 2017 Simon Rodriguez
|
||||
|
||||
#ifndef FXAA_REDUCE_MIN
|
||||
#define FXAA_REDUCE_MIN (1.0/ 128.0)
|
||||
#endif
|
||||
#ifndef FXAA_REDUCE_MUL
|
||||
#define FXAA_REDUCE_MUL (1.0 / 8.0)
|
||||
#endif
|
||||
#ifndef FXAA_SPAN_MAX
|
||||
#define FXAA_SPAN_MAX 8.0
|
||||
#endif
|
||||
#define QUALITY(q) ((q) < 5 ? 1.0 : ((q) > 5 ? ((q) < 10 ? 2.0 : ((q) < 11 ? 4.0 : 8.0)) : 1.5))
|
||||
|
||||
vec4 fxaa(sampler2D tex, const in vec2 fragCoord, const in vec2 resolution) {
|
||||
vec2 inverseVP = 1.0 / resolution;
|
||||
vec2 v_rgbNW = (fragCoord + vec2(-1.0, -1.0)) * inverseVP;
|
||||
vec2 v_rgbNE = (fragCoord + vec2(1.0, -1.0)) * inverseVP;
|
||||
vec2 v_rgbSW = (fragCoord + vec2(-1.0, 1.0)) * inverseVP;
|
||||
vec2 v_rgbSE = (fragCoord + vec2(1.0, 1.0)) * inverseVP;
|
||||
vec2 v_rgbM = vec2(fragCoord * inverseVP);
|
||||
float rgb2luma(vec3 rgb){
|
||||
return sqrt(dot(rgb, vec3(0.299, 0.587, 0.114)));
|
||||
}
|
||||
|
||||
vec4 col = vec4(0.0);
|
||||
vec3 rgbNW = texture2D(tex, v_rgbNW).xyz;
|
||||
vec3 rgbNE = texture2D(tex, v_rgbNE).xyz;
|
||||
vec3 rgbSW = texture2D(tex, v_rgbSW).xyz;
|
||||
vec3 rgbSE = texture2D(tex, v_rgbSE).xyz;
|
||||
vec4 texColor = texture2D(tex, v_rgbM);
|
||||
vec3 rgbM = texColor.xyz;
|
||||
vec3 luma = vec3(0.299, 0.587, 0.114);
|
||||
float lumaNW = dot(rgbNW, luma);
|
||||
float lumaNE = dot(rgbNE, luma);
|
||||
float lumaSW = dot(rgbSW, luma);
|
||||
float lumaSE = dot(rgbSE, luma);
|
||||
float lumaM = dot(rgbM, luma);
|
||||
float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
|
||||
float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));
|
||||
float sampleLuma(vec2 uv) {
|
||||
return rgb2luma(texture2D(tColor, uv).rgb);
|
||||
}
|
||||
|
||||
vec2 dir;
|
||||
dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
|
||||
dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));
|
||||
|
||||
float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) *
|
||||
(0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);
|
||||
|
||||
float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);
|
||||
dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX),
|
||||
max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),
|
||||
dir * rcpDirMin)) * inverseVP;
|
||||
|
||||
vec4 rgbA1 = texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5));
|
||||
vec4 rgbA2 = texture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5));
|
||||
vec4 rgbA = 0.5 * (rgbA1 + rgbA2);
|
||||
|
||||
vec4 rgbB1 = texture2D(tex, fragCoord * inverseVP + dir * -0.5);
|
||||
vec4 rgbB2 = texture2D(tex, fragCoord * inverseVP + dir * 0.5);
|
||||
vec4 rgbB = rgbA * 0.5 + 0.25 * (rgbB1 + rgbB2);
|
||||
|
||||
float lumaB = dot(rgbB.rgb, luma);
|
||||
if ((lumaB < lumaMin) || (lumaB > lumaMax))
|
||||
col = vec4(rgbA.rgb, rgbA.a);
|
||||
else
|
||||
col = vec4(rgbB.rgb, rgbB.a);
|
||||
return col;
|
||||
float sampleLuma(vec2 uv, float uOffset, float vOffset) {
|
||||
uv += uTexSizeInv * vec2(uOffset, vOffset);
|
||||
return sampleLuma(uv);
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = fxaa(tColor, gl_FragCoord.xy, uTexSize);
|
||||
vec2 coords = gl_FragCoord.xy * uTexSizeInv;
|
||||
vec2 inverseScreenSize = uTexSizeInv;
|
||||
|
||||
vec4 colorCenter = texture2D(tColor, coords);
|
||||
|
||||
// Luma at the current fragment
|
||||
float lumaCenter = rgb2luma(colorCenter.rgb);
|
||||
|
||||
// Luma at the four direct neighbours of the current fragment.
|
||||
float lumaDown = sampleLuma(coords, 0.0, -1.0);
|
||||
float lumaUp = sampleLuma(coords, 0.0, 1.0);
|
||||
float lumaLeft = sampleLuma(coords, -1.0, 0.0);
|
||||
float lumaRight = sampleLuma(coords, 1.0, 0.0);
|
||||
|
||||
// Find the maximum and minimum luma around the current fragment.
|
||||
float lumaMin = min(lumaCenter, min(min(lumaDown, lumaUp), min(lumaLeft, lumaRight)));
|
||||
float lumaMax = max(lumaCenter, max(max(lumaDown, lumaUp), max(lumaLeft, lumaRight)));
|
||||
|
||||
// Compute the delta.
|
||||
float lumaRange = lumaMax - lumaMin;
|
||||
|
||||
// If the luma variation is lower that a threshold (or if we are in a really dark area),
|
||||
// we are not on an edge, don't perform any AA.
|
||||
if (lumaRange < max(dEdgeThresholdMin, lumaMax * dEdgeThresholdMax)) {
|
||||
gl_FragColor = colorCenter;
|
||||
return;
|
||||
}
|
||||
|
||||
// Query the 4 remaining corners lumas.
|
||||
float lumaDownLeft = sampleLuma(coords, -1.0, -1.0);
|
||||
float lumaUpRight = sampleLuma(coords, 1.0, 1.0);
|
||||
float lumaUpLeft = sampleLuma(coords, -1.0, 1.0);
|
||||
float lumaDownRight = sampleLuma(coords, 1.0, -1.0);
|
||||
|
||||
// Combine the four edges lumas (using intermediary variables for future computations
|
||||
// with the same values).
|
||||
float lumaDownUp = lumaDown + lumaUp;
|
||||
float lumaLeftRight = lumaLeft + lumaRight;
|
||||
|
||||
// Same for corners
|
||||
float lumaLeftCorners = lumaDownLeft + lumaUpLeft;
|
||||
float lumaDownCorners = lumaDownLeft + lumaDownRight;
|
||||
float lumaRightCorners = lumaDownRight + lumaUpRight;
|
||||
float lumaUpCorners = lumaUpRight + lumaUpLeft;
|
||||
|
||||
// Compute an estimation of the gradient along the horizontal and vertical axis.
|
||||
float edgeHorizontal = abs(-2.0 * lumaLeft + lumaLeftCorners) + abs(-2.0 * lumaCenter + lumaDownUp) * 2.0 + abs(-2.0 * lumaRight + lumaRightCorners);
|
||||
float edgeVertical = abs(-2.0 * lumaUp + lumaUpCorners) + abs(-2.0 * lumaCenter + lumaLeftRight) * 2.0 + abs(-2.0 * lumaDown + lumaDownCorners);
|
||||
|
||||
// Is the local edge horizontal or vertical ?
|
||||
bool isHorizontal = (edgeHorizontal >= edgeVertical);
|
||||
|
||||
// Choose the step size (one pixel) accordingly.
|
||||
float stepLength = isHorizontal ? inverseScreenSize.y : inverseScreenSize.x;
|
||||
|
||||
// Select the two neighboring texels lumas in the opposite direction to the local edge.
|
||||
float luma1 = isHorizontal ? lumaDown : lumaLeft;
|
||||
float luma2 = isHorizontal ? lumaUp : lumaRight;
|
||||
// Compute gradients in this direction.
|
||||
float gradient1 = luma1 - lumaCenter;
|
||||
float gradient2 = luma2 - lumaCenter;
|
||||
|
||||
// Which direction is the steepest ?
|
||||
bool is1Steepest = abs(gradient1) >= abs(gradient2);
|
||||
|
||||
// Gradient in the corresponding direction, normalized.
|
||||
float gradientScaled = 0.25 * max(abs(gradient1), abs(gradient2));
|
||||
|
||||
// Average luma in the correct direction.
|
||||
float lumaLocalAverage = 0.0;
|
||||
if(is1Steepest){
|
||||
// Switch the direction
|
||||
stepLength = -stepLength;
|
||||
lumaLocalAverage = 0.5 * (luma1 + lumaCenter);
|
||||
} else {
|
||||
lumaLocalAverage = 0.5 * (luma2 + lumaCenter);
|
||||
}
|
||||
|
||||
// Shift UV in the correct direction by half a pixel.
|
||||
vec2 currentUv = coords;
|
||||
if(isHorizontal){
|
||||
currentUv.y += stepLength * 0.5;
|
||||
} else {
|
||||
currentUv.x += stepLength * 0.5;
|
||||
}
|
||||
|
||||
// Compute offset (for each iteration step) in the right direction.
|
||||
vec2 offset = isHorizontal ? vec2(inverseScreenSize.x, 0.0) : vec2(0.0, inverseScreenSize.y);
|
||||
// Compute UVs to explore on each side of the edge, orthogonally.
|
||||
// The QUALITY allows us to step faster.
|
||||
vec2 uv1 = currentUv - offset * QUALITY(0);
|
||||
vec2 uv2 = currentUv + offset * QUALITY(0);
|
||||
|
||||
// Read the lumas at both current extremities of the exploration segment,
|
||||
// and compute the delta wrt to the local average luma.
|
||||
float lumaEnd1 = sampleLuma(uv1);
|
||||
float lumaEnd2 = sampleLuma(uv2);
|
||||
lumaEnd1 -= lumaLocalAverage;
|
||||
lumaEnd2 -= lumaLocalAverage;
|
||||
|
||||
// If the luma deltas at the current extremities is larger than the local gradient,
|
||||
// we have reached the side of the edge.
|
||||
bool reached1 = abs(lumaEnd1) >= gradientScaled;
|
||||
bool reached2 = abs(lumaEnd2) >= gradientScaled;
|
||||
bool reachedBoth = reached1 && reached2;
|
||||
|
||||
// If the side is not reached, we continue to explore in this direction.
|
||||
if(!reached1){
|
||||
uv1 -= offset * QUALITY(1);
|
||||
}
|
||||
if(!reached2){
|
||||
uv2 += offset * QUALITY(1);
|
||||
}
|
||||
|
||||
// If both sides have not been reached, continue to explore.
|
||||
if(!reachedBoth){
|
||||
for(int i = 2; i < dIterations; i++){
|
||||
// If needed, read luma in 1st direction, compute delta.
|
||||
if(!reached1){
|
||||
lumaEnd1 = sampleLuma(uv1);
|
||||
lumaEnd1 = lumaEnd1 - lumaLocalAverage;
|
||||
}
|
||||
// If needed, read luma in opposite direction, compute delta.
|
||||
if(!reached2){
|
||||
lumaEnd2 = sampleLuma(uv2);
|
||||
lumaEnd2 = lumaEnd2 - lumaLocalAverage;
|
||||
}
|
||||
// If the luma deltas at the current extremities is larger than the local gradient,
|
||||
// we have reached the side of the edge.
|
||||
reached1 = abs(lumaEnd1) >= gradientScaled;
|
||||
reached2 = abs(lumaEnd2) >= gradientScaled;
|
||||
reachedBoth = reached1 && reached2;
|
||||
|
||||
// If the side is not reached, we continue to explore in this direction,
|
||||
// with a variable quality.
|
||||
if(!reached1){
|
||||
uv1 -= offset * QUALITY(i);
|
||||
}
|
||||
if(!reached2){
|
||||
uv2 += offset * QUALITY(i);
|
||||
}
|
||||
|
||||
// If both sides have been reached, stop the exploration.
|
||||
if(reachedBoth){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the distances to each side edge of the edge (!).
|
||||
float distance1 = isHorizontal ? (coords.x - uv1.x) : (coords.y - uv1.y);
|
||||
float distance2 = isHorizontal ? (uv2.x - coords.x) : (uv2.y - coords.y);
|
||||
|
||||
// In which direction is the side of the edge closer ?
|
||||
bool isDirection1 = distance1 < distance2;
|
||||
float distanceFinal = min(distance1, distance2);
|
||||
|
||||
// Thickness of the edge.
|
||||
float edgeThickness = (distance1 + distance2);
|
||||
|
||||
// Is the luma at center smaller than the local average ?
|
||||
bool isLumaCenterSmaller = lumaCenter < lumaLocalAverage;
|
||||
|
||||
// If the luma at center is smaller than at its neighbour,
|
||||
// the delta luma at each end should be positive (same variation).
|
||||
bool correctVariation1 = (lumaEnd1 < 0.0) != isLumaCenterSmaller;
|
||||
bool correctVariation2 = (lumaEnd2 < 0.0) != isLumaCenterSmaller;
|
||||
|
||||
// Only keep the result in the direction of the closer side of the edge.
|
||||
bool correctVariation = isDirection1 ? correctVariation1 : correctVariation2;
|
||||
|
||||
// UV offset: read in the direction of the closest side of the edge.
|
||||
float pixelOffset = - distanceFinal / edgeThickness + 0.5;
|
||||
|
||||
// If the luma variation is incorrect, do not offset.
|
||||
float finalOffset = correctVariation ? pixelOffset : 0.0;
|
||||
|
||||
// Sub-pixel shifting
|
||||
// Full weighted average of the luma over the 3x3 neighborhood.
|
||||
float lumaAverage = (1.0 / 12.0) * (2.0 * (lumaDownUp + lumaLeftRight) + lumaLeftCorners + lumaRightCorners);
|
||||
// Ratio of the delta between the global average and the center luma,
|
||||
// over the luma range in the 3x3 neighborhood.
|
||||
float subPixelOffset1 = clamp(abs(lumaAverage - lumaCenter) / lumaRange, 0.0, 1.0);
|
||||
float subPixelOffset2 = (-2.0 * subPixelOffset1 + 3.0) * subPixelOffset1 * subPixelOffset1;
|
||||
// Compute a sub-pixel offset based on this delta.
|
||||
float subPixelOffsetFinal = subPixelOffset2 * subPixelOffset2 * float(dSubpixelQuality);
|
||||
|
||||
// Pick the biggest of the two offsets.
|
||||
finalOffset = max(finalOffset, subPixelOffsetFinal);
|
||||
|
||||
// Compute the final UV coordinates.
|
||||
vec2 finalUv = coords;
|
||||
if(isHorizontal){
|
||||
finalUv.y += finalOffset * stepLength;
|
||||
} else {
|
||||
finalUv.x += finalOffset * stepLength;
|
||||
}
|
||||
|
||||
// Read the color at the new UV coordinates, and use it.
|
||||
gl_FragColor = texture2D(tColor, finalUv);
|
||||
}
|
||||
`;
|
||||
@@ -22,7 +22,7 @@ import { VertexArray, createVertexArray } from './vertex-array';
|
||||
|
||||
function defineValueHash(v: boolean | number | string): number {
|
||||
return typeof v === 'boolean' ? (v ? 1 : 0) :
|
||||
typeof v === 'number' ? v : hashString(v);
|
||||
typeof v === 'number' ? (v * 10000) : hashString(v);
|
||||
}
|
||||
|
||||
function wrapCached<T extends Resource>(resourceItem: ReferenceItem<T>) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import { AtomSiteAnisotrop } from './property/anisotropic';
|
||||
import { ComponentBond } from './property/bonds/chem_comp';
|
||||
import { StructConn } from './property/bonds/struct_conn';
|
||||
import { Trajectory } from '../../mol-model/structure';
|
||||
import { GlobalModelTransformInfo } from '../../mol-model/structure/model/properties/global-transform';
|
||||
|
||||
function modelSymmetryFromMmcif(model: Model) {
|
||||
if (!MmcifFormat.is(model.sourceData)) return;
|
||||
@@ -69,6 +70,8 @@ function structConnFromMmcif(model: Model) {
|
||||
}
|
||||
StructConn.Provider.formatRegistry.add('mmCIF', structConnFromMmcif);
|
||||
|
||||
GlobalModelTransformInfo.Provider.formatRegistry.add('mmCIF', GlobalModelTransformInfo.fromMmCif, GlobalModelTransformInfo.hasData);
|
||||
|
||||
//
|
||||
|
||||
export { MmcifFormat };
|
||||
|
||||
@@ -132,7 +132,8 @@ function getCustomPropCategories(customProp: CustomPropertyDescriptor, ctx: CifE
|
||||
type encode_mmCIF_categories_Params = {
|
||||
skipCategoryNames?: Set<string>,
|
||||
exportCtx?: CifExportContext,
|
||||
copyAllCategories?: boolean
|
||||
copyAllCategories?: boolean,
|
||||
customProperties?: CustomPropertyDescriptor[]
|
||||
}
|
||||
|
||||
/** Doesn't start a data block */
|
||||
@@ -144,7 +145,7 @@ export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structures:
|
||||
const ctx: CifExportContext = params?.exportCtx || CifExportContext.create(structures);
|
||||
|
||||
if (params?.copyAllCategories && MmcifFormat.is(models[0].sourceData)) {
|
||||
encode_mmCIF_categories_copyAll(encoder, ctx);
|
||||
encode_mmCIF_categories_copyAll(encoder, ctx, params);
|
||||
} else {
|
||||
encode_mmCIF_categories_default(encoder, ctx, params);
|
||||
}
|
||||
@@ -168,6 +169,14 @@ function encode_mmCIF_categories_default(encoder: CifWriter.Encoder, ctx: CifExp
|
||||
}
|
||||
}
|
||||
|
||||
if (params?.customProperties) {
|
||||
for (const customProp of params?.customProperties) {
|
||||
for (const [cat, propCtx] of getCustomPropCategories(customProp, ctx, _params)) {
|
||||
encoder.writeCategory(cat, propCtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const s of ctx.structures) {
|
||||
if (!s.hasCustomProperties) continue;
|
||||
for (const customProp of s.customPropertyDescriptors.all) {
|
||||
@@ -178,7 +187,7 @@ function encode_mmCIF_categories_default(encoder: CifWriter.Encoder, ctx: CifExp
|
||||
}
|
||||
}
|
||||
|
||||
function encode_mmCIF_categories_copyAll(encoder: CifWriter.Encoder, ctx: CifExportContext) {
|
||||
function encode_mmCIF_categories_copyAll(encoder: CifWriter.Encoder, ctx: CifExportContext, params?: encode_mmCIF_categories_Params) {
|
||||
const providedCategories = new Map<string, CifExportCategoryInfo>();
|
||||
|
||||
for (const cat of Categories) {
|
||||
@@ -188,12 +197,21 @@ function encode_mmCIF_categories_copyAll(encoder: CifWriter.Encoder, ctx: CifExp
|
||||
const mapping = atom_site_operator_mapping(ctx);
|
||||
if (mapping) providedCategories.set(mapping[0].name, mapping);
|
||||
|
||||
const _params = params || { };
|
||||
for (const customProp of ctx.firstModel.customProperties.all) {
|
||||
for (const info of getCustomPropCategories(customProp, ctx)) {
|
||||
for (const info of getCustomPropCategories(customProp, ctx, _params)) {
|
||||
providedCategories.set(info[0].name, info);
|
||||
}
|
||||
}
|
||||
|
||||
if (params?.customProperties) {
|
||||
for (const customProp of params?.customProperties) {
|
||||
for (const info of getCustomPropCategories(customProp, ctx, _params)) {
|
||||
providedCategories.set(info[0].name, info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const s of ctx.structures) {
|
||||
if (!s.hasCustomProperties) continue;
|
||||
for (const customProp of s.customPropertyDescriptors.all) {
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
// TODO add access to things like MOL2 charge ...
|
||||
80
src/mol-model/structure/model/properties/global-transform.ts
Normal file
80
src/mol-model/structure/model/properties/global-transform.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { Mat4, Tensor } from '../../../../mol-math/linear-algebra';
|
||||
import { FormatPropertyProvider } from '../../../../mol-model-formats/structure/common/property';
|
||||
import { CustomPropertyDescriptor } from '../../../custom-property';
|
||||
import { CifExportContext } from '../../structure';
|
||||
import { Model } from '../model';
|
||||
import { Column, Table } from '../../../../mol-data/db';
|
||||
import { CifWriter } from '../../../../mol-io/writer/cif';
|
||||
import { MmcifFormat } from '../../../../mol-model-formats/structure/mmcif';
|
||||
import { toTable } from '../../../../mol-io/reader/cif/schema';
|
||||
|
||||
export namespace GlobalModelTransformInfo {
|
||||
const CategoryName = 'molstar_global_model_transform_info' as const;
|
||||
export const Schema = {
|
||||
[CategoryName]: {
|
||||
matrix: Column.Schema.Matrix(4, 4, Column.Schema.float)
|
||||
}
|
||||
};
|
||||
export type Schema = typeof Schema
|
||||
|
||||
export const Descriptor = CustomPropertyDescriptor({
|
||||
name: CategoryName,
|
||||
cifExport: {
|
||||
categories: [{
|
||||
name: CategoryName,
|
||||
instance(ctx: CifExportContext) {
|
||||
const mat = get(ctx.firstModel);
|
||||
if (!mat) return CifWriter.Category.Empty;
|
||||
const table = Table.ofRows(Schema.molstar_global_model_transform_info, [{ matrix: mat as unknown as Tensor.Data }]);
|
||||
return CifWriter.Category.ofTable(table);
|
||||
}
|
||||
}],
|
||||
prefix: 'molstar'
|
||||
}
|
||||
});
|
||||
|
||||
export const Provider = FormatPropertyProvider.create<Mat4>(Descriptor);
|
||||
|
||||
export function attach(model: Model, matrix: Mat4) {
|
||||
if (!model.customProperties.has(Descriptor)) {
|
||||
model.customProperties.add(Descriptor);
|
||||
}
|
||||
Provider.set(model, matrix);
|
||||
}
|
||||
|
||||
export function get(model: Model): Mat4 | undefined {
|
||||
return Provider.get(model);
|
||||
}
|
||||
|
||||
export function fromMmCif(model: Model) {
|
||||
if (!MmcifFormat.is(model.sourceData)) return;
|
||||
|
||||
const cat = model.sourceData.data.frame.categories[CategoryName];
|
||||
if (!cat) return;
|
||||
const table = toTable(Schema[CategoryName], cat);
|
||||
if (table._rowCount === 0) return;
|
||||
return table.matrix.value(0) as unknown as Mat4;
|
||||
}
|
||||
|
||||
export function hasData(model: Model) {
|
||||
if (!MmcifFormat.is(model.sourceData)) return false;
|
||||
const cat = model.sourceData.data.frame.categories[CategoryName];
|
||||
return !!cat && cat.rowCount > 0;
|
||||
}
|
||||
|
||||
export function writeMmCif(encoder: CifWriter.Encoder, matrix: Mat4) {
|
||||
encoder.writeCategory({
|
||||
name: CategoryName,
|
||||
instance() {
|
||||
const table = Table.ofRows(Schema.molstar_global_model_transform_info, [{ matrix: matrix as unknown as Tensor.Data }]);
|
||||
return CifWriter.Category.ofTable(table);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import { UniqueArray } from '../../../../mol-data/generic';
|
||||
import { OrderedSet, SortedArray, Interval } from '../../../../mol-data/int';
|
||||
import { Vec3 } from '../../../../mol-math/linear-algebra';
|
||||
import { Mat4, Vec3 } from '../../../../mol-math/linear-algebra';
|
||||
import { MolScriptBuilder as MS } from '../../../../mol-script/language/builder';
|
||||
import Structure from '../structure';
|
||||
import Unit from '../unit';
|
||||
@@ -489,7 +489,7 @@ export namespace Loci {
|
||||
|
||||
const boundaryHelper = new BoundaryHelper('98');
|
||||
const tempPosBoundary = Vec3();
|
||||
export function getBoundary(loci: Loci): Boundary {
|
||||
export function getBoundary(loci: Loci, transform?: Mat4): Boundary {
|
||||
boundaryHelper.reset();
|
||||
|
||||
for (const e of loci.elements) {
|
||||
@@ -499,6 +499,7 @@ export namespace Loci {
|
||||
for (let i = 0, _i = OrderedSet.size(indices); i < _i; i++) {
|
||||
const eI = elements[OrderedSet.getAt(indices, i)];
|
||||
pos(eI, tempPosBoundary);
|
||||
if (transform) Vec3.transformMat4(tempPosBoundary, tempPosBoundary, transform);
|
||||
boundaryHelper.includePositionRadius(tempPosBoundary, r(eI));
|
||||
}
|
||||
}
|
||||
@@ -510,6 +511,7 @@ export namespace Loci {
|
||||
for (let i = 0, _i = OrderedSet.size(indices); i < _i; i++) {
|
||||
const eI = elements[OrderedSet.getAt(indices, i)];
|
||||
pos(eI, tempPosBoundary);
|
||||
if (transform) Vec3.transformMat4(tempPosBoundary, tempPosBoundary, transform);
|
||||
boundaryHelper.radiusPositionRadius(tempPosBoundary, r(eI));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { PluginStateObject } from '../../../../mol-plugin-state/objects';
|
||||
import { Volume, Grid } from '../../../../mol-model/volume';
|
||||
import { VolumeServerHeader, VolumeServerInfo } from './model';
|
||||
import { Box3D } from '../../../../mol-math/geometry';
|
||||
import { Vec3 } from '../../../../mol-math/linear-algebra';
|
||||
import { Mat4, Vec3 } from '../../../../mol-math/linear-algebra';
|
||||
import { Color } from '../../../../mol-util/color';
|
||||
import { PluginBehavior } from '../../behavior';
|
||||
import { LRUCache } from '../../../../mol-util/lru-cache';
|
||||
@@ -23,6 +23,7 @@ import { StructureElement, Structure } from '../../../../mol-model/structure';
|
||||
import { PluginContext } from '../../../context';
|
||||
import { EmptyLoci, Loci, isEmptyLoci } from '../../../../mol-model/loci';
|
||||
import { Asset } from '../../../../mol-util/assets';
|
||||
import { GlobalModelTransformInfo } from '../../../../mol-model/structure/model/properties/global-transform';
|
||||
|
||||
export class VolumeStreaming extends PluginStateObject.CreateBehavior<VolumeStreaming.Behavior>({ name: 'Volume Streaming' }) { }
|
||||
|
||||
@@ -302,6 +303,7 @@ export namespace VolumeStreaming {
|
||||
}
|
||||
}
|
||||
|
||||
private _invTransform: Mat4 = Mat4();
|
||||
private getBoxFromLoci(loci: StructureElement.Loci | EmptyLoci): Box3D {
|
||||
if (Loci.isEmpty(loci)) {
|
||||
return Box3D();
|
||||
@@ -312,11 +314,16 @@ export namespace VolumeStreaming {
|
||||
const root = this.getStructureRoot();
|
||||
if (!root || root.obj?.data !== parent.obj?.data) return Box3D();
|
||||
|
||||
const transform = GlobalModelTransformInfo.get(root.obj?.data.models[0]!);
|
||||
if (transform) Mat4.invert(this._invTransform, transform);
|
||||
|
||||
const extendedLoci = StructureElement.Loci.extendToWholeResidues(loci);
|
||||
const box = StructureElement.Loci.getBoundary(extendedLoci).box;
|
||||
const box = StructureElement.Loci.getBoundary(extendedLoci, transform && !Number.isNaN(this._invTransform[0]) ? this._invTransform : void 0).box;
|
||||
|
||||
if (StructureElement.Loci.size(extendedLoci) === 1) {
|
||||
Box3D.expand(box, box, Vec3.create(1, 1, 1));
|
||||
}
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
@@ -360,7 +367,6 @@ export namespace VolumeStreaming {
|
||||
const switchedToSelection = params.entry.params.view.name === 'selection-box' && this.params && this.params.entry && this.params.entry.params && this.params.entry.params.view && this.params.entry.params.view.name !== 'selection-box';
|
||||
|
||||
this.params = params;
|
||||
|
||||
let box: Box3D | undefined = void 0, emptyData = false;
|
||||
|
||||
switch (params.entry.params.view.name) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import { Box3D } from '../../../../mol-math/geometry';
|
||||
import { Vec3 } from '../../../../mol-math/linear-algebra';
|
||||
import { PluginConfig } from '../../../config';
|
||||
import { Model } from '../../../../mol-model/structure';
|
||||
import { GlobalModelTransformInfo } from '../../../../mol-model/structure/model/properties/global-transform';
|
||||
|
||||
function addEntry(entries: InfoEntryProps[], method: VolumeServerInfo.Kind, dataId: string, emDefaultContourLevel: number) {
|
||||
entries.push({
|
||||
@@ -253,20 +254,22 @@ const VolumeStreamingVisual = PluginStateTransform.BuiltIn({
|
||||
channel: PD.Select<VolumeStreaming.ChannelType>('em', VolumeStreaming.ChannelTypeOptions, { isHidden: true })
|
||||
}
|
||||
})({
|
||||
apply: ({ a, params: srcParams }, plugin: PluginContext) => Task.create('Volume Representation', async ctx => {
|
||||
apply: ({ a, params: srcParams, spine }, plugin: PluginContext) => Task.create('Volume Representation', async ctx => {
|
||||
const channel = a.data.channels[srcParams.channel];
|
||||
if (!channel) return StateObject.Null;
|
||||
|
||||
const params = createVolumeProps(a.data, srcParams.channel);
|
||||
|
||||
const provider = VolumeRepresentationRegistry.BuiltIn.isosurface;
|
||||
const props = params.type.params || {};
|
||||
const repr = provider.factory({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.volume.themes }, provider.getParams);
|
||||
repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: channel.data }, params));
|
||||
const structure = spine.getAncestorOfType(SO.Molecule.Structure)?.data;
|
||||
const transform = structure?.models.length === 0 ? void 0 : GlobalModelTransformInfo.get(structure?.models[0]!);
|
||||
await repr.createOrUpdate(props, channel.data).runInContext(ctx);
|
||||
if (transform) repr.setState({ transform });
|
||||
return new SO.Volume.Representation3D({ repr, source: a }, { label: `${Math.round(channel.isoValue.relativeValue * 100) / 100} σ [${srcParams.channel}]` });
|
||||
}),
|
||||
update: ({ a, b, oldParams, newParams }, plugin: PluginContext) => Task.create('Volume Representation', async ctx => {
|
||||
update: ({ a, b, newParams, spine }, plugin: PluginContext) => Task.create('Volume Representation', async ctx => {
|
||||
// TODO : check if params/underlying data/etc have changed; maybe will need to export "data" or some other "tag" in the Representation for this to work
|
||||
|
||||
const channel = a.data.channels[newParams.channel];
|
||||
@@ -277,6 +280,13 @@ const VolumeStreamingVisual = PluginStateTransform.BuiltIn({
|
||||
const props = { ...b.data.repr.props, ...params.type.params };
|
||||
b.data.repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: channel.data }, params));
|
||||
await b.data.repr.createOrUpdate(props, channel.data).runInContext(ctx);
|
||||
|
||||
// TODO: set the transform here as well in case the structure moves?
|
||||
// doing this here now breaks the code for some reason...
|
||||
// const structure = spine.getAncestorOfType(SO.Molecule.Structure)?.data;
|
||||
// const transform = structure?.models.length === 0 ? void 0 : GlobalModelTransformInfo.get(structure?.models[0]!);
|
||||
// if (transform) b.data.repr.setState({ transform });
|
||||
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
})
|
||||
});
|
||||
|
||||
@@ -117,7 +117,7 @@ class ViewportScreenshotHelper extends PluginComponent {
|
||||
},
|
||||
postprocessing: {
|
||||
...c.props.postprocessing,
|
||||
antialiasing: false
|
||||
antialiasing: { name: 'off', params: {} }
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -136,7 +136,7 @@ class ViewportScreenshotHelper extends PluginComponent {
|
||||
// TODO: optimize because this creates a copy of a large object!
|
||||
postprocessing: {
|
||||
...this.plugin.canvas3d!.props.postprocessing,
|
||||
antialiasing: false
|
||||
antialiasing: { name: 'off', params: {} }
|
||||
}
|
||||
});
|
||||
return this._imagePass;
|
||||
|
||||
@@ -131,7 +131,7 @@ export function createLinkCylinderMesh(ctx: VisualContext, linkBuilder: LinkBuil
|
||||
|
||||
addFixedCountDashedCylinder(builderState, va, vb, 0.5, 7, cylinderProps);
|
||||
} else if (linkStyle === LinkStyle.Double || linkStyle === LinkStyle.Triple) {
|
||||
const order = LinkStyle.Double ? 2 : 3;
|
||||
const order = linkStyle === LinkStyle.Double ? 2 : 3;
|
||||
const multiRadius = linkRadius * (linkScale / (0.5 * order));
|
||||
const absOffset = (linkRadius - multiRadius) * linkSpacing;
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# 0.9.5
|
||||
* Support molstar_global_model_transform_info category.
|
||||
|
||||
# 0.9.4
|
||||
* bug fix for /ligand queries on metal ions
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import { MolEncoder } from '../../../mol-io/writer/mol/encoder';
|
||||
import { Mol2Encoder } from '../../../mol-io/writer/mol2/encoder';
|
||||
import { ComponentAtom } from '../../../mol-model-formats/structure/property/atoms/chem_comp';
|
||||
import { Mat4 } from '../../../mol-math/linear-algebra';
|
||||
import { GlobalModelTransformInfo } from '../../../mol-model/structure/model/properties/global-transform';
|
||||
|
||||
export interface Stats {
|
||||
structure: StructureWrapper,
|
||||
@@ -247,6 +248,7 @@ async function resolveJobEntry(entry: JobEntry, structure: StructureWrapper, enc
|
||||
|
||||
if (!entry.copyAllCategories && entry.queryDefinition.filter) encoder.setFilter(entry.queryDefinition.filter);
|
||||
if (result.length > 0) encode_mmCIF_categories(encoder, result, { copyAllCategories: entry.copyAllCategories });
|
||||
if (entry.transform && !Mat4.isIdentity(entry.transform)) GlobalModelTransformInfo.writeMmCif(encoder, entry.transform);
|
||||
if (!entry.copyAllCategories && entry.queryDefinition.filter) encoder.setFilter();
|
||||
perf.end('encode');
|
||||
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
export default '0.9.4';
|
||||
export default '0.9.5';
|
||||
Reference in New Issue
Block a user