diff --git a/.gitignore b/.gitignore index 96fb178..274f332 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ paper/paper.html paper/paper.log paper/paper.pdf +/py/src/democapsid.egg-info diff --git a/CITATION.cff b/CITATION.cff index 7076e31..009c159 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -3,94 +3,29 @@ cff-version: 1.2.0 title: >- - democapsid -message: 'If you use this software, please cite it as below.' + democapsid +message: "If you use this software, please cite it as below." type: software authors: - - given-names: Daniel - family-names: Negrón - email: dnegron2@gmu.edu - affiliation: George Mason University - orcid: 'https://orcid.org/0000-0002-6123-2441' -identifiers: - - type: doi - value: 10.1101/2020.12.02.408252 - description: The bioRxiv preprint of the paper. -repository-code: 'https://github.com/dnanto/democapsid' -url: 'https://github.com/dnanto/democapsid/capsid.html' -abstract: >- - This work implements Caspar-Klug Theory to - generate high-quality, vectorized capsid cartoons - in the browser and is the first online tool that - provides comprehensive style customization, - geometry, and SVG export of icosahedra and nets - with different lattice structures. -keywords: - - virus - - capsid - - caspar-klug - - moody - - icosahedron - - prolate - - oblate - - elongated - - lattice - - 2D - - 3D - - svg - - biology - - graphics - - render - - projection - - perspective - - publication - - figure - - demoscene -license: MIT -preferred-citation: - type: article - authors: - given-names: Daniel family-names: Negrón email: dnegron2@gmu.edu affiliation: George Mason University - orcid: 'https://orcid.org/0000-0002-6123-2441' - doi: 10.1101/2020.12.02.408252 - journal: bioRxiv - abstract: >- + orcid: "https://orcid.org/0000-0002-6123-2441" +identifiers: + - type: doi + value: 10.1101/2020.12.02.408252 + description: The bioRxiv preprint of the paper. +repository-code: "https://github.com/dnanto/democapsid" +url: "https://github.com/dnanto/democapsid/capsid.html" +abstract: >- This work implements Caspar-Klug Theory to generate high-quality, vectorized capsid cartoons in the browser and is the first online tool that provides comprehensive style customization, geometry, and SVG export of icosahedra and nets with different lattice structures. - Motivation Few online services exist for rendering - high-quality viral capsid figures compatible with - common productivity software to develop effective - infographics in the field of virology. - - - Results The capsid.js library renders class I viral - capsids within an online application that - parameterizes style options, perspectives, and - lattice patterns with SVG export. - - - Availability This project is actively developed on - GitHub (https://github.com/dnanto/capsid), - distributed under the MIT License, hosted on GitHub - Pages, and runs on modern browsers - (https://dnanto.github.io/capsid/capsid.html). - - - Supplementary information Supplementary data are - available on GitHub. - month: 12 - title: >- - Vectorized Capsid Rendering in the Browser with - Capsid.js - year: 2021 - keywords: +keywords: - virus - capsid - caspar-klug @@ -98,7 +33,7 @@ preferred-citation: - icosahedron - prolate - oblate - - elongated + - elongated - lattice - 2D - 3D @@ -110,4 +45,69 @@ preferred-citation: - perspective - publication - figure - - demoscene \ No newline at end of file + - demoscene +license: MIT +preferred-citation: + type: article + authors: + - given-names: Daniel + family-names: Negrón + email: dnegron2@gmu.edu + affiliation: George Mason University + orcid: "https://orcid.org/0000-0002-6123-2441" + doi: 10.1101/2020.12.02.408252 + journal: bioRxiv + abstract: >- + This work implements Caspar-Klug Theory to + generate high-quality, vectorized capsid cartoons + in the browser and is the first online tool that + provides comprehensive style customization, + geometry, and SVG export of icosahedra and nets + with different lattice structures. + Motivation Few online services exist for rendering + high-quality viral capsid figures compatible with + common productivity software to develop effective + infographics in the field of virology. + + + Results The capsid.js library renders class I viral + capsids within an online application that + parameterizes style options, perspectives, and + lattice patterns with SVG export. + + + Availability This project is actively developed on + GitHub (https://github.com/dnanto/capsid), + distributed under the MIT License, hosted on GitHub + Pages, and runs on modern browsers + (https://dnanto.github.io/capsid/capsid.html). + + + Supplementary information Supplementary data are + available on GitHub. + month: 12 + title: >- + Vectorized Capsid Rendering in the Browser with + Capsid.js + year: 2021 + keywords: + - 2D + - 3D + - biology + - capsid + - caspar-klug + - demoscene + - elongated + - figure + - graphics + - icosahedron + - lattice + - oblate + - perspective + - projection + - prolate + - publication + - render + - svg + - virology + - virus diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..2d13056 --- /dev/null +++ b/TODO.md @@ -0,0 +1,4 @@ +- [ ] comments/documentation +- [ ] python package +- [ ] R package +- [ ] Blender addo diff --git a/democapsid/README.md b/democapsid/README.md deleted file mode 100644 index 211d9cd..0000000 --- a/democapsid/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# democapsid - -The [democapsid](https://github.com/dnanto/democapsid) package provides methods to render virus capsids. diff --git a/democapsid/src/__init__.py b/democapsid/src/__init__.py deleted file mode 100644 index e5a0d9b..0000000 --- a/democapsid/src/__init__.py +++ /dev/null @@ -1 +0,0 @@ -#!/usr/bin/env python3 diff --git a/env.yml b/env.yml deleted file mode 100644 index 83ff01a..0000000 --- a/env.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: democapsid -channels: - - conda-forge -dependencies: - - python=3.12.2 - - numpy=1.26.4 diff --git a/py/LICENSE b/py/LICENSE new file mode 100644 index 0000000..ded7e3c --- /dev/null +++ b/py/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 - 2024, Daniel Antonio Negrón (dnanto/remaindeer) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/py/README.rst b/py/README.rst new file mode 100644 index 0000000..f225232 --- /dev/null +++ b/py/README.rst @@ -0,0 +1,6 @@ +========== +democapsid +========== + + +Calculate meshes for icosahedral virus capsids. diff --git a/democapsid/pyproject.toml b/py/pyproject.toml similarity index 91% rename from democapsid/pyproject.toml rename to py/pyproject.toml index d246276..77ef724 100644 --- a/democapsid/pyproject.toml +++ b/py/pyproject.toml @@ -11,7 +11,7 @@ authors = [ maintainers = [ { name="Daniel Antonio Negrón", email="dnegron2@gmu.edu" }, ] -description = "This package includes methods to calculate the coordinates of viral capsids." +description = "Calculate meshes for icosahedral virus capsids." readme = "README.md" license = {text = "MIT License"} requires-python = ">=3.9.11" diff --git a/py/src/democapsid/__init__.py b/py/src/democapsid/__init__.py new file mode 100644 index 0000000..0e6cc90 --- /dev/null +++ b/py/src/democapsid/__init__.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 + +"""Top-level package for democapsid.""" + +__author__ = """Daniel Antonio Negrón""" +__email__ = 'dnegron2@gmu.edu' +__version__ = '0.0.1' diff --git a/py/src/democapsid/__main__.py b/py/src/democapsid/__main__.py new file mode 100644 index 0000000..b2e9b45 --- /dev/null +++ b/py/src/democapsid/__main__.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 + +import sys +from functools import partial + +from .cli import parse_args +from .democapsid import calc_ckm, calc_ckv, calc_ico, calc_lattice, cylinderize + +args = parse_args(sys.argv[1:]) +h, k, H, K, s, R, t, c = args.h, args.k, args.H, args.K, args.symmetry, args.radius, args.tile, args.sphericity + +lattice = calc_lattice(t, R) +ckv = calc_ckv(h, k, H, K, lattice[0]) +ckm = calc_ckm(ckv, lattice) + +if args.mode == "ico": + inflater = spherize if h == H and k == K else partial(cylinderize, s=s) + meshes = calc_ico(ckv, ckm, s, c, inflater) + +print("x", "y", "z", "face", "polygon", "point", sep="\t") +for i, mesh in enumerate(meshes[1:], start=1): + for j, polygon in enumerate(mesh, start=1): + for k, point in enumerate(polygon[0], start=1): + print(*point, i, j, k, sep="\t") diff --git a/py/src/democapsid/cli.py b/py/src/democapsid/cli.py new file mode 100644 index 0000000..b950fd9 --- /dev/null +++ b/py/src/democapsid/cli.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +def parse_args(argv): + from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser + parser = ArgumentParser( + prog="democapsid", + formatter_class=ArgumentDefaultsHelpFormatter, + description="Calculate meshes for icosahedral virus capsids." + ) + for ele in "hkHK": + parser.add_argument(ele, default=1, type=int, help=f"the {ele} lattice parameter") + choices = (5, 3, 2) + parser.add_argument("-symmetry", default=choices[0], type=int, help=f"the axial symmetry: {choices}") + parser.add_argument("-radius", default=1, type=int, help="the hexagonal lattice unit radius") + choices = ("hex", "trihex", "snubhex", "rhombitrihex") + choices = (*choices, *("dual" + ele for ele in choices)) + parser.add_argument("-tile", default=choices[0], help="the hexagonal lattice unit tile") + parser.add_argument("-sphericity", default=0, type=float, help="the sphericity value") + choices = ("ico", "net") + parser.add_argument("-mode", default=choices[0], help="the sphericity value") + return parser.parse_args(argv) diff --git a/democapsid/src/democapsid.py b/py/src/democapsid/democapsid.py similarity index 87% rename from democapsid/src/democapsid.py rename to py/src/democapsid/democapsid.py index 272ae25..261e0c4 100755 --- a/democapsid/src/democapsid.py +++ b/py/src/democapsid/democapsid.py @@ -1,23 +1,17 @@ #!/usr/bin/env python3 import sys -from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser -from itertools import chain from functools import partial +from itertools import chain import numpy as np -try: - import bpy -except ImportError: - pass - - SQRT2 = np.sqrt(2) SQRT3 = np.sqrt(3) SQRT5 = np.sqrt(5) PHI = (1 + SQRT5) / 2 + ICO_CONFIG = ( (), (), @@ -561,63 +555,3 @@ def calc_ico(ckv, ckm, s, c, inflater): M = np.transpose(np.apply_along_axis(roro, 1, coors[v_idx,], t=i * (2 * np.pi) / s)) @ A meshes.append([([inflater(M @ point, coors, c) for point in vertices], edges) for vertices, edges in ckm[t_idx]]) return meshes - - -def parse_args(argv): - parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter) - for ele in "hkHK": - parser.add_argument(ele, default=1, type=int, help=f"the {ele} lattice parameter") - choices = (5, 3, 2) - parser.add_argument("-symmetry", default=choices[0], type=int, help=f"the axial symmetry: {choices}") - parser.add_argument("-radius", default=1, type=int, help="the hexagonal lattice unit radius") - choices = ("hex", "trihex", "snubhex", "rhombitrihex") - choices = (*choices, *("dual" + ele for ele in choices)) - parser.add_argument("-tile", default=choices[0], help="the hexagonal lattice unit tile") - parser.add_argument("-sphericity", default=0, type=float, help="the sphericity value") - choices = ("ico", "net") - parser.add_argument("-mode", default=choices[0], help="the sphericity value") - return parser.parse_args(argv) - - -def main(argv): - args = parse_args(argv[1:]) - h, k, H, K, s, R, t, c = args.h, args.k, args.H, args.K, args.symmetry, args.radius, args.tile, args.sphericity - - lattice = calc_lattice(t, R) - ckv = calc_ckv(h, k, H, K, lattice[0]) - ckm = calc_ckm(ckv, lattice) - - if args.mode == "ico": - inflater = spherize if h == H and k == K else partial(cylinderize, s=s) - meshes = calc_ico(ckv, ckm, s, c, inflater) - else: - meshes = ckm - - if "bpy" in sys.modules: - for obj in bpy.data.objects: - bpy.data.objects.remove(obj, do_unlink=True) - - for i, mesh in enumerate(meshes[1:], start=1): - collection = bpy.data.collections.new(f"face-{i}") - bpy.context.scene.collection.children.link(collection) - for j, polygon in enumerate(mesh, start=1): - mesh = bpy.data.meshes.new(name=f"polygon_msh[{i},{j}]") - mesh.from_pydata(*polygon, []) - mesh.validate(verbose=True) - obj = bpy.data.objects.new(f"polygon_obj-[{i},{j}]", mesh) - collection.objects.link(obj) - else: - print("x", "y", "z", "face", "polygon", "point", sep="\t") - for i, mesh in enumerate(meshes[1:], start=1): - for j, polygon in enumerate(mesh, start=1): - for k, point in enumerate(polygon[0], start=1): - print(*point, i, j, k, sep="\t") - - return 0 - - -if __name__ == "__main__": - if "bpy" in sys.modules: - main(["capsid", "1", "1", "1", "2", "-symmetry", "5", "-sphericity", "0"]) - else: - sys.exit(main(sys.argv))