mirror of
https://github.com/molstar/molstar.git
synced 2026-06-06 06:34:23 +08:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5f2767efc | ||
|
|
66f5a81a5d | ||
|
|
9e90e11bfc | ||
|
|
ab372a89d6 | ||
|
|
c6506d515f | ||
|
|
7d0f84ff72 | ||
|
|
31495ab02a | ||
|
|
853ad5c916 | ||
|
|
51fc525215 | ||
|
|
6f9fed180d | ||
|
|
5ecd176f20 |
12
CHANGELOG.md
12
CHANGELOG.md
@@ -5,6 +5,16 @@ Note that since we don't clearly distinguish between a public and private interf
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v5.2.0] - 2025-10-31
|
||||
- Handle transparency updates on ImagePass
|
||||
- Fix CIF parser edge case when the last token is escaped
|
||||
- MolViewSpec
|
||||
- Fix tooltips persisting across snapshots
|
||||
- Fix CIF annotations with no selector columns being ignored
|
||||
- Fix trackpad lock when camera up parallel to direction
|
||||
- Add clipping support for primitives
|
||||
- Support near camera distance
|
||||
|
||||
## [v5.1.0] - 2025-10-25
|
||||
- Fix createColorScaleByType when offsets are available
|
||||
- Get bond orders from non-standard CONECT records in PDB files
|
||||
@@ -13,7 +23,7 @@ Note that since we don't clearly distinguish between a public and private interf
|
||||
- Support "magic window" style AR (via WebXR)
|
||||
- Fix `PluginState.getStateTransitionFrameIndex`
|
||||
- Update `GlycamSaccharideNames` and `Monosaccharides` in `carbohydrates/constants.ts`
|
||||
- Support custom ref resolvers in `State`
|
||||
- Support custom ref resolvers in `State`
|
||||
- Add full-screen mode support to layout manager
|
||||
- Add `show-toggle-fullscreen` URL param option to Viewer app
|
||||
- MolViewSpec
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "5.1.2",
|
||||
"version": "5.2.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "molstar",
|
||||
"version": "5.1.2",
|
||||
"version": "5.2.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/argparse": "^2.0.17",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "5.1.2",
|
||||
"version": "5.2.0",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
|
||||
@@ -62,7 +62,15 @@ export function cameraParamsToCameraSnapshot(plugin: PluginContext, params: Mols
|
||||
if (plugin.canvas3d) position = fovAdjustedPosition(target, position, plugin.canvas3d.camera.state.mode, plugin.canvas3d.camera.state.fov);
|
||||
const up = Vec3.create(...params.up);
|
||||
Vec3.orthogonalize(up, Vec3.sub(_tmpVec, target, position), up);
|
||||
const snapshot: Partial<Camera.Snapshot> = { target, position, up, radius, radiusMax: radius };
|
||||
|
||||
const snapshot: Partial<Camera.Snapshot> = {
|
||||
target,
|
||||
position,
|
||||
up,
|
||||
radius,
|
||||
radiusMax: radius,
|
||||
minNear: params.near ?? undefined,
|
||||
};
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
|
||||
@@ -377,6 +377,7 @@ function getRowsFromCif(data: CifCategory, schema: MVSAnnotationSchema, fieldRem
|
||||
const columnArray = getArrayFromCifCategory(data, srcKey, cifSchema[key]); // Avoiding `column.toArray` as it replaces . and ? fields by 0 or ''
|
||||
if (columnArray) columns[key] = columnArray;
|
||||
}
|
||||
if (Object.keys(columns).length === 0) return new Array(data.rowCount).fill({});
|
||||
return objectOfArraysToArrayOfObjects(columns);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import { Task } from '../../../mol-task';
|
||||
import { round } from '../../../mol-util';
|
||||
import { range } from '../../../mol-util/array';
|
||||
import { Asset } from '../../../mol-util/assets';
|
||||
import { Clip } from '../../../mol-util/clip';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
import { MarkerActions } from '../../../mol-util/marker-action';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
@@ -141,7 +142,8 @@ export const MVSBuildPrimitiveShape = MVSTransform({
|
||||
from: MVSPrimitivesData,
|
||||
to: SO.Shape.Provider,
|
||||
params: {
|
||||
kind: PD.Text<'mesh' | 'labels' | 'lines'>('mesh')
|
||||
kind: PD.Text<'mesh' | 'labels' | 'lines'>('mesh'),
|
||||
clip: PD.Value<Clip.Props | undefined>(undefined, { isHidden: true })
|
||||
}
|
||||
})({
|
||||
apply({ a, params, dependencies }) {
|
||||
@@ -160,7 +162,7 @@ export const MVSBuildPrimitiveShape = MVSTransform({
|
||||
label,
|
||||
data: context,
|
||||
params: {
|
||||
...PD.withDefaults(Mesh.Params, { alpha: a.data.options?.opacity ?? 1, ...customMeshParams }),
|
||||
...PD.withDefaults(Mesh.Params, { alpha: a.data.options?.opacity ?? 1, clip: params.clip, ...customMeshParams }),
|
||||
...snapshotKey,
|
||||
...markdownCommands,
|
||||
},
|
||||
@@ -184,6 +186,7 @@ export const MVSBuildPrimitiveShape = MVSTransform({
|
||||
tetherLength: options?.label_tether_length ?? 1,
|
||||
background: isDefined(bgColor),
|
||||
backgroundColor: isDefined(bgColor) ? decodeColor(bgColor) : undefined,
|
||||
clip: params.clip,
|
||||
...customLabelParams,
|
||||
}),
|
||||
...snapshotKey,
|
||||
@@ -200,7 +203,7 @@ export const MVSBuildPrimitiveShape = MVSTransform({
|
||||
label,
|
||||
data: context,
|
||||
params: {
|
||||
...PD.withDefaults(Lines.Params, { alpha: a.data.options?.opacity ?? 1, ...customLineParams }),
|
||||
...PD.withDefaults(Lines.Params, { alpha: a.data.options?.opacity ?? 1, clip: params.clip, ...customLineParams }),
|
||||
...snapshotKey,
|
||||
...markdownCommands,
|
||||
},
|
||||
|
||||
@@ -479,7 +479,7 @@ function getClipObject(node: MolstarNode<'clip'>): Clip.Props['objects'][number]
|
||||
}
|
||||
}
|
||||
|
||||
export function clippingForNode(node: MolstarSubtree<'representation' | 'volume_representation'>): Clip.Props | undefined {
|
||||
export function clippingForNode(node: MolstarSubtree<'representation' | 'volume_representation' | 'primitives' | 'primitives_from_uri'>): Clip.Props | undefined {
|
||||
const children = getChildren(node).filter(c => c.kind === 'clip');
|
||||
if (!children.length) return;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import { PluginStateSnapshotManager } from '../../mol-plugin-state/manager/snapshots';
|
||||
import { PluginStateObject } from '../../mol-plugin-state/objects';
|
||||
import { Download, ParseCif, ParseCcp4, ParseDx } from '../../mol-plugin-state/transforms/data';
|
||||
import { Download, ParseCcp4, ParseCif, ParseDx } from '../../mol-plugin-state/transforms/data';
|
||||
import { CoordinatesFromLammpstraj, CoordinatesFromXtc, CustomModelProperties, CustomStructureProperties, ModelFromTrajectory, StructureComponent, StructureFromModel, TrajectoryFromGRO, TrajectoryFromLammpsTrajData, TrajectoryFromMmCif, TrajectoryFromMOL, TrajectoryFromMOL2, TrajectoryFromPDB, TrajectoryFromSDF, TrajectoryFromXYZ } from '../../mol-plugin-state/transforms/model';
|
||||
import { StructureRepresentation3D, VolumeRepresentation3D } from '../../mol-plugin-state/transforms/representation';
|
||||
import { VolumeFromCcp4, VolumeFromDensityServerCif, VolumeFromDx } from '../../mol-plugin-state/transforms/volume';
|
||||
@@ -17,6 +17,7 @@ import { PluginContext } from '../../mol-plugin/context';
|
||||
import { PluginState } from '../../mol-plugin/state';
|
||||
import { StateObjectSelector, StateTree } from '../../mol-state';
|
||||
import { RuntimeContext, Task } from '../../mol-task';
|
||||
import { Clip } from '../../mol-util/clip';
|
||||
import { MolViewSpec } from './behavior';
|
||||
import { createPluginStateSnapshotCamera, modifyCanvasProps, resetCanvasProps } from './camera';
|
||||
import { MVSAnnotationsProvider } from './components/annotation-prop';
|
||||
@@ -31,7 +32,7 @@ import { generateStateTransition } from './helpers/animation';
|
||||
import { IsHiddenCustomStateExtension } from './load-extensions/is-hidden-custom-state';
|
||||
import { NonCovalentInteractionsExtension } from './load-extensions/non-covalent-interactions';
|
||||
import { LoadingActions, LoadingExtension, loadTreeVirtual, UpdateTarget } from './load-generic';
|
||||
import { AnnotationFromSourceKind, AnnotationFromUriKind, collectAnnotationReferences, collectAnnotationTooltips, collectInlineLabels, collectInlineTooltips, colorThemeForNode, componentFromXProps, componentPropsFromSelector, isPhantomComponent, labelFromXProps, makeNearestReprMap, prettyNameFromSelector, representationProps, structureProps, transformAndInstantiateStructure, transformAndInstantiateVolume, volumeColorThemeForNode, volumeRepresentationProps } from './load-helpers';
|
||||
import { AnnotationFromSourceKind, AnnotationFromUriKind, clippingForNode, collectAnnotationReferences, collectAnnotationTooltips, collectInlineLabels, collectInlineTooltips, colorThemeForNode, componentFromXProps, componentPropsFromSelector, isPhantomComponent, labelFromXProps, makeNearestReprMap, prettyNameFromSelector, representationProps, structureProps, transformAndInstantiateStructure, transformAndInstantiateVolume, volumeColorThemeForNode, volumeRepresentationProps } from './load-helpers';
|
||||
import { MVSData, MVSData_States, Snapshot, SnapshotMetadata } from './mvs-data';
|
||||
import { MVSAnimationNode, MVSAnimationSchema } from './tree/animation/animation-tree';
|
||||
import { validateTree } from './tree/generic/tree-validation';
|
||||
@@ -325,18 +326,16 @@ const MolstarLoadingActions: LoadingActions<MolstarTree, MolstarLoadingContext>
|
||||
const transformed = transformAndInstantiateStructure(struct, node);
|
||||
const annotationTooltips = collectAnnotationTooltips(node, context);
|
||||
const inlineTooltips = collectInlineTooltips(node, context);
|
||||
if (annotationTooltips.length + inlineTooltips.length > 0) {
|
||||
UpdateTarget.apply(struct, CustomStructureProperties, {
|
||||
properties: {
|
||||
[MVSAnnotationTooltipsProvider.descriptor.name]: { tooltips: annotationTooltips },
|
||||
[CustomTooltipsProvider.descriptor.name]: { tooltips: inlineTooltips },
|
||||
},
|
||||
autoAttach: [
|
||||
MVSAnnotationTooltipsProvider.descriptor.name,
|
||||
CustomTooltipsProvider.descriptor.name,
|
||||
],
|
||||
});
|
||||
}
|
||||
UpdateTarget.apply(struct, CustomStructureProperties, {
|
||||
properties: {
|
||||
[MVSAnnotationTooltipsProvider.descriptor.name]: { tooltips: annotationTooltips },
|
||||
[CustomTooltipsProvider.descriptor.name]: { tooltips: inlineTooltips },
|
||||
},
|
||||
autoAttach: [
|
||||
MVSAnnotationTooltipsProvider.descriptor.name,
|
||||
CustomTooltipsProvider.descriptor.name,
|
||||
],
|
||||
}); // CustomStructureProperties must be applied even when `annotationTooltips` and `inlineTooltips` are empty, otherwise tooltips would persists across MVS snapshots
|
||||
const inlineLabels = collectInlineLabels(node, context);
|
||||
if (inlineLabels.length > 0) {
|
||||
const nearestReprNode = context.nearestReprMap?.get(node);
|
||||
@@ -426,21 +425,23 @@ const MolstarLoadingActions: LoadingActions<MolstarTree, MolstarLoadingContext>
|
||||
},
|
||||
primitives(updateParent: UpdateTarget, tree: MolstarSubtree<'primitives'>, context: MolstarLoadingContext): UpdateTarget {
|
||||
const refs = getPrimitiveStructureRefs(tree);
|
||||
const clip = clippingForNode(tree);
|
||||
const data = UpdateTarget.apply(updateParent, MVSInlinePrimitiveData, { node: tree as any });
|
||||
return applyPrimitiveVisuals(data, refs);
|
||||
return applyPrimitiveVisuals(data, refs, clip);
|
||||
},
|
||||
primitives_from_uri(updateParent: UpdateTarget, tree: MolstarNode<'primitives_from_uri'>, context: MolstarLoadingContext): UpdateTarget {
|
||||
const data = UpdateTarget.apply(updateParent, MVSDownloadPrimitiveData, { uri: tree.params.uri, format: tree.params.format });
|
||||
return applyPrimitiveVisuals(data, new Set(tree.params.references));
|
||||
const clip = clippingForNode(tree);
|
||||
return applyPrimitiveVisuals(data, new Set(tree.params.references), clip);
|
||||
},
|
||||
};
|
||||
|
||||
function applyPrimitiveVisuals(data: UpdateTarget, refs: Set<string>) {
|
||||
const mesh = UpdateTarget.setMvsDependencies(UpdateTarget.apply(data, MVSBuildPrimitiveShape, { kind: 'mesh' }, { state: { isGhost: true } }), refs);
|
||||
function applyPrimitiveVisuals(data: UpdateTarget, refs: Set<string>, clip: Clip.Props | undefined) {
|
||||
const mesh = UpdateTarget.setMvsDependencies(UpdateTarget.apply(data, MVSBuildPrimitiveShape, { kind: 'mesh', clip }, { state: { isGhost: true } }), refs);
|
||||
UpdateTarget.apply(mesh, MVSShapeRepresentation3D);
|
||||
const labels = UpdateTarget.setMvsDependencies(UpdateTarget.apply(data, MVSBuildPrimitiveShape, { kind: 'labels' }, { state: { isGhost: true } }), refs);
|
||||
const labels = UpdateTarget.setMvsDependencies(UpdateTarget.apply(data, MVSBuildPrimitiveShape, { kind: 'labels', clip }, { state: { isGhost: true } }), refs);
|
||||
UpdateTarget.apply(labels, MVSShapeRepresentation3D);
|
||||
const lines = UpdateTarget.setMvsDependencies(UpdateTarget.apply(data, MVSBuildPrimitiveShape, { kind: 'lines' }, { state: { isGhost: true } }), refs);
|
||||
const lines = UpdateTarget.setMvsDependencies(UpdateTarget.apply(data, MVSBuildPrimitiveShape, { kind: 'lines', clip }, { state: { isGhost: true } }), refs);
|
||||
UpdateTarget.apply(lines, MVSShapeRepresentation3D);
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -403,12 +403,24 @@ export class Primitives extends _Base<'primitives'> implements FocusMixin {
|
||||
return this;
|
||||
}
|
||||
focus = bindMethod(this, FocusMixinImpl, 'focus');
|
||||
|
||||
/** Add a 'clip' node and return builder pointing back to the representation node. 'clip' node instructs to apply clipping to a visual representation. */
|
||||
clip(params: MVSNodeParams<'clip'> & CustomAndRef): Primitives {
|
||||
this.addChild('clip', params);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** MVS builder pointing to a 'primitives_from_uri' node */
|
||||
class PrimitivesFromUri extends _Base<'primitives_from_uri'> implements FocusMixin {
|
||||
focus = bindMethod(this, FocusMixinImpl, 'focus');
|
||||
|
||||
/** Add a 'clip' node and return builder pointing back to the representation node. 'clip' node instructs to apply clipping to a visual representation. */
|
||||
clip(params: MVSNodeParams<'clip'> & CustomAndRef): PrimitivesFromUri {
|
||||
this.addChild('clip', params);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@ export const MVSTreeSchema = TreeSchema({
|
||||
/** This node instructs to apply clipping to a visual representation. */
|
||||
clip: {
|
||||
description: 'This node instructs to apply clipping to a visual representation.',
|
||||
parent: ['representation', 'volume_representation'],
|
||||
parent: ['representation', 'volume_representation', 'primitives', 'primitives_from_uri'],
|
||||
params: MVSClipParams,
|
||||
},
|
||||
/** This node instructs to apply opacity/transparency to a visual representation. */
|
||||
@@ -324,6 +324,8 @@ export const MVSTreeSchema = TreeSchema({
|
||||
position: RequiredField(Vector3, 'Coordinates of the camera.'),
|
||||
/** Vector which will be aligned with the screen Y axis. */
|
||||
up: OptionalField(Vector3, [0, 1, 0], 'Vector which will be aligned with the screen Y axis.'),
|
||||
/** Near clipping plane distance from the position. */
|
||||
near: OptionalField(nullable(float), null, 'Near clipping plane distance from the position.'),
|
||||
}),
|
||||
},
|
||||
/** This node sets canvas properties. */
|
||||
|
||||
@@ -1359,7 +1359,7 @@ namespace Canvas3D {
|
||||
}
|
||||
},
|
||||
getImagePass: (props: Partial<ImageProps> = {}) => {
|
||||
return new ImagePass(webgl, assetManager, renderer, scene, camera, helper, passes.draw.transparency, props);
|
||||
return new ImagePass(webgl, assetManager, renderer, scene, camera, helper, props);
|
||||
},
|
||||
getRenderObjects(): GraphicsRenderObject[] {
|
||||
const renderObjects: GraphicsRenderObject[] = [];
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Gianluca Tomasello <giagitom@gmail.com>
|
||||
*/
|
||||
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
@@ -57,10 +58,10 @@ export class ImagePass {
|
||||
get width() { return this._width; }
|
||||
get height() { return this._height; }
|
||||
|
||||
constructor(private webgl: WebGLContext, assetManager: AssetManager, private renderer: Renderer, private scene: Scene, private camera: Camera, helper: Helper, transparency: 'wboit' | 'dpoit' | 'blended', props: Partial<ImageProps>) {
|
||||
constructor(private webgl: WebGLContext, assetManager: AssetManager, private renderer: Renderer, private scene: Scene, private camera: Camera, helper: Helper, props: Partial<ImageProps>) {
|
||||
this.props = { ...PD.getDefaultValues(ImageParams), ...props };
|
||||
|
||||
this.drawPass = new DrawPass(webgl, assetManager, 128, 128, transparency);
|
||||
this.drawPass = new DrawPass(webgl, assetManager, 128, 128, scene.transparency);
|
||||
this.illuminationPass = new IlluminationPass(webgl, this.drawPass);
|
||||
this.multiSamplePass = new MultiSamplePass(webgl, this.drawPass);
|
||||
this.multiSampleHelper = new MultiSampleHelper(this.multiSamplePass);
|
||||
@@ -104,6 +105,7 @@ export class ImagePass {
|
||||
}
|
||||
|
||||
async render(runtime: RuntimeContext) {
|
||||
this.drawPass.setTransparency(this.scene.transparency);
|
||||
ShaderManager.ensureRequired(this.webgl, this.scene, this.props);
|
||||
Camera.copySnapshot(this._camera.state, this.camera.state);
|
||||
Viewport.set(this._camera.viewport, 0, 0, this._width, this._height);
|
||||
|
||||
@@ -70,6 +70,7 @@ interface Scene extends Object3D {
|
||||
readonly renderables: ReadonlyArray<GraphicsRenderable>
|
||||
readonly boundingSphere: Sphere3D
|
||||
readonly boundingSphereVisible: Sphere3D
|
||||
readonly transparency: Transparency
|
||||
|
||||
readonly primitives: Scene.Group
|
||||
readonly volumes: Scene.Group
|
||||
@@ -383,6 +384,9 @@ namespace Scene {
|
||||
}
|
||||
return boundingSphereVisible;
|
||||
},
|
||||
get transparency() {
|
||||
return transparency;
|
||||
},
|
||||
get markerAverage() {
|
||||
if (markerAverageDirty) {
|
||||
markerAverage = calculateMarkerAverage();
|
||||
|
||||
@@ -108,9 +108,9 @@ function resetValueChanges(valueChanges: ValueChanges) {
|
||||
|
||||
//
|
||||
|
||||
export type Transparency = 'blended' | 'wboit' | 'dpoit' | undefined
|
||||
export type Transparency = 'blended' | 'wboit' | 'dpoit'
|
||||
|
||||
function getRenderVariant(variant: string, transparency: Transparency): string {
|
||||
function getRenderVariant(variant: string, transparency: Transparency | undefined): string {
|
||||
if (variant === 'color') {
|
||||
switch (transparency) {
|
||||
case 'blended': return 'colorBlended';
|
||||
@@ -136,7 +136,7 @@ export function createComputeRenderItem(ctx: WebGLContext, drawMode: DrawMode, s
|
||||
*
|
||||
* - assumes that `values.drawCount` and `values.instanceCount` exist
|
||||
*/
|
||||
export function createRenderItem<T extends string>(ctx: WebGLContext, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues, materialId: number, renderVariants: T[], transparency: Transparency): RenderItem<T> {
|
||||
export function createRenderItem<T extends string>(ctx: WebGLContext, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues, materialId: number, renderVariants: T[], transparency: Transparency | undefined): RenderItem<T> {
|
||||
const id = getNextRenderItemId();
|
||||
const { stats, state, resources } = ctx;
|
||||
const { instancedArrays, vertexArrayObject, multiDrawInstancedBaseVertexBaseInstance, drawInstancedBaseVertexBaseInstance } = ctx.extensions;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
@@ -109,7 +109,7 @@ function eatEscaped(state: TokenizerState, esc: number) {
|
||||
++state.position;
|
||||
return;
|
||||
default:
|
||||
if (next === void 0) { // = "end of stream"
|
||||
if (!Number.isFinite(next)) { // = "end of stream"
|
||||
// get rid of the quotes.
|
||||
state.tokenStart++;
|
||||
state.tokenEnd = state.position;
|
||||
|
||||
@@ -573,8 +573,8 @@ export namespace Vec3 {
|
||||
const a0 = a[0], a1 = a[1], a2 = a[2];
|
||||
const b0 = b[0], b1 = b[1], b2 = b[2];
|
||||
return (Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
|
||||
Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
|
||||
Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)));
|
||||
Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
|
||||
Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)));
|
||||
}
|
||||
|
||||
const rotTemp = zero();
|
||||
@@ -620,8 +620,28 @@ export namespace Vec3 {
|
||||
}
|
||||
|
||||
/** Get a vector that is similar to `b` but orthogonal to `a` */
|
||||
export function orthogonalize(out: Vec3, a: Vec3, b: Vec3) {
|
||||
return normalize(out, cross(out, cross(out, a, b), a));
|
||||
export function orthogonalize(out: Vec3, a: Vec3, b: Vec3): Vec3 {
|
||||
// Regular case (`b` not parallel to `a`)
|
||||
normalize(out, cross(out, cross(out, a, b), a));
|
||||
if (!Vec3.isZero(out)) return out;
|
||||
|
||||
// `b` was parallel to `a`, try orthogonalize(a, X)
|
||||
out[0] = 1; out[1] = 0; out[2] = 0;
|
||||
normalize(out, cross(out, cross(out, a, out), a));
|
||||
if (!Vec3.isZero(out)) return out;
|
||||
|
||||
// `X` was parallel to `a`, try orthogonalize(a, Y)
|
||||
out[0] = 0; out[1] = 1; out[2] = 0;
|
||||
normalize(out, cross(out, cross(out, a, out), a));
|
||||
if (!Vec3.isZero(out)) return out;
|
||||
|
||||
// `a` was zero, return normalized `b`
|
||||
normalize(out, b);
|
||||
if (!Vec3.isZero(out)) return out;
|
||||
|
||||
// `b` was zero, return whatever
|
||||
out[0] = 1; out[1] = 0; out[2] = 0;
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user