From dbd17733d3031e7dc7032277b8806187470e1fc3 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 10 Aug 2026 09:39:59 +0300 Subject: [PATCH 1/6] Update dependencies. --- Cargo.lock | 521 +++++++++++++----------- Cargo.toml | 12 +- lib_ts_chainalign/Cargo.toml | 2 +- lib_tsalign/Cargo.toml | 2 +- lib_tsshow/Cargo.toml | 10 +- lib_tsshow/src/svg.rs | 23 +- lib_tsshow/src/ts_arrangement.rs | 4 +- lib_tsshow/src/ts_arrangement/inner.rs | 18 +- lib_tsshow/src/ts_arrangement/source.rs | 4 +- tsalign/Cargo.toml | 2 +- 10 files changed, 327 insertions(+), 271 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 320a43d..03813ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,15 +60,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "arc-swap" -version = "1.8.2" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9f3647c145568cec02c42054e07bdf9a5a698e15b466fb2341bfc393cd24aa5" +checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b" dependencies = [ "rustversion", ] @@ -81,21 +81,21 @@ checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "base64" -version = "0.22.1" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" [[package]] name = "binary-heap-plus" @@ -128,21 +128,15 @@ dependencies = [ [[package]] name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.11.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] name = "bitvec" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" dependencies = [ "funty", "radium", @@ -158,15 +152,29 @@ checksum = "e3acc5ce9c60e68df21b877f13f908ef95c89f01cb6c656cf76ba95f10bc72f5" [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" -version = "1.25.0" +version = "1.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "fc0e56a716f1e132ff6bf4bdac1c944a3fcdc1cae65f70a4a2a1ac3b401d2d1f" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] [[package]] name = "byteorder-lite" @@ -182,9 +190,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "clap" -version = "4.6.0" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" dependencies = [ "clap_builder", "clap_derive", @@ -192,9 +200,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" dependencies = [ "anstream", "anstyle", @@ -204,14 +212,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.0" +version = "4.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" +checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061" dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -241,7 +249,7 @@ dependencies = [ "bitvec", "enum-iterator", "flate2", - "itertools", + "itertools 0.14.0", "lazy_static", "ref-cast", "thiserror", @@ -256,9 +264,9 @@ checksum = "120133d4db2ec47efe2e26502ee984747630c67f51974fca0b6c1340cf2368d3" [[package]] name = "console" -version = "0.16.3" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" +checksum = "4fe5f465a4f6fee88fad41b85d990f84c835335e85b5d9e6e63e0d06d28cba7c" dependencies = [ "encode_unicode", "libc", @@ -266,15 +274,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "core_maths" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" -dependencies = [ - "libm", -] - [[package]] name = "crc32fast" version = "1.5.0" @@ -295,15 +294,12 @@ name = "deranged" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", -] [[package]] name = "either" -version = "1.15.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" [[package]] name = "ena" @@ -337,7 +333,7 @@ checksum = "685adfa4d6f3d765a26bc5dbc936577de9abf756c1feeb3089b01dd395034842" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -348,9 +344,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "euclid" -version = "0.22.13" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df61bf483e837f88d5c2291dcf55c67be7e676b3a51acc48db3a7b163b91ed63" +checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" dependencies = [ "num-traits", ] @@ -386,27 +382,35 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +[[package]] +name = "font-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a7299a780854a6d391be2ae1c8521c9368471b559dbfd6a8dbd9f407eaff100" +dependencies = [ + "bytemuck", +] + [[package]] name = "fontconfig-parser" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" dependencies = [ - "roxmltree", + "roxmltree 0.20.0", ] [[package]] name = "fontdb" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" +checksum = "2660c5e9157bf76d2db1294e4a9feba604ef610819a3b591088d0d8392a3290f" dependencies = [ "fontconfig-parser", "log", "memmap2", "slotmap", "tinyvec", - "ttf-parser", ] [[package]] @@ -415,6 +419,30 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "futures-core" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" + +[[package]] +name = "futures-task" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" + +[[package]] +name = "futures-util" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + [[package]] name = "generic_a_star" version = "4.1.0" @@ -430,19 +458,31 @@ dependencies = [ [[package]] name = "gif" -version = "0.13.3" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" +checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" dependencies = [ "color_quant", "weezl", ] +[[package]] +name = "harfrust" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03d949a14aa089bbb282f7dd76a498a7f684428e4257202efc119ec010376f9" +dependencies = [ + "bitflags", + "bytemuck", + "read-fonts", + "smallvec", +] + [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heck" @@ -462,15 +502,15 @@ dependencies = [ [[package]] name = "imagesize" -version = "0.13.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" +checksum = "65b27460c2c92b037f3f94c538ed9a3342f3fdf923606781629ccb35f82d042a" [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", "hashbrown", @@ -478,9 +518,9 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.18.4" +version = "0.18.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb" +checksum = "9433806cd6b4ec1aba79c021c7e4c58fb4c3b9977c085062e611ac929998fb0c" dependencies = [ "console", "portable-atomic", @@ -514,29 +554,34 @@ dependencies = [ ] [[package]] -name = "itoa" -version = "1.0.17" +name = "itertools" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc" +dependencies = [ + "either", +] [[package]] name = "js-sys" -version = "0.3.91" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" dependencies = [ - "once_cell", + "cfg-if", + "futures-util", "wasm-bindgen", ] [[package]] name = "kurbo" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" +checksum = "4b60dfc32f652b926df6192e55525b16d186c69d47876c3ead4da5cc9f8450e2" dependencies = [ "arrayvec", "euclid", + "polycool", "smallvec", ] @@ -557,7 +602,7 @@ dependencies = [ "compact-genome", "generic_a_star", "indicatif", - "itertools", + "itertools 0.15.0", "lib_tsalign", "log", "ndarray", @@ -592,7 +637,7 @@ name = "lib_tsshow" version = "4.1.0" dependencies = [ "ena", - "itertools", + "itertools 0.15.0", "lib_tsalign", "log", "resvg", @@ -604,27 +649,21 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.183" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" - -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "log" -version = "0.4.29" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "matrixmultiply" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +checksum = "3f607c237553f086e7043417a51df26b2eb899d3caff94e6a67592ff992fedc7" dependencies = [ "autocfg", "rawpointer", @@ -632,15 +671,15 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "memmap2" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" dependencies = [ "libc", ] @@ -710,9 +749,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-integer" @@ -759,30 +798,45 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + [[package]] name = "png" -version = "0.17.16" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "bitflags 1.3.2", + "bitflags", "crc32fast", "fdeflate", "flate2", "miniz_oxide", ] +[[package]] +name = "polycool" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50596ddc09eb5ad5f75cacd40209568e66df71baf86e1499a0e99c4cff12a5a6" +dependencies = [ + "arrayvec", +] + [[package]] name = "portable-atomic" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" [[package]] name = "portable-atomic-util" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" dependencies = [ "portable-atomic", ] @@ -795,9 +849,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] @@ -853,9 +907,9 @@ dependencies = [ [[package]] name = "pyo3-log" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c2ec80932c5c3b2d4fbc578c9b56b2d4502098587edb8bef5b6bfcad43682e" +checksum = "f64083bd3a16a353d9d62335808e8e13d0552d2a2b83fdb084496192dcfa9fcd" dependencies = [ "arc-swap", "log", @@ -871,7 +925,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -884,7 +938,7 @@ dependencies = [ "proc-macro2", "pyo3-build-config", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -905,9 +959,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quote" -version = "1.0.45" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -924,32 +978,44 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" +[[package]] +name = "read-fonts" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046a7d674daf459825b32f5062056d6882db0d2f5a479fbd76ccfc870ac18709" +dependencies = [ + "bytemuck", + "font-types", + "once_cell", +] + [[package]] name = "ref-cast" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +checksum = "216e8f773d7923bcba9ceb86a86c93cabb3903a11872fc3f138c49630e50b96d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +checksum = "2c9283685feec7d69af75fb0e858d5e7378f33fe4fc699383b2916ab9273e03c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "resvg" -version = "0.45.1" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43" +checksum = "67e3803f97b999e80cbf7c6ecdd07a8102204d92e1633cf48783720c521196bd" dependencies = [ + "bytemuck", "gif", "image-webp", "log", @@ -977,34 +1043,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" [[package]] -name = "rustc-hash" -version = "2.1.1" +name = "roxmltree" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "f1964b10c76125c36f8afe190065a4bf9a87bf324842c05701330bba9f1cacbb" +dependencies = [ + "memchr", +] [[package]] -name = "rustversion" -version = "1.0.22" +name = "rustc-hash" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" [[package]] -name = "rustybuzz" -version = "0.20.1" +name = "rustversion" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" -dependencies = [ - "bitflags 2.11.0", - "bytemuck", - "core_maths", - "log", - "smallvec", - "ttf-parser", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-properties", - "unicode-script", -] +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "seed_chain" @@ -1018,9 +1075,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -1028,29 +1085,29 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "serde_spanned" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -1066,9 +1123,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" [[package]] name = "simplecss" @@ -1092,9 +1149,25 @@ dependencies = [ [[package]] name = "siphasher" -version = "1.0.2" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "skrifa" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819ab7d62b1d3e72d9d9dea5650bac30424f9111364bb94928dbf5ecad1baa68" +dependencies = [ + "bytemuck", + "read-fonts", +] + +[[package]] +name = "slab" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "slotmap" @@ -1107,9 +1180,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "strict-num" @@ -1137,7 +1210,7 @@ checksum = "dadfd688d4a48103816a8339b0f5600484bd7db0af502f7ef861f7be3615f710" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1160,9 +1233,9 @@ checksum = "94afda9cd163c04f6bee8b4bf2501c91548deae308373c436f36aeff3cf3c4a3" [[package]] name = "svgtypes" -version = "0.15.3" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" +checksum = "695b5790b3131dafa99b3bbfd25a216edb3d216dad9ca208d4657bfb8f2abc3d" dependencies = [ "kurbo", "siphasher", @@ -1170,9 +1243,20 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.117" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" dependencies = [ "proc-macro2", "quote", @@ -1181,9 +1265,9 @@ dependencies = [ [[package]] name = "tagged-vec" -version = "0.3.6" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7aaeb1a61db84ad2bfac1da3920289b4cddcb93957313993a3bc1c764925e96" +checksum = "413ad37b1c9a27cdfbbdb7b677cbaf578fd2b9729c61060302d57e1d212208e5" [[package]] name = "tap" @@ -1221,32 +1305,31 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "time" -version = "0.3.47" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" dependencies = [ "deranged", - "itoa", "libc", "num-conv", "num_threads", @@ -1258,15 +1341,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" dependencies = [ "num-conv", "time-core", @@ -1274,9 +1357,9 @@ dependencies = [ [[package]] name = "tiny-skia" -version = "0.11.4" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +checksum = "47ffee5eaaf5527f630fb0e356b90ebdec84d5d18d937c5e440350f88c5a91ea" dependencies = [ "arrayref", "arrayvec", @@ -1289,9 +1372,9 @@ dependencies = [ [[package]] name = "tiny-skia-path" -version = "0.11.4" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +checksum = "edca365c3faccca67d06593c5980fa6c57687de727a03131735bb85f01fdeeb9" dependencies = [ "arrayref", "bytemuck", @@ -1300,9 +1383,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ "tinyvec_macros", ] @@ -1315,9 +1398,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" -version = "0.9.12+spec-1.1.0" +version = "1.1.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" dependencies = [ "indexmap", "serde_core", @@ -1330,27 +1413,27 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.5+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] [[package]] name = "toml_parser" -version = "1.0.9+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ "winnow", ] [[package]] name = "toml_writer" -version = "1.0.6+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" [[package]] name = "traitsequence" @@ -1388,45 +1471,18 @@ dependencies = [ "tsalign", ] -[[package]] -name = "ttf-parser" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" -dependencies = [ - "core_maths", -] - [[package]] name = "unicode-bidi" version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" -[[package]] -name = "unicode-bidi-mirroring" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe" - -[[package]] -name = "unicode-ccc" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e" - [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" -[[package]] -name = "unicode-properties" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" - [[package]] name = "unicode-script" version = "0.5.8" @@ -1465,22 +1521,23 @@ checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" [[package]] name = "usvg" -version = "0.45.1" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef" +checksum = "977d0a4abdef933f424a99fe09f95576e089b90aebc6f016a3bc813762493e91" dependencies = [ "base64", "data-url", "flate2", "fontdb", + "harfrust", "imagesize", "kurbo", "log", "pico-args", - "roxmltree", - "rustybuzz", + "roxmltree 0.21.1", "simplecss", "siphasher", + "skrifa", "strict-num", "svgtypes", "tiny-skia-path", @@ -1492,9 +1549,9 @@ dependencies = [ [[package]] name = "utf8-chars" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe49e006d6df172d7f14794568a90fe41e05a1fa9e03dc276fa6da4bb747ec3" +checksum = "92498c67ea3511b37eccff13b573ed871faf0ce9c4056ab032bd01a681f445a0" dependencies = [ "arrayvec", ] @@ -1519,9 +1576,9 @@ checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" [[package]] name = "wasm-bindgen" -version = "0.2.114" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" dependencies = [ "cfg-if", "once_cell", @@ -1532,9 +1589,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.114" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1542,22 +1599,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.114" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.114" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" dependencies = [ "unicode-ident", ] @@ -1604,9 +1661,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.15" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" [[package]] name = "wyz" @@ -1625,15 +1682,15 @@ checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" [[package]] name = "zune-core" -version = "0.4.12" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" +checksum = "d56377fd46368984a170bc5aac5567e52ca5da874caa60bea39fcbca78fb658b" [[package]] name = "zune-jpeg" -version = "0.4.21" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" dependencies = [ "zune-core", ] diff --git a/Cargo.toml b/Cargo.toml index 34a5243..3e79adb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,17 +22,19 @@ package.rust-version = "1.85.1" package.repository = "https://github.com/sebschmi/template-switch-aligner" [workspace.dependencies] -clap = { version = "4.5.54", features = ["derive"] } -serde = "1.0.228" +clap = { version = "4.6.6", features = ["derive"] } +serde = "1.0.229" compact-genome = "12.5.0" traitsequence = "8.1.2" -log = "0.4.29" +log = "0.4.33" num-traits = "0.2.19" -rustc-hash = "2.1.1" +rustc-hash = "2.1.3" binary-heap-plus = "0.5.0" +itertools = "0.15.0" +thiserror = "2.0.12" [profile.dev] -debug = 1 # or "line-tables-only" instead of full debug info +debug = 1 # or "line-tables-only" instead of full debug info split-debuginfo = "unpacked" # Keeps symbols out of the main object file [profile.release] diff --git a/lib_ts_chainalign/Cargo.toml b/lib_ts_chainalign/Cargo.toml index c954eac..a785847 100644 --- a/lib_ts_chainalign/Cargo.toml +++ b/lib_ts_chainalign/Cargo.toml @@ -15,7 +15,7 @@ ndarray = { version = "0.17.1", features = ["serde"] } num-traits.workspace = true serde = { workspace = true, features = ["derive"] } compact-genome.workspace = true -itertools = "0.14.0" +itertools.workspace = true log.workspace = true indicatif = "0.18.3" bitvec = "1.0.1" diff --git a/lib_tsalign/Cargo.toml b/lib_tsalign/Cargo.toml index 76702c8..d941aeb 100644 --- a/lib_tsalign/Cargo.toml +++ b/lib_tsalign/Cargo.toml @@ -22,7 +22,7 @@ traitsequence.workspace = true ndarray = "0.17.1" binary-heap-plus = "0.5.0" nom = "8.0.0" -thiserror = "2.0.3" +thiserror.workspace = true num-traits.workspace = true serde = { workspace = true, features = ["derive"], optional = true } noisy_float = { version = "0.2.0" } diff --git a/lib_tsshow/Cargo.toml b/lib_tsshow/Cargo.toml index 8c6986f..709b659 100644 --- a/lib_tsshow/Cargo.toml +++ b/lib_tsshow/Cargo.toml @@ -12,9 +12,9 @@ repository.workspace = true lib_tsalign = { version = "4.1.0", path = "../lib_tsalign" } log.workspace = true svg = "0.18.0" -resvg = "0.45.0" -thiserror = "2.0.12" -tagged-vec = "0.3.5" -strong-type = "1.0.0" +resvg = "0.48.1" +thiserror.workspace = true +tagged-vec = "1.1.0" +strong-type = "1.1.1" ena = "0.14.3" -itertools = "0.14.0" +itertools.workspace = true diff --git a/lib_tsshow/src/svg.rs b/lib_tsshow/src/svg.rs index 44339d3..a4dfa29 100644 --- a/lib_tsshow/src/svg.rs +++ b/lib_tsshow/src/svg.rs @@ -255,14 +255,12 @@ pub fn create_ts_svg( ts_arrangement.reference_arrangement_char_to_arrangement_column(*sp1_reference), ts_arrangement .reference() - .iter() - .take( - usize::from( - ts_arrangement - .reference_arrangement_char_to_arrangement_column(*sp4_reference), - ) + 1usize, + .iter( + descendant_sp4_minus_one.unwrap_or(0.into()) + ..ts_arrangement + .reference_arrangement_char_to_arrangement_column(*sp4_reference) + + 1usize, ) - .skip(descendant_sp4_minus_one.unwrap_or(0.into()).into()) .find(|(_, c)| !c.is_blank()) .unwrap() .0, @@ -272,13 +270,12 @@ pub fn create_ts_svg( ts_arrangement.query_arrangement_char_to_arrangement_column(*sp1_query), ts_arrangement .query() - .iter() - .take( - usize::from( - ts_arrangement.query_arrangement_char_to_arrangement_column(*sp4_query), - ) + 1usize, + .iter( + descendant_sp4_minus_one.unwrap_or(0.into()) + ..ts_arrangement + .query_arrangement_char_to_arrangement_column(*sp4_query) + + 1usize, ) - .skip(descendant_sp4_minus_one.unwrap_or(0.into()).into()) .find(|(_, c)| !c.is_blank()) .map(|(column, _)| column) .unwrap_or(ts_arrangement.query().len().into()), diff --git a/lib_tsshow/src/ts_arrangement.rs b/lib_tsshow/src/ts_arrangement.rs index 0413299..79c5b5f 100644 --- a/lib_tsshow/src/ts_arrangement.rs +++ b/lib_tsshow/src/ts_arrangement.rs @@ -68,7 +68,7 @@ impl TsArrangement { pub fn remove_empty_columns(&mut self) { let mut remove_columns = Vec::new(); - 'column_iter: for column in self.reference().iter_indices() { + 'column_iter: for column in self.reference().iter_indices(..) { if !self.reference()[column].is_blank_or_hidden() { continue; } @@ -218,7 +218,7 @@ impl TsArrangement { pub fn template_switches(&self) -> impl Iterator { self.inners() - .iter() + .iter(..) .map(|(identifier, inner)| (identifier, inner.template_switch())) } diff --git a/lib_tsshow/src/ts_arrangement/inner.rs b/lib_tsshow/src/ts_arrangement/inner.rs index bb3c74c..6634743 100644 --- a/lib_tsshow/src/ts_arrangement/inner.rs +++ b/lib_tsshow/src/ts_arrangement/inner.rs @@ -327,19 +327,19 @@ impl TsInnerArrangement { // Insert range characters before point 2. let last_initial_blank = inner - .iter() + .iter(..) .take_while(|(_, c)| c.is_blank()) .last() .map(|(i, _)| i); let first_final_blank = inner - .iter() + .iter(..) .rev() .take_while(|(_, c)| c.is_blank()) .last() .map(|(i, _)| i) .unwrap_or(inner.len().into()); let first_non_blank = inner - .iter() + .iter(..) .find(|(_, c)| !c.is_blank()) .map(|(i, _)| i) .unwrap(); @@ -446,13 +446,13 @@ impl TsInnerArrangement { &self, ) -> impl DoubleEndedIterator { self.inners - .iter() + .iter(..) .filter(|inner| inner.1.reference && !inner.1.complement) } pub fn query_inners(&self) -> impl DoubleEndedIterator { self.inners - .iter() + .iter(..) .filter(|inner| !inner.1.reference && !inner.1.complement) } @@ -460,7 +460,7 @@ impl TsInnerArrangement { &self, ) -> impl DoubleEndedIterator { self.inners - .iter() + .iter(..) .filter(|inner| inner.1.reference && inner.1.complement) } @@ -468,7 +468,7 @@ impl TsInnerArrangement { &self, ) -> impl DoubleEndedIterator { self.inners - .iter() + .iter(..) .filter(|inner| !inner.1.reference && inner.1.complement) } @@ -479,7 +479,7 @@ impl TsInnerArrangement { let sequence = &self.inners[inner_identifier].sequence; sequence - .iter() + .iter(..) .find(|(_, c)| !c.is_blank()) .map(|(i, _)| i) .unwrap_or(sequence.len().into()) @@ -492,7 +492,7 @@ impl TsInnerArrangement { let sequence = &self.inners[inner_identifier].sequence; sequence - .iter() + .iter(..) .rev() .find(|(_, c)| !c.is_blank()) .map(|(i, _)| i) diff --git a/lib_tsshow/src/ts_arrangement/source.rs b/lib_tsshow/src/ts_arrangement/source.rs index 3587a92..66c327b 100644 --- a/lib_tsshow/src/ts_arrangement/source.rs +++ b/lib_tsshow/src/ts_arrangement/source.rs @@ -608,7 +608,7 @@ impl TsSourceArrangement { source_column: SourceColumn, ) -> Option { sequence - .iter() + .iter(..) .filter_map(|(i, c)| match c { SourceChar::Source { column, .. } | SourceChar::Hidden { column, .. } if *column + 1usize == source_column => @@ -726,7 +726,7 @@ impl TsSourceArrangement { column: ArrangementCharColumn, ) -> ArrangementColumn { sequence - .iter() + .iter(..) .filter_map(|(i, c)| if c.is_char() { Some(i) } else { None }) .chain(iter::once(sequence.len().into())) .nth(column.primitive()) diff --git a/tsalign/Cargo.toml b/tsalign/Cargo.toml index 62d9c8c..09a240e 100644 --- a/tsalign/Cargo.toml +++ b/tsalign/Cargo.toml @@ -32,7 +32,7 @@ clap.workspace = true compact-genome = { workspace = true, features = ["io"] } traitsequence.workspace = true serde.workspace = true -toml = "0.9.8" +toml = "1.1.4" log.workspace = true simplelog = "0.12.2" anyhow = "1.0.97" From a83604ae6a363a8357b46d79ac518fb7beb09ac7 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 10 Aug 2026 10:11:00 +0300 Subject: [PATCH 2/6] Support rendering source copies, but not arranging them. --- lib_tsshow/src/svg.rs | 130 ++++++++++++++------ lib_tsshow/src/ts_arrangement/complement.rs | 9 ++ lib_tsshow/src/ts_arrangement/inner.rs | 2 +- lib_tsshow/src/ts_arrangement/source.rs | 74 ++++++++++- test_files/twin_show_ts_optional.fa | 4 + tsalign/Cargo.toml | 2 +- 6 files changed, 173 insertions(+), 48 deletions(-) create mode 100644 test_files/twin_show_ts_optional.fa diff --git a/lib_tsshow/src/svg.rs b/lib_tsshow/src/svg.rs index a4dfa29..e0d47bd 100644 --- a/lib_tsshow/src/svg.rs +++ b/lib_tsshow/src/svg.rs @@ -43,8 +43,10 @@ mod numbers; const SVG_PADDING: f32 = 10.0; const COPY_COLORS: &[&str] = &["#00CC00", "#009900", "#006600", "#003300"]; -const OPTIONAL_COPY_COLORS: &[&str] = &["#88CC88", "#669966", "#446644", "#223322"]; -const OPTIONAL_SOURCE_COLOR: &str = "blue"; +const OPTIONAL_INNER_COPY_COLORS: &[&str] = &["#88CC88", "#669966", "#446644", "#223322"]; +const OPTIONAL_SOURCE_COPY_COLORS: &[&str] = &["#0000FF66"]; +const OPTIONAL_INNER_COLOR: &str = "#0000FF"; +const OPTIONAL_SOURCE_COLOR: &str = "#0000FF66"; const COMPLEMENT_SOURCE_HIDDEN_COLOR: &str = "grey"; const TS_RUNNING_NUMBER: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; @@ -752,6 +754,7 @@ pub fn create_ts_svg( &statistics.sequences.reference_name, &statistics.sequences.query_name, 0.6, + config, ); let vertical_spacer_height = typewriter::FONT.character_height; body_group = body_group.add( @@ -804,11 +807,31 @@ fn render_source_char( c }; - Character::new_char(c, CharacterData::new_colored(copy_color(copy_depth, false))) + Character::new_char( + c, + CharacterData::new_colored(copy_color(copy_depth, OptionalChar::NotOptional)), + ) + } + SourceChar::OptionalSource { + column, + lower_case, + copy_depth, + } => { + let c = source_sequence.char_at(column.into()); + let c = if *lower_case { + c.to_ascii_lowercase() + } else { + c + }; + + Character::new_char( + c, + CharacterData::new_colored(copy_color(copy_depth, OptionalChar::Source)), + ) } SourceChar::Gap { copy_depth } => Character::new_char( '-', - CharacterData::new_colored(copy_color(copy_depth, false)), + CharacterData::new_colored(copy_color(copy_depth, OptionalChar::NotOptional)), ), SourceChar::Separator => Character::new_char_with_default('|'), SourceChar::Hidden { .. } | SourceChar::Spacer | SourceChar::Blank => { @@ -871,7 +894,10 @@ fn render_inner_char( } else { c }; - Character::new_char(c, CharacterData::new_colored(copy_color(copy_depth, false))) + Character::new_char( + c, + CharacterData::new_colored(copy_color(copy_depth, OptionalChar::NotOptional)), + ) } InnerChar::OptionalInner { column, @@ -884,11 +910,14 @@ fn render_inner_char( } else { c }; - Character::new_char(c, CharacterData::new_colored(copy_color(copy_depth, true))) + Character::new_char( + c, + CharacterData::new_colored(copy_color(copy_depth, OptionalChar::Inner)), + ) } InnerChar::Gap { copy_depth } => Character::new_char( '-', - CharacterData::new_colored(copy_color(copy_depth, false)), + CharacterData::new_colored(copy_color(copy_depth, OptionalChar::NotOptional)), ), InnerChar::Blank => Character::new_char(' ', Default::default()), } @@ -898,22 +927,39 @@ fn render_label_char(c: char) -> Character { Character::new_char(c, CharacterData::new_colored("#555555")) } -fn copy_color(copy_depth: &Option, is_optional: bool) -> impl ToString { +enum OptionalChar { + NotOptional, + Source, + Inner, +} + +fn copy_color(copy_depth: &Option, optional: OptionalChar) -> impl ToString { if let Some(copy_depth) = copy_depth { - if is_optional { - OPTIONAL_COPY_COLORS[copy_depth % OPTIONAL_COPY_COLORS.len()] - } else { - COPY_COLORS[copy_depth % COPY_COLORS.len()] + match optional { + OptionalChar::NotOptional => COPY_COLORS[copy_depth % COPY_COLORS.len()], + OptionalChar::Source => { + OPTIONAL_SOURCE_COPY_COLORS[copy_depth % OPTIONAL_SOURCE_COPY_COLORS.len()] + } + OptionalChar::Inner => { + OPTIONAL_INNER_COPY_COLORS[copy_depth % OPTIONAL_INNER_COPY_COLORS.len()] + } } - } else if is_optional { - OPTIONAL_SOURCE_COLOR } else { - "black" + match optional { + OptionalChar::NotOptional => "black", + OptionalChar::Source => OPTIONAL_SOURCE_COLOR, + OptionalChar::Inner => OPTIONAL_INNER_COLOR, + } } } /// Returns the SVG legend along with its width and height. -fn legend(reference_name: &str, query_name: &str, scale: f32) -> (Group, f32, f32) { +fn legend( + reference_name: &str, + query_name: &str, + scale: f32, + config: &SvgConfig, +) -> (Group, f32, f32) { let mut result = Group::new(); let headline = "Legend:"; @@ -974,16 +1020,18 @@ fn legend(reference_name: &str, query_name: &str, scale: f32) -> (Group, f32, f3 .character_height .max(sans_serif_mono::FONT.character_height); - let uncertainty_label = "BLUE CHARACTERS"; - result = result.add(svg_string( - uncertainty_label - .chars() - .map(|c| Character::new_char(c, CharacterData::new_colored(OPTIONAL_SOURCE_COLOR))), - &SvgLocation { x: 0.0, y }, - &typewriter::FONT, - )); - label_width = label_width - .max(uncertainty_label.chars().count() as f32 * typewriter::FONT.character_width); + if config.visualise_equal_cost_ranges { + let uncertainty_label = "BLUE CHARACTERS"; + result = result.add(svg_string( + uncertainty_label + .chars() + .map(|c| Character::new_char(c, CharacterData::new_colored(OPTIONAL_SOURCE_COLOR))), + &SvgLocation { x: 0.0, y }, + &typewriter::FONT, + )); + label_width = label_width + .max(uncertainty_label.chars().count() as f32 * typewriter::FONT.character_width); + } // Explanations. let label_width = label_width + typewriter::FONT.character_width; @@ -1017,20 +1065,22 @@ fn legend(reference_name: &str, query_name: &str, scale: f32) -> (Group, f32, f3 .character_height .max(sans_serif_mono::FONT.character_height); - let uncertainty_explanation = "Equal-cost range of the TSM"; - result = result.add(svg_string( - uncertainty_explanation - .chars() - .map(Character::::new_char_with_default), - &SvgLocation { x: label_width, y }, - &sans_serif_mono::FONT, - )); - explanation_width = explanation_width.max( - uncertainty_explanation.chars().count() as f32 * sans_serif_mono::FONT.character_width, - ); - y += typewriter::FONT - .character_height - .max(sans_serif_mono::FONT.character_height); + if config.visualise_equal_cost_ranges { + let uncertainty_explanation = "Equal-cost range of the TSM"; + result = result.add(svg_string( + uncertainty_explanation + .chars() + .map(Character::::new_char_with_default), + &SvgLocation { x: label_width, y }, + &sans_serif_mono::FONT, + )); + explanation_width = explanation_width.max( + uncertainty_explanation.chars().count() as f32 * sans_serif_mono::FONT.character_width, + ); + y += typewriter::FONT + .character_height + .max(sans_serif_mono::FONT.character_height); + } result = result.set("transform", format!("scale({scale})")); let legend_width = headline_width.max(label_width + explanation_width) * scale; diff --git a/lib_tsshow/src/ts_arrangement/complement.rs b/lib_tsshow/src/ts_arrangement/complement.rs index b31c925..3b6f849 100644 --- a/lib_tsshow/src/ts_arrangement/complement.rs +++ b/lib_tsshow/src/ts_arrangement/complement.rs @@ -46,6 +46,11 @@ impl TsComplementArrangement { column, copy_depth: None, .. + } + | SourceChar::OptionalSource { + column, + copy_depth: None, + .. } => { sequence_c.push(ComplementChar::new_hidden(*column, false)); } @@ -59,6 +64,10 @@ impl TsComplementArrangement { copy_depth: Some(_), .. } + | SourceChar::OptionalSource { + copy_depth: Some(_), + .. + } | SourceChar::Hidden { copy_depth: Some(_), .. diff --git a/lib_tsshow/src/ts_arrangement/inner.rs b/lib_tsshow/src/ts_arrangement/inner.rs index 6634743..04289c8 100644 --- a/lib_tsshow/src/ts_arrangement/inner.rs +++ b/lib_tsshow/src/ts_arrangement/inner.rs @@ -569,7 +569,7 @@ impl From for InnerChar { lower_case, copy_depth, }, - SourceChar::Hidden { .. } => { + SourceChar::OptionalSource { .. } | SourceChar::Hidden { .. } => { panic!("Cannot be translated into InnerChar") } SourceChar::Gap { copy_depth } => Self::Gap { copy_depth }, diff --git a/lib_tsshow/src/ts_arrangement/source.rs b/lib_tsshow/src/ts_arrangement/source.rs index 66c327b..4a1c580 100644 --- a/lib_tsshow/src/ts_arrangement/source.rs +++ b/lib_tsshow/src/ts_arrangement/source.rs @@ -36,6 +36,11 @@ pub enum SourceChar { lower_case: bool, copy_depth: Option, }, + OptionalSource { + column: SourceColumn, + lower_case: bool, + copy_depth: Option, + }, Hidden { column: SourceColumn, copy_depth: Option, @@ -807,6 +812,7 @@ impl SourceChar { pub fn is_copy(&self) -> bool { match self { Self::Source { copy_depth, .. } + | Self::OptionalSource { copy_depth, .. } | Self::Hidden { copy_depth, .. } | Self::Gap { copy_depth } => copy_depth.is_some(), Self::Separator | Self::Spacer | Self::Blank => panic!("Blank has no copy property"), @@ -815,7 +821,9 @@ impl SourceChar { pub fn copy_depth(&self) -> Option { match self { - Self::Source { copy_depth, .. } | Self::Hidden { copy_depth, .. } => *copy_depth, + Self::Source { copy_depth, .. } + | Self::OptionalSource { copy_depth, .. } + | Self::Hidden { copy_depth, .. } => *copy_depth, Self::Gap { copy_depth } => *copy_depth, Self::Separator | Self::Spacer | Self::Blank => panic!("Blank has no copy property"), } @@ -823,7 +831,9 @@ impl SourceChar { pub fn to_lower_case(&mut self) { match self { - Self::Source { lower_case, .. } => *lower_case = true, + Self::Source { lower_case, .. } | Self::OptionalSource { lower_case, .. } => { + *lower_case = true + } Self::Hidden { .. } | Self::Gap { .. } | Self::Separator @@ -836,7 +846,9 @@ impl SourceChar { pub fn to_upper_case(&mut self) { match self { - Self::Source { lower_case, .. } => *lower_case = false, + Self::Source { lower_case, .. } | Self::OptionalSource { lower_case, .. } => { + *lower_case = false + } Self::Hidden { .. } | Self::Gap { .. } | Self::Separator @@ -861,12 +873,40 @@ impl SourceChar { }; } Self::Hidden { .. } => unreachable!("Already hidden"), - Self::Gap { .. } | Self::Separator | Self::Spacer | Self::Blank => { + Self::OptionalSource { .. } + | Self::Gap { .. } + | Self::Separator + | Self::Spacer + | Self::Blank => { unreachable!("Cannot be hidden: {self:?}") } } } + pub fn make_optional(&mut self) { + match self { + Self::Source { + column, + lower_case, + copy_depth, + } => { + *self = Self::OptionalSource { + column: *column, + lower_case: *lower_case, + copy_depth: *copy_depth, + }; + } + Self::OptionalSource { .. } => unreachable!("Already optional"), + Self::Hidden { .. } + | Self::Gap { .. } + | Self::Separator + | Self::Spacer + | Self::Blank => { + unreachable!("Cannot be made optional: {self:?}") + } + } + } + pub fn make_copy(&self) -> Self { match self { Self::Source { @@ -876,6 +916,13 @@ impl SourceChar { lower_case: false, copy_depth: Some(copy_depth.map(|copy_depth| copy_depth + 1).unwrap_or(0)), }, + Self::OptionalSource { + column, copy_depth, .. + } => Self::OptionalSource { + column: *column, + lower_case: false, + copy_depth: Some(copy_depth.map(|copy_depth| copy_depth + 1).unwrap_or(0)), + }, Self::Hidden { column, copy_depth } => Self::Hidden { column: *column, copy_depth: Some(copy_depth.map(|copy_depth| copy_depth + 1).unwrap_or(0)), @@ -896,6 +943,13 @@ impl SourceChar { lower_case: false, copy_depth: Some(copy_depth.map(|copy_depth| copy_depth + 1).unwrap_or(0)), }, + Self::OptionalSource { + column, copy_depth, .. + } => Self::OptionalSource { + column: *column, + lower_case: false, + copy_depth: Some(copy_depth.map(|copy_depth| copy_depth + 1).unwrap_or(0)), + }, Self::Gap { .. } | Self::Separator | Self::Spacer | Self::Blank => { panic!("Should never be copied: {self:?}") } @@ -906,13 +960,18 @@ impl SourceChar { impl Char for SourceChar { fn source_column(&self) -> SourceColumn { match self { - Self::Source { column, .. } | Self::Hidden { column, .. } => *column, + Self::Source { column, .. } + | Self::OptionalSource { column, .. } + | Self::Hidden { column, .. } => *column, Self::Gap { .. } | Self::Separator | Self::Spacer | Self::Blank => panic!("Not a char"), } } fn is_char(&self) -> bool { - matches!(self, Self::Source { .. } | Self::Hidden { .. }) + matches!( + self, + Self::Source { .. } | Self::OptionalSource { .. } | Self::Hidden { .. } + ) } fn is_gap(&self) -> bool { @@ -934,6 +993,9 @@ impl Char for SourceChar { Self::Source { copy_depth: None, .. + } | Self::OptionalSource { + copy_depth: None, + .. } | Self::Hidden { copy_depth: None, .. diff --git a/test_files/twin_show_ts_optional.fa b/test_files/twin_show_ts_optional.fa new file mode 100644 index 0000000..5156128 --- /dev/null +++ b/test_files/twin_show_ts_optional.fa @@ -0,0 +1,4 @@ +>REFERENCE +AAAAAAAAAAAAAAAAAAAAAAAAAACCTTTTTTTTTTTTTTTTTTTTTTAAAAAAAAAAAAAAAAAAATTTTTTTTTTTTTTTTTTTTTT +>QUERY +AAAAAAAAAAAAAAAAAAAAAAAAAACGTTTTTTTTTTTTTTTTTTTTTTAAAAAAAAAAAAAAAAAAGGTTTTTTTTTTTTTTTTTTTTT \ No newline at end of file diff --git a/tsalign/Cargo.toml b/tsalign/Cargo.toml index 09a240e..c8d1021 100644 --- a/tsalign/Cargo.toml +++ b/tsalign/Cargo.toml @@ -35,7 +35,7 @@ serde.workspace = true toml = "1.1.4" log.workspace = true simplelog = "0.12.2" -anyhow = "1.0.97" +anyhow = "1.0.104" utf8-chars = "3.0.5" sha = "1.0.3" bincode = { version = "2.0.1", features = ["serde"] } From 7dc4344517ef2d33e037cc230bbf65f9e6993c8a Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 10 Aug 2026 11:20:20 +0300 Subject: [PATCH 3/6] Arrange equal cost ranges in source arrangement without rearranging arrows. --- Cargo.lock | 1 + lib_tsshow/Cargo.toml | 1 + lib_tsshow/src/svg.rs | 23 +++- lib_tsshow/src/ts_arrangement.rs | 7 +- lib_tsshow/src/ts_arrangement/index_types.rs | 2 +- lib_tsshow/src/ts_arrangement/inner.rs | 11 +- lib_tsshow/src/ts_arrangement/source.rs | 137 +++++++++++++++++-- test_files/twin_show_ts_optional.fa | 2 +- test_files/twin_show_ts_optional.sh | 6 + tsalign/src/show.rs | 8 +- 10 files changed, 165 insertions(+), 33 deletions(-) create mode 100755 test_files/twin_show_ts_optional.sh diff --git a/Cargo.lock b/Cargo.lock index 03813ff..859e88f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -636,6 +636,7 @@ dependencies = [ name = "lib_tsshow" version = "4.1.0" dependencies = [ + "clap", "ena", "itertools 0.15.0", "lib_tsalign", diff --git a/lib_tsshow/Cargo.toml b/lib_tsshow/Cargo.toml index 709b659..75819e5 100644 --- a/lib_tsshow/Cargo.toml +++ b/lib_tsshow/Cargo.toml @@ -18,3 +18,4 @@ tagged-vec = "1.1.0" strong-type = "1.1.1" ena = "0.14.3" itertools.workspace = true +clap.workspace = true diff --git a/lib_tsshow/src/svg.rs b/lib_tsshow/src/svg.rs index e0d47bd..aff0147 100644 --- a/lib_tsshow/src/svg.rs +++ b/lib_tsshow/src/svg.rs @@ -1,6 +1,7 @@ use std::{collections::HashMap, io::Write}; use arrows::{Arrow, ArrowEndpointDirection, add_arrow_defs}; +use clap::ValueEnum; use font::{CharacterData, sans_serif_mono, svg_string, typewriter}; use indexed_str::IndexedStr; use lib_tsalign::{ @@ -67,7 +68,18 @@ pub struct SvgConfig { /// Restrict the context around the template switches to this many characters on each side. /// If `None`, the full sequences will be rendered. pub restrict_context: Option, - pub visualise_equal_cost_ranges: bool, + pub equal_cost_range_mode: EqualCostRangeMode, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)] +pub enum EqualCostRangeMode { + /// Do not render equal cost ranges. + None, + /// Render the equal cost ranges only on the inner sequences. + InnerOnly, + /// Render the equal cost ranges on all sequences. + /// This may be messy if there are overlaps. + Full, } pub fn create_ts_svg( @@ -99,7 +111,7 @@ pub fn create_ts_svg( reference.len(), query.len(), alignment.iter_flat_cloned(), - config.visualise_equal_cost_ranges, + config.equal_cost_range_mode, )?; if config.render_more_complement { @@ -140,6 +152,7 @@ pub fn create_ts_svg( reference.len(), query.len(), alignment.iter_flat_cloned(), + config.equal_cost_range_mode, &mut Vec::new(), ) .map(|mut no_ts_arrangement| { @@ -1020,12 +1033,12 @@ fn legend( .character_height .max(sans_serif_mono::FONT.character_height); - if config.visualise_equal_cost_ranges { + if config.equal_cost_range_mode != EqualCostRangeMode::None { let uncertainty_label = "BLUE CHARACTERS"; result = result.add(svg_string( uncertainty_label .chars() - .map(|c| Character::new_char(c, CharacterData::new_colored(OPTIONAL_SOURCE_COLOR))), + .map(|c| Character::new_char(c, CharacterData::new_colored(OPTIONAL_INNER_COLOR))), &SvgLocation { x: 0.0, y }, &typewriter::FONT, )); @@ -1065,7 +1078,7 @@ fn legend( .character_height .max(sans_serif_mono::FONT.character_height); - if config.visualise_equal_cost_ranges { + if config.equal_cost_range_mode != EqualCostRangeMode::None { let uncertainty_explanation = "Equal-cost range of the TSM"; result = result.add(svg_string( uncertainty_explanation diff --git a/lib_tsshow/src/ts_arrangement.rs b/lib_tsshow/src/ts_arrangement.rs index 79c5b5f..a596e40 100644 --- a/lib_tsshow/src/ts_arrangement.rs +++ b/lib_tsshow/src/ts_arrangement.rs @@ -15,7 +15,7 @@ use source::{SourceChar, TsSourceArrangement}; use tagged_vec::TaggedVec; use template_switch::TemplateSwitch; -use crate::error::Result; +use crate::{error::Result, svg::EqualCostRangeMode}; pub mod character; pub mod complement; @@ -38,7 +38,7 @@ impl TsArrangement { reference_length: usize, query_length: usize, alignment: impl IntoIterator, - visualise_equal_cost_ranges: bool, + equal_cost_range_mode: EqualCostRangeMode, ) -> Result { let mut template_switches = Vec::new(); let mut source = TsSourceArrangement::new( @@ -47,6 +47,7 @@ impl TsArrangement { reference_length, query_length, alignment, + equal_cost_range_mode, &mut template_switches, )?; let mut complement = TsComplementArrangement::new(&source); @@ -54,7 +55,7 @@ impl TsArrangement { &mut source, &mut complement, template_switches, - visualise_equal_cost_ranges, + equal_cost_range_mode, ); Ok(Self { diff --git a/lib_tsshow/src/ts_arrangement/index_types.rs b/lib_tsshow/src/ts_arrangement/index_types.rs index dcc4aea..53ae82b 100644 --- a/lib_tsshow/src/ts_arrangement/index_types.rs +++ b/lib_tsshow/src/ts_arrangement/index_types.rs @@ -7,7 +7,7 @@ pub struct SourceColumn(usize); /// A column index over the characters in an arrangement. /// -/// This includes copies and hidden characters, but not gaps and blanks. +/// This includes copies, optional and hidden characters, but not gaps and blanks. #[derive(StrongType)] #[strong_type(conversion, addable)] pub struct ArrangementCharColumn(usize); diff --git a/lib_tsshow/src/ts_arrangement/inner.rs b/lib_tsshow/src/ts_arrangement/inner.rs index 04289c8..2c56f54 100644 --- a/lib_tsshow/src/ts_arrangement/inner.rs +++ b/lib_tsshow/src/ts_arrangement/inner.rs @@ -6,7 +6,7 @@ use lib_tsalign::a_star_aligner::template_switch_distance::{ use log::{trace, warn}; use tagged_vec::TaggedVec; -use crate::ts_arrangement::character::Char; +use crate::{svg::EqualCostRangeMode, ts_arrangement::character::Char}; use super::{ complement::TsComplementArrangement, @@ -49,7 +49,7 @@ impl TsInnerArrangement { source_arrangement: &mut TsSourceArrangement, complement_arrangement: &mut TsComplementArrangement, template_switches: Vec, - visualise_equal_cost_ranges: bool, + equal_cost_range_mode: EqualCostRangeMode, ) -> Self { let mut result = Self { inners: Default::default(), @@ -319,7 +319,10 @@ impl TsInnerArrangement { .skip(inner.len()); inner.extend(suffix_blanks); - if visualise_equal_cost_ranges { + if matches!( + equal_cost_range_mode, + EqualCostRangeMode::InnerOnly | EqualCostRangeMode::Full, + ) { // Add characters to visualise TSM equal cost range. if forward { warn!("TSM equal cost range visualisation is not implemented for forward TSMs.") @@ -362,7 +365,7 @@ impl TsInnerArrangement { let mut arrangement_column = last_initial_blank.map(|i| i + 1usize).unwrap_or(0.into()); let mut source_column = first_source_column; - #[allow(clippy::explicit_counter_loop)] + #[expect(clippy::explicit_counter_loop)] for _ in 0..ts.equal_cost_range.max_end { arrangement_column -= 1; source_column += 1; diff --git a/lib_tsshow/src/ts_arrangement/source.rs b/lib_tsshow/src/ts_arrangement/source.rs index 4a1c580..8e79c4c 100644 --- a/lib_tsshow/src/ts_arrangement/source.rs +++ b/lib_tsshow/src/ts_arrangement/source.rs @@ -15,7 +15,7 @@ use super::{ index_types::{ArrangementCharColumn, ArrangementColumn, SourceColumn}, template_switch::TemplateSwitch, }; -use crate::error::Result; +use crate::{error::Result, svg::EqualCostRangeMode}; pub struct TsSourceArrangement { reference: TaggedVec, @@ -64,6 +64,7 @@ impl TsSourceArrangement { reference_length: usize, query_length: usize, alignment: impl IntoIterator, + equal_cost_range_mode: EqualCostRangeMode, template_switches_out: &mut impl Extend, ) -> Result { let mut ts_index = 0; @@ -160,6 +161,7 @@ impl TsSourceArrangement { direction, first_offset, equal_cost_range, + equal_cost_range_mode, &mut alignment, &mut current_reference_index, &mut current_query_index, @@ -230,6 +232,7 @@ impl TsSourceArrangement { ts_direction: TemplateSwitchDirection, first_offset: isize, equal_cost_range: EqualCostRange, + equal_cost_range_mode: EqualCostRangeMode, mut alignment: impl Iterator, current_reference_index: &mut ArrangementColumn, current_query_index: &mut ArrangementColumn, @@ -237,7 +240,9 @@ impl TsSourceArrangement { debug!("Aligning template switch #{ts_index}"); let sp1_reference = self.reference_arrangement_to_arrangement_char_column(*current_reference_index); + let sp1_reference_arrangement_column = *current_reference_index; let sp1_query = self.query_arrangement_to_arrangement_char_column(*current_query_index); + let sp1_query_arrangement_column = *current_query_index; let sp2_ancestor = match ts_ancestor { TemplateSwitchAncestor::Reference => { let source_current_reference_index = @@ -371,15 +376,76 @@ impl TsSourceArrangement { // Insert spacers. let mut required_spacer_count = 4usize.saturating_sub(anti_descendant_inner_length); + let descendant_minimal_equal_cost_range_start = + match ts_descendant { + TemplateSwitchDescendant::Reference => sp1_reference, + TemplateSwitchDescendant::Query => sp1_query, + } + usize::try_from(equal_cost_range.max_start).unwrap(); + let descendant_minimal_equal_cost_range_end = + match ts_descendant { + TemplateSwitchDescendant::Reference => { + self.reference_arrangement_to_arrangement_char_column(*current_descendant_index) + } + TemplateSwitchDescendant::Query => { + self.query_arrangement_to_arrangement_char_column(*current_descendant_index) + } + } - usize::try_from(-equal_cost_range.min_end).unwrap(); + let (descendant_minimal_equal_cost_range_start, descendant_minimal_equal_cost_range_end) = + match ts_descendant { + TemplateSwitchDescendant::Reference => ( + self.reference_arrangement_char_to_arrangement_column( + descendant_minimal_equal_cost_range_start, + ), + self.reference_arrangement_char_to_arrangement_column( + descendant_minimal_equal_cost_range_end, + ), + ), + TemplateSwitchDescendant::Query => ( + self.query_arrangement_char_to_arrangement_column( + descendant_minimal_equal_cost_range_start, + ), + self.query_arrangement_char_to_arrangement_column( + descendant_minimal_equal_cost_range_end, + ), + ), + }; + + let (descendant, anti_descendant) = match ts_descendant { + TemplateSwitchDescendant::Reference => (&mut self.reference, &mut self.query), + TemplateSwitchDescendant::Query => (&mut self.query, &mut self.reference), + }; + match descendant_inner_length.cmp(&anti_descendant_inner_length) { Ordering::Less => { let delta = anti_descendant_inner_length .checked_sub(descendant_inner_length) .unwrap(); - descendant.splice( - *current_descendant_index..*current_descendant_index, - iter::repeat_n(SourceChar::Blank, delta), - ); + + if descendant_minimal_equal_cost_range_start + <= descendant_minimal_equal_cost_range_end + { + descendant.splice( + descendant_minimal_equal_cost_range_end + ..descendant_minimal_equal_cost_range_end, + iter::repeat_n(SourceChar::Blank, delta), + ); + } else { + // If the ranges overlap, we place the spacers around the overlap. + let start_delta = delta / 2; + let end_delta = delta - start_delta; + + descendant.splice( + descendant_minimal_equal_cost_range_start + ..descendant_minimal_equal_cost_range_start, + iter::repeat_n(SourceChar::Blank, start_delta), + ); + descendant.splice( + descendant_minimal_equal_cost_range_end + ..descendant_minimal_equal_cost_range_end, + iter::repeat_n(SourceChar::Blank, end_delta), + ); + } + *current_descendant_index += delta; } Ordering::Equal => { /* Do nothing */ } @@ -418,6 +484,51 @@ impl TsSourceArrangement { } }; + if equal_cost_range_mode == EqualCostRangeMode::Full { + let sp1_descendant = match ts_descendant { + TemplateSwitchDescendant::Reference => sp1_reference_arrangement_column, + TemplateSwitchDescendant::Query => sp1_query_arrangement_column, + }; + let sp4_descendant = match ts_descendant { + TemplateSwitchDescendant::Reference => *current_reference_index, + TemplateSwitchDescendant::Query => *current_query_index, + }; + + // Mark characters left of SP1 as optional. + descendant + .iter_values_mut() + .take(usize::from(sp1_descendant)) + .rev() + .filter(|c| c.is_char()) + .take(usize::try_from(-equal_cost_range.min_start).unwrap()) + .for_each(|c| c.make_optional()); + + // Mark characters right of SP4 as optional. + descendant + .iter_values_mut() + .skip(usize::from(sp4_descendant)) + .filter(|c| c.is_char()) + .take(usize::try_from(equal_cost_range.max_end).unwrap()) + .for_each(|c| c.make_optional()); + + // Mark characters right of SP1 as optional. + descendant + .iter_values_mut() + .skip(usize::from(sp1_descendant)) + .filter(|c| c.is_char()) + .take(usize::try_from(equal_cost_range.max_start).unwrap()) + .for_each(|c| c.make_optional()); + + // Mark characters left of SP4 as optional. + descendant + .iter_values_mut() + .take(usize::from(sp4_descendant)) + .rev() + .filter(|c| c.is_char()) + .take(usize::try_from(-equal_cost_range.min_end).unwrap()) + .for_each(|c| c.make_optional()); + } + let sp4_reference = self.reference_arrangement_to_arrangement_char_column(*current_reference_index); let sp4_query = self.query_arrangement_to_arrangement_char_column(*current_query_index); @@ -886,22 +997,17 @@ impl SourceChar { pub fn make_optional(&mut self) { match self { Self::Source { - column, - lower_case, - copy_depth, - } => { + column, copy_depth, .. + } + | Self::Hidden { column, copy_depth } => { *self = Self::OptionalSource { column: *column, - lower_case: *lower_case, + lower_case: false, copy_depth: *copy_depth, }; } Self::OptionalSource { .. } => unreachable!("Already optional"), - Self::Hidden { .. } - | Self::Gap { .. } - | Self::Separator - | Self::Spacer - | Self::Blank => { + Self::Gap { .. } | Self::Separator | Self::Spacer | Self::Blank => { unreachable!("Cannot be made optional: {self:?}") } } @@ -987,6 +1093,7 @@ impl Char for SourceChar { } /// Returns true if this is a source char and not a copy of a source char. + /// It may be optional or hidden. fn is_source_char(&self) -> bool { matches!( self, diff --git a/test_files/twin_show_ts_optional.fa b/test_files/twin_show_ts_optional.fa index 5156128..e75d1b9 100644 --- a/test_files/twin_show_ts_optional.fa +++ b/test_files/twin_show_ts_optional.fa @@ -1,4 +1,4 @@ >REFERENCE -AAAAAAAAAAAAAAAAAAAAAAAAAACCTTTTTTTTTTTTTTTTTTTTTTAAAAAAAAAAAAAAAAAAATTTTTTTTTTTTTTTTTTTTTT +AAAAAAAAAAAAAAAAAAAAAAAAAACCTTTTTTTTTTTTTTTTTTTTTTAAAAAAAAAAAAAAAAAAAGGGGGGGGGTTTTTTTTTTTTTTTTTTTTTT >QUERY AAAAAAAAAAAAAAAAAAAAAAAAAACGTTTTTTTTTTTTTTTTTTTTTTAAAAAAAAAAAAAAAAAAGGTTTTTTTTTTTTTTTTTTTTT \ No newline at end of file diff --git a/test_files/twin_show_ts_optional.sh b/test_files/twin_show_ts_optional.sh new file mode 100755 index 0000000..ea6f9ea --- /dev/null +++ b/test_files/twin_show_ts_optional.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +cargo run -- align -p test_files/twin_show_ts_optional.fa -c test_files/config/small --alphabet dna-n -o test_files/twin_show_ts_optional_no_ts.toml --rq-ranges R65..80Q65..75 --no-ts +cargo run -- align -p test_files/twin_show_ts_optional.fa -c test_files/config/small --alphabet dna-n -o test_files/twin_show_ts_optional.toml --rq-ranges R65..80Q65..75 +cargo run -- show -i test_files/twin_show_ts_optional.toml -n test_files/twin_show_ts_optional_no_ts.toml -pas test_files/twin_show_ts_optional.svg -e full \ No newline at end of file diff --git a/tsalign/src/show.rs b/tsalign/src/show.rs index 2fdafa4..90b0bcb 100644 --- a/tsalign/src/show.rs +++ b/tsalign/src/show.rs @@ -8,7 +8,7 @@ use anyhow::{Context, Result, bail}; use clap::Parser; use lib_tsshow::{ plain_text::show_template_switches, - svg::{SvgConfig, create_error_svg, create_ts_svg}, + svg::{EqualCostRangeMode, SvgConfig, create_error_svg, create_ts_svg}, svg_to_png, }; use log::{LevelFilter, error, info, warn}; @@ -64,8 +64,8 @@ pub struct Cli { restrict_context_to: Option, /// Add a visualisation of the equal-cost ranges to the SVG output. - #[clap(long, short = 'e')] - visualise_equal_cost_ranges: bool, + #[clap(long, short = 'e', default_value = "none")] + equal_cost_range_mode: EqualCostRangeMode, } pub fn cli(cli: Cli) -> Result<()> { @@ -115,7 +115,7 @@ pub fn cli(cli: Cli) -> Result<()> { render_arrows: cli.svg_arrows, render_more_complement: cli.more_svg_complement, restrict_context: cli.restrict_context_to, - visualise_equal_cost_ranges: cli.visualise_equal_cost_ranges, + equal_cost_range_mode: cli.equal_cost_range_mode, }, ) { if cli.render_always { From 4197bc1f28734ce70ae490df30847ff541291a72 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 10 Aug 2026 11:38:00 +0300 Subject: [PATCH 4/6] Make show arrows display maximum of equal cost ranges. --- lib_tsshow/src/svg.rs | 71 +++++++++++-------------- lib_tsshow/src/ts_arrangement.rs | 24 ++++++++- lib_tsshow/src/ts_arrangement/source.rs | 10 ++++ 3 files changed, 65 insertions(+), 40 deletions(-) diff --git a/lib_tsshow/src/svg.rs b/lib_tsshow/src/svg.rs index aff0147..c56a0c7 100644 --- a/lib_tsshow/src/svg.rs +++ b/lib_tsshow/src/svg.rs @@ -199,6 +199,7 @@ pub fn create_ts_svg( sp3_ancestor, sp4_reference, sp4_query, + equal_cost_range, .. }, ) in ts_arrangement.template_switches() @@ -256,46 +257,38 @@ pub fn create_ts_svg( } }*/ - let descendant_sp4_minus_one = match descendant { - TemplateSwitchDescendant::Reference => sp4_reference.checked_sub(1).map(|column| { - ts_arrangement.reference_arrangement_char_to_arrangement_column(column) + 1usize - }), - TemplateSwitchDescendant::Query => sp4_query.checked_sub(1).map(|column| { - ts_arrangement.query_arrangement_char_to_arrangement_column(column) + 1usize - }), - }; + let descendant_sp1 = ts_arrangement.descendant_arrangement_char_to_arrangement_column( + match descendant { + TemplateSwitchDescendant::Reference => *sp1_reference, + TemplateSwitchDescendant::Query => *sp1_query, + } - usize::try_from(-equal_cost_range.min_start).unwrap(), + *descendant, + ); - let (descendant_sp1, descendant_sp4, descendant_row) = match descendant { - TemplateSwitchDescendant::Reference => ( - ts_arrangement.reference_arrangement_char_to_arrangement_column(*sp1_reference), - ts_arrangement - .reference() - .iter( - descendant_sp4_minus_one.unwrap_or(0.into()) - ..ts_arrangement - .reference_arrangement_char_to_arrangement_column(*sp4_reference) - + 1usize, - ) - .find(|(_, c)| !c.is_blank()) - .unwrap() - .0, - TsArrangementRow::Reference, - ), - TemplateSwitchDescendant::Query => ( - ts_arrangement.query_arrangement_char_to_arrangement_column(*sp1_query), - ts_arrangement - .query() - .iter( - descendant_sp4_minus_one.unwrap_or(0.into()) - ..ts_arrangement - .query_arrangement_char_to_arrangement_column(*sp4_query) - + 1usize, - ) - .find(|(_, c)| !c.is_blank()) - .map(|(column, _)| column) - .unwrap_or(ts_arrangement.query().len().into()), - TsArrangementRow::Query, - ), + let descendant_sp4 = match descendant { + TemplateSwitchDescendant::Reference => *sp4_reference, + TemplateSwitchDescendant::Query => *sp4_query, + } + usize::try_from(equal_cost_range.max_end).unwrap(); + let descendant_sp4_minus_one = descendant_sp4.checked_sub(1).map(|column| { + ts_arrangement.descendant_arrangement_char_to_arrangement_column(column, *descendant) + + 1usize + }); + let descendant_sp4 = ts_arrangement + .descendant(*descendant) + .iter( + descendant_sp4_minus_one.unwrap_or(0.into()) + ..ts_arrangement.descendant_arrangement_char_to_arrangement_column( + descendant_sp4, + *descendant, + ) + 1usize, + ) + .find(|(_, c)| !c.is_blank()) + .unwrap() + .0; + + let descendant_row = match descendant { + TemplateSwitchDescendant::Reference => TsArrangementRow::Reference, + TemplateSwitchDescendant::Query => TsArrangementRow::Query, }; let forward = sp2_ancestor < sp3_ancestor; diff --git a/lib_tsshow/src/ts_arrangement.rs b/lib_tsshow/src/ts_arrangement.rs index a596e40..af2859c7 100644 --- a/lib_tsshow/src/ts_arrangement.rs +++ b/lib_tsshow/src/ts_arrangement.rs @@ -8,7 +8,7 @@ use complement::{ComplementChar, TsComplementArrangement}; use index_types::{ArrangementCharColumn, ArrangementColumn, SourceColumn, TsInnerIdentifier}; use inner::{TsInner, TsInnerArrangement}; use lib_tsalign::a_star_aligner::template_switch_distance::{ - AlignmentType, TemplateSwitchAncestor, + AlignmentType, TemplateSwitchAncestor, TemplateSwitchDescendant, }; use log::debug; use source::{SourceChar, TsSourceArrangement}; @@ -183,6 +183,13 @@ impl TsArrangement { self.source.query() } + pub fn descendant( + &self, + descendant: TemplateSwitchDescendant, + ) -> &TaggedVec { + self.source.descendant(descendant) + } + pub fn reference_complement(&self) -> &TaggedVec { self.complement.reference_complement() } @@ -243,6 +250,21 @@ impl TsArrangement { .query_arrangement_char_to_arrangement_column(column) } + pub fn descendant_arrangement_char_to_arrangement_column( + &self, + column: ArrangementCharColumn, + descendant: TemplateSwitchDescendant, + ) -> ArrangementColumn { + match descendant { + TemplateSwitchDescendant::Reference => { + self.reference_arrangement_char_to_arrangement_column(column) + } + TemplateSwitchDescendant::Query => { + self.query_arrangement_char_to_arrangement_column(column) + } + } + } + pub fn reference_source_to_arrangement_column( &self, column: SourceColumn, diff --git a/lib_tsshow/src/ts_arrangement/source.rs b/lib_tsshow/src/ts_arrangement/source.rs index 8e79c4c..e10f395 100644 --- a/lib_tsshow/src/ts_arrangement/source.rs +++ b/lib_tsshow/src/ts_arrangement/source.rs @@ -559,6 +559,16 @@ impl TsSourceArrangement { } } + pub fn descendant( + &self, + descendant: TemplateSwitchDescendant, + ) -> &TaggedVec { + match descendant { + TemplateSwitchDescendant::Reference => self.reference(), + TemplateSwitchDescendant::Query => self.query(), + } + } + pub fn reference(&self) -> &TaggedVec { &self.reference } From 15eeacea1362dff65dc9f239069fc66503fe82f2 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 10 Aug 2026 11:41:22 +0300 Subject: [PATCH 5/6] Update rust version to fix broken lints. --- lib_tsshow/src/ts_arrangement/inner.rs | 1 - rust-toolchain.toml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib_tsshow/src/ts_arrangement/inner.rs b/lib_tsshow/src/ts_arrangement/inner.rs index 2c56f54..3d93d74 100644 --- a/lib_tsshow/src/ts_arrangement/inner.rs +++ b/lib_tsshow/src/ts_arrangement/inner.rs @@ -365,7 +365,6 @@ impl TsInnerArrangement { let mut arrangement_column = last_initial_blank.map(|i| i + 1usize).unwrap_or(0.into()); let mut source_column = first_source_column; - #[expect(clippy::explicit_counter_loop)] for _ in 0..ts.equal_cost_range.max_end { arrangement_column -= 1; source_column += 1; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index b212d24..65486df 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.95.0" +channel = "1.97.1" components = ["rust-analyzer"] From 609a9351ae3e86fbe38518912b5b7ea40b68f270 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 10 Aug 2026 11:47:45 +0300 Subject: [PATCH 6/6] Make CLI change non-breaking. --- tsalign/src/show.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsalign/src/show.rs b/tsalign/src/show.rs index 90b0bcb..d617694 100644 --- a/tsalign/src/show.rs +++ b/tsalign/src/show.rs @@ -64,7 +64,7 @@ pub struct Cli { restrict_context_to: Option, /// Add a visualisation of the equal-cost ranges to the SVG output. - #[clap(long, short = 'e', default_value = "none")] + #[clap(long, short = 'e', default_value = "none", default_missing_value = "full", num_args = 0..=1)] equal_cost_range_mode: EqualCostRangeMode, }