Keep the flux into the current selection highlighted
#6
by lvwerra HF Staff - opened
- atlas.css +7 -1
- taxonomy.py +4 -1
- tests/test_taxonomy.py +13 -0
atlas.css
CHANGED
|
@@ -26,9 +26,15 @@
|
|
| 26 |
.flow { stroke:none; transition:opacity .15s; }
|
| 27 |
.flow-annotated { fill:#40916f; opacity:.42; }
|
| 28 |
.flow-missing { fill:#cfd5c9; opacity:.6; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
/* Lighting a group also lights the flux arriving at it, so you can see where
|
| 30 |
a group's assemblies came from. Applied from atlas.js, since the ribbons are
|
| 31 |
-
painted under every node and cannot be reached with a CSS sibling rule.
|
|
|
|
| 32 |
.flow.is-lit { opacity:.9; }
|
| 33 |
.flow-annotated.is-lit { fill:#2f7d5c; }
|
| 34 |
.flow-missing.is-lit { fill:#b4bdac; }
|
|
|
|
| 26 |
.flow { stroke:none; transition:opacity .15s; }
|
| 27 |
.flow-annotated { fill:#40916f; opacity:.42; }
|
| 28 |
.flow-missing { fill:#cfd5c9; opacity:.6; }
|
| 29 |
+
/* The flux feeding the current selection stays emphasised, so the trunk you
|
| 30 |
+
have walked reads at a glance against the lineages you passed over. */
|
| 31 |
+
.flow.is-path { opacity:.74; }
|
| 32 |
+
.flow-annotated.is-path { fill:#39875f; }
|
| 33 |
+
.flow-missing.is-path { fill:#c2c9ba; }
|
| 34 |
/* Lighting a group also lights the flux arriving at it, so you can see where
|
| 35 |
a group's assemblies came from. Applied from atlas.js, since the ribbons are
|
| 36 |
+
painted under every node and cannot be reached with a CSS sibling rule.
|
| 37 |
+
Listed after the trunk rules so a hover still wins on the selected path. */
|
| 38 |
.flow.is-lit { opacity:.9; }
|
| 39 |
.flow-annotated.is-lit { fill:#2f7d5c; }
|
| 40 |
.flow-missing.is-lit { fill:#b4bdac; }
|
taxonomy.py
CHANGED
|
@@ -249,8 +249,11 @@ class Taxonomy:
|
|
| 249 |
|
| 250 |
def ribbon(y0, a0, a1, y1, b0, b1, kind, n):
|
| 251 |
bend = (y0 + y1) / 2
|
|
|
|
|
|
|
|
|
|
| 252 |
return (f'<path d="M{a0:.1f},{y0} C{a0:.1f},{bend} {b0:.1f},{bend} {b0:.1f},{y1} '
|
| 253 |
-
f'L{b1:.1f},{y1} C{b1:.1f},{bend} {a1:.1f},{bend} {a1:.1f},{y0} Z" class="flow flow-{kind}" '
|
| 254 |
f'data-node="{n["uid"]}" data-detail="{escape(n["detail"], quote=True)}"/>')
|
| 255 |
|
| 256 |
for depth, column in enumerate(columns):
|
|
|
|
| 249 |
|
| 250 |
def ribbon(y0, a0, a1, y1, b0, b1, kind, n):
|
| 251 |
bend = (y0 + y1) / 2
|
| 252 |
+
# The flux into a selected group is the trunk you have walked, so it
|
| 253 |
+
# stays emphasised rather than waiting for a hover.
|
| 254 |
+
trunk = " is-path" if n['selected'] else ""
|
| 255 |
return (f'<path d="M{a0:.1f},{y0} C{a0:.1f},{bend} {b0:.1f},{bend} {b0:.1f},{y1} '
|
| 256 |
+
f'L{b1:.1f},{y1} C{b1:.1f},{bend} {a1:.1f},{bend} {a1:.1f},{y0} Z" class="flow flow-{kind}{trunk}" '
|
| 257 |
f'data-node="{n["uid"]}" data-detail="{escape(n["detail"], quote=True)}"/>')
|
| 258 |
|
| 259 |
for depth, column in enumerate(columns):
|
tests/test_taxonomy.py
CHANGED
|
@@ -164,6 +164,19 @@ class TaxonomyTests(unittest.TestCase):
|
|
| 164 |
self.assertTrue(path.startswith("2759/"), name)
|
| 165 |
self.assertTrue(path.endswith(f"/{taxid}"), name)
|
| 166 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
def test_rejects_paths_outside_the_tree(self):
|
| 168 |
for path in ("1", "33208", "2759/33208", "2759/2759~3", "2759/9606", "abc"):
|
| 169 |
with self.assertRaises(ValueError, msg=path):
|
|
|
|
| 164 |
self.assertTrue(path.startswith("2759/"), name)
|
| 165 |
self.assertTrue(path.endswith(f"/{taxid}"), name)
|
| 166 |
|
| 167 |
+
def test_selected_path_flux_stays_highlighted(self):
|
| 168 |
+
for path in (DEFAULT_PATH, "2759/33154/33208", "2759/33154/33208/6072/33213"):
|
| 169 |
+
html = self.taxonomy.view(path)
|
| 170 |
+
chart = html[html.index("<svg"):html.index("</svg>")]
|
| 171 |
+
trunk = re.findall(r'class="flow flow-(\w+) is-path" data-node="([\d-]+)"', chart)
|
| 172 |
+
selected = re.findall(r'<g class="tree-node is-selected"[^>]*data-node="([\d-]+)"', chart)
|
| 173 |
+
# One level transition per step below the root, each carrying the
|
| 174 |
+
# annotated and not-annotated halves of one group's inflow.
|
| 175 |
+
self.assertEqual(sorted(set(uid for _, uid in trunk)), sorted(selected[1:]), path)
|
| 176 |
+
self.assertLessEqual(len(trunk), 2 * (len(selected) - 1), path)
|
| 177 |
+
# A trunk ribbon is still an ordinary ribbon, hoverable like any other.
|
| 178 |
+
self.assertNotIn('class="flow flow-annotated is-path" data-node="0-0"', chart)
|
| 179 |
+
|
| 180 |
def test_rejects_paths_outside_the_tree(self):
|
| 181 |
for path in ("1", "33208", "2759/33208", "2759/2759~3", "2759/9606", "abc"):
|
| 182 |
with self.assertRaises(ValueError, msg=path):
|