@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protein.js","sourceRoot":"","sources":["../../src/protein/protein.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n command,\n driveCommand,\n easeInOut,\n node,\n type Command,\n type CommandArgs,\n Canvas3D,\n Geo,\n Graphics3D,\n Mat,\n Scene3D,\n property,\n type BufferGeometry3D,\n type Canvas3DProps,\n type Color,\n type NodeConfig,\n type Transform3D,\n type Vector3,\n} from \"@motionscript/core\"\n\nimport {\n lerpColor3D,\n cameraOrbit,\n resolveColor3D,\n snapValue,\n orbitProps,\n type OrbitTarget,\n} from \"@motionscript/core/component\"\nimport {\n atomColors,\n atomRadius,\n backboneRuns,\n DEFAULT_PROTEIN_PALETTE,\n hexOf,\n lerpHexColor,\n stickPlacement,\n traceColors,\n type ProteinColorScheme,\n type ProteinPalette,\n type ProteinRepresentation,\n} from \"./shared\"\nimport { cartoonGeometry, cartoonSpans } from \"./ribbon\"\nimport {\n EMPTY_STRUCTURE,\n type ProteinAtom,\n type ProteinStructure,\n} from \"./structure\"\n\nexport interface ProteinProps extends Canvas3DProps {\n /**\n * The molecule to draw, already parsed.\n *\n * A value rather than a source, and that is the whole arrangement: reading a\n * coordinate file means a network round-trip or a file read, and this node is\n * constructed inside a synchronous render pass. So the fetching and the\n * parsing happen *before* the build — see `parseStructure` and the app's\n * structure store — and what arrives here is finished data. It is the same\n * bargain a chart makes with its rows.\n */\n structure: ProteinStructure\n /** How the molecule is drawn. See {@link ProteinRepresentation}. */\n representation: ProteinRepresentation\n /** What decides an atom's colour. See {@link ProteinColorScheme}. */\n colorScheme: ProteinColorScheme\n /** The colour the `uniform` scheme paints, and every scheme's fallback. */\n color: Color\n /**\n * Every colour the *other* schemes paint from — see {@link ProteinPalette},\n * which is the shape these are read as rather than a prop of its own.\n *\n * Thirteen props rather than one palette because a prop is a whole value: a\n * single one would make a `to` that reddens the helices a statement about the\n * eight chain colours too, and the ones the author never touched would snap.\n * Each of these carries over and tweens on its own, like every other colour in\n * the app.\n */\n helixColor: string\n sheetColor: string\n coilColor: string\n /** The two ends of the N→C ramp, swept through HSL between them. */\n residueStartColor: string\n residueEndColor: string\n /** One per chain, cycling past the eighth. */\n chainColor1: string\n chainColor2: string\n chainColor3: string\n chainColor4: string\n chainColor5: string\n chainColor6: string\n chainColor7: string\n chainColor8: string\n /**\n * Atom size as a fraction of the true van der Waals radius.\n *\n * 1 is the space-filling model — atoms at the size they actually are, touching\n * their neighbours. Ball-and-stick shrinks them further on its own, so this\n * stays \"how big are the atoms\" in both rather than meaning something\n * different in each.\n */\n atomScale: number\n /** Bond stick radius, in Ångströms. */\n bondRadius: number\n /** Backbone tube radius, in Ångströms. A ribbon swells past this on a helix. */\n ribbonRadius: number\n /** Whether to draw ligands, ions and cofactors — everything that isn't polymer. */\n ligands: boolean\n /** Whether to draw crystallographic waters. Off, because there are hundreds. */\n waters: boolean\n /** Mesh detail: segments around a sphere or a stick. */\n quality: number\n /** Camera orbit about the vertical axis, in **degrees**. Animate this to spin. */\n orbit: number\n /** Camera elevation above the horizon, in **degrees**. */\n elevation: number\n /**\n * How far back the camera sits, as a multiple of the molecule's own radius.\n *\n * A multiple rather than a distance, because the things this node draws differ\n * in size by two orders of magnitude — crambin is 12 Å across and a ribosome\n * is 300 — and a stored distance that framed one would put the other off\n * screen or inside the camera. As a ratio the same number frames both, and it\n * goes on meaning the same thing when the structure is swapped.\n */\n zoom: number\n /** Strength of the ambient fill. */\n ambientIntensity: number\n /** Colour of the ambient fill. White leaves the molecule's own colours alone. */\n ambientColor: Color\n /** Strength of the key light; the rim light is derived from it. */\n keyIntensity: number\n /** Colour of the key light, and of the rim derived from it. */\n keyColor: Color\n}\n\n/** {@link ProteinProps.zoom}, held inside what can actually be framed. */\nconst MIN_ZOOM = 0.2\nconst MAX_ZOOM = 12\nconst DEFAULT_ZOOM = 2.6\n\n/**\n * How much of its true size an atom is drawn at in ball-and-stick, on top of\n * whatever {@link ProteinProps.atomScale} says.\n *\n * Ball-and-stick is not a smaller space-filling model — it is a different claim\n * about what matters. Atoms at a quarter of their radius stop touching, which is\n * what makes room for the bonds that are the whole point of the representation.\n */\nconst BALL_SCALE = 0.28\n\n/**\n * A molecular structure viewer.\n *\n * Extends {@link Canvas3D} — as `Graph3D` does — so this class only has to say\n * *what* to draw, and the bridge from the 2D scene graph into the 3D renderer is\n * the base class's business. It overrides `buildScene3D`, the single seam both\n * the real render and the asset declaration pass go through.\n *\n * <Protein width=\"fill\" height=\"fill\"\n * structure={parseStructure(text, \"6LU7\")}\n * representation=\"ribbon\" colorScheme=\"secondary\" />\n *\n * **There is no pointer here.** Every other molecular viewer is driven by\n * dragging the molecule around, and this one cannot be: the studio renders a\n * timeline, so the camera has to be something a `to` command can tween and a\n * scrub can reproduce exactly — frame 300 must be identical whether it was\n * reached by playing forward or by dragging the playhead backwards. So the\n * camera is three tweenable numbers (`orbit`, `elevation`, `zoom`), and spinning\n * the molecule is a command rather than a gesture.\n *\n * Atoms and bonds are drawn **instanced**: one sphere geometry and one cylinder\n * geometry, placed thousands of times in a single draw call each. A mesh per\n * atom would be tens of thousands of draw calls a frame, which no renderer\n * survives — and the instance lists themselves are cached (see {@link built}),\n * because they don't change when the camera moves and the camera is what\n * normally moves.\n */\n@node({\n key: \"protein\",\n parentKey: \"node\",\n forkable: true,\n layout: {\n children: \"freeform\",\n acceptsChildren: false,\n defaultWidthMode: \"fixed\",\n defaultHeightMode: \"fixed\",\n },\n seed: {\n width: 720,\n height: 560,\n },\n})\nexport class Protein extends Canvas3D<ProteinProps> {\n /**\n * The molecule.\n *\n * Snaps rather than interpolating, and every other non-scalar prop here\n * interpolates. There is no halfway point between two different molecules —\n * atom 4,000 of one is not the same atom as atom 4,000 of the other, and\n * blending their coordinates would produce a cloud that is neither. Swapping\n * the structure is a change of *subject*, so it lands at the end of a tween\n * the way a change of text does.\n */\n @property({ default: EMPTY_STRUCTURE, tween: snapValue })\n declare structure: ProteinStructure\n\n @property({ default: \"ribbon\", tween: snapValue })\n declare representation: ProteinRepresentation\n\n @property({ default: \"secondary\", tween: snapValue })\n declare colorScheme: ProteinColorScheme\n\n @property({ default: \"#4f9dff\", mapper: resolveColor3D, tween: lerpColor3D })\n declare color: Color\n\n /**\n * The palette, thirteen props of it — see {@link ProteinProps.helixColor} for\n * why it is thirteen and not one.\n *\n * No `mapper`, unlike the three `Color` props around them: these stay hex\n * strings because a hex string is what an instance tint is handed, and\n * {@link lerpHexColor} is the tween that goes with that. The defaults are\n * written out rather than read off {@link DEFAULT_PROTEIN_PALETTE} because a\n * decorator default has to be a literal the class carries; the two are pinned\n * against each other by a test.\n */\n @property({ default: \"#f0605d\", tween: lerpHexColor })\n declare helixColor: string\n\n @property({ default: \"#f5c451\", tween: lerpHexColor })\n declare sheetColor: string\n\n @property({ default: \"#c8ccd4\", tween: lerpHexColor })\n declare coilColor: string\n\n @property({\n default: DEFAULT_PROTEIN_PALETTE.residueStart,\n tween: lerpHexColor,\n })\n declare residueStartColor: string\n\n @property({\n default: DEFAULT_PROTEIN_PALETTE.residueEnd,\n tween: lerpHexColor,\n })\n declare residueEndColor: string\n\n @property({ default: \"#4f9dff\", tween: lerpHexColor })\n declare chainColor1: string\n\n @property({ default: \"#ff8a3d\", tween: lerpHexColor })\n declare chainColor2: string\n\n @property({ default: \"#4ddb9a\", tween: lerpHexColor })\n declare chainColor3: string\n\n @property({ default: \"#e05fd0\", tween: lerpHexColor })\n declare chainColor4: string\n\n @property({ default: \"#ffd166\", tween: lerpHexColor })\n declare chainColor5: string\n\n @property({ default: \"#8b7bff\", tween: lerpHexColor })\n declare chainColor6: string\n\n @property({ default: \"#ff6b6b\", tween: lerpHexColor })\n declare chainColor7: string\n\n @property({ default: \"#3fd0d6\", tween: lerpHexColor })\n declare chainColor8: string\n\n @property({ default: 1 }) declare atomScale: number\n @property({ default: 0.18 }) declare bondRadius: number\n @property({ default: 0.45 }) declare ribbonRadius: number\n\n @property({ default: true, tween: snapValue }) declare ligands: boolean\n @property({ default: false, tween: snapValue }) declare waters: boolean\n\n /**\n * Structural, like `Graph3D.segments`: a geometry is immutable, so every\n * intermediate value of a tween reallocates the sphere and the cylinder. Set\n * once; the appearance schema marks it non-animatable to say the same thing\n * from the other side.\n */\n @property({ default: 12 }) declare quality: number\n\n @property({ default: 35 }) declare orbit: number\n @property({ default: 18 }) declare elevation: number\n @property({ default: DEFAULT_ZOOM }) declare zoom: number\n\n /**\n * The default rig, and only the default rig — the same four controls the 3D\n * viewport carries, in the same order and with the same defaults, because\n * \"how is this lit\" is one question and a molecule is not a special case of\n * it. Each light is a **strength and a colour**, in that order: how much\n * light, and what colour it is.\n *\n * Both colours default to white rather than to a tint, so a rig nobody has\n * touched grades nothing. A coloured default would be a look applied to every\n * structure in every scene by a control the author never opened.\n */\n @property({ default: 0.55 }) declare ambientIntensity: number\n\n @property({ default: \"#ffffff\", mapper: resolveColor3D, tween: lerpColor3D })\n declare ambientColor: Color\n\n @property({ default: 1.6 }) declare keyIntensity: number\n\n @property({ default: \"#ffffff\", mapper: resolveColor3D, tween: lerpColor3D })\n declare keyColor: Color\n\n constructor(props?: NodeConfig<Protein, ProteinProps>) {\n super(props as NodeConfig<Canvas3D<ProteinProps>, ProteinProps>)\n }\n\n /**\n * Frame the scene at a spherical camera placement. Every axis is optional,\n * so \"pull back\" and \"spin round\" stay separate intentions — see\n * {@link OrbitTarget}.\n */\n @command({\n args: [\n {\n key: \"target\", kind: \"orbit\", properties: {\n orbit: { kind: \"number\", step: 1, unit: \"°\" },\n elevation: { kind: \"number\", min: -89, max: 89, step: 1, unit: \"°\" },\n zoom: { kind: \"number\", min: 0.2, max: 24, step: 0.1, unit: \"×\" },\n },\n },\n ],\n })\n orbitTo(args: CommandArgs<{ target: OrbitTarget }> & { duration: number }): Command<ProteinProps> {\n return this.to({\n data: orbitProps(args.data?.target ?? {}) as Partial<ProteinProps>,\n duration: args.duration,\n easing: args.easing ?? easeInOut(),\n })\n }\n\n /**\n * Drift the camera around the molecule for the command's duration — a\n * continuous orbit at `speed` with a lateral `sway` and a `zoom` pulse\n * (`breathe`), both on a shared `cycle` so they don't beat against each\n * other. `settle` eases in from wherever the camera already is, so `float`\n * after an `orbitTo` does not snap.\n */\n @command({\n args: [\n { key: \"speed\", kind: \"number\", default: 36, min: -360, max: 360, step: 1, unit: \"°/s\" },\n { key: \"elevation\", kind: \"number\", default: 18, min: -89, max: 89, step: 1, unit: \"°\" },\n { key: \"zoom\", kind: \"number\", default: 2.6, min: 0.2, max: 12, step: 0.1, unit: \"×\" },\n { key: \"sway\", kind: \"number\", default: 6, min: 0, max: 45, step: 1, unit: \"°\" },\n { key: \"breathe\", kind: \"number\", default: 0.08, min: 0, max: 0.6, step: 0.01, scale: 100, unit: \"%\" },\n { key: \"cycle\", kind: \"number\", default: 8, min: 0.5, max: 60, step: 0.5, unit: \"s\" },\n { key: \"settle\", kind: \"number\", default: 1, min: 0, max: 20, step: 0.1, unit: \"s\" },\n ],\n })\n float(args: CommandArgs<{\n speed?: number\n elevation?: number\n zoom?: number\n sway?: number\n breathe?: number\n cycle?: number\n settle?: number\n }> & { duration?: number }): Command<ProteinProps> {\n const {\n speed = 36,\n elevation = 18,\n zoom = 2.6,\n sway = 6,\n breathe = 0.08,\n cycle = 8,\n settle = 1,\n } = args.data ?? {}\n const duration = args.duration ?? 8\n const easing = args.easing ?? easeInOut()\n\n const fromOrbit = this.orbit\n const fromElevation = this.elevation\n const fromZoom = this.zoom\n const period = Math.max(cycle, 0.0001)\n\n return driveCommand(duration, (t) => {\n const seconds = t * duration\n // Ease from the camera's current placement into the drift over `settle`\n // seconds. A zero `settle` starts the drift immediately.\n const blend = settle <= 0 ? 1 : easing(Math.min(1, seconds / settle))\n const phase = (seconds / period) * Math.PI * 2\n\n const driftOrbit = fromOrbit + speed * seconds + Math.sin(phase) * sway\n const driftZoom = zoom * (1 + Math.sin(phase) * breathe)\n\n this.set({\n orbit: fromOrbit + (driftOrbit - fromOrbit) * blend,\n elevation: fromElevation + (elevation - fromElevation) * blend,\n zoom: fromZoom + (driftZoom - fromZoom) * blend,\n } as Partial<ProteinProps>)\n }) as Command<ProteinProps>\n }\n\n /**\n * The thirteen colour props, as the one value the colourers take.\n *\n * Rebuilt per call rather than cached: it is thirteen property reads behind\n * `built()`'s own cache, which is what stops it happening per frame.\n */\n private paletteOf(): ProteinPalette {\n return {\n helix: this.helixColor,\n sheet: this.sheetColor,\n coil: this.coilColor,\n residueStart: this.residueStartColor,\n residueEnd: this.residueEndColor,\n chains: [\n this.chainColor1,\n this.chainColor2,\n this.chainColor3,\n this.chainColor4,\n this.chainColor5,\n this.chainColor6,\n this.chainColor7,\n this.chainColor8,\n ],\n }\n }\n\n // ---- Drawing ------------------------------------------------------------\n\n protected override buildScene3D(): Scene3D {\n const scene = new Scene3D()\n const structure = this.structure\n // Framed on what is actually **drawn**, not on the whole file. A crystal\n // structure carries hundreds of ordered waters scattered well beyond the\n // molecule, and they are hidden by default — framing the sphere that holds\n // them would park the camera 40% further back than the picture needs, which\n // reads as a molecule adrift in an empty box.\n const radius = this.built().radius\n const distance = radius * clampZoom(this.zoom)\n\n // The clipping planes are cut to the molecule rather than fixed, because\n // \"far enough away to see all of it\" differs a hundredfold between a peptide\n // and a virus capsid — and a depth buffer stretched over a range it doesn't\n // need spends its precision on empty space, which shows up as z-fighting\n // between atoms that are genuinely an Ångström apart.\n // Kept explicit rather than left to the camera's own derivation: that sizes\n // the planes to the scene's bounding box, and a molecule's *atoms* are what\n // need the precision here, not its extent.\n //\n // Polar placement is the camera's vocabulary now, so the three props pass\n // straight through; `cameraOrbit` is the quarter turn between the stored\n // zero and the camera's.\n scene.perspective({\n fov: 45,\n near: Math.max(0.1, radius * 0.02),\n far: distance + radius * 4,\n orbit: cameraOrbit(this.orbit),\n elevation: this.elevation,\n distance,\n })\n\n // The backdrop is the node's own **Fills** section — a `Canvas3D`\n // composites its 3D pass over its fill layers, so a paint stack behind a\n // molecule is not merely possible but strictly better than one colour, and\n // an empty stack is what transparency already means everywhere else in the\n // app. There is no 3D background pass to reach for and no clear colour to\n // set; see `resolveViewport3DFills`, which carries the colour this node\n // used to hold in its appearance bag across to that stack.\n\n // A key light, a weaker rim from the opposite side, and enough ambient that\n // an atom facing away is still an atom rather than a silhouette. Placed\n // relative to the molecule so the lighting holds at any size — a light's\n // parameters and its placement are separate arguments, because placement is\n // a transform rather than a property of the lamp.\n //\n // The rim takes the key's colour as well as a fraction of its strength: it\n // is the key seen from the other side rather than a light of its own, which\n // is why there are four controls and not six. The same rig, and the same\n // argument, as the 3D viewport's — see `Canvas3DView.buildScene3D`.\n scene\n .light({\n type: \"ambient\",\n intensity: this.ambientIntensity,\n color: this.ambientColor,\n })\n .light(\n {\n type: \"directional\",\n intensity: this.keyIntensity,\n color: this.keyColor,\n },\n { position: [radius, radius * 2, radius * 2] }\n )\n .light(\n {\n type: \"directional\",\n intensity: this.keyIntensity * 0.4,\n color: this.keyColor,\n },\n { position: [-radius * 2, -radius, -radius * 1.5] }\n )\n\n // Everything is drawn inside one group that puts the molecule's centroid on\n // the origin, so the camera — which always looks at the origin — frames it\n // wherever the crystallographer's coordinate system happened to put it. PDB\n // coordinates are in the crystal's frame and routinely sit hundreds of\n // Ångströms from zero.\n //\n // `begin`/`end` is what a `Graphics3D.group` was: a transform pushed over\n // everything drawn between them. It lives on the scene now rather than on\n // the graphics, because hierarchy is a property of the scene.\n const g3 = new Graphics3D()\n this.addMolecule(g3)\n\n const { center } = structure\n scene.begin({\n id: \"molecule\",\n transform: { position: [-center.x, -center.y, -center.z] },\n })\n scene.draw(g3)\n scene.end()\n\n return scene\n }\n\n /** The molecule itself, in whichever representation is showing. */\n private addMolecule(g3: Graphics3D): void {\n const built = this.built()\n\n this.addSpheres(g3, built.spheres, built.sphereTints, \"atoms\")\n this.addSticks(g3, built.sticks, built.stickTints, \"bonds\")\n\n for (const run of built.runs) {\n g3.mesh(\n Geo.tube({\n points: run.points,\n radius: run.radius,\n // `[along the curve, around it]`. Segments along the curve are\n // proportional to the run's own length, so the sweep follows it rather\n // than cutting corners across a tight turn — and a short run doesn't\n // pay for a long one's tessellation.\n segments: [Math.max(6, run.points.length * 4), this.stickQuality()],\n }),\n Mat.standard({ color: run.color, roughness: 0.45, metalness: 0.05 })\n )\n }\n\n for (const ribbon of built.ribbons) {\n g3.mesh(\n ribbon,\n // `vertexColors`, where every other material here takes a `color`. A\n // cartoon span is one mesh covering many residues and the colouring is\n // per residue, so the colour varies *within* the primitive — which is\n // the one thing a material's uniform colour cannot express. Safe in a\n // way it would not be on the instanced spheres (see {@link addSpheres}):\n // this geometry genuinely carries a `color` attribute.\n Mat.standard({\n vertexColors: true,\n roughness: 0.45,\n metalness: 0.05,\n })\n )\n }\n }\n\n /** One instanced sphere op, skipped entirely when there is nothing in it. */\n private addSpheres(\n g3: Graphics3D,\n placements: Transform3D[],\n tints: string[],\n key: string\n ): void {\n if (placements.length === 0) return\n const quality = this.sphereQuality()\n g3.instances(\n // A unit sphere, sized per instance. One geometry for the whole molecule:\n // the renderer dedupes by identity and uploads it once, where a radius\n // baked into the geometry would mean one upload per element.\n Geo.sphere({\n radius: 1,\n // `[longitude, latitude]` — half as many bands as meridians, which is\n // what a sphere wants and what three's own defaults do.\n segments: [quality, Math.max(4, Math.round(quality / 2))],\n }),\n // No `vertexColors` here, and that is the whole trick rather than an\n // omission. An instance's tint arrives as its own attribute and the\n // renderer samples it whenever one is present; asking for *vertex* colours\n // as well makes the shader read a per-vertex `color` the geometry doesn't\n // have, which supplies zeroes — and the molecule renders solid black.\n Mat.standard({ roughness: 0.35, metalness: 0.05 }),\n placements,\n { colors: tints, transform: { key } }\n )\n }\n\n /** One instanced cylinder op. */\n private addSticks(\n g3: Graphics3D,\n placements: Transform3D[],\n tints: string[],\n key: string\n ): void {\n if (placements.length === 0) return\n g3.instances(\n // A unit cylinder — one tall, centred on the origin, running along +Y —\n // which `stickPlacement` moves, turns and stretches onto each bond.\n Geo.cylinder({\n radius: 1,\n height: 1,\n segments: this.stickQuality(),\n // The ends are inside the atoms they join, so nothing can see them.\n capped: false,\n }),\n // Bare of `vertexColors` for the reason the spheres are — see above.\n Mat.standard({ roughness: 0.4, metalness: 0.05 }),\n placements,\n { colors: tints, transform: { key } }\n )\n }\n\n // ---- What gets drawn ----------------------------------------------------\n\n /**\n * Everything the builder needs, cached against the inputs that decide it.\n *\n * This is the node's one piece of memoisation and it earns its keep. The\n * builder runs every frame; between two frames the thing that has normally\n * changed is the camera; and none of what is cached here depends on the\n * camera. Without it, orbiting a 20,000-atom structure would rebuild a\n * 20,000-entry placement list and a 40,000-entry stick list sixty times a\n * second — for a picture whose atoms have not moved.\n *\n * The structure is compared by **identity** rather than by contents: it is a\n * parsed value produced once, upstream, so a different object genuinely means\n * a different molecule.\n */\n private cache: {\n key: string\n structure: ProteinStructure\n built: Built\n } | null = null\n\n private built(): Built {\n const structure = this.structure\n const key = [\n this.representation,\n this.colorScheme,\n hexOf(this.color),\n // The palette decides every colour the scheme paints, so a change to it\n // changes the tint arrays this cache is holding — exactly as `color` does.\n // Missing it meant editing a chain colour repainted nothing until\n // something else in this key happened to move.\n paletteKey(this.paletteOf()),\n this.ligands,\n this.waters,\n this.atomScale,\n this.bondRadius,\n this.ribbonRadius,\n // In the key now, where it never used to be: the cartoon's curve is\n // subdivided against the mesh detail (see `subdivisionsFor`), so a change\n // to it changes the geometry this cache is holding. The atoms never\n // needed it — `quality` reaches their spheres as a `segments` count the\n // renderer reads off the descriptor, not as anything built here.\n this.quality,\n ].join(\"|\")\n\n const cached = this.cache\n if (cached && cached.structure === structure && cached.key === key) {\n return cached.built\n }\n\n const built = this.build(structure)\n this.cache = { key, structure, built }\n return built\n }\n\n /** Builds the placement lists for the current representation. */\n private build(structure: ProteinStructure): Built {\n const palette = this.paletteOf()\n const colors = atomColors(\n structure,\n this.colorScheme,\n hexOf(this.color),\n palette\n )\n const showing = structure.atoms.map((atom) => this.isShowing(atom))\n const representation = this.representation\n const spaceFilling = representation === \"spacefill\"\n const atomic = spaceFilling || representation === \"ballAndStick\"\n\n // On a backbone or a ribbon the polymer is the curve, so only what *isn't*\n // polymer keeps its atoms — an inhibitor sitting in an active site is\n // usually the entire subject of the picture, and a bare backbone doesn't\n // show it. On the atomic representations everything showing is drawn.\n const drawAtom = (index: number): boolean => {\n if (!showing[index]) return false\n if (atomic) return true\n const kind = structure.atoms[index]!.kind\n return kind !== \"amino\" && kind !== \"nucleic\"\n }\n\n const built: Built = {\n spheres: [],\n sphereTints: [],\n sticks: [],\n stickTints: [],\n runs: [],\n ribbons: [],\n radius: structure.radius,\n }\n\n const scale = this.atomScale * (spaceFilling ? 1 : BALL_SCALE)\n for (let i = 0; i < structure.atoms.length; i++) {\n if (!drawAtom(i)) continue\n const atom = structure.atoms[i]!\n built.spheres.push({\n position: [atom.x, atom.y, atom.z],\n scale: atomRadius(atom, scale),\n })\n built.sphereTints.push(colors[i]!)\n }\n\n // A space-filling model has no visible bonds — the atoms are at their true\n // radii and already overlapping — so the sticks are simply not built.\n if (!spaceFilling) {\n for (const bond of structure.bonds) {\n if (!drawAtom(bond.a) || !drawAtom(bond.b)) continue\n const a = structure.atoms[bond.a]!\n const b = structure.atoms[bond.b]!\n // Two half-sticks rather than one, each in its own atom's colour: a\n // single stick has to be *some* colour, and whichever atom's it takes,\n // the far end reads as belonging to the wrong one. Splitting at the\n // midpoint is what ball-and-stick models have done since they were brass.\n const mid: Vector3 = {\n x: (a.x + b.x) / 2,\n y: (a.y + b.y) / 2,\n z: (a.z + b.z) / 2,\n }\n built.sticks.push(stickPlacement(a, mid, this.bondRadius))\n built.stickTints.push(colors[bond.a]!)\n built.sticks.push(stickPlacement(mid, b, this.bondRadius))\n built.stickTints.push(colors[bond.b]!)\n }\n }\n\n if (!atomic) this.addRuns(built, structure)\n built.radius = framingRadius(built, structure)\n return built\n }\n\n /**\n * The polymer, as whichever sweep its representation calls for.\n *\n * Two genuinely different builds rather than one with a radius switch, and\n * the split is the same one the representations themselves are: `backbone`\n * asks *where does the chain go*, which an even round tube answers exactly\n * and completely; `ribbon` asks *what is it folded into*, which is the\n * cartoon convention and needs a cross-section with a width, a thickness and\n * a side (see `ribbon.ts`).\n */\n private addRuns(built: Built, structure: ProteinStructure): void {\n const cartoon = this.representation === \"ribbon\"\n const uniform = hexOf(this.color)\n const palette = this.paletteOf()\n\n structure.chains.forEach((chain, index) => {\n const colors = traceColors(\n structure,\n chain,\n this.colorScheme,\n uniform,\n palette\n )\n\n if (cartoon) {\n for (const span of cartoonSpans(chain, colors)) {\n const geometry = cartoonGeometry(span, {\n radius: this.ribbonRadius,\n quality: this.quality,\n })\n if (geometry) built.ribbons.push(geometry)\n }\n return\n }\n\n backboneRuns(chain, colors, false).forEach((run, runIndex) => {\n built.runs.push({\n points: run.points,\n color: run.color,\n radius: this.ribbonRadius,\n // Keyed by its place in the chain rather than by its slot in the op\n // list, so adding a run doesn't renumber every later one and force the\n // tail of the renderer's cache to rebuild.\n key: `chain:${index}:${runIndex}`,\n })\n })\n })\n }\n\n /** Whether an atom passes the ligand and water filters. */\n private isShowing(atom: ProteinAtom): boolean {\n if (atom.kind === \"water\") return this.waters\n if (atom.kind === \"amino\" || atom.kind === \"nucleic\") return true\n return this.ligands\n }\n\n /** Segments around a sphere's equator. */\n private sphereQuality(): number {\n return Math.max(4, Math.round(this.quality))\n }\n\n /** Segments around a stick or a tube — half a sphere's, since it has no poles. */\n private stickQuality(): number {\n return Math.max(3, Math.round(this.quality / 2))\n }\n}\n\n/** A palette flattened for {@link Protein.built}'s cache key. */\nfunction paletteKey(palette: ProteinPalette): string {\n return [\n palette.helix,\n palette.sheet,\n palette.coil,\n palette.residueStart,\n palette.residueEnd,\n ...palette.chains,\n ].join(\",\")\n}\n\n/** One swept stretch of backbone. */\ninterface BuiltRun {\n points: Vector3[]\n color: string\n radius: number\n key: string\n}\n\n/** Everything the per-frame builder emits, built once per change and cached. */\ninterface Built {\n spheres: Transform3D[]\n sphereTints: string[]\n sticks: Transform3D[]\n stickTints: string[]\n /** Even round tubes — the `backbone` representation. */\n runs: BuiltRun[]\n /** Cartoon meshes, one per secondary-structure span — the `ribbon` one. */\n ribbons: BufferGeometry3D[]\n /** How far the drawn parts reach from the molecule's centroid, in Ångströms. */\n radius: number\n}\n\n/**\n * The radius the camera frames: the furthest *drawn* thing from the centroid.\n *\n * Measured over the placements rather than over the atom list, so it covers\n * exactly what will appear — the backbone runs when a ribbon is showing, the\n * atoms when it isn't, and neither the hidden waters nor the ligands somebody\n * switched off.\n *\n * Falls back to the whole structure's radius when nothing is drawn at all, so a\n * node whose filters have hidden everything still has a camera distance rather\n * than a zero.\n */\nfunction framingRadius(built: Built, structure: ProteinStructure): number {\n const { center } = structure\n let furthest = 0\n\n const reach = (x: number, y: number, z: number) => {\n const d = Math.hypot(x - center.x, y - center.y, z - center.z)\n if (d > furthest) furthest = d\n }\n\n for (const sphere of built.spheres) {\n const [x, y, z] = sphere.position as [number, number, number]\n reach(x, y, z)\n }\n for (const run of built.runs) {\n for (const point of run.points) reach(point.x, point.y, point.z)\n }\n // The cartoon's vertices rather than its residues: it is already a finished\n // buffer by the time this runs, and reading it is both exact — a strand's\n // arrowhead reaches wider than the α-carbons it was built from — and cheaper\n // than keeping a parallel copy of the curve alive to measure instead.\n for (const ribbon of built.ribbons) {\n const position = ribbon.position\n for (let i = 0; i + 2 < position.length; i += 3) {\n reach(position[i]!, position[i + 1]!, position[i + 2]!)\n }\n }\n\n return furthest > 0 ? furthest : structure.radius\n}\n\nfunction clampZoom(zoom: number): number {\n if (!Number.isFinite(zoom)) return DEFAULT_ZOOM\n return Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, zoom))\n}\n"]}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The cartoon: a chain's backbone swept as a **ribbon** rather than as a tube.
|
|
3
|
+
*
|
|
4
|
+
* This is the one representation whose whole job is to be recognised. A figure
|
|
5
|
+
* in a paper draws a helix as a twisted band and a strand as a flat arrow, and
|
|
6
|
+
* has since Jane Richardson drew the first one by hand in 1980 — the convention
|
|
7
|
+
* is old enough that a reader decodes it without a legend, which is exactly why
|
|
8
|
+
* a picture that departs from it reads as *wrong* rather than as different.
|
|
9
|
+
*
|
|
10
|
+
* The node drew that with `Geo.tube` before this module existed, and the two
|
|
11
|
+
* things it could not express are the two the convention is made of:
|
|
12
|
+
*
|
|
13
|
+
* **A cross-section has a width and a thickness, not a radius.** A tube's is a
|
|
14
|
+
* circle, so a helix could only be told from a coil by being *fatter* — and a
|
|
15
|
+
* fat circle swept along the α-carbons of a helix is close to self-intersecting,
|
|
16
|
+
* because those carbons corkscrew about the helix axis at ~2.3 Å with a 5.4 Å
|
|
17
|
+
* pitch. What comes out is a lumpy sausage: the tube's own diameter is most of
|
|
18
|
+
* the coil's, so the spiral fills in and the shape stops being legible. Swept
|
|
19
|
+
* flat instead — ~2.2 Å across and ~0.5 Å thick — the same spiral reads as the
|
|
20
|
+
* band every textbook draws, because the eye is now reading the *face* turning
|
|
21
|
+
* rather than a diameter changing.
|
|
22
|
+
*
|
|
23
|
+
* **A band has a side, and a curve through α-carbons does not say which.** So
|
|
24
|
+
* the frame is built from the residue's own carbonyl (see
|
|
25
|
+
* {@link TracePoint.normal}), which is what turns with the fold. A ribbon swept
|
|
26
|
+
* on an arbitrary frame twists at random and looks like a misprint.
|
|
27
|
+
*
|
|
28
|
+
* Everything here is derived **per structure**, cached by the node against the
|
|
29
|
+
* inputs that produced it, and never rebuilt for a camera move — the same
|
|
30
|
+
* bargain the instance lists make in `shared.ts`.
|
|
31
|
+
*/
|
|
32
|
+
import type { BufferGeometry3D } from "@motionscript/core";
|
|
33
|
+
import type { ProteinChain, SecondaryStructure, TracePoint } from "./structure.js";
|
|
34
|
+
/**
|
|
35
|
+
* A run of backbone drawn as one mesh: a whole stretch of one secondary
|
|
36
|
+
* structure, with its per-residue colours carried as a vertex attribute.
|
|
37
|
+
*
|
|
38
|
+
* The unit is the **span**, not the run of constant colour that a tube is swept
|
|
39
|
+
* as (see `backboneRuns`), and both halves of that matter. An arrowhead is a
|
|
40
|
+
* statement about where a strand *ends*, so it can only be placed by something
|
|
41
|
+
* that can see the whole strand — split the strand into four differently
|
|
42
|
+
* coloured pieces first and each piece grows an arrow of its own. And a colour
|
|
43
|
+
* that varies along the span is what a vertex attribute is for, which is also
|
|
44
|
+
* what retires the faceted-polyline compromise the tube path has to make under
|
|
45
|
+
* the residue spectrum.
|
|
46
|
+
*/
|
|
47
|
+
export interface CartoonSpan {
|
|
48
|
+
/** What this stretch is doing, which decides its cross-section. */
|
|
49
|
+
secondary: SecondaryStructure;
|
|
50
|
+
/**
|
|
51
|
+
* The residues of the span, N→C, **overlapping its neighbours by one point**
|
|
52
|
+
* at each end so consecutive spans meet rather than leaving a gap.
|
|
53
|
+
*/
|
|
54
|
+
points: TracePoint[];
|
|
55
|
+
/** One sRGB colour per entry of {@link points}. */
|
|
56
|
+
colors: string[];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Splits a chain's trace into the spans a cartoon is built from — one per
|
|
60
|
+
* unbroken stretch of the same secondary structure.
|
|
61
|
+
*
|
|
62
|
+
* Consecutive spans overlap by one point, for the reason `backboneRuns`'
|
|
63
|
+
* consecutive runs do: the shared residue is the last sample of one sweep and
|
|
64
|
+
* the first of the next, so the two meshes butt up rather than leaving a hole
|
|
65
|
+
* at every helix-to-loop transition.
|
|
66
|
+
*/
|
|
67
|
+
export declare function cartoonSpans(chain: ProteinChain, colors: string[]): CartoonSpan[];
|
|
68
|
+
/**
|
|
69
|
+
* One span, as a mesh.
|
|
70
|
+
*
|
|
71
|
+
* `null` when there is nothing to sweep, so the caller can skip the op rather
|
|
72
|
+
* than hand the renderer an empty buffer.
|
|
73
|
+
*
|
|
74
|
+
* The arrays are freshly allocated and never mutated afterwards, which is what
|
|
75
|
+
* makes `staticData` honest: the renderer then compares them by identity and
|
|
76
|
+
* uploads each exactly once, where re-deriving them per frame would re-upload
|
|
77
|
+
* the whole fold sixty times a second to spin the camera.
|
|
78
|
+
*/
|
|
79
|
+
export declare function cartoonGeometry(span: CartoonSpan, options: {
|
|
80
|
+
radius: number;
|
|
81
|
+
quality: number;
|
|
82
|
+
}): BufferGeometry3D | null;
|
|
83
|
+
//# sourceMappingURL=ribbon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ribbon.d.ts","sourceRoot":"","sources":["../../src/protein/ribbon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAkB,MAAM,oBAAoB,CAAA;AAG1E,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE/E;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,WAAW;IAC1B,mEAAmE;IACnE,SAAS,EAAE,kBAAkB,CAAA;IAC7B;;;OAGG;IACH,MAAM,EAAE,UAAU,EAAE,CAAA;IACpB,mDAAmD;IACnD,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB;AAwDD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,MAAM,EAAE,GACf,WAAW,EAAE,CA0Bf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC3C,gBAAgB,GAAG,IAAI,CAIzB"}
|