@motionscript/molecule 0.0.0-stage → 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/LICENSE +201 -0
- package/dist/browser/index.js +4 -0
- package/dist/browser/index.js.map +7 -0
- package/dist/browser/manifest.json +11 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/nodes.d.ts +18 -0
- package/dist/nodes.d.ts.map +1 -0
- package/dist/nodes.js +18 -0
- package/dist/nodes.js.map +1 -0
- package/dist/protein/chemistry.d.ts +52 -0
- package/dist/protein/chemistry.d.ts.map +1 -0
- package/dist/protein/chemistry.js +208 -0
- package/dist/protein/chemistry.js.map +1 -0
- package/dist/protein/index.d.ts +35 -0
- package/dist/protein/index.d.ts.map +1 -0
- package/dist/protein/index.js +35 -0
- package/dist/protein/index.js.map +1 -0
- package/dist/protein/parse.d.ts +32 -0
- package/dist/protein/parse.d.ts.map +1 -0
- package/dist/protein/parse.js +387 -0
- package/dist/protein/parse.js.map +1 -0
- package/dist/protein/protein.d.ts +265 -0
- package/dist/protein/protein.d.ts.map +1 -0
- package/dist/protein/protein.js +645 -0
- package/dist/protein/protein.js.map +1 -0
- package/dist/protein/ribbon.d.ts +83 -0
- package/dist/protein/ribbon.d.ts.map +1 -0
- package/dist/protein/ribbon.js +468 -0
- package/dist/protein/ribbon.js.map +1 -0
- package/dist/protein/shared.d.ts +221 -0
- package/dist/protein/shared.d.ts.map +1 -0
- package/dist/protein/shared.js +478 -0
- package/dist/protein/shared.js.map +1 -0
- package/dist/protein/structure.d.ts +184 -0
- package/dist/protein/structure.d.ts.map +1 -0
- package/dist/protein/structure.js +324 -0
- package/dist/protein/structure.js.map +1 -0
- package/package.json +64 -3
- package/registry.json +22 -0
- package/src/index.ts +2 -0
- package/src/nodes.ts +18 -0
- package/src/protein/chemistry.ts +223 -0
- package/src/protein/index.ts +34 -0
- package/src/protein/parse.ts +427 -0
- package/src/protein/protein.ts +897 -0
- package/src/protein/ribbon.ts +658 -0
- package/src/protein/shared.ts +622 -0
- package/src/protein/structure.ts +491 -0
- package/README.md +0 -4
package/src/index.ts
ADDED
package/src/nodes.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Protein } from "./protein";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Every node type this package publishes.
|
|
5
|
+
*
|
|
6
|
+
* A host registers these by handing the array to an engine —
|
|
7
|
+
* `new Engine(platform, { nodes: NODES })` — which reads each class's `@node()`
|
|
8
|
+
* key. The decorator only *declares* that key: nothing is registered by the mere
|
|
9
|
+
* act of importing a module, so a registry holds exactly what its owner asked
|
|
10
|
+
* for and two engines in one process can differ about it.
|
|
11
|
+
*
|
|
12
|
+
* Listing class **values** is also what survives bundling. This package declares
|
|
13
|
+
* `sideEffects: false`, which licenses a bundler to drop a module nothing
|
|
14
|
+
* imports a value from, and a document names a node type by string — so
|
|
15
|
+
* `import "./protein"` for its side effect is exactly what gets shaken out, where an
|
|
16
|
+
* array of bindings is a data dependency that cannot be.
|
|
17
|
+
*/
|
|
18
|
+
export const NODES = [Protein];
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The chemistry the {@link Protein} node draws from: how big an atom is, what
|
|
3
|
+
* colour it conventionally takes, what counts as a bond, and which residues are
|
|
4
|
+
* part of a polymer chain rather than sitting beside it.
|
|
5
|
+
*
|
|
6
|
+
* Tables rather than a dependency, for the same reason `graph3d/expression.ts`
|
|
7
|
+
* is a parser rather than a maths library: this package has to stay loadable in
|
|
8
|
+
* a bare Node process with no DOM, and what a molecular-graphics library would
|
|
9
|
+
* bring with it — a renderer, a fetch layer, a DOM — is everything the scene
|
|
10
|
+
* builder was split apart to avoid.
|
|
11
|
+
*
|
|
12
|
+
* The numbers are the standard ones. Van der Waals radii are Bondi's, covalent
|
|
13
|
+
* radii are Cordero's, and the colours are the CPK scheme every molecular viewer
|
|
14
|
+
* has used since Corey and Pauling built theirs out of painted wood — which is
|
|
15
|
+
* the point of using them: a biologist reads oxygen as red before they have
|
|
16
|
+
* read the legend.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** Ångströms. What an atom's electron cloud actually occupies. */
|
|
20
|
+
const VAN_DER_WAALS: Readonly<Record<string, number>> = {
|
|
21
|
+
H: 1.1,
|
|
22
|
+
C: 1.7,
|
|
23
|
+
N: 1.55,
|
|
24
|
+
O: 1.52,
|
|
25
|
+
F: 1.47,
|
|
26
|
+
NA: 2.27,
|
|
27
|
+
MG: 1.73,
|
|
28
|
+
P: 1.8,
|
|
29
|
+
S: 1.8,
|
|
30
|
+
CL: 1.75,
|
|
31
|
+
K: 2.75,
|
|
32
|
+
CA: 2.31,
|
|
33
|
+
MN: 2.05,
|
|
34
|
+
FE: 2.04,
|
|
35
|
+
CO: 2.0,
|
|
36
|
+
NI: 1.97,
|
|
37
|
+
CU: 1.96,
|
|
38
|
+
ZN: 2.01,
|
|
39
|
+
SE: 1.9,
|
|
40
|
+
BR: 1.85,
|
|
41
|
+
I: 1.98,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** The fallback radius, carbon's — the element most of a protein is made of. */
|
|
45
|
+
const DEFAULT_VAN_DER_WAALS = 1.7
|
|
46
|
+
|
|
47
|
+
/** Ångströms. Half of how far two of these sit apart when bonded. */
|
|
48
|
+
const COVALENT: Readonly<Record<string, number>> = {
|
|
49
|
+
H: 0.31,
|
|
50
|
+
C: 0.76,
|
|
51
|
+
N: 0.71,
|
|
52
|
+
O: 0.66,
|
|
53
|
+
F: 0.57,
|
|
54
|
+
NA: 1.66,
|
|
55
|
+
MG: 1.41,
|
|
56
|
+
P: 1.07,
|
|
57
|
+
S: 1.05,
|
|
58
|
+
CL: 1.02,
|
|
59
|
+
K: 2.03,
|
|
60
|
+
CA: 1.76,
|
|
61
|
+
MN: 1.39,
|
|
62
|
+
FE: 1.32,
|
|
63
|
+
CO: 1.26,
|
|
64
|
+
NI: 1.24,
|
|
65
|
+
CU: 1.32,
|
|
66
|
+
ZN: 1.22,
|
|
67
|
+
SE: 1.2,
|
|
68
|
+
BR: 1.2,
|
|
69
|
+
I: 1.39,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const DEFAULT_COVALENT = 0.77
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The CPK colours, as Jmol renders them.
|
|
76
|
+
*
|
|
77
|
+
* Only the elements a biological structure actually contains, plus the metals
|
|
78
|
+
* that turn up in cofactors. Anything else takes {@link UNKNOWN_ELEMENT_COLOR},
|
|
79
|
+
* which is deliberately garish — an unrecognised element should look like a
|
|
80
|
+
* question rather than blend into the carbons.
|
|
81
|
+
*/
|
|
82
|
+
const CPK: Readonly<Record<string, string>> = {
|
|
83
|
+
H: "#ffffff",
|
|
84
|
+
C: "#909090",
|
|
85
|
+
N: "#3050f8",
|
|
86
|
+
O: "#ff0d0d",
|
|
87
|
+
F: "#90e050",
|
|
88
|
+
NA: "#ab5cf2",
|
|
89
|
+
MG: "#8aff00",
|
|
90
|
+
P: "#ff8000",
|
|
91
|
+
S: "#ffff30",
|
|
92
|
+
CL: "#1ff01f",
|
|
93
|
+
K: "#8f40d4",
|
|
94
|
+
CA: "#3dff00",
|
|
95
|
+
MN: "#9c7ac7",
|
|
96
|
+
FE: "#e06633",
|
|
97
|
+
CO: "#f090a0",
|
|
98
|
+
NI: "#50d050",
|
|
99
|
+
CU: "#c88033",
|
|
100
|
+
ZN: "#7d80b0",
|
|
101
|
+
SE: "#ffa100",
|
|
102
|
+
BR: "#a62929",
|
|
103
|
+
I: "#940094",
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** What an element nobody has a colour for is drawn in. */
|
|
107
|
+
export const UNKNOWN_ELEMENT_COLOR = "#ff1493"
|
|
108
|
+
|
|
109
|
+
/** An element's van der Waals radius in Ångströms. */
|
|
110
|
+
export function vanDerWaalsRadius(element: string): number {
|
|
111
|
+
return VAN_DER_WAALS[element] ?? DEFAULT_VAN_DER_WAALS
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** An element's covalent radius in Ångströms — half of a bond it makes. */
|
|
115
|
+
export function covalentRadius(element: string): number {
|
|
116
|
+
return COVALENT[element] ?? DEFAULT_COVALENT
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** The CPK colour for an element, as a `#rrggbb` string. */
|
|
120
|
+
export function elementColor(element: string): string {
|
|
121
|
+
return CPK[element] ?? UNKNOWN_ELEMENT_COLOR
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* The standard amino acids, plus the two modified residues common enough that
|
|
126
|
+
* treating them as foreign would put a hole in the middle of a chain:
|
|
127
|
+
* selenomethionine (`MSE`, how a crystallographer phases a new structure) and
|
|
128
|
+
* pyroglutamate (`PCA`).
|
|
129
|
+
*
|
|
130
|
+
* Both arrive as `HETATM` records, which is exactly why the set is consulted
|
|
131
|
+
* rather than the record type — "is this part of the chain" is a question about
|
|
132
|
+
* the residue, and the file answers a different one.
|
|
133
|
+
*/
|
|
134
|
+
const AMINO_ACIDS = new Set([
|
|
135
|
+
"ALA", "ARG", "ASN", "ASP", "CYS", "GLN", "GLU", "GLY", "HIS", "ILE",
|
|
136
|
+
"LEU", "LYS", "MET", "PHE", "PRO", "SER", "THR", "TRP", "TYR", "VAL",
|
|
137
|
+
"MSE", "PCA", "SEC", "PYL",
|
|
138
|
+
])
|
|
139
|
+
|
|
140
|
+
/** The nucleotides, RNA and DNA. */
|
|
141
|
+
const NUCLEOTIDES = new Set([
|
|
142
|
+
"A", "C", "G", "U", "I",
|
|
143
|
+
"DA", "DC", "DG", "DT", "DI",
|
|
144
|
+
])
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* The waters. Named separately from the rest of the hetero atoms because they
|
|
148
|
+
* are the one kind a viewer hides by default: a crystal structure carries
|
|
149
|
+
* hundreds of them, they are chemically uninteresting to the picture being
|
|
150
|
+
* drawn, and left on they bury the molecule in red dots.
|
|
151
|
+
*/
|
|
152
|
+
const WATERS = new Set(["HOH", "DOD", "WAT", "H2O", "TIP"])
|
|
153
|
+
|
|
154
|
+
/** What a residue is, which decides whether it joins a backbone trace. */
|
|
155
|
+
export type ResidueKind = "amino" | "nucleic" | "water" | "other"
|
|
156
|
+
|
|
157
|
+
/** Classifies a residue by its three-letter code. */
|
|
158
|
+
export function residueKind(name: string): ResidueKind {
|
|
159
|
+
const code = name.trim().toUpperCase()
|
|
160
|
+
if (AMINO_ACIDS.has(code)) return "amino"
|
|
161
|
+
if (NUCLEOTIDES.has(code)) return "nucleic"
|
|
162
|
+
if (WATERS.has(code)) return "water"
|
|
163
|
+
return "other"
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The atom a residue's backbone trace passes through: the α-carbon of an amino
|
|
168
|
+
* acid, the phosphorus of a nucleotide.
|
|
169
|
+
*
|
|
170
|
+
* One atom per residue rather than the whole backbone, because what the trace is
|
|
171
|
+
* *for* is the smooth curve a ribbon is swept along, and the N–CA–C zig-zag
|
|
172
|
+
* would put a kink at every residue. Every molecular viewer traces α-carbons for
|
|
173
|
+
* the same reason.
|
|
174
|
+
*/
|
|
175
|
+
export function traceAtomName(kind: ResidueKind): string | null {
|
|
176
|
+
if (kind === "amino") return "CA"
|
|
177
|
+
if (kind === "nucleic") return "P"
|
|
178
|
+
return null
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* The element an atom belongs to, from the file's element column when it has
|
|
183
|
+
* one and from the atom's *name* when it doesn't.
|
|
184
|
+
*
|
|
185
|
+
* The fallback matters more than it looks: the element column is right-justified
|
|
186
|
+
* in columns 77–78 of a PDB record and plenty of older files, and most files
|
|
187
|
+
* written by hand, simply stop before it. The name is then all there is, and
|
|
188
|
+
* reading it is not quite trimming the digits off — an amino acid's `CA` is a
|
|
189
|
+
* carbon and an ion's `CA` is calcium, and the two are told apart only by what
|
|
190
|
+
* residue they sit in. Hence {@link kind}.
|
|
191
|
+
*/
|
|
192
|
+
export function elementOf(
|
|
193
|
+
declared: string,
|
|
194
|
+
atomName: string,
|
|
195
|
+
kind: ResidueKind
|
|
196
|
+
): string {
|
|
197
|
+
const stated = declared.trim().toUpperCase()
|
|
198
|
+
if (stated !== "") return stated
|
|
199
|
+
|
|
200
|
+
// PDB atom names are padded so that the element starts in column 14 for a
|
|
201
|
+
// two-letter element and column 15 for a one-letter one — but only in files
|
|
202
|
+
// that respect the alignment, which is why this reads the characters instead:
|
|
203
|
+
// strip the leading digit an alternate hydrogen carries (`1HB`), then take the
|
|
204
|
+
// letters.
|
|
205
|
+
const name = atomName.trim().toUpperCase().replace(/^\d+/, "")
|
|
206
|
+
const letters = name.replace(/[^A-Z]/g, "")
|
|
207
|
+
if (letters === "") return ""
|
|
208
|
+
|
|
209
|
+
// Inside a polymer residue the only two-letter elements are the ones that
|
|
210
|
+
// can't be confused: everything else is C, N, O, S or P followed by the
|
|
211
|
+
// position letters that name the atom (`CB`, `NZ`, `OG1`). A `CA` there is the
|
|
212
|
+
// α-carbon, never calcium.
|
|
213
|
+
if (kind === "amino" || kind === "nucleic") {
|
|
214
|
+
if (letters.startsWith("SE")) return "SE"
|
|
215
|
+
return letters.slice(0, 1)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Outside one — a ligand, an ion, a cofactor — a two-letter element is real,
|
|
219
|
+
// so prefer the longest match the tables know before falling back to one.
|
|
220
|
+
const pair = letters.slice(0, 2)
|
|
221
|
+
if (pair in COVALENT) return pair
|
|
222
|
+
return letters.slice(0, 1)
|
|
223
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Protein node — a **native** node type (see `node-types.ts` /
|
|
3
|
+
* `NODE_CLASS_BY_KEY`) like the chart family and the 3D graph beside it: built,
|
|
4
|
+
* themed and animated from the inspector rather than loaded as a code package.
|
|
5
|
+
*
|
|
6
|
+
* It draws a molecular structure — a protein, a nucleic acid, a complex of both
|
|
7
|
+
* with whatever is bound to them — in one of four representations, from a
|
|
8
|
+
* coordinate file the Protein Data Bank publishes.
|
|
9
|
+
*
|
|
10
|
+
* Three things about it are worth knowing before reading the parts:
|
|
11
|
+
*
|
|
12
|
+
* **The file is parsed outside the render.** `buildScene` is synchronous and a
|
|
13
|
+
* coordinate file arrives over a network, so the fetch and the parse happen
|
|
14
|
+
* ahead of the build and what reaches the node is a finished
|
|
15
|
+
* {@link ProteinStructure} — the same arrangement a chart has with its rows.
|
|
16
|
+
* That is why `parse.ts` is exported: the app's structure store is its caller,
|
|
17
|
+
* not this node.
|
|
18
|
+
*
|
|
19
|
+
* **The chemistry is a table, not a dependency.** Radii, colours and what counts
|
|
20
|
+
* as a bond live in `chemistry.ts` for the same reason `graph3d/expression.ts`
|
|
21
|
+
* is a parser rather than a maths library: this package has to stay loadable in
|
|
22
|
+
* a bare Node process, and a molecular-graphics library brings a renderer, a
|
|
23
|
+
* fetch layer and a DOM with it.
|
|
24
|
+
*
|
|
25
|
+
* **There is no pointer.** The camera is three tweenable numbers rather than a
|
|
26
|
+
* drag, because the studio renders a timeline and every frame has to be
|
|
27
|
+
* reproducible under scrubbing and export. See {@link Protein}.
|
|
28
|
+
*/
|
|
29
|
+
export * from "./chemistry";
|
|
30
|
+
export * from "./parse";
|
|
31
|
+
export * from "./protein";
|
|
32
|
+
export * from "./ribbon";
|
|
33
|
+
export * from "./shared";
|
|
34
|
+
export * from "./structure";
|