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

Merge branch 'master' into bond-repr

parents 18ec79cd 52a8a0e5
No related branches found
No related tags found
No related merge requests found
Showing
with 359 additions and 55 deletions
......@@ -51,17 +51,34 @@ This project builds on experience from previous solutions:
npm run watch-extra
### Build/watch mol-viewer
Build:
**Build**
npm run build
npm run build-viewer
Watch:
**Watch**
npm run watch
npm run watch-extra
npm run watch-viewer
**Run**
If not installed previously:
npm install -g http-server
...or a similar solution.
From the root of the project:
http-server -p PORT-NUMBER
and navigate to `build/viewer`
## Contributing
Just open an issue or make a pull request. All contributions are welcome.
......
......@@ -72,20 +72,44 @@ export function printSecStructure(model: Model) {
}
}
export function printBonds(structure: Structure) {
for (const unit of structure.units) {
if (!Unit.isAtomic(unit)) continue;
export function printLinks(structure: Structure, showIntra: boolean, showInter: boolean) {
if (showIntra) {
console.log('\nIntra Unit Links\n=============');
for (const unit of structure.units) {
if (!Unit.isAtomic(unit)) continue;
const elements = unit.elements;
const { a, b } = unit.links;
const { model } = unit;
if (!a.length) continue;
for (let bI = 0, _bI = a.length; bI < _bI; bI++) {
const x = a[bI], y = b[bI];
if (x >= y) continue;
console.log(`${atomLabel(model, elements[x])} -- ${atomLabel(model, elements[y])}`);
}
}
}
const elements = unit.elements;
const { a, b } = unit.bonds;
const { model } = unit;
if (showInter) {
console.log('\nInter Unit Links\n=============');
const links = structure.links;
for (const unit of structure.units) {
if (!Unit.isAtomic(unit)) continue;
if (!a.length) continue;
for (const pairLinks of links.getLinkedUnits(unit)) {
if (!pairLinks.areUnitsOrdered || pairLinks.bondCount === 0) continue;
for (let bI = 0, _bI = a.length; bI < _bI; bI++) {
const x = a[bI], y = b[bI];
if (x >= y) continue;
console.log(`${atomLabel(model, elements[x])} -- ${atomLabel(model, elements[y])}`);
const { unitA, unitB } = pairLinks;
console.log(`${pairLinks.unitA.id} - ${pairLinks.unitB.id}: ${pairLinks.bondCount} bond(s)`);
for (const aI of pairLinks.linkedElementIndices) {
for (const link of pairLinks.getBonds(aI)) {
console.log(`${atomLabel(unitA.model, unitA.elements[aI])} -- ${atomLabel(unitB.model, unitB.elements[link.indexB])}`);
}
}
}
}
}
}
......@@ -101,6 +125,18 @@ export function printSequence(model: Model) {
console.log();
}
export function printModRes(model: Model) {
console.log('\nModified Residues\n=============');
const map = model.properties.modifiedResidueNameMap;
const { label_comp_id, _rowCount } = model.atomicHierarchy.residues;
for (let i = 0; i < _rowCount; i++) {
const comp_id = label_comp_id.value(i);
if (!map.has(comp_id)) continue;
console.log(`[${i}] ${map.get(comp_id)} -> ${comp_id}`);
}
console.log();
}
export function printRings(structure: Structure) {
console.log('\nRings\n=============');
for (const unit of structure.units) {
......@@ -156,11 +192,12 @@ export function printIHMModels(model: Model) {
async function run(mmcif: mmCIF_Database) {
const models = await Model.create({ kind: 'mmCIF', data: mmcif }).run();
const structure = Structure.ofModel(models[0]);
printSequence(models[0]);
//printSequence(models[0]);
//printIHMModels(models[0]);
printUnits(structure);
printRings(structure);
//printBonds(structure);
//printRings(structure);
printLinks(structure, true, true);
//printModRes(models[0]);
//printSecStructure(models[0]);
}
......@@ -175,13 +212,13 @@ async function runFile(filename: string) {
}
const parser = new argparse.ArgumentParser({
addHelp: true,
description: 'Print info about a structure, mainly to test and showcase the mol-model module'
addHelp: true,
description: 'Print info about a structure, mainly to test and showcase the mol-model module'
});
parser.addArgument([ '--download', '-d' ], {
parser.addArgument(['--download', '-d'], {
help: 'Pdb entry id'
});
parser.addArgument([ '--file', '-f' ], {
parser.addArgument(['--file', '-f'], {
help: 'filename'
});
interface Args {
......
......@@ -22,6 +22,7 @@ import { EntityTree } from 'mol-app/ui/entity/tree';
import { EntityTreeController } from 'mol-app/controller/entity/tree';
import { TransformListController } from 'mol-app/controller/transform/list';
import { TransformList } from 'mol-app/ui/transform/list';
import { SequenceView } from 'mol-app/ui/visualization/sequence-view';
const elm = document.getElementById('app')
if (!elm) throw new Error('Can not find element with id "app".')
......@@ -45,6 +46,14 @@ targets[LayoutRegion.Bottom].components.push({
isStatic: true
});
targets[LayoutRegion.Top].components.push({
key: 'molstar-sequence-view',
controller: ctx.components.sequenceView,
region: LayoutRegion.Top,
view: SequenceView,
isStatic: true
});
targets[LayoutRegion.Main].components.push({
key: 'molstar-background-jobs',
controller: new JobsController(ctx, 'Background'),
......
......@@ -13,4 +13,5 @@ declare module Helpers {
export type NumberArray = TypedArray | number[]
export type UintArray = Uint8Array | Uint16Array | Uint32Array | number[]
export type ValueOf<T> = T[keyof T]
export type ArrayCtor<T> = { new(size: number): { [i: number]: T, length: number } }
}
\ No newline at end of file
......@@ -15,6 +15,7 @@ import { Stage } from 'mol-view/stage';
import { AnyTransform } from 'mol-view/state/transform';
import { BehaviorSubject } from 'rxjs';
import { AnyEntity } from 'mol-view/state/entity';
import { SequenceViewController } from '../controller/visualization/sequence-view';
export class Settings {
private settings = new Map<string, any>();
......@@ -35,11 +36,16 @@ export class Context {
logger = new Logger(this);
performance = new PerformanceMonitor();
stage = new Stage();
stage = new Stage(this);
viewport = new ViewportController(this);
layout: LayoutController;
settings = new Settings();
// TODO: this is a temporary solution
components = {
sequenceView: new SequenceViewController(this)
};
currentEntity = new BehaviorSubject(undefined) as BehaviorSubject<AnyEntity | undefined>
currentTransforms = new BehaviorSubject([] as AnyTransform[])
......
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { shallowClone } from 'mol-util';
import { Context } from '../../context/context'
import { Controller } from '../controller';
import { Structure } from 'mol-model/structure';
export const DefaultSequenceViewState = {
structure: void 0 as (Structure | undefined)
}
export type SequenceViewState = typeof DefaultSequenceViewState
export class SequenceViewController extends Controller<SequenceViewState> {
constructor(context: Context) {
super(context, shallowClone(DefaultSequenceViewState));
}
}
\ No newline at end of file
......@@ -11,6 +11,7 @@ import { Dispatcher } from '../service/dispatcher'
import { LayoutState } from '../controller/layout';
import { ViewportOptions } from '../controller/visualization/viewport';
import { Job } from '../service/job';
import { Element } from 'mol-model/structure'
const Lane = Dispatcher.Lane;
......@@ -31,3 +32,7 @@ export namespace LayoutEvents {
export const SetState = Event.create<Partial<LayoutState>>('lm.cmd.Layout.SetState', Lane.Slow);
export const SetViewportOptions = Event.create<ViewportOptions>('bs.cmd.Layout.SetViewportOptions', Lane.Slow);
}
export namespace InteractivityEvents {
export const HighlightElementLoci = Event.create<Element.Loci | undefined>('bs.Interactivity.HighlightElementLoci', Lane.Slow);
}
.molstar-sequence-view-wrap {
position: absolute;
right: 0;
top: 0;
left: 0;
bottom: 0;
overflow: hidden;
overflow-x: scroll;
}
\ No newline at end of file
......@@ -23,7 +23,7 @@
overflow: hidden;
}
.molstar-layout-main, .molstar-layout-bottom {
.molstar-layout-main, .molstar-layout-bottom, .molstar-layout-top {
.molstar-layout-static {
left: 0;
right: 0;
......
......@@ -35,4 +35,5 @@
@import 'components/misc';
@import 'components/panel';
@import 'components/slider';
@import 'components/viewport';
\ No newline at end of file
@import 'components/viewport';
@import 'components/sequence-view';
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import * as React from 'react'
import { View } from '../view';
import { SequenceViewController } from '../../controller/visualization/sequence-view';
import { Structure, StructureSequence, Queries, Selection } from 'mol-model/structure';
import { Context } from '../../context/context';
import { InteractivityEvents } from '../../event/basic';
import { SyncRuntimeContext } from 'mol-task/execution/synchronous';
export class SequenceView extends View<SequenceViewController, {}, {}> {
render() {
const s = this.controller.latestState.structure;
if (!s) return <div className='molstar-sequence-view-wrap'>No structure available.</div>;
const seqs = Structure.getModels(s)[0].sequence.sequences;
return <div className='molstar-sequence-view-wrap'>
{seqs.map((seq, i) => <EntitySequence key={i} ctx={this.controller.context} seq={seq} structure={s} /> )}
</div>;
}
}
function createQuery(entityId: string, label_seq_id: number) {
return Queries.generators.atoms({
entityTest: l => Queries.props.entity.id(l) === entityId,
residueTest: l => Queries.props.residue.label_seq_id(l) === label_seq_id
});
}
// TODO: this is really ineffective and should be done using a canvas.
class EntitySequence extends React.Component<{ ctx: Context, seq: StructureSequence.Entity, structure: Structure }> {
async raiseInteractityEvent(seqId?: number) {
if (typeof seqId === 'undefined') {
InteractivityEvents.HighlightElementLoci.dispatch(this.props.ctx, void 0);
return;
}
const query = createQuery(this.props.seq.entityId, seqId);
const loci = Selection.toLoci(await query(this.props.structure, SyncRuntimeContext));
if (loci.elements.length === 0) InteractivityEvents.HighlightElementLoci.dispatch(this.props.ctx, void 0);
else InteractivityEvents.HighlightElementLoci.dispatch(this.props.ctx, loci);
}
render() {
const { ctx, seq } = this.props;
const { offset, sequence } = seq.sequence;
const elems: JSX.Element[] = [];
for (let i = 0, _i = sequence.length; i < _i; i++) {
elems[elems.length] = <ResidueView ctx={ctx} seqId={offset + i} letter={sequence[i]} parent={this} key={i} />;
}
return <div style={{ wordWrap: 'break-word' }}>
<span style={{ fontWeight: 'bold' }}>{this.props.seq.entityId}:{offset}&nbsp;</span>
{elems}
</div>;
}
}
class ResidueView extends React.Component<{ ctx: Context, seqId: number, letter: string, parent: EntitySequence }, { isHighlighted: boolean }> {
state = { isHighlighted: false }
mouseEnter = () => {
this.setState({ isHighlighted: true });
this.props.parent.raiseInteractityEvent(this.props.seqId);
}
mouseLeave = () => {
this.setState({ isHighlighted: false });
this.props.parent.raiseInteractityEvent();
}
render() {
return <span onMouseEnter={this.mouseEnter} onMouseLeave={this.mouseLeave}
style={{ cursor: 'pointer', backgroundColor: this.state.isHighlighted ? 'yellow' : void 0 }}>
{this.props.letter}
</span>;
}
}
\ No newline at end of file
......@@ -59,6 +59,10 @@ describe('sortedArray', () => {
compareArrays(SortedArray.deduplicate(SortedArray.ofSortedArray([1, 2, 3])), [1, 2, 3]);
});
it('indicesOf', () => {
compareArrays(SortedArray.indicesOf(SortedArray.ofSortedArray([10, 11, 12]), SortedArray.ofSortedArray([10, 12, 14])), [0, 2]);
})
// console.log(Interval.findPredecessorIndexInInterval(Interval.ofBounds(0, 3), 2, Interval.ofBounds(0, 3)))
// console.log(SortedArray.findPredecessorIndexInInterval(SortedArray.ofSortedArray([0, 1, 2]), 2, Interval.ofBounds(0, 3)))
});
\ No newline at end of file
......@@ -4,7 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { sortArray, hash3, hash4 } from '../../util'
import { sortArray, hash3, hash4, createRangeArray } from '../../util'
import Interval from '../interval'
type Nums = ArrayLike<number>
......@@ -289,6 +289,39 @@ export function deduplicate(xs: Nums) {
return ret;
}
export function indicesOf(a: Nums, b: Nums): Nums {
if (a === b) return ofSortedArray(createRangeArray(0, a.length - 1));
const { startI: sI, startJ: sJ, endI, endJ } = getSuitableIntersectionRange(a, b);
let i = sI, j = sJ;
let commonCount = 0;
while (i < endI && j < endJ) {
const x = a[i], y = b[j];
if (x < y) { i++; }
else if (x > y) { j++; }
else { i++; j++; commonCount++; }
}
const lenA = a.length;
// no common elements
if (!commonCount) return Empty;
// A is subset of B ==> A
if (commonCount === lenA) return ofSortedArray(createRangeArray(0, a.length - 1));
const indices = new Int32Array(commonCount);
let offset = 0;
i = sI;
j = sJ;
while (i < endI && j < endJ) {
const x = a[i], y = b[j];
if (x < y) { i++; }
else if (x > y) { j++; }
else { indices[offset++] = i; i++; j++; }
}
return ofSortedArray(indices);
}
const _maxIntRangeRet = { startI: 0, startJ: 0, endI: 0, endJ: 0 };
// for small sets, just gets the whole range, for large sets does a bunch of binary searches
function getSuitableIntersectionRange(a: Nums, b: Nums) {
......
......@@ -41,7 +41,9 @@ namespace SortedArray {
export const findPredecessorIndexInInterval: (array: SortedArray, x: number, bounds: Interval) => number = Impl.findPredecessorIndexInInterval as any;
export const findRange: (array: SortedArray, min: number, max: number) => Interval = Impl.findRange as any;
export const deduplicate: (arrat: SortedArray) => SortedArray = Impl.deduplicate as any;
export const deduplicate: (array: SortedArray) => SortedArray = Impl.deduplicate as any;
/** Returns indices of xs in the array. E.g. indicesOf([10, 11, 12], [10, 12]) ==> [0, 2] */
export const indicesOf: (array: SortedArray, xs: SortedArray) => SortedArray = Impl.indicesOf as any;
}
interface SortedArray extends ArrayLike<number> { '@type': 'int-sorted-array' }
......
......@@ -22,4 +22,28 @@ export function iterableToArray<T>(it: IterableIterator<T>): T[] {
ret[ret.length] = value;
}
return ret;
}
/** Fills the array so that array[0] = start and array[array.length - 1] = end */
export function createRangeArray(start: number, end: number, ctor?: Helpers.ArrayCtor<number>) {
const len = end - start + 1;
const array = ctor ? new ctor(len) : new Int32Array(len);
for (let i = 0; i < len; i++) {
array[i] = i + start;
}
return array;
}
export function arrayPickIndices<T>(array: ArrayLike<T>, indices: ArrayLike<number>) {
const ret = new (arrayGetCtor(array))(indices.length);
for (let i = 0, _i = indices.length; i < _i; i++) {
ret[i] = array[indices[i]];
}
return ret;
}
export function arrayGetCtor<T>(data: ArrayLike<T>): Helpers.ArrayCtor<T> {
const ret = (data as any).constructor;
if (!ret) throw new Error('data does not define a constructor and it should');
return ret;
}
\ No newline at end of file
......@@ -10,7 +10,7 @@
import { ValueCell } from 'mol-util/value-cell'
import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
import { Unit, Element, Bond } from 'mol-model/structure';
import { Unit, Element, Link } from 'mol-model/structure';
import { UnitsRepresentation, DefaultStructureProps } from './index';
import { Task } from 'mol-task'
import { createTransforms } from './utils';
......@@ -31,7 +31,7 @@ function createBondMesh(unit: Unit, mesh?: Mesh) {
if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
const elements = unit.elements;
const bonds = unit.bonds
const bonds = unit.links
const { edgeCount, a, b } = bonds
if (!edgeCount) return Mesh.createEmpty(mesh)
......@@ -98,7 +98,7 @@ export default function BondUnitsRepresentation(): UnitsRepresentation<BondProps
currentGroup = group
const unit = group.units[0]
const elementCount = Unit.isAtomic(unit) ? unit.bonds.edgeCount * 2 : 0
const elementCount = Unit.isAtomic(unit) ? unit.links.edgeCount * 2 : 0
const instanceCount = group.units.length
mesh = await createBondMesh(unit).runAsChild(ctx, 'Computing bond mesh')
......@@ -167,11 +167,11 @@ export default function BondUnitsRepresentation(): UnitsRepresentation<BondProps
const { objectId, instanceId, elementId } = pickingId
const unit = currentGroup.units[instanceId]
if (cylinders.id === objectId && Unit.isAtomic(unit)) {
return Bond.Loci([{
return Link.Loci([{
aUnit: unit,
aIndex: unit.bonds.a[elementId],
aIndex: unit.links.a[elementId],
bUnit: unit,
bIndex: unit.bonds.b[elementId]
bIndex: unit.links.b[elementId]
}])
}
return null
......@@ -182,7 +182,7 @@ export default function BondUnitsRepresentation(): UnitsRepresentation<BondProps
const unit = group.units[0]
if (!Unit.isAtomic(unit)) return
const elementCount = unit.bonds.edgeCount * 2
const elementCount = unit.links.edgeCount * 2
const instanceCount = group.units.length
let changed = false
......@@ -190,11 +190,11 @@ export default function BondUnitsRepresentation(): UnitsRepresentation<BondProps
if (isEveryLoci(loci)) {
applyMarkerAction(array, 0, elementCount * instanceCount, action)
changed = true
} else if (Bond.isLoci(loci)) {
for (const b of loci.bonds) {
} else if (Link.isLoci(loci)) {
for (const b of loci.links) {
const unitIdx = Unit.findUnitById(b.aUnit.id, group.units)
if (unitIdx !== -1) {
const _idx = unit.bonds.getEdgeIndex(b.aIndex, b.bIndex)
const _idx = unit.links.getEdgeIndex(b.aIndex, b.bIndex)
if (_idx !== -1) {
const idx = _idx
if (applyMarkerAction(array, idx, idx + 1, action) && !changed) {
......
......@@ -4,4 +4,4 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
export * from './graph/int/graph'
\ No newline at end of file
export * from './graph/int-adjacency-graph'
\ No newline at end of file
......@@ -4,7 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { IntGraph } from '../int/graph';
import { IntAdjacencyGraph } from '../int-adjacency-graph';
describe('IntGraph', () => {
const vc = 3;
......@@ -12,7 +12,7 @@ describe('IntGraph', () => {
const ys = [1, 2, 0];
const _prop = [10, 11, 12];
const builder = new IntGraph.EdgeBuilder(vc, xs, ys);
const builder = new IntAdjacencyGraph.EdgeBuilder(vc, xs, ys);
const prop: number[] = new Array(builder.slotCount);
for (let i = 0; i < builder.edgeCount; i++) {
builder.addNextEdge();
......@@ -28,9 +28,16 @@ describe('IntGraph', () => {
});
it('triangle-propAndEdgeIndex', () => {
const prop = graph.prop;
const prop = graph.edgeProps.prop;
expect(prop[graph.getEdgeIndex(0, 1)]).toBe(10);
expect(prop[graph.getEdgeIndex(1, 2)]).toBe(11);
expect(prop[graph.getEdgeIndex(2, 0)]).toBe(12);
});
it('induce', () => {
const induced = IntAdjacencyGraph.induceByVertices(graph, [1, 2]);
expect(induced.vertexCount).toBe(2);
expect(induced.edgeCount).toBe(1);
expect(induced.edgeProps.prop[induced.getEdgeIndex(0, 1)]).toBe(11);
})
});
\ No newline at end of file
......@@ -4,6 +4,8 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { arrayPickIndices } from 'mol-data/util';
/**
* Represent a graph using vertex adjacency list.
*
......@@ -12,12 +14,13 @@
*
* Edge properties are indexed same as in the arrays a and b.
*/
type IntGraph<EdgeProperties extends object = { }> = {
interface IntAdjacencyGraph<EdgeProps extends IntAdjacencyGraph.EdgePropsBase = {}> {
readonly offset: ArrayLike<number>,
readonly a: ArrayLike<number>,
readonly b: ArrayLike<number>,
readonly vertexCount: number,
readonly edgeCount: number,
readonly edgeProps: Readonly<EdgeProps>
/**
* Get the edge index between i-th and j-th vertex.
......@@ -28,11 +31,14 @@ type IntGraph<EdgeProperties extends object = { }> = {
*/
getEdgeIndex(i: number, j: number): number,
getVertexEdgeCount(i: number): number
} & EdgeProperties
}
namespace IntGraph {
class Impl implements IntGraph<any> {
namespace IntAdjacencyGraph {
export type EdgePropsBase = { [name: string]: ArrayLike<any> }
class IntGraphImpl implements IntAdjacencyGraph<any> {
readonly vertexCount: number;
readonly edgeProps: object;
getEdgeIndex(i: number, j: number): number {
let a, b;
......@@ -48,18 +54,14 @@ namespace IntGraph {
return this.offset[i + 1] - this.offset[i];
}
constructor(public offset: ArrayLike<number>, public a: ArrayLike<number>, public b: ArrayLike<number>, public edgeCount: number, props?: any) {
constructor(public offset: ArrayLike<number>, public a: ArrayLike<number>, public b: ArrayLike<number>, public edgeCount: number, edgeProps?: any) {
this.vertexCount = offset.length - 1;
if (props) {
for (const p of Object.keys(props)) {
(this as any)[p] = props[p];
}
}
this.edgeProps = edgeProps || {};
}
}
export function create<EdgeProps extends object = { }>(offset: ArrayLike<number>, a: ArrayLike<number>, b: ArrayLike<number>, edgeCount: number, edgeProps?: EdgeProps): IntGraph<EdgeProps> {
return new Impl(offset, a, b, edgeCount, edgeProps) as IntGraph<EdgeProps>;
export function create<EdgeProps extends IntAdjacencyGraph.EdgePropsBase = {}>(offset: ArrayLike<number>, a: ArrayLike<number>, b: ArrayLike<number>, edgeCount: number, edgeProps?: EdgeProps): IntAdjacencyGraph<EdgeProps> {
return new IntGraphImpl(offset, a, b, edgeCount, edgeProps) as IntAdjacencyGraph<EdgeProps>;
}
export class EdgeBuilder {
......@@ -75,7 +77,7 @@ namespace IntGraph {
a: Int32Array;
b: Int32Array;
createGraph<EdgeProps extends object = { }>(edgeProps?: EdgeProps) {
createGraph<EdgeProps extends IntAdjacencyGraph.EdgePropsBase = {}>(edgeProps?: EdgeProps) {
return create(this.offsets, this.a, this.b, this.edgeCount, edgeProps);
}
......@@ -132,6 +134,47 @@ namespace IntGraph {
this.b = new Int32Array(offset);
}
}
export function induceByVertices<P extends IntAdjacencyGraph.EdgePropsBase>(graph: IntAdjacencyGraph<P>, vertexIndices: ArrayLike<number>): IntAdjacencyGraph<P> {
const { b, offset, vertexCount, edgeProps } = graph;
const vertexMap = new Int32Array(vertexCount);
for (let i = 0, _i = vertexIndices.length; i < _i; i++) vertexMap[vertexIndices[i]] = i + 1;
let newEdgeCount = 0;
for (let i = 0; i < vertexCount; i++) {
if (vertexMap[i] === 0) continue;
for (let j = offset[i], _j = offset[i + 1]; j < _j; j++) {
if (b[j] > i && vertexMap[b[j]] !== 0) newEdgeCount++;
}
}
const newOffsets = new Int32Array(vertexIndices.length + 1);
const edgeIndices = new Int32Array(2 * newEdgeCount);
const newA = new Int32Array(2 * newEdgeCount);
const newB = new Int32Array(2 * newEdgeCount);
let eo = 0, vo = 0;
for (let i = 0; i < vertexCount; i++) {
if (vertexMap[i] === 0) continue;
const aa = vertexMap[i] - 1;
for (let j = offset[i], _j = offset[i + 1]; j < _j; j++) {
const bb = vertexMap[b[j]];
if (bb === 0) continue;
newA[eo] = aa;
newB[eo] = bb - 1;
edgeIndices[eo] = j;
eo++;
}
newOffsets[++vo] = eo;
}
const newEdgeProps: P = {} as any;
for (const key of Object.keys(edgeProps)) {
newEdgeProps[key] = arrayPickIndices(edgeProps[key], edgeIndices);
}
return create(newOffsets, newA, newB, newEdgeCount, newEdgeProps);
}
}
export { IntGraph }
\ No newline at end of file
export { IntAdjacencyGraph }
\ No newline at end of file
......@@ -5,7 +5,7 @@
*/
import { Element } from './structure'
import { Bond } from './structure/structure/unit/bonds'
import { Link } from './structure/structure/unit/links'
/** A Loci that includes every loci */
export const EveryLoci = { kind: 'every-loci' as 'every-loci' }
......@@ -14,4 +14,4 @@ export function isEveryLoci(x: any): x is EveryLoci {
return !!x && x.kind === 'every-loci';
}
export type Loci = Element.Loci | Bond.Loci | EveryLoci
\ No newline at end of file
export type Loci = Element.Loci | Link.Loci | EveryLoci
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment