From fdcad6fbf9bd2a73062029ecb95648ea08e58b4f Mon Sep 17 00:00:00 2001 From: Anton Petrov Date: Mon, 12 Aug 2024 15:43:19 +0100 Subject: [PATCH 1/3] Document animate.py usage --- docs/usage.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/usage.md b/docs/usage.md index b85a8276e..14b3b5486 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -188,3 +188,18 @@ Please note that this option should be used with caution as sequences with unexp ```bash svg2json.py diagram.svg .json ``` + +## Animating RNA secondary structures + +R2DT can be used to generate animations of RNA secondary structures. The `animate.py` script takes two R2DT SVG files and generates an animated SVG file that transitions between the two structures. + +```bash +python3 utils/animate.py \ + examples/animate/PZ39_solution.svg \ + examples/animate/PZ39_Dfold_3.svg \ + animated.svg +``` + +:::{note} +Note that the two input SVG files must have the same number of nucleotides. +::: From 8333eb2ec3daf3e3ad25bc910e49620f6c4268f3 Mon Sep 17 00:00:00 2001 From: Anton Petrov Date: Mon, 12 Aug 2024 15:43:40 +0100 Subject: [PATCH 2/3] Test animation --- examples/animate/PZ39_Dfold_3.svg | 262 ++++++++++++ examples/animate/PZ39_solution.svg | 268 ++++++++++++ .../animate/PZ39_Dfold_3.animated.svg | 387 ++++++++++++++++++ tests/tests.py | 18 + 4 files changed, 935 insertions(+) create mode 100644 examples/animate/PZ39_Dfold_3.svg create mode 100644 examples/animate/PZ39_solution.svg create mode 100644 tests/examples/animate/PZ39_Dfold_3.animated.svg diff --git a/examples/animate/PZ39_Dfold_3.svg b/examples/animate/PZ39_Dfold_3.svg new file mode 100644 index 000000000..5f86d2fef --- /dev/null +++ b/examples/animate/PZ39_Dfold_3.svg @@ -0,0 +1,262 @@ + + +0 (position.label in template: 0.5')5' +1 (position.label in template: 1.G)G + +2 (position.label in template: 2.G)G + +3 (position.label in template: 3.U)U + +4 (position.label in template: 4.A)A + +5 (position.label in template: 5.A)A + +6 (position.label in template: 6.A)A +7 (position.label in template: 7.A)A + +8 (position.label in template: 8.C)C + +9 (position.label in template: 9.A)A +10 (position.label in template: 10.G)G + +10 + +11 (position.label in template: 11.C)C + +12 (position.label in template: 12.C)C + +13 (position.label in template: 13.U)U + +14 (position.label in template: 14.G)G + +15 (position.label in template: 15.U)U + +16 (position.label in template: 16.G)G +17 (position.label in template: 17.G)G + +18 (position.label in template: 18.G)G + +19 (position.label in template: 19.U)U + +20 (position.label in template: 20.G)G + +20 + +21 (position.label in template: 21.A)A +22 (position.label in template: 22.A)A +23 (position.label in template: 23.A)A +24 (position.label in template: 24.C)C +25 (position.label in template: 25.A)A +26 (position.label in template: 26.C)C +27 (position.label in template: 27.A)A +28 (position.label in template: 28.C)C +29 (position.label in template: 29.C)C +30 (position.label in template: 30.C)C +30 + +31 (position.label in template: 31.A)A +32 (position.label in template: 32.C)C +33 (position.label in template: 33.A)A +34 (position.label in template: 34.G)G +35 (position.label in template: 35.G)G +36 (position.label in template: 36.G)G +37 (position.label in template: 37.C)C +38 (position.label in template: 38.C)C +39 (position.label in template: 39.C)C +40 (position.label in template: 40.A)A +41 (position.label in template: 41.U)U +42 (position.label in template: 42.U)U +43 (position.label in template: 43.G)G + +44 (position.label in template: 44.G)G + +45 (position.label in template: 45.G)G + +46 (position.label in template: 46.C)C + +47 (position.label in template: 47.G)G + +48 (position.label in template: 48.C)C + +49 (position.label in template: 49.U)U + +50 (position.label in template: 50.A)A + +50 + +51 (position.label in template: 51.G)G + +52 (position.label in template: 52.C)C + +53 (position.label in template: 53.A)A + +54 (position.label in template: 54.C)C + +55 (position.label in template: 55.U)U + +56 (position.label in template: 56.C)C +57 (position.label in template: 57.U)U + +58 (position.label in template: 58.G)G + +59 (position.label in template: 59.G)G +60 (position.label in template: 60.U)U +61 (position.label in template: 61.A)A +62 (position.label in template: 62.U)U +63 (position.label in template: 63.C)C +64 (position.label in template: 64.A)A +65 (position.label in template: 65.C)C +66 (position.label in template: 66.G)G +67 (position.label in template: 67.G)G +68 (position.label in template: 68.U)U +69 (position.label in template: 69.A)A +70 (position.label in template: 70.C)C +71 (position.label in template: 71.C)C +72 (position.label in template: 72.U)U +73 (position.label in template: 73.U)U +74 (position.label in template: 74.U)U +75 (position.label in template: 75.G)G +76 (position.label in template: 76.U)U +77 (position.label in template: 77.G)G +78 (position.label in template: 78.C)C +79 (position.label in template: 79.G)G +80 (position.label in template: 80.C)C +81 (position.label in template: 81.C)C +82 (position.label in template: 82.U)U +83 (position.label in template: 83.G)G +84 (position.label in template: 84.U)U +85 (position.label in template: 85.U)U +86 (position.label in template: 86.U)U +87 (position.label in template: 87.U)U +88 (position.label in template: 88.A)A +89 (position.label in template: 89.C)C +90 (position.label in template: 90.C)C +91 (position.label in template: 91.3')3' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/animate/PZ39_solution.svg b/examples/animate/PZ39_solution.svg new file mode 100644 index 000000000..ea4fcf72c --- /dev/null +++ b/examples/animate/PZ39_solution.svg @@ -0,0 +1,268 @@ + + +0 (position.label in template: 0.5')5' +1 (position.label in template: 1.G)G + +2 (position.label in template: 2.G)G + +3 (position.label in template: 3.U)U + +4 (position.label in template: 4.A)A + +5 (position.label in template: 5.A)A + +6 (position.label in template: 6.A)A + +7 (position.label in template: 7.A)A + +8 (position.label in template: 8.C)C + +9 (position.label in template: 9.A)A + +10 (position.label in template: 10.G)G + +10 + +11 (position.label in template: 11.C)C + +12 (position.label in template: 12.C)C + +13 (position.label in template: 13.U)U + +14 (position.label in template: 14.G)G + +15 (position.label in template: 15.U)U + +16 (position.label in template: 16.G)G + +17 (position.label in template: 17.G)G + +18 (position.label in template: 18.G)G + +19 (position.label in template: 19.U)U + +20 (position.label in template: 20.G)G + +20 + +21 (position.label in template: 21.A)A +22 (position.label in template: 22.A)A +23 (position.label in template: 23.A)A +24 (position.label in template: 24.C)C +25 (position.label in template: 25.A)A +26 (position.label in template: 26.C)C +27 (position.label in template: 27.A)A +28 (position.label in template: 28.C)C +29 (position.label in template: 29.C)C +30 (position.label in template: 30.C)C +30 + +31 (position.label in template: 31.A)A +32 (position.label in template: 32.C)C +33 (position.label in template: 33.A)A +34 (position.label in template: 34.G)G +35 (position.label in template: 35.G)G +36 (position.label in template: 36.G)G + +37 (position.label in template: 37.C)C + +38 (position.label in template: 38.C)C + +39 (position.label in template: 39.C)C + +40 (position.label in template: 40.A)A +41 (position.label in template: 41.U)U +42 (position.label in template: 42.U)U +43 (position.label in template: 43.G)G +44 (position.label in template: 44.G)G +45 (position.label in template: 45.G)G +46 (position.label in template: 46.C)C +47 (position.label in template: 47.G)G + +48 (position.label in template: 48.C)C + +49 (position.label in template: 49.U)U +50 (position.label in template: 50.A)A +50 + +51 (position.label in template: 51.G)G + +52 (position.label in template: 52.C)C + +53 (position.label in template: 53.A)A + +54 (position.label in template: 54.C)C + +55 (position.label in template: 55.U)U + +56 (position.label in template: 56.C)C + +57 (position.label in template: 57.U)U + +58 (position.label in template: 58.G)G + +59 (position.label in template: 59.G)G + +60 (position.label in template: 60.U)U + +61 (position.label in template: 61.A)A + +62 (position.label in template: 62.U)U + +63 (position.label in template: 63.C)C +64 (position.label in template: 64.A)A +65 (position.label in template: 65.C)C +66 (position.label in template: 66.C)C +67 (position.label in template: 67.G)G +68 (position.label in template: 68.U)U +69 (position.label in template: 69.A)A +70 (position.label in template: 70.C)C +71 (position.label in template: 71.C)C +72 (position.label in template: 72.U)U +73 (position.label in template: 73.U)U +74 (position.label in template: 74.U)U +75 (position.label in template: 75.G)G +76 (position.label in template: 76.U)U +77 (position.label in template: 77.G)G +78 (position.label in template: 78.C)C +79 (position.label in template: 79.G)G +80 (position.label in template: 80.C)C +81 (position.label in template: 81.C)C +82 (position.label in template: 82.U)U +83 (position.label in template: 83.G)G +84 (position.label in template: 84.U)U +85 (position.label in template: 85.U)U +86 (position.label in template: 86.U)U +87 (position.label in template: 87.U)U +88 (position.label in template: 88.A)A +89 (position.label in template: 89.C)C +90 (position.label in template: 90.C)C +91 (position.label in template: 91.3')3' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/examples/animate/PZ39_Dfold_3.animated.svg b/tests/examples/animate/PZ39_Dfold_3.animated.svg new file mode 100644 index 000000000..d452a9139 --- /dev/null +++ b/tests/examples/animate/PZ39_Dfold_3.animated.svg @@ -0,0 +1,387 @@ + + + +1 (position.label in template: 1.G)G +2 (position.label in template: 2.G)G +3 (position.label in template: 3.U)U +4 (position.label in template: 4.A)A +5 (position.label in template: 5.A)A +6 (position.label in template: 6.A)A +7 (position.label in template: 7.A)A +8 (position.label in template: 8.C)C +9 (position.label in template: 9.A)A +10 (position.label in template: 10.G)G +11 (position.label in template: 11.C)C +12 (position.label in template: 12.C)C +13 (position.label in template: 13.U)U +14 (position.label in template: 14.G)G +15 (position.label in template: 15.U)U +16 (position.label in template: 16.G)G +17 (position.label in template: 17.G)G +18 (position.label in template: 18.G)G +19 (position.label in template: 19.U)U +20 (position.label in template: 20.G)G +21 (position.label in template: 21.A)A +22 (position.label in template: 22.A)A +23 (position.label in template: 23.A)A +24 (position.label in template: 24.C)C +25 (position.label in template: 25.A)A +26 (position.label in template: 26.C)C +27 (position.label in template: 27.A)A +28 (position.label in template: 28.C)C +29 (position.label in template: 29.C)C +30 (position.label in template: 30.C)C +31 (position.label in template: 31.A)A +32 (position.label in template: 32.C)C +33 (position.label in template: 33.A)A +34 (position.label in template: 34.G)G +35 (position.label in template: 35.G)G +36 (position.label in template: 36.G)G +37 (position.label in template: 37.C)C +38 (position.label in template: 38.C)C +39 (position.label in template: 39.C)C +40 (position.label in template: 40.A)A +41 (position.label in template: 41.U)U +42 (position.label in template: 42.U)U +43 (position.label in template: 43.G)G +44 (position.label in template: 44.G)G +45 (position.label in template: 45.G)G +46 (position.label in template: 46.C)C +47 (position.label in template: 47.G)G +48 (position.label in template: 48.C)C +49 (position.label in template: 49.U)U +50 (position.label in template: 50.A)A +51 (position.label in template: 51.G)G +52 (position.label in template: 52.C)C +53 (position.label in template: 53.A)A +54 (position.label in template: 54.C)C +55 (position.label in template: 55.U)U +56 (position.label in template: 56.C)C +57 (position.label in template: 57.U)U +58 (position.label in template: 58.G)G +59 (position.label in template: 59.G)G +60 (position.label in template: 60.U)U +61 (position.label in template: 61.A)A +62 (position.label in template: 62.U)U +63 (position.label in template: 63.C)C +64 (position.label in template: 64.A)A +65 (position.label in template: 65.C)C +66 (position.label in template: 66.C)C +67 (position.label in template: 67.G)G +68 (position.label in template: 68.U)U +69 (position.label in template: 69.A)A +70 (position.label in template: 70.C)C +71 (position.label in template: 71.C)C +72 (position.label in template: 72.U)U +73 (position.label in template: 73.U)U +74 (position.label in template: 74.U)U +75 (position.label in template: 75.G)G +76 (position.label in template: 76.U)U +77 (position.label in template: 77.G)G +78 (position.label in template: 78.C)C +79 (position.label in template: 79.G)G +80 (position.label in template: 80.C)C +81 (position.label in template: 81.C)C +82 (position.label in template: 82.U)U +83 (position.label in template: 83.G)G +84 (position.label in template: 84.U)U +85 (position.label in template: 85.U)U +86 (position.label in template: 86.U)U +87 (position.label in template: 87.U)U +88 (position.label in template: 88.A)A +89 (position.label in template: 89.C)C +90 (position.label in template: 90.C)C + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/tests.py b/tests/tests.py index ff832d78f..c311a1450 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -295,6 +295,24 @@ def test_examples(self): self.check_examples() +class TestAnimation(R2dtTestCase): + """Check that the SVG animation works.""" + + svg1 = Path("examples") / "animate" / "PZ39_solution.svg" + svg2 = Path("examples") / "animate" / "PZ39_Dfold_3.svg" + test_results = Path("tests") / "results" / "animate" + precomputed_results = Path("tests") / "examples" / "animate" + files = ["PZ39_Dfold_3.animated.svg"] + cmd = ( + f"mkdir -p {test_results} && " + f"python3 utils/animate.py {svg1} {svg2} {test_results / files[0]}" + ) + + def test_animation(self): + """Check that the animation works.""" + self.check_examples() + + class TestRfamAccession(R2dtTestCase): """Test Rfam visualisation when specifying an Rfam accession.""" From a30b3fa6a7e5de510558f8408cd3f1ca942559a4 Mon Sep 17 00:00:00 2001 From: Anton Petrov Date: Mon, 12 Aug 2024 15:43:58 +0100 Subject: [PATCH 3/3] Minor fixes in animate.py --- utils/animate.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/utils/animate.py b/utils/animate.py index c0beb2257..6bf7b4a34 100644 --- a/utils/animate.py +++ b/utils/animate.py @@ -8,8 +8,9 @@ SVG_HEADER = """ + viewBox="0 0 WIDTH HEIGHT" + width="WIDTH" + height="HEIGHT">