Skip to content

Commit ebbd93f

Browse files
committed
Allow sessions in with shared globals across code playgrounds
1 parent da3e2b9 commit ebbd93f

File tree

11 files changed

+230
-68
lines changed

11 files changed

+230
-68
lines changed

docs/src/js/extra-notebook.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
} catch {
1212
webspace = 'srgb'
1313
}
14+
const sessions = {}
1415
let pyodide = null
1516
let busy = false
1617
let raw = ""
@@ -56,6 +57,15 @@ ${content}
5657
let notebookInstalled = false
5758
let playgroundInstalled = false
5859

60+
const destroySessions = () => {
61+
// Destroy a sessions
62+
63+
Object.keys(sessions).forEach(key => {
64+
sessions[key].destroy()
65+
delete sessions[key]
66+
})
67+
}
68+
5969
const fakeDOMContentLoaded = () => {
6070
// Send a fake `DOMContentLoaded`
6171
fake = true
@@ -93,18 +103,40 @@ ${content}
93103
// Execute Python code inside a playground
94104

95105
const currentInputs = document.getElementById(`__playground-inputs_${currentID}`)
106+
const session = currentInputs.getAttribute("session")
96107
currentInputs.setAttribute("readonly", "")
97108
pyodide.globals.set("id_num", currentID)
98109
pyodide.globals.set("action", "playground")
110+
pyodide.globals.set("session_id", session)
111+
const main = document.querySelector('main.md-main')
112+
const live = main.getAttribute('livecode')
113+
if (!live) {
114+
destroySessions()
115+
main.setAttribute('livecode', 'live')
116+
}
117+
if (session in sessions) {
118+
pyodide.globals.set('SESSIONS', sessions[session])
119+
} else {
120+
pyodide.globals.set('SESSIONS', null)
121+
}
99122
await pyodide.runPythonAsync(pycode)
123+
if (session) {
124+
sessions[session] = pyodide.globals.get('SESSIONS').copy()
125+
}
100126
currentInputs.removeAttribute("readonly")
101127
}
102128

103129
const pyrender = async text => {
104130
// Render an entire notebook page
105131

132+
destroySessions()
133+
const main = document.querySelector('main.md-main')
134+
if (main.getAttribute('livecode')) {
135+
main.removeAttribute('livecode')
136+
}
106137
pyodide.globals.set("content", text)
107138
pyodide.globals.set("action", "notebook")
139+
pyodide.globals.set('SESSIONS', null)
108140
await pyodide.runPythonAsync(pycode)
109141
const src = document.getElementById("__notebook-input")
110142
if (src) {

docs/src/markdown/filters.md

Lines changed: 105 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -120,18 +120,111 @@ represents deficiencies with the L cone, deuteranopia with the M cone, and trita
120120
properties only vary in the properties specific to a person's deficient cone(s) will have the potential to cause
121121
confusion for that person.
122122

123-
Consider the example below. We generate 3 different color series, each specifically targeting a specific deficiency.
124-
This is done by generating a series of colors that have all properties equal except that they have variance in a
125-
different cone response. The first row varies only with the L cone response, the second only with the M cone response,
126-
and the third only with the S cone response. We then apply the filters for protanopia, deuteranopia, and tritanopia. We
127-
can see that while many of the colors are altered, the row that targets the deficient cone specific to the CVD all
128-
appear to be of the same color making it difficult to distinguish between any of them.
123+
Consider the example below. First we do a little setup so we can translate colors to the LMS space.
129124

130-
/// tab | Normal
131-
```py play
125+
```py {.md-max-height play session="cvd"}
126+
from coloraide import Color as Base
127+
from coloraide.cat import WHITES
128+
from coloraide.spaces import Space
129+
from coloraide import algebra as alg
130+
from coloraide.types import Vector
131+
from coloraide.channels import Channel
132+
133+
134+
class LMS(Space):
135+
"""The LMS class."""
136+
137+
BASE = "srgb-linear"
138+
NAME = "lms"
139+
SERIALIZE = ("--lms",)
140+
CHANNELS = (
141+
Channel("l", 0.0, 1.0),
142+
Channel("m", 0.0, 1.0),
143+
Channel("s", 0.0, 1.0)
144+
)
145+
CHANNEL_ALIASES = {
146+
"long": "l",
147+
"medium": "m",
148+
"short": "s"
149+
}
150+
WHITE = WHITES['2deg']['D65']
151+
152+
LRGB_TO_LMS = [
153+
[0.178824041258, 0.4351609057000001, 0.04119349692],
154+
[0.034556423182, 0.27155382458, 0.038671308360000003],
155+
[0.000299565576, 0.0018430896, 0.01467086136]
156+
]
157+
158+
LMS_TO_LRGB = [
159+
[8.094435598032371, -13.050431460496926, 11.672058453917323],
160+
[-1.0248505586646686, 5.401931309674973, -11.361471490598712],
161+
[-0.03652974715933318, -0.412162807001268, 69.35132423820858]
162+
]
163+
164+
def to_base(self, coords: Vector) -> Vector:
165+
"""To XYZ."""
166+
167+
return alg.dot(self.LMS_TO_LRGB, coords, dims=alg.D2_D1)
168+
169+
def from_base(self, coords: Vector) -> Vector:
170+
"""From XYZ."""
171+
172+
return alg.dot(self.LRGB_TO_LMS, coords, dims=alg.D2_D1)
173+
174+
175+
class Color(Base):
176+
...
177+
178+
179+
Color.register(LMS())
132180

133-
--8<-- "confusion_lines.md"
134181

182+
def get_limit(c, channel, upper=False):
183+
184+
if upper:
185+
low = c[channel]
186+
high = 1
187+
else:
188+
high = c[channel]
189+
low = 0
190+
191+
temp = c.clone()
192+
193+
while abs(high - low) >= 0.002:
194+
value = (high + low) * 0.5
195+
temp[channel] = value
196+
if temp.in_gamut('srgb', tolerance=0.01):
197+
if upper:
198+
low = value
199+
else:
200+
high = value
201+
else:
202+
if upper:
203+
high = value
204+
else:
205+
low = value
206+
207+
return temp.clip('srgb')
208+
209+
210+
def confusion_line(c, cone):
211+
"""Generate colors on a line of confusion."""
212+
213+
lms = c.convert('lms')
214+
high = get_limit(lms, cone, upper=True)
215+
low = get_limit(lms, cone)
216+
return Color.steps([low, high], steps=6, space='lms', out_space='srgb')
217+
```
218+
219+
Then We generate 3 different color series, each specifically targeting a specific deficiency. This is done by generating
220+
a series of colors that have all properties equal except that they have variance in a different cone response. The first
221+
row varies only with the L cone response, the second only with the M cone response, and the third only with the S cone
222+
response. We then apply the filters for protanopia, deuteranopia, and tritanopia. We can see that while many of the
223+
colors are altered, the row that targets the deficient cone specific to the CVD all appear to be of the same color
224+
making it difficult to distinguish between any of them.
225+
226+
/// tab | Normal
227+
```py play session="cvd"
135228
confusing_colors = confusion_line(Color('orange'), 'l')
136229
Steps([c.clip() for c in confusing_colors])
137230

@@ -144,10 +237,7 @@ Steps([c.clip() for c in confusing_colors])
144237
///
145238

146239
/// tab | Protanopia
147-
```py play
148-
149-
---8<-- "confusion_lines.md"
150-
240+
```py play session="cvd"
151241
confusing_colors = confusion_line(Color('orange'), 'l')
152242
Steps([c.filter('protan').clip() for c in confusing_colors])
153243

@@ -160,10 +250,7 @@ Steps([c.filter('protan').clip() for c in confusing_colors])
160250
///
161251

162252
/// tab | Deuteranopia
163-
```py play
164-
165-
---8<-- "confusion_lines.md"
166-
253+
```py play session="cvd"
167254
confusing_colors = confusion_line(Color('orange'), 'l')
168255
Steps([c.filter('deutan').clip() for c in confusing_colors])
169256

@@ -176,10 +263,7 @@ Steps([c.filter('deutan').clip() for c in confusing_colors])
176263
///
177264

178265
/// tab | Tritanopia
179-
```py play
180-
181-
---8<-- "confusion_lines.md"
182-
266+
```py play session="cvd"
183267
confusing_colors = confusion_line(Color('orange'), 'l')
184268
Steps([c.filter('tritan').clip() for c in confusing_colors])
185269

0 commit comments

Comments
 (0)