Skip to content

direct transition from a table to a 3d plot¤

jump to the demo

from nbconvert_a11y.repr import get_table, new
from nbconvert_a11y.table import aria

df = DataFrame(numpy.random.randn(30, 4), columns= list("xyza"))
%%
<style>
/**ff only demo gear**/
a[href="#www"] {
    display: block;
    height: 300px;
    width: 400px;
    background: -moz-element(#demo);
    background-size: cover;
    background-repeat: no-repeat;
    font-size: 4rem;
}
</style>

what if this email never finds you well?

%%
css properties and 3d plot settings

    PROPERTIES =\
```text/css
@property --w {
    syntax: "<length>";
    initial-value: 600px;
    inherits: true;
}
@property --h {
    syntax: "<length>";
    initial-value: 400px;
    inherits: true;
}
@property --d {
    syntax: "<length>";
    initial-value: 400px;
    inherits: true;
}
@property --r {
    syntax: "<transform-function>";
    initial-value: scale(1);
    inherits: true;
}
@property --rr {
    syntax: "<transform-function>";
    initial-value: scale(1);
    inherits: true;
}
@property --t {
    syntax: "<time>";
    initial-value: 1.5s;
    inherits: true;
}
```

    DDD =\
```text/css
figure {
    position: relative;
    margin: 150px;
    table {
        width: var(--w);
        height: var(--h);
    }
    table.ddd {
        position: relative;
        display: block;
        transform-origin: 
            center center 0;
        transform: var(--r);
        transform-style: preserve-3d;
        transition: all var(--t) linear;
        tbody {
            display: block;
            transform-style: preserve-3d;
            tr {
                &amp;[aria-selected="true"] {
                    border: solid 2px !important;
                }
                display: grid;
                grid-template-columns: repeat(2, 1fr);
                grid-template-rows: repeat(2, 1fr);
                background: black !important;
                color: white;
                position: absolute;
                transition: all var(--t) linear;
                transform-style: preserve-3d;
                 --1-: calc((var(--1) - var(--1-min)) / var(--1-diff));
                 --2-: calc((var(--2) - var(--2-min)) / var(--2-diff));
                 --3-: calc((var(--3) - var(--3-min)) / var(--3-diff));
                 transform-origin: center center;
                 font-size: .5rem;
                 transform: 
                     translate3d(
                         calc(var(--1-) * var(--w)),
                         calc((1 - var(--2-)) * var(--h)),
                         calc(var(--3-) * var(--d))
                     )  translate3d(-50%, -50%, 0) var(--rr) ;
                &amp;:hover {
                    font-size: 1.1rem;
                }
                th {display: none;}

            }
        }
    }
}
```
    FLAT =\
```text/css


```
    XY =\
```text/css    
table.ddd {
}
```

    YZ =\
```text/css
table.ddd {
    --r: rotateY(270deg);
    --rr: rotateY(-270deg);
}
```

    XZ =\
```text/css    
table.ddd {
    --r: rotateX(270deg);
    --rr: rotateX(-270deg);
}
```

the self contained event to fire when the `section.form.fieldset` is updated.

    update_view =\
```text/javascript
if (this.checked) {
    document.getElementById(`www`).classList.toggle(`ddd`, this.getAttribute(`aria-controls`) != `www`);
}
document.querySelectorAll(`[name=view][aria-controls]`).forEach((thing)=&gt;{    
    var target = document.getElementById(thing.getAttribute(`aria-controls`));
    if (target.tagName == `STYLE`) {
        target.setAttribute("media", thing.checked ? "all" : "none");
    } 
});
```

css properties and 3d plot settings

PROPERTIES =\
@property --w {
    syntax: "<length>";
    initial-value: 600px;
    inherits: true;
}
@property --h {
    syntax: "<length>";
    initial-value: 400px;
    inherits: true;
}
@property --d {
    syntax: "<length>";
    initial-value: 400px;
    inherits: true;
}
@property --r {
    syntax: "<transform-function>";
    initial-value: scale(1);
    inherits: true;
}
@property --rr {
    syntax: "<transform-function>";
    initial-value: scale(1);
    inherits: true;
}
@property --t {
    syntax: "<time>";
    initial-value: 1.5s;
    inherits: true;
}
DDD =\
figure {
    position: relative;
    margin: 150px;
    table {
        width: var(--w);
        height: var(--h);
    }
    table.ddd {
        position: relative;
        display: block;
        transform-origin: 
            center center 0;
        transform: var(--r);
        transform-style: preserve-3d;
        transition: all var(--t) linear;
        tbody {
            display: block;
            transform-style: preserve-3d;
            tr {
                &[aria-selected="true"] {
                    border: solid 2px !important;
                }
                display: grid;
                grid-template-columns: repeat(2, 1fr);
                grid-template-rows: repeat(2, 1fr);
                background: black !important;
                color: white;
                position: absolute;
                transition: all var(--t) linear;
                transform-style: preserve-3d;
                 --1-: calc((var(--1) - var(--1-min)) / var(--1-diff));
                 --2-: calc((var(--2) - var(--2-min)) / var(--2-diff));
                 --3-: calc((var(--3) - var(--3-min)) / var(--3-diff));
                 transform-origin: center center;
                 font-size: .5rem;
                 transform: 
                     translate3d(
                         calc(var(--1-) * var(--w)),
                         calc((1 - var(--2-)) * var(--h)),
                         calc(var(--3-) * var(--d))
                     )  translate3d(-50%, -50%, 0) var(--rr) ;
                &:hover {
                    font-size: 1.1rem;
                }
                th {display: none;}

            }
        }
    }
}
FLAT =\
    

XY =\
table.ddd {
}
YZ =\
table.ddd {
    --r: rotateY(270deg);
    --rr: rotateY(-270deg);
}
XZ =\
table.ddd {
    --r: rotateX(270deg);
    --rr: rotateX(-270deg);
}

the self contained event to fire when the section.form.fieldset is updated.

update_view =\
if (this.checked) {
    document.getElementById(`www`).classList.toggle(`ddd`, this.getAttribute(`aria-controls`) != `www`);
}
document.querySelectorAll(`[name=view][aria-controls]`).forEach((thing)=>{    
    var target = document.getElementById(thing.getAttribute(`aria-controls`));
    if (target.tagName == `STYLE`) {
        target.setAttribute("media", thing.checked ? "all" : "none");
    } 
});

build the table representation

section = df.pipe(get_table, id="www", caption="random ass 3d data")
section.style.string = "\n".join((PROPERTIES, DDD, section.style.string))
section.attrs.update(id="demo")

for row in section.table.select("tr"):
    row.attrs.update(onclick="""
    this.hasAttribute(`aria-selected`) ? this.removeAttribute(`aria-selected`) : this.setAttribute(`aria-selected`, `true`);
""".strip())
section.table.attrs.update({"class": "ddd"})
section.form.attrs.setdefault("style", "") 
section.form.attrs["style"] += "display: block;"
section.form.extend((
    new("fieldset", new(
        "legend", "Prepared views", 
        new("ul", 
            new("li", new("label", 
                 new("input", type="radio", name="view",  **aria(controls="www"), onchange=update_view),
                "default table view"
            ), 
            new("li", 
                 new("input", type="radio", name="view", **aria(controls="xy"), checked="", onchange=update_view), "x-y scatter view"
            ), 
            new("li", 
                 new("input", type="radio", name="view", **aria(controls="yz"), onchange=update_view), "y-z scatter view"
            ), new("li", 
                 new("input", type="radio", name="view", **aria(controls="xz"), onchange=update_view), "x-z scatter view"
            ), role="group")
    ))

), 
    new("style", FLAT, id="flat",  media="none"), new("style", XY, id="xy",  media="all"),
    new("style", YZ, id="yz", media="none"), new("style", XZ, id="xz", media="none"),
))
section
random ass 3d data
pandas dataframe with 30 rows, 4 columns with 1 index levels and 1 columns levels.
None 'x' 'y' 'z' 'a'
0 -0.712 -0.128 -0.606 1.079
1 1.525 -1.167 -0.747 -0.699
2 0.246 0.645 -0.250 0.817
3 -0.400 0.387 -0.488 0.632
4 -0.798 0.550 0.700 -1.050
5 -0.629 0.645 1.632 -0.553
6 0.990 1.546 1.400 -0.225
7 0.109 -0.207 -0.315 -0.372
8 0.365 0.074 0.782 0.736
9 0.881 1.488 0.903 -1.372
10 1.994 -0.231 1.480 1.034
11 0.295 -0.066 -0.137 1.542
12 -1.850 0.650 -0.653 0.034
13 -0.420 0.079 0.509 -0.001
14 -0.119 -2.251 2.107 1.354
15 0.859 -0.489 0.607 0.610
16 0.269 0.245 -0.115 -0.986
17 -0.012 1.670 -2.095 0.206
18 -0.348 0.568 0.857 -1.539
19 0.874 -0.086 -0.330 -1.461
20 0.844 -0.220 -0.274 1.412
21 1.597 0.428 -1.164 -0.409
22 1.204 -1.903 -0.988 0.777
23 -0.660 -0.192 -1.275 1.683
24 -0.125 -1.493 0.423 -0.147
25 1.140 -2.100 1.006 -0.901
26 -0.757 1.709 0.395 -1.142
27 -0.258 0.286 1.367 0.827
28 -0.259 -0.417 -1.340 -1.244
29 -0.505 1.065 0.639 0.905
min -1.850 -2.251 -2.095 -1.539
max 1.994 1.709 2.107 1.683
diff 3.844 3.960 4.202 3.222
Prepared views
  • x-y scatter view
  • y-z scatter view
  • x-z scatter view

marginalia¤

a better user interface will have numeric inputs and selectors for units. using a json-ld-ish syntax we can map the time css syntax to css values and units module level 4

%%
    schema=\
```toml
["@context"]
"css:values" = "https://drafts.csswg.org/css-values/"
[properties.time]
title = "title"
description = "the transition time"
"@type" = "css:values#time"
```
schema=\
["@context"]
"css:values" = "https://drafts.csswg.org/css-values/"
[properties.time]
title = "title"
description = "the transition time"
"@type" = "css:values#time"