a graph from a table¤
we'll use the extended chartability principles to experiment with a small graph.s
import networkx
from nbconvert_a11y.repr import get_table, TableOptions
shell.tangle.parser.fence_methods["graphviz"] = "graphviz:Source"
%%
create a graph in graphviz
graph =\
```graphviz
graph LR {
Compromising -- {Robust Understandable}
Assistive -- {Perceivable Understandable}
Flexible -- {Perceivable Operable Robust}
Robust -- Perceivable -- Operable -- Robust [style=dashed]
Perceivable -- Understandable -- Robust -- Perceivable [style=dashed]
}
```
pour, caf = map(str.split, (
Percievable Operable Understandable Robust
Compromising Assistive Flexible
).splitlines())
then cast it to a networkx graph object
G = networkx.nx_pydot.read_dot(io.StringIO(graph.source))
nodes are indexes that have meaning in space. there are multiple layouts
the positions would be hidden visually and non visually
chartability = await Series(["https://raw.githubusercontent.com/Chartability/POUR-CAF/main/README.md"]).http.get()
from nbconvert_a11y.table import new
%%
rules = chartability.apply(shell.tangle.parser.parser.render).apply(bs4.BeautifulSoup, features="lxml")[0]
hs = "h1,h2,h3,h4,h5,h6"
headings = collections.defaultdict(list)
for node in rules.select(F"{hs}, :is({hs}) ~ :not({hs})"):
if node.name in hs.split(","):
key = node
else:
headings[key].append(node)
headings = Series(headings).to_frame("body")
headings["level"] = headings.index.map(lambda x: int(x.name.lstrip("h"))).values
headings = headings.reset_index().set_index(headings.index.attrgetter("string").rename("Heading"))
headings["body"] = headings["body"].apply(lambda x: new("section", *x)).apply(lambda x: new("section", *x))
headings["description"] = headings["body"].attrgetter("string")
%%
compute different layouts for our graph
positions = Series({
networkx.arf_layout: networkx.arf_layout(G),
# networkx.bipartite_layout: networkx.bipartite_layout(G, G.nodes),
networkx.circular_layout: networkx.circular_layout(G),
networkx.spring_layout: networkx.spring_layout(G),
networkx.kamada_kawai_layout: networkx.kamada_kawai_layout(G),
networkx.planar_layout: networkx.planar_layout(G),
networkx.shell_layout: networkx.shell_layout(G),
networkx.spectral_layout: networkx.spectral_layout(G),
networkx.spiral_layout: networkx.spiral_layout(G),
networkx.spring_layout: networkx.spring_layout(G),
}).series().T
positions.columns = positions.columns.attrgetter("__name__").str.split("_").itemgetter(0)
positions = positions.stack().series().rename(columns=dict(zip(range(2), "xy"))).unstack()
positions = pandas.concat(dict(info=headings.loc[positions.index]), axis=1).join(
positions
).drop(columns=[("info", "body"), ("info", "index"), ("info", "level")])
positions["info", "heuristic"] = positions.index.map(set(pour).__contains__).map(
["wcag", "chartability"].__getitem__
).values
%%
make a dataframe of the information required to present the graph nodes and edges
df = DataFrame(index=DataFrame(G.edges).drop(columns=2).drop_duplicates().set_index([0, 1]).index.rename(list("xy")))
df = pandas.concat([
positions.loc[df.index.get_level_values(0)].rename(columns={"x": "x0", "y": "y0"}, level=0).drop(("info",), axis=1).reset_index(drop=True),
positions.loc[df.index.get_level_values(1)].rename(columns={"x": "x1", "y": "y1"}, level=0).drop(("info",), axis=1).reset_index(drop=True)
], axis=1).set_index(df.index)
%%
show the whole thing
section = df.pipe(get_table, id="edges", options=TableOptions(max_columns=1000))
section.table.caption.clear()
section.table.insert(0, new("caption", F"a graph with {G.number_of_nodes()} nodes and {G.number_of_edges()} edges."))
section.table.attrs["class"] = "visual"
layouts = new(
"fieldset",
new("legend","layouts"),
new("label", new("input", type="checkbox", checked="", name="active", onchange=
```javascript
document.getElementById(`edges`).classList.toggle("visual", this.checked);
console.log(this)
```
), "active"),
*(
new("label",
new("input", type="radio", name="layout", **{"aria-controls": F"nodes-layout-{layout}"}, onchange=
```javascript
document.querySelectorAll(`[name=layout]`).forEach((element) => {
var target = document.getElementById(element.getAttribute(`aria-controls`));
target.setAttribute(`media`, element.checked ? `all` : `none`);
})
```
), layout) for layout in positions["x"].columns
)
)
layouts.select_one("[name=layout]").attrs["checked"] = ""
section.form.append(layouts)
for i, layout in enumerate(positions["x"].columns):
section.append(new("style",
```css
#edges.visual {
tr{
--x0: var(--x0-%s);
--x0-min: var(--x0-%s-min);
--x0-max: var(--x0-%s-max);
--x0-diff: var(--x0-%s-diff);
--x1: var(--x1-%s);
--x1-min: var(--x0-%s-min);
--x1-max: var(--x0-%s-max);
--x1-diff: var(--x0-%s-diff);
--y0: var(--y0-%s);
--y0-min: var(--y0-%s-min);
--y0-max: var(--y0-%s-max);
--y0-diff: var(--y0-%s-diff);
--y1: var(--y1-%s);
--y1-min: var(--y0-%s-min);
--y1-max: var(--y0-%s-max);
--y1-diff: var(--y0-%s-diff);
}
}
```
.replace("%s", layout), id=F"nodes-layout-{layout}", media=i and "none" or "all"))
section.form.append(layouts)
HTML(section)
%%
visual styling for the graph
```css
#edges.visual {
--w: 400;
--h: 400;
width: var(--w); height: calc(200px + var(--h) * 1px);
display: block;
padding: 100px;
tbody {
position: relative;
display: inline-block;
width: var(--w); height: var(--h);
tr {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr;
position: absolute;
--X0: calc((var(--x0) - var(--x0-min)) / var(--x0-diff) * var(--w));
--Y0: calc((var(--y0) - var(--y0-min)) / var(--y0-diff) * var(--h));
--X1: calc((var(--x1) - var(--x0-min)) / var(--x0-diff) * var(--w));
--Y1: calc((var(--y1) - var(--y0-min)) / var(--y0-diff) * var(--h));
--d: calc(
pow(pow(var(--X1) - var(--X0), 2) + pow(var(--Y1) - var(--Y0), 2), .5)
);
--r: calc(atan2(var(--Y1) - var(--Y0), var(--X1) - var(--X0)));
th {
position: absolute;
display: inline-block;
transition: 2s all;
&:nth-of-type(1) {
transform:
translate(
calc(var(--X0) * 1px), calc(var(--Y0) * 1px)
)
translate(-50%, -50%)
;
}
&:nth-of-type(2) {
transform:
translate(
calc(var(--X1) * 1px), calc(var(--Y1) * 1px)
)
translate(-50%, -50%)
;
}
}
td {
display: none;
}
&::before {
content: "";
display: content;
position: absolute;
transform-origin: calc(1px *var(--X0)) calc(1px *var(--Y0));
height: 1px;
border: solid 2px;
transition: 2s all;
width: calc(var(--d) * 1px);
transform:
rotate(var(--r))
translate(
calc(var(--X0) * 1px), calc(var(--Y0) * 1px)
)
;
}
}
}
```
marginalia¤
what describes the figure and form of a graph?
the graph form has many typographical layouts that new to be considered
there are two tables, two maps, a legend of nodes, and an index of relative positions.
nodes and edges
nodes are primary foreground forms edges are second mid ground forms all other attributes are background