@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.
Files changed (53) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE +201 -0
  3. package/dist/browser/index.js +4 -0
  4. package/dist/browser/index.js.map +7 -0
  5. package/dist/browser/manifest.json +11 -0
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +3 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/nodes.d.ts +18 -0
  11. package/dist/nodes.d.ts.map +1 -0
  12. package/dist/nodes.js +18 -0
  13. package/dist/nodes.js.map +1 -0
  14. package/dist/protein/chemistry.d.ts +52 -0
  15. package/dist/protein/chemistry.d.ts.map +1 -0
  16. package/dist/protein/chemistry.js +208 -0
  17. package/dist/protein/chemistry.js.map +1 -0
  18. package/dist/protein/index.d.ts +35 -0
  19. package/dist/protein/index.d.ts.map +1 -0
  20. package/dist/protein/index.js +35 -0
  21. package/dist/protein/index.js.map +1 -0
  22. package/dist/protein/parse.d.ts +32 -0
  23. package/dist/protein/parse.d.ts.map +1 -0
  24. package/dist/protein/parse.js +387 -0
  25. package/dist/protein/parse.js.map +1 -0
  26. package/dist/protein/protein.d.ts +265 -0
  27. package/dist/protein/protein.d.ts.map +1 -0
  28. package/dist/protein/protein.js +645 -0
  29. package/dist/protein/protein.js.map +1 -0
  30. package/dist/protein/ribbon.d.ts +83 -0
  31. package/dist/protein/ribbon.d.ts.map +1 -0
  32. package/dist/protein/ribbon.js +468 -0
  33. package/dist/protein/ribbon.js.map +1 -0
  34. package/dist/protein/shared.d.ts +221 -0
  35. package/dist/protein/shared.d.ts.map +1 -0
  36. package/dist/protein/shared.js +478 -0
  37. package/dist/protein/shared.js.map +1 -0
  38. package/dist/protein/structure.d.ts +184 -0
  39. package/dist/protein/structure.d.ts.map +1 -0
  40. package/dist/protein/structure.js +324 -0
  41. package/dist/protein/structure.js.map +1 -0
  42. package/package.json +64 -3
  43. package/registry.json +22 -0
  44. package/src/index.ts +2 -0
  45. package/src/nodes.ts +18 -0
  46. package/src/protein/chemistry.ts +223 -0
  47. package/src/protein/index.ts +34 -0
  48. package/src/protein/parse.ts +427 -0
  49. package/src/protein/protein.ts +897 -0
  50. package/src/protein/ribbon.ts +658 -0
  51. package/src/protein/shared.ts +622 -0
  52. package/src/protein/structure.ts +491 -0
  53. package/README.md +0 -4
@@ -0,0 +1,208 @@
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
+ /** Ångströms. What an atom's electron cloud actually occupies. */
19
+ const VAN_DER_WAALS = {
20
+ H: 1.1,
21
+ C: 1.7,
22
+ N: 1.55,
23
+ O: 1.52,
24
+ F: 1.47,
25
+ NA: 2.27,
26
+ MG: 1.73,
27
+ P: 1.8,
28
+ S: 1.8,
29
+ CL: 1.75,
30
+ K: 2.75,
31
+ CA: 2.31,
32
+ MN: 2.05,
33
+ FE: 2.04,
34
+ CO: 2.0,
35
+ NI: 1.97,
36
+ CU: 1.96,
37
+ ZN: 2.01,
38
+ SE: 1.9,
39
+ BR: 1.85,
40
+ I: 1.98,
41
+ };
42
+ /** The fallback radius, carbon's — the element most of a protein is made of. */
43
+ const DEFAULT_VAN_DER_WAALS = 1.7;
44
+ /** Ångströms. Half of how far two of these sit apart when bonded. */
45
+ const COVALENT = {
46
+ H: 0.31,
47
+ C: 0.76,
48
+ N: 0.71,
49
+ O: 0.66,
50
+ F: 0.57,
51
+ NA: 1.66,
52
+ MG: 1.41,
53
+ P: 1.07,
54
+ S: 1.05,
55
+ CL: 1.02,
56
+ K: 2.03,
57
+ CA: 1.76,
58
+ MN: 1.39,
59
+ FE: 1.32,
60
+ CO: 1.26,
61
+ NI: 1.24,
62
+ CU: 1.32,
63
+ ZN: 1.22,
64
+ SE: 1.2,
65
+ BR: 1.2,
66
+ I: 1.39,
67
+ };
68
+ const DEFAULT_COVALENT = 0.77;
69
+ /**
70
+ * The CPK colours, as Jmol renders them.
71
+ *
72
+ * Only the elements a biological structure actually contains, plus the metals
73
+ * that turn up in cofactors. Anything else takes {@link UNKNOWN_ELEMENT_COLOR},
74
+ * which is deliberately garish — an unrecognised element should look like a
75
+ * question rather than blend into the carbons.
76
+ */
77
+ const CPK = {
78
+ H: "#ffffff",
79
+ C: "#909090",
80
+ N: "#3050f8",
81
+ O: "#ff0d0d",
82
+ F: "#90e050",
83
+ NA: "#ab5cf2",
84
+ MG: "#8aff00",
85
+ P: "#ff8000",
86
+ S: "#ffff30",
87
+ CL: "#1ff01f",
88
+ K: "#8f40d4",
89
+ CA: "#3dff00",
90
+ MN: "#9c7ac7",
91
+ FE: "#e06633",
92
+ CO: "#f090a0",
93
+ NI: "#50d050",
94
+ CU: "#c88033",
95
+ ZN: "#7d80b0",
96
+ SE: "#ffa100",
97
+ BR: "#a62929",
98
+ I: "#940094",
99
+ };
100
+ /** What an element nobody has a colour for is drawn in. */
101
+ export const UNKNOWN_ELEMENT_COLOR = "#ff1493";
102
+ /** An element's van der Waals radius in Ångströms. */
103
+ export function vanDerWaalsRadius(element) {
104
+ return VAN_DER_WAALS[element] ?? DEFAULT_VAN_DER_WAALS;
105
+ }
106
+ /** An element's covalent radius in Ångströms — half of a bond it makes. */
107
+ export function covalentRadius(element) {
108
+ return COVALENT[element] ?? DEFAULT_COVALENT;
109
+ }
110
+ /** The CPK colour for an element, as a `#rrggbb` string. */
111
+ export function elementColor(element) {
112
+ return CPK[element] ?? UNKNOWN_ELEMENT_COLOR;
113
+ }
114
+ /**
115
+ * The standard amino acids, plus the two modified residues common enough that
116
+ * treating them as foreign would put a hole in the middle of a chain:
117
+ * selenomethionine (`MSE`, how a crystallographer phases a new structure) and
118
+ * pyroglutamate (`PCA`).
119
+ *
120
+ * Both arrive as `HETATM` records, which is exactly why the set is consulted
121
+ * rather than the record type — "is this part of the chain" is a question about
122
+ * the residue, and the file answers a different one.
123
+ */
124
+ const AMINO_ACIDS = new Set([
125
+ "ALA", "ARG", "ASN", "ASP", "CYS", "GLN", "GLU", "GLY", "HIS", "ILE",
126
+ "LEU", "LYS", "MET", "PHE", "PRO", "SER", "THR", "TRP", "TYR", "VAL",
127
+ "MSE", "PCA", "SEC", "PYL",
128
+ ]);
129
+ /** The nucleotides, RNA and DNA. */
130
+ const NUCLEOTIDES = new Set([
131
+ "A", "C", "G", "U", "I",
132
+ "DA", "DC", "DG", "DT", "DI",
133
+ ]);
134
+ /**
135
+ * The waters. Named separately from the rest of the hetero atoms because they
136
+ * are the one kind a viewer hides by default: a crystal structure carries
137
+ * hundreds of them, they are chemically uninteresting to the picture being
138
+ * drawn, and left on they bury the molecule in red dots.
139
+ */
140
+ const WATERS = new Set(["HOH", "DOD", "WAT", "H2O", "TIP"]);
141
+ /** Classifies a residue by its three-letter code. */
142
+ export function residueKind(name) {
143
+ const code = name.trim().toUpperCase();
144
+ if (AMINO_ACIDS.has(code))
145
+ return "amino";
146
+ if (NUCLEOTIDES.has(code))
147
+ return "nucleic";
148
+ if (WATERS.has(code))
149
+ return "water";
150
+ return "other";
151
+ }
152
+ /**
153
+ * The atom a residue's backbone trace passes through: the α-carbon of an amino
154
+ * acid, the phosphorus of a nucleotide.
155
+ *
156
+ * One atom per residue rather than the whole backbone, because what the trace is
157
+ * *for* is the smooth curve a ribbon is swept along, and the N–CA–C zig-zag
158
+ * would put a kink at every residue. Every molecular viewer traces α-carbons for
159
+ * the same reason.
160
+ */
161
+ export function traceAtomName(kind) {
162
+ if (kind === "amino")
163
+ return "CA";
164
+ if (kind === "nucleic")
165
+ return "P";
166
+ return null;
167
+ }
168
+ /**
169
+ * The element an atom belongs to, from the file's element column when it has
170
+ * one and from the atom's *name* when it doesn't.
171
+ *
172
+ * The fallback matters more than it looks: the element column is right-justified
173
+ * in columns 77–78 of a PDB record and plenty of older files, and most files
174
+ * written by hand, simply stop before it. The name is then all there is, and
175
+ * reading it is not quite trimming the digits off — an amino acid's `CA` is a
176
+ * carbon and an ion's `CA` is calcium, and the two are told apart only by what
177
+ * residue they sit in. Hence {@link kind}.
178
+ */
179
+ export function elementOf(declared, atomName, kind) {
180
+ const stated = declared.trim().toUpperCase();
181
+ if (stated !== "")
182
+ return stated;
183
+ // PDB atom names are padded so that the element starts in column 14 for a
184
+ // two-letter element and column 15 for a one-letter one — but only in files
185
+ // that respect the alignment, which is why this reads the characters instead:
186
+ // strip the leading digit an alternate hydrogen carries (`1HB`), then take the
187
+ // letters.
188
+ const name = atomName.trim().toUpperCase().replace(/^\d+/, "");
189
+ const letters = name.replace(/[^A-Z]/g, "");
190
+ if (letters === "")
191
+ return "";
192
+ // Inside a polymer residue the only two-letter elements are the ones that
193
+ // can't be confused: everything else is C, N, O, S or P followed by the
194
+ // position letters that name the atom (`CB`, `NZ`, `OG1`). A `CA` there is the
195
+ // α-carbon, never calcium.
196
+ if (kind === "amino" || kind === "nucleic") {
197
+ if (letters.startsWith("SE"))
198
+ return "SE";
199
+ return letters.slice(0, 1);
200
+ }
201
+ // Outside one — a ligand, an ion, a cofactor — a two-letter element is real,
202
+ // so prefer the longest match the tables know before falling back to one.
203
+ const pair = letters.slice(0, 2);
204
+ if (pair in COVALENT)
205
+ return pair;
206
+ return letters.slice(0, 1);
207
+ }
208
+ //# sourceMappingURL=chemistry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chemistry.js","sourceRoot":"","sources":["../../src/protein/chemistry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The chemistry the {@link Protein} node draws from: how big an atom is, what\n * colour it conventionally takes, what counts as a bond, and which residues are\n * part of a polymer chain rather than sitting beside it.\n *\n * Tables rather than a dependency, for the same reason `graph3d/expression.ts`\n * is a parser rather than a maths library: this package has to stay loadable in\n * a bare Node process with no DOM, and what a molecular-graphics library would\n * bring with it — a renderer, a fetch layer, a DOM — is everything the scene\n * builder was split apart to avoid.\n *\n * The numbers are the standard ones. Van der Waals radii are Bondi's, covalent\n * radii are Cordero's, and the colours are the CPK scheme every molecular viewer\n * has used since Corey and Pauling built theirs out of painted wood — which is\n * the point of using them: a biologist reads oxygen as red before they have\n * read the legend.\n */\n\n/** Ångströms. What an atom's electron cloud actually occupies. */\nconst VAN_DER_WAALS: Readonly<Record<string, number>> = {\n H: 1.1,\n C: 1.7,\n N: 1.55,\n O: 1.52,\n F: 1.47,\n NA: 2.27,\n MG: 1.73,\n P: 1.8,\n S: 1.8,\n CL: 1.75,\n K: 2.75,\n CA: 2.31,\n MN: 2.05,\n FE: 2.04,\n CO: 2.0,\n NI: 1.97,\n CU: 1.96,\n ZN: 2.01,\n SE: 1.9,\n BR: 1.85,\n I: 1.98,\n}\n\n/** The fallback radius, carbon's — the element most of a protein is made of. */\nconst DEFAULT_VAN_DER_WAALS = 1.7\n\n/** Ångströms. Half of how far two of these sit apart when bonded. */\nconst COVALENT: Readonly<Record<string, number>> = {\n H: 0.31,\n C: 0.76,\n N: 0.71,\n O: 0.66,\n F: 0.57,\n NA: 1.66,\n MG: 1.41,\n P: 1.07,\n S: 1.05,\n CL: 1.02,\n K: 2.03,\n CA: 1.76,\n MN: 1.39,\n FE: 1.32,\n CO: 1.26,\n NI: 1.24,\n CU: 1.32,\n ZN: 1.22,\n SE: 1.2,\n BR: 1.2,\n I: 1.39,\n}\n\nconst DEFAULT_COVALENT = 0.77\n\n/**\n * The CPK colours, as Jmol renders them.\n *\n * Only the elements a biological structure actually contains, plus the metals\n * that turn up in cofactors. Anything else takes {@link UNKNOWN_ELEMENT_COLOR},\n * which is deliberately garish — an unrecognised element should look like a\n * question rather than blend into the carbons.\n */\nconst CPK: Readonly<Record<string, string>> = {\n H: \"#ffffff\",\n C: \"#909090\",\n N: \"#3050f8\",\n O: \"#ff0d0d\",\n F: \"#90e050\",\n NA: \"#ab5cf2\",\n MG: \"#8aff00\",\n P: \"#ff8000\",\n S: \"#ffff30\",\n CL: \"#1ff01f\",\n K: \"#8f40d4\",\n CA: \"#3dff00\",\n MN: \"#9c7ac7\",\n FE: \"#e06633\",\n CO: \"#f090a0\",\n NI: \"#50d050\",\n CU: \"#c88033\",\n ZN: \"#7d80b0\",\n SE: \"#ffa100\",\n BR: \"#a62929\",\n I: \"#940094\",\n}\n\n/** What an element nobody has a colour for is drawn in. */\nexport const UNKNOWN_ELEMENT_COLOR = \"#ff1493\"\n\n/** An element's van der Waals radius in Ångströms. */\nexport function vanDerWaalsRadius(element: string): number {\n return VAN_DER_WAALS[element] ?? DEFAULT_VAN_DER_WAALS\n}\n\n/** An element's covalent radius in Ångströms — half of a bond it makes. */\nexport function covalentRadius(element: string): number {\n return COVALENT[element] ?? DEFAULT_COVALENT\n}\n\n/** The CPK colour for an element, as a `#rrggbb` string. */\nexport function elementColor(element: string): string {\n return CPK[element] ?? UNKNOWN_ELEMENT_COLOR\n}\n\n/**\n * The standard amino acids, plus the two modified residues common enough that\n * treating them as foreign would put a hole in the middle of a chain:\n * selenomethionine (`MSE`, how a crystallographer phases a new structure) and\n * pyroglutamate (`PCA`).\n *\n * Both arrive as `HETATM` records, which is exactly why the set is consulted\n * rather than the record type — \"is this part of the chain\" is a question about\n * the residue, and the file answers a different one.\n */\nconst AMINO_ACIDS = new Set([\n \"ALA\", \"ARG\", \"ASN\", \"ASP\", \"CYS\", \"GLN\", \"GLU\", \"GLY\", \"HIS\", \"ILE\",\n \"LEU\", \"LYS\", \"MET\", \"PHE\", \"PRO\", \"SER\", \"THR\", \"TRP\", \"TYR\", \"VAL\",\n \"MSE\", \"PCA\", \"SEC\", \"PYL\",\n])\n\n/** The nucleotides, RNA and DNA. */\nconst NUCLEOTIDES = new Set([\n \"A\", \"C\", \"G\", \"U\", \"I\",\n \"DA\", \"DC\", \"DG\", \"DT\", \"DI\",\n])\n\n/**\n * The waters. Named separately from the rest of the hetero atoms because they\n * are the one kind a viewer hides by default: a crystal structure carries\n * hundreds of them, they are chemically uninteresting to the picture being\n * drawn, and left on they bury the molecule in red dots.\n */\nconst WATERS = new Set([\"HOH\", \"DOD\", \"WAT\", \"H2O\", \"TIP\"])\n\n/** What a residue is, which decides whether it joins a backbone trace. */\nexport type ResidueKind = \"amino\" | \"nucleic\" | \"water\" | \"other\"\n\n/** Classifies a residue by its three-letter code. */\nexport function residueKind(name: string): ResidueKind {\n const code = name.trim().toUpperCase()\n if (AMINO_ACIDS.has(code)) return \"amino\"\n if (NUCLEOTIDES.has(code)) return \"nucleic\"\n if (WATERS.has(code)) return \"water\"\n return \"other\"\n}\n\n/**\n * The atom a residue's backbone trace passes through: the α-carbon of an amino\n * acid, the phosphorus of a nucleotide.\n *\n * One atom per residue rather than the whole backbone, because what the trace is\n * *for* is the smooth curve a ribbon is swept along, and the N–CA–C zig-zag\n * would put a kink at every residue. Every molecular viewer traces α-carbons for\n * the same reason.\n */\nexport function traceAtomName(kind: ResidueKind): string | null {\n if (kind === \"amino\") return \"CA\"\n if (kind === \"nucleic\") return \"P\"\n return null\n}\n\n/**\n * The element an atom belongs to, from the file's element column when it has\n * one and from the atom's *name* when it doesn't.\n *\n * The fallback matters more than it looks: the element column is right-justified\n * in columns 77–78 of a PDB record and plenty of older files, and most files\n * written by hand, simply stop before it. The name is then all there is, and\n * reading it is not quite trimming the digits off — an amino acid's `CA` is a\n * carbon and an ion's `CA` is calcium, and the two are told apart only by what\n * residue they sit in. Hence {@link kind}.\n */\nexport function elementOf(\n declared: string,\n atomName: string,\n kind: ResidueKind\n): string {\n const stated = declared.trim().toUpperCase()\n if (stated !== \"\") return stated\n\n // PDB atom names are padded so that the element starts in column 14 for a\n // two-letter element and column 15 for a one-letter one — but only in files\n // that respect the alignment, which is why this reads the characters instead:\n // strip the leading digit an alternate hydrogen carries (`1HB`), then take the\n // letters.\n const name = atomName.trim().toUpperCase().replace(/^\\d+/, \"\")\n const letters = name.replace(/[^A-Z]/g, \"\")\n if (letters === \"\") return \"\"\n\n // Inside a polymer residue the only two-letter elements are the ones that\n // can't be confused: everything else is C, N, O, S or P followed by the\n // position letters that name the atom (`CB`, `NZ`, `OG1`). A `CA` there is the\n // α-carbon, never calcium.\n if (kind === \"amino\" || kind === \"nucleic\") {\n if (letters.startsWith(\"SE\")) return \"SE\"\n return letters.slice(0, 1)\n }\n\n // Outside one — a ligand, an ion, a cofactor — a two-letter element is real,\n // so prefer the longest match the tables know before falling back to one.\n const pair = letters.slice(0, 2)\n if (pair in COVALENT) return pair\n return letters.slice(0, 1)\n}\n"]}
@@ -0,0 +1,35 @@
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.js";
30
+ export * from "./parse.js";
31
+ export * from "./protein.js";
32
+ export * from "./ribbon.js";
33
+ export * from "./shared.js";
34
+ export * from "./structure.js";
35
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/protein/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC"}
@@ -0,0 +1,35 @@
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.js";
30
+ export * from "./parse.js";
31
+ export * from "./protein.js";
32
+ export * from "./ribbon.js";
33
+ export * from "./shared.js";
34
+ export * from "./structure.js";
35
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/protein/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC","sourcesContent":["/**\n * The Protein node — a **native** node type (see `node-types.ts` /\n * `NODE_CLASS_BY_KEY`) like the chart family and the 3D graph beside it: built,\n * themed and animated from the inspector rather than loaded as a code package.\n *\n * It draws a molecular structure — a protein, a nucleic acid, a complex of both\n * with whatever is bound to them — in one of four representations, from a\n * coordinate file the Protein Data Bank publishes.\n *\n * Three things about it are worth knowing before reading the parts:\n *\n * **The file is parsed outside the render.** `buildScene` is synchronous and a\n * coordinate file arrives over a network, so the fetch and the parse happen\n * ahead of the build and what reaches the node is a finished\n * {@link ProteinStructure} — the same arrangement a chart has with its rows.\n * That is why `parse.ts` is exported: the app's structure store is its caller,\n * not this node.\n *\n * **The chemistry is a table, not a dependency.** Radii, colours and what counts\n * as a bond live in `chemistry.ts` for the same reason `graph3d/expression.ts`\n * is a parser rather than a maths library: this package has to stay loadable in\n * a bare Node process, and a molecular-graphics library brings a renderer, a\n * fetch layer and a DOM with it.\n *\n * **There is no pointer.** The camera is three tweenable numbers rather than a\n * drag, because the studio renders a timeline and every frame has to be\n * reproducible under scrubbing and export. See {@link Protein}.\n */\nexport * from \"./chemistry\";\nexport * from \"./parse\";\nexport * from \"./protein\";\nexport * from \"./ribbon\";\nexport * from \"./shared\";\nexport * from \"./structure\";\n"]}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Reads a coordinate file into a {@link ProteinStructure}.
3
+ *
4
+ * Two formats, because the Protein Data Bank serves two and which one an entry
5
+ * has is not the author's choice: the legacy **PDB** format numbers atoms in
6
+ * five columns and names chains in one, so an entry that outgrew either — a
7
+ * ribosome, a capsid — exists only as **mmCIF**. Refusing the second would mean
8
+ * refusing exactly the structures most worth looking at.
9
+ *
10
+ * Both are read the same way: pull out an atom list and whatever secondary
11
+ * structure the file annotates, then hand both to `deriveStructure`, which does
12
+ * the geometry. {@link parseStructure} sniffs which is which, so a caller
13
+ * holding a downloaded file never has to.
14
+ *
15
+ * **The first model only.** An NMR ensemble is twenty superposed conformations
16
+ * of the same molecule; drawing all of them at once produces a blur, and
17
+ * choosing between them is a question this node does not ask. The first is the
18
+ * conventional representative.
19
+ */
20
+ import { type ProteinStructure } from "./structure.js";
21
+ /**
22
+ * Reads a coordinate file, detecting its format from the contents.
23
+ *
24
+ * `id` labels the result — an accession, or the asset's name. Anything that
25
+ * can't be read at all comes back as {@link EMPTY_STRUCTURE} rather than
26
+ * throwing: this runs against a file someone just uploaded or an accession
27
+ * someone is halfway through typing, and both of those are ordinary states
28
+ * rather than errors. An empty structure draws nothing, which is the honest
29
+ * picture of a file with no atoms in it.
30
+ */
31
+ export declare function parseStructure(text: string, id?: string): ProteinStructure;
32
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/protein/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAIL,KAAK,gBAAgB,EAEtB,MAAM,aAAa,CAAA;AAEpB;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAK,GAAG,gBAAgB,CAKtE"}