diff --git a/Dockerfile b/Dockerfile
index be6e12c5..4cb72db6 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -11,3 +11,6 @@ COPY web/package-lock.json /www
RUN npm install --quiet
COPY web/src /www/src
COPY web/view /www/view
+COPY .git /tmp/
+RUN git -C /tmp rev-parse --verify HEAD > /www/git-sha
+RUN rm -rf /tmp/.git
diff --git a/README.md b/README.md
index acf55b20..7c1dfc11 100644
--- a/README.md
+++ b/README.md
@@ -50,9 +50,9 @@ Or by using Image type, can just define in EPL preRenderFunc! See eyechart-sacca
## TODOs
replace koa-socket-session with something better supported (1.2.0 fails)
update to koa-socket-2
-make sure I didn't break shuffle with new randint
-- Chunk number of stimmuli returned
-
+- Chunk number of stimmuli returned by server to lower number of requsets
+- make preRenderFunc a generator, store images in localStorage
+- render: read from localStorage
### video
- Always save video
diff --git a/docker-compose.yml b/docker-compose.yml
index 5bb0d4e5..d17eecda 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -4,6 +4,7 @@ web:
command: node --max-old-space-size=8192 --harmony src/app.js
volumes:
- /data/eye-candy:/data
+ - ./web/programs:/www/programs
links:
- redis
redis:
diff --git a/web/src/programs/acuity-with-flashes.js b/web/programs/acuity-with-flashes.js
similarity index 95%
rename from web/src/programs/acuity-with-flashes.js
rename to web/programs/acuity-with-flashes.js
index 1b4b0afe..cdf3b47c 100644
--- a/web/src/programs/acuity-with-flashes.js
+++ b/web/programs/acuity-with-flashes.js
@@ -32,7 +32,7 @@ r.shuffle(stimuli)
stimuli = flatten(stimuli)
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/acuity.js b/web/programs/acuity.js
similarity index 76%
rename from web/src/programs/acuity.js
rename to web/programs/acuity.js
index 3bcc431e..04eaff9d 100644
--- a/web/src/programs/acuity.js
+++ b/web/programs/acuity.js
@@ -22,9 +22,19 @@ function* measureIntegrity(stimuli,every=5*60) {
// special function for pre-rendering. This is passed as a string
// and run client-side
-function preRenderFunc(binaryNoiseNframes, randomSeed) {
+function* preRenderFunc(binaryNoiseNframes, randomSeed) {
console.log("In preRender...")
+ function renderFrame(flatPixelArray) {
+ var canvas = document.createElement("canvas");
+ var context = canvas.getContext("2d")
+ canvas.width = WIDTH
+ canvas.height = HEIGHT
+ imageData = new ImageData(flatPixelArray, WIDTH, HEIGHT)
+ context.putImageData(imageData, 0, 0)
+ return canvas
+ }
+
console.assert(binaryNoiseNframes % 2 == 0)
// client-side
let r = new DeterministicRandom(randomSeed)
@@ -38,59 +48,53 @@ function preRenderFunc(binaryNoiseNframes, randomSeed) {
// let binaryNoiseNframes =14
let pixelArrays = []
let singlePixel = Uint8ClampedArray.from(Array(binaryNoiseNframes/2).fill(0).concat(Array(binaryNoiseNframes/2).fill(255)))
- for (var p = 0; p < nPixels; p++) {
- // benchmark: 50% of time is from shuffle
- r.shuffle(singlePixel)
- // ... allows for array copy
- pixelArrays.push([...singlePixel])
- if (p % 10000 == 0) {
- console.log("pushed array for pixel: ", p)
- }
- }
-
-
// RGBA array https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas
- let allFrames = new Uint8ClampedArray(binaryNoiseNframes*nPixels*4)
- // values of single pixel over time
- // TODO why does making this nPixels/2 leave only ~1/4 of pixels with value??
- for (var p = 0; p < nPixels; p++) {
- singlePixel = pixelArrays[p]
- for (var n = 0; n < binaryNoiseNframes; n++) {
- // For example, to read the blue component's value from the pixel at column 200, row 50 in the image, you would do the following:
- // blueComponent = imageData.data[(50 * (imageData.width * 4)) + (200 * 4) + 2]
- allFrames[p*4 + n*nPixels*4] = singlePixel[n] // red
- allFrames[1+p*4 + n*nPixels*4] = singlePixel[n] // green
- allFrames[2+p*4 + n*nPixels*4] = singlePixel[n] // blue
- allFrames[3+p*4 + n*nPixels*4] = 255 // alpha
- }
- if (p % 10000 == 0) {
- console.log("pushed array for pixel: ", p)
- }
- }
- function renderFrame(flatPixelArray) {
- var canvas = document.createElement("canvas");
- var context = canvas.getContext("2d")
- canvas.width = WIDTH
- canvas.height = HEIGHT
- imageData = new ImageData(flatPixelArray, WIDTH, HEIGHT)
- context.putImageData(imageData, 0, 0)
- return canvas
- }
+ // chunk N frame increments to avoid memory overflow
+ let chunkNframes = 100
+ let chunkFrames
+ for (var i = 0; i < binaryNoiseNframes; i = i + chunkNframes) {
+ // equal to chunkNframes except for final iteration
+ chunkNframes = Math.min(chunkNframes, binaryNoiseNframes-i)
+ chunkFrames = new Uint8ClampedArray(chunkNframes*nPixels*4)
+
+ // create balanced pixel assignments
+ pixelArrays = []
+ for (var p = 0; p < nPixels; p++) {
+ // benchmark: 50% of time is from shuffle
+ r.shuffle(singlePixel)
+ // ... allows for array copy
+ pixelArrays.push([...singlePixel])
+ if (p % 10000 == 0) {
+ console.log("pushed array for pixel: ", p)
+ }
+ }
- let frames = []
- for (var n = 0; n < binaryNoiseNframes; n++) {
- frames.push(renderFrame(allFrames.slice(n*nPixels*4,(n+1)*nPixels*4)))
+ // assign values of each pixel over time
+ for (var p = 0; p < nPixels; p++) {
+ singlePixel = pixelArrays[p]
+ for (var n = 0; n < chunkNframes; n++) {
+ // For example, to read the blue component's value from the pixel at column 200, row 50 in the image, you would do the following:
+ // blueComponent = imageData.data[(50 * (imageData.width * 4)) + (200 * 4) + 2]
+ chunkFrames[p*4 + n*nPixels*4] = singlePixel[n] // red
+ chunkFrames[1+p*4 + n*nPixels*4] = singlePixel[n] // green
+ chunkFrames[2+p*4 + n*nPixels*4] = singlePixel[n] // blue
+ chunkFrames[3+p*4 + n*nPixels*4] = 255 // alpha
+ }
+ if (p % 10000 == 0) {
+ console.log("pushed array for pixel: ", p)
+ }
+ }
+ // yield each frame from chunk
+ for (var n = 0; n < chunkNframes; n++) {
+ yield renderFrame(chunkFrames.slice(n*nPixels*4,(n+1)*nPixels*4))
+ }
}
- console.log("frames[0]", frames[0])
-
- return {renders: frames,
- yield: {}}
}
// special object for pre-rendering
-const binaryNoiseDuration = 0.1*60
+const binaryNoiseDuration = 5*60
const frameRate = 60
const hz = 5
const binaryNoiseLifespan = 1 / hz
@@ -197,7 +201,7 @@ r.shuffle(stimuli)
stimuli = measureIntegrity(flatten(stimuli))
stimuli = celltypeStimuli.concat(stimuli)
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/checkerboard-contrast.js b/web/programs/checkerboard-contrast.js
similarity index 98%
rename from web/src/programs/checkerboard-contrast.js
rename to web/programs/checkerboard-contrast.js
index 26dac323..1977230a 100644
--- a/web/src/programs/checkerboard-contrast.js
+++ b/web/programs/checkerboard-contrast.js
@@ -147,7 +147,7 @@ r.shuffle(stimuli)
stimuli = insertBreaks(measureIntegrity(flatten(stimuli)))
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/checkerboard-durations.js b/web/programs/checkerboard-durations.js
similarity index 98%
rename from web/src/programs/checkerboard-durations.js
rename to web/programs/checkerboard-durations.js
index e6030213..904ee64c 100644
--- a/web/src/programs/checkerboard-durations.js
+++ b/web/programs/checkerboard-durations.js
@@ -97,7 +97,7 @@ r.shuffle(stimuli)
stimuli = measureIntegrity(flatten(stimuli))
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/checkerboard-flicker.js b/web/programs/checkerboard-flicker.js
similarity index 100%
rename from web/src/programs/checkerboard-flicker.js
rename to web/programs/checkerboard-flicker.js
diff --git a/web/src/programs/checkerboard.js b/web/programs/checkerboard.js
similarity index 98%
rename from web/src/programs/checkerboard.js
rename to web/programs/checkerboard.js
index 859e148e..1103a663 100644
--- a/web/src/programs/checkerboard.js
+++ b/web/programs/checkerboard.js
@@ -97,7 +97,7 @@ r.shuffle(stimuli)
stimuli = measureIntegrity(flatten(stimuli))
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/dev_vidimage_test.js b/web/programs/dev_vidimage_test.js
similarity index 92%
rename from web/src/programs/dev_vidimage_test.js
rename to web/programs/dev_vidimage_test.js
index ea0bddc7..8972cf3c 100644
--- a/web/src/programs/dev_vidimage_test.js
+++ b/web/programs/dev_vidimage_test.js
@@ -15,7 +15,7 @@ for (var i = 0; i < repetitions; i++) {
}
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/eyechart-balanced.js b/web/programs/eyechart-balanced.js
similarity index 98%
rename from web/src/programs/eyechart-balanced.js
rename to web/programs/eyechart-balanced.js
index 95aa74a1..954fea81 100644
--- a/web/src/programs/eyechart-balanced.js
+++ b/web/programs/eyechart-balanced.js
@@ -108,7 +108,7 @@ r.shuffle(stimuli)
stimuli = measureIntegrity(flatten(stimuli))
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/eyechart-saccade.js b/web/programs/eyechart-saccade.js
similarity index 97%
rename from web/src/programs/eyechart-saccade.js
rename to web/programs/eyechart-saccade.js
index 2049e867..c1eb91d3 100644
--- a/web/src/programs/eyechart-saccade.js
+++ b/web/programs/eyechart-saccade.js
@@ -65,7 +65,7 @@ function twoLetterMatrices(nrows) {
for (var j = 0; j < ncols; j++) {
firstLetterMatrix[i][j] = newLetters[j]
secondLetterMatrix[i][j] = newLetters[j+5]
-
+
cohortMatrix[i][j] = cohortID
}
}
@@ -93,7 +93,7 @@ function calcFixationPoints(sizes, ncols) {
let rowWidth
for (var i = 0; i < nrows; i++) {
size = sizes[i]
- y = y + 2*size
+ y = y + 2*size
rowWidth = (2*ncols+1) * size
if (i==0) {
@@ -186,14 +186,13 @@ function preRenderFunc(sizes, reps, ncols, color, letterTensor,
let nrows = sizes.length
let eyecharts = []
-
+
for (var i = 0; i < letterTensor.length; i++) {
// console.log("letterTensor[i]", letterTensor[i])
let image = renderEyechart(sizes, ncols, letterTensor[i], color)
eyecharts.push(image)
}
- return {renders: eyecharts,
- yield: {}}
+ return {renders: eyecharts}
}
// special object for pre-rendering
@@ -221,8 +220,8 @@ function* gen() {
}
// TODO add integrity
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (let s of measureIntegrity(gen())) {
yield s
}
-}
\ No newline at end of file
+}
diff --git a/web/src/programs/eyechart-uniform.backup.js b/web/programs/eyechart-uniform.backup.js
similarity index 98%
rename from web/src/programs/eyechart-uniform.backup.js
rename to web/programs/eyechart-uniform.backup.js
index 59b17061..38784035 100644
--- a/web/src/programs/eyechart-uniform.backup.js
+++ b/web/programs/eyechart-uniform.backup.js
@@ -81,7 +81,7 @@ r.shuffle(stimuli)
stimuli = measureIntegrity(flatten(stimuli))
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/generic_video.js b/web/programs/generic_video.js
similarity index 82%
rename from web/src/programs/generic_video.js
rename to web/programs/generic_video.js
index 600fee9e..ce67eff8 100644
--- a/web/src/programs/generic_video.js
+++ b/web/programs/generic_video.js
@@ -6,7 +6,7 @@ let meta = {}
stimuli.push!(new Video(1000, "black", DATADIR+"videos/cropped.mp4", meta))
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/grating-contrast.js b/web/programs/grating-contrast.js
similarity index 98%
rename from web/src/programs/grating-contrast.js
rename to web/programs/grating-contrast.js
index e6daf1b8..26a4d855 100644
--- a/web/src/programs/grating-contrast.js
+++ b/web/programs/grating-contrast.js
@@ -126,7 +126,7 @@ r.shuffle(stimuli)
stimuli = insertBreaks(measureIntegrity(flatten(stimuli)))
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/grating-durations.js b/web/programs/grating-durations.js
similarity index 98%
rename from web/src/programs/grating-durations.js
rename to web/programs/grating-durations.js
index 30ad2ff3..80ea9ab1 100644
--- a/web/src/programs/grating-durations.js
+++ b/web/programs/grating-durations.js
@@ -83,7 +83,7 @@ r.shuffle(stimuli)
stimuli = measureIntegrity(flatten(stimuli))
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/grating-sinusoidal-contrast.js b/web/programs/grating-sinusoidal-contrast.js
similarity index 98%
rename from web/src/programs/grating-sinusoidal-contrast.js
rename to web/programs/grating-sinusoidal-contrast.js
index fdea4657..6de1da90 100644
--- a/web/src/programs/grating-sinusoidal-contrast.js
+++ b/web/programs/grating-sinusoidal-contrast.js
@@ -126,7 +126,7 @@ r.shuffle(stimuli)
stimuli = insertBreaks(measureIntegrity(flatten(stimuli)))
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/grating-sinusoidal-durations.js b/web/programs/grating-sinusoidal-durations.js
similarity index 98%
rename from web/src/programs/grating-sinusoidal-durations.js
rename to web/programs/grating-sinusoidal-durations.js
index 83ca9bfb..9fa607ab 100644
--- a/web/src/programs/grating-sinusoidal-durations.js
+++ b/web/programs/grating-sinusoidal-durations.js
@@ -83,7 +83,7 @@ r.shuffle(stimuli)
stimuli = measureIntegrity(flatten(stimuli))
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/grating-sinusoidal-speeds.js b/web/programs/grating-sinusoidal-speeds.js
similarity index 98%
rename from web/src/programs/grating-sinusoidal-speeds.js
rename to web/programs/grating-sinusoidal-speeds.js
index b59a0080..223d8266 100644
--- a/web/src/programs/grating-sinusoidal-speeds.js
+++ b/web/programs/grating-sinusoidal-speeds.js
@@ -83,7 +83,7 @@ r.shuffle(stimuli)
stimuli = measureIntegrity(flatten(stimuli))
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/grating-sinusoidal.js b/web/programs/grating-sinusoidal.js
similarity index 98%
rename from web/src/programs/grating-sinusoidal.js
rename to web/programs/grating-sinusoidal.js
index 5cdf35af..f5b08a32 100644
--- a/web/src/programs/grating-sinusoidal.js
+++ b/web/programs/grating-sinusoidal.js
@@ -83,7 +83,7 @@ r.shuffle(stimuli)
stimuli = measureIntegrity(flatten(stimuli))
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/grating-speeds.js b/web/programs/grating-speeds.js
similarity index 98%
rename from web/src/programs/grating-speeds.js
rename to web/programs/grating-speeds.js
index 585ef08c..9f79c02c 100644
--- a/web/src/programs/grating-speeds.js
+++ b/web/programs/grating-speeds.js
@@ -83,7 +83,7 @@ r.shuffle(stimuli)
stimuli = measureIntegrity(flatten(stimuli))
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/grating-vs-gray.js b/web/programs/grating-vs-gray.js
similarity index 100%
rename from web/src/programs/grating-vs-gray.js
rename to web/programs/grating-vs-gray.js
diff --git a/web/src/programs/grating.js b/web/programs/grating.js
similarity index 98%
rename from web/src/programs/grating.js
rename to web/programs/grating.js
index 5cbc943f..06fe864a 100644
--- a/web/src/programs/grating.js
+++ b/web/programs/grating.js
@@ -83,7 +83,7 @@ r.shuffle(stimuli)
stimuli = measureIntegrity(flatten(stimuli))
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/kinetics.js b/web/programs/kinetics.js
similarity index 100%
rename from web/src/programs/kinetics.js
rename to web/programs/kinetics.js
diff --git a/web/src/programs/letters-inverted.js b/web/programs/letters-inverted.js
similarity index 97%
rename from web/src/programs/letters-inverted.js
rename to web/programs/letters-inverted.js
index 0a2c9385..521196b4 100644
--- a/web/src/programs/letters-inverted.js
+++ b/web/programs/letters-inverted.js
@@ -61,7 +61,7 @@ r.shuffle(stimuli)
stimuli = measureIntegrity(flatten(stimuli))
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/letters-saccade.js b/web/programs/letters-saccade.js
similarity index 97%
rename from web/src/programs/letters-saccade.js
rename to web/programs/letters-saccade.js
index a7df2bcf..62912294 100644
--- a/web/src/programs/letters-saccade.js
+++ b/web/programs/letters-saccade.js
@@ -82,8 +82,7 @@ function preRenderFunc(sizes, letters, color) {
renderedLetters[idx] = image
}
}
- return {renders: renderedLetters,
- yield: {}}
+ return {renders: renderedLetters}
}
// special object for pre-rendering
@@ -121,7 +120,7 @@ r.shuffle(stimuli)
stimuli = measureIntegrity(flatten(stimuli))
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (let s of stimuli) {
yield s
}
diff --git a/web/src/programs/letters-tiled.js b/web/programs/letters-tiled.js
similarity index 97%
rename from web/src/programs/letters-tiled.js
rename to web/programs/letters-tiled.js
index 54d6211f..f880178f 100644
--- a/web/src/programs/letters-tiled.js
+++ b/web/programs/letters-tiled.js
@@ -67,7 +67,7 @@ r.shuffle(stimuli)
stimuli = measureIntegrity(flatten(stimuli))
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/letters.js b/web/programs/letters.js
similarity index 97%
rename from web/src/programs/letters.js
rename to web/programs/letters.js
index c6ca9278..a06fb197 100644
--- a/web/src/programs/letters.js
+++ b/web/programs/letters.js
@@ -61,7 +61,7 @@ r.shuffle(stimuli)
stimuli = measureIntegrity(flatten(stimuli))
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/program-templates/single-video.js b/web/programs/templates/single-video.js
similarity index 91%
rename from web/src/program-templates/single-video.js
rename to web/programs/templates/single-video.js
index 9f40e1d9..b4fe4c7b 100644
--- a/web/src/program-templates/single-video.js
+++ b/web/programs/templates/single-video.js
@@ -15,7 +15,7 @@ for (var i = 0; i < repetitions; i++) {
}
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/test.js b/web/programs/test.js
similarity index 84%
rename from web/src/programs/test.js
rename to web/programs/test.js
index 54744ae1..d55714e8 100644
--- a/web/src/programs/test.js
+++ b/web/programs/test.js
@@ -8,7 +8,7 @@ stimuli.push(new Solid(2, "white", meta))
stimuli.push(new Wait(1, meta))
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/programs/wedge.js b/web/programs/wedge.js
similarity index 92%
rename from web/src/programs/wedge.js
rename to web/programs/wedge.js
index 33c8b59c..26af19c3 100644
--- a/web/src/programs/wedge.js
+++ b/web/programs/wedge.js
@@ -26,7 +26,7 @@ for (let i = 0; i < 5; i++) {
stimuli = stimuli
-function* stimulusGenerator(renderResults) {
+function* stimulusGenerator() {
for (s of stimuli) {
yield s
}
diff --git a/web/src/app.js b/web/src/app.js
index 45bf253d..c89398b8 100644
--- a/web/src/app.js
+++ b/web/src/app.js
@@ -23,6 +23,11 @@ const {compileYAMLProgram, compileJSProgram} = require("./epl/eval")
const {DATADIR} = require('./vars.js')
console.log("DATADIR: " + DATADIR)
+
+GIT_SHA = fs.readFileSync(
+ '/www/git-sha',
+ "utf-8")
+
const app = new Koa();
app.use(logger())
@@ -151,6 +156,7 @@ function makeLabNotebook(labNotebook) {
labNotebook.date = date
labNotebook.version = 0.5
labNotebook.flickerVersion = 0.3
+ labNotebook.gitSHA = GIT_SHA
console.log("labNotebook", labNotebook)
return "---\n" + yaml.safeDump(labNotebook)
@@ -297,7 +303,7 @@ app.context.render = co.wrap(app.context.render);
app.use(async (ctx, next) => {
- const programChoices = fs.readdirSync("/www/src/programs/").map(s => s.slice(0, -3))
+ const programChoices = fs.readdirSync("/www/programs/").map(s => s.slice(0, -3))
let videoChoices = fs.readdirSync(DATADIR+"videos/")
await ctx.render("index", {
programChoices,
@@ -362,13 +368,13 @@ io.on("load", (ctx, data) => {
const regex = /\$\{VID\_SRC\}/gi;
const videoSrc = data.video
epl = fs.readFileSync(
- '/www/src/program-templates/single-video.js',
+ '/www/programs/templates/single-video.js',
"utf-8").replace(regex, "videos/" + videoSrc)
} else {
// this is likely a security vulnerability but sure is convenient
// convention over customization
epl = fs.readFileSync(
- '/www/src/programs/'+eplProgram+'.js',
+ '/www/programs/'+eplProgram+'.js',
"utf-8")
}
console.log("socket 'load': compiling for sid", sid)
@@ -384,7 +390,7 @@ io.on("load", (ctx, data) => {
io.on("renderResults", (ctx, data) => {
console.log("socket 'renderResults'")
const sid = data.sid
- program[sid].initialize(data.renderResults)
+ program[sid].initialize()
io.broadcast("enableSubmitButton")
})
diff --git a/web/src/epl/eval.js b/web/src/epl/eval.js
index a9519863..e7612999 100644
--- a/web/src/epl/eval.js
+++ b/web/src/epl/eval.js
@@ -17,14 +17,12 @@ let EPL = Object.assign({log: console.log, JSON: JSON, DATADIR: DATADIR}, {R: R}
function compileJSProgram(programJS,seed, windowHeight, windowWidth) {
console.log('compiling EPL.')
- let renderResults = {}
// FOR VM2 (production)
const vm = new VM({
sandbox: Object.assign({
windowHeight: windowHeight,
windowWidth: windowWidth,
seed: seed,
- renderResults: renderResults
}, EPL),
console: 'inherit'
})
@@ -35,7 +33,6 @@ function compileJSProgram(programJS,seed, windowHeight, windowWidth) {
// windowHeight: windowHeight,
// windowWidth: windowWidth,
// seed: seed,
- // renderResults: renderResults
// }, EPL)
// const vm = VM.createContext(sandbox)
// ---- FOR VM ----
@@ -50,8 +47,8 @@ function compileJSProgram(programJS,seed, windowHeight, windowWidth) {
"if (typeof preRenderFunc !== 'undefined') {" +
"preRenderFunc.toString()" +
"} else {" +
- "'function preRenderFunc() {" +
- "return {renders: undefined, yield: undefined}" +
+ "'function* preRenderFunc() {" +
+ "" +
"}'" +
"}")
let preRenderArgs = vm.run(
@@ -62,22 +59,11 @@ function compileJSProgram(programJS,seed, windowHeight, windowWidth) {
"}")
console.log("eval preRenderFunc")
- function initialize(clientResults) {
+
+ function initialize() {
console.log("Initializing EPL stimulus generator")
- // TODO warning: will this potentially break with multiple connections?
- renderResults.yield = clientResults
- // console.log("renderResults outside VM:", renderResults)
- // let r
- // if (renderResults===undefined) {
- // r = "undefined"
- // } else {
- // r = renderResults.toString()
- // }
- // console.log("renderResults", renderResults.yield)
- // we use stimulus index to ensure correct order and
- // avoid race condition
- // vm.run("log('renderResults inside VM:', renderResults.yield)")
- vm.run("let generator = stimulusGenerator(renderResults.yield); " +
+
+ vm.run("let generator = stimulusGenerator(); " +
"let s='uninitialized'; let si = 0;");
}
diff --git a/web/src/epl/types.js b/web/src/epl/types.js
index 375a68bf..0ae0fbe4 100644
--- a/web/src/epl/types.js
+++ b/web/src/epl/types.js
@@ -121,7 +121,7 @@ class Image extends Stimulus {
fixationPoint, metadata) {
super(lifespan, backgroundColor, metadata)
this.stimulusType = STIMULUS.IMAGE
- // image can be a number (index) for client-side `renders` object
+ // image can be a number (index) for client-side `renders` object that is stored in indexedDB
this.image = image
// e.g. {x: 0, y: 0}
this.fixationPoint = fixationPoint
diff --git a/web/static/js/dispatchers.js b/web/static/js/dispatchers.js
index dd6db794..24bca10c 100644
--- a/web/static/js/dispatchers.js
+++ b/web/static/js/dispatchers.js
@@ -123,29 +123,27 @@ function eyeChartDispatcher(lifespan, letterMatrix, size, padding, color) {
function imageDispatcher(lifespan, backgroundColor, image,
fixationPoint) {
- let img
let dont_dispatch = false
- if (typeof(image)==="number") {
- // renders is a special client-side object
- img = renders[image]
- } else if (typeof(image)==="string") {
- // assume image src (get from server)
- img = document.createElement("img")
- img.onload = (event) => {
- if (fixationPoint===undefined) {
- fixationPoint = {x: img.width / 2, y: img.height / 2}
- }
- store.dispatch(setGraphicsAC([{
- graphicType: GRAPHIC.IMAGE,
- image: img,
- fixationPoint: fixationPoint,
- lifespan: lifespan,
- age: 0
- }]))
+
+ let img = new Image()
+ img.onload = (event) => {
+ if (fixationPoint===undefined) {
+ fixationPoint = {x: img.width / 2, y: img.height / 2}
}
+ store.dispatch(setGraphicsAC([{
+ graphicType: GRAPHIC.IMAGE,
+ image: img,
+ fixationPoint: fixationPoint,
+ lifespan: lifespan,
+ age: 0
+ }]))
+ }
+ if (typeof(image)==="string") {
+ // assume image src (get from server)
img.src = image
dont_dispatch = true
} else {
+ // TODO can this be deleted? Or maybe used for older letter rendering?
img = image
}
@@ -395,5 +393,15 @@ function newStimulusDispatcher() {
async function queueStimulusDispatcher() {
const stimulus = await nextStimulus()
const stimulusIndex = stimulus.stimulusIndex
+ let image
+ if (typeof(stimulus.value.image)==="number") {
+ // retrieve image from indexedDB
+ try {
+ image = await SimpleIDB.get("render-"+stimulus.value.image)
+ stimulus.value.image = image
+ } catch (err) {
+ console.warn("Failed to get preRender: " + err)
+ }
+ }
store.dispatch(addStimulusAC(stimulus, stimulusIndex))
}
diff --git a/web/static/js/simpleIDB.js b/web/static/js/simpleIDB.js
new file mode 100644
index 00000000..9182a067
--- /dev/null
+++ b/web/static/js/simpleIDB.js
@@ -0,0 +1,146 @@
+// https://medium.com/@xon5/replacing-localstorage-with-indexeddb-2e11a759ff0c
+
+/** SimpleIDB **/
+SimpleIDB = {
+ initialize() {
+ return new Promise((resolve, reject) => {
+ // This first deletes any database of the same name
+ let deleteRequest = indexedDB.deleteDatabase('eyeCandyDB')
+ deleteRequest.onerror = function() {
+ reject(deleteRequest.error)
+ }
+ // Then creates a new one
+ let request = indexedDB.open('eyeCandyDB')
+ request.onupgradeneeded = function() {
+ request.result.createObjectStore('myStore')
+ resolve()
+ }
+ request.onerror = function() {
+ reject(request.error)
+ }
+ })
+ },
+
+ get(key) {
+ return new Promise((resolve, reject) => {
+ let openRequest = indexedDB.open('eyeCandyDB')
+ openRequest.onsuccess = function() {
+ let db = openRequest.result
+ let transaction = db.transaction('myStore', 'readonly')
+ let objectStore = transaction.objectStore('myStore')
+ let getRequest = objectStore.get(key)
+ getRequest.onsuccess = function() {
+ resolve(getRequest.result)
+ }
+ getRequest.onerror = function() {
+ reject(getRequest.error)
+ }
+ }
+ openRequest.onerror = function() {
+ reject(openRequest.error)
+ }
+ })
+ },
+
+ clearAll() {
+ // delete all objects in store
+ return new Promise((resolve, reject) => {
+ let openRequest = indexedDB.open('eyeCandyDB')
+ openRequest.onsuccess = function() {
+ let db = openRequest.result
+ let transaction = db.transaction('myStore', 'readwrite')
+ let objectStore = transaction.objectStore('myStore')
+ let clearRequest = objectStore.clear()
+ clearRequest.onsuccess = function() {
+ resolve()
+ }
+ clearRequest.onerror = function() {
+ reject(clearRequest.error)
+ }
+ }
+ openRequest.onerror = function() {
+ reject(openRequest.error)
+ }
+ })
+ },
+
+ set(key, value) {
+ return new Promise((resolve, reject) => {
+ let openRequest = indexedDB.open('eyeCandyDB')
+ openRequest.onsuccess = function() {
+ let db = openRequest.result
+ let transaction = db.transaction('myStore', 'readwrite')
+ let objectStore = transaction.objectStore('myStore')
+ let putRequest = objectStore.put(value, key)
+ putRequest.onsuccess = function() {
+ resolve()
+ }
+ putRequest.onerror = function() {
+ reject(putRequest.error)
+ }
+ }
+ openRequest.onerror = function() {
+ reject(openRequest.error)
+ }
+ })
+ },
+
+ remove(key) {
+ return new Promise((resolve, reject) => {
+ let openRequest = indexedDB.open('eyeCandyDB')
+ openRequest.onsuccess = function() {
+ let db = openRequest.result
+ let transaction = db.transaction('myStore', 'readwrite')
+ let objectStore = transaction.objectStore('myStore')
+ let deleteRequest = objectStore.delete(key)
+ deleteRequest.onsuccess = function() {
+ resolve()
+ }
+ deleteRequest.onerror = function() {
+ reject(deleteRequest.error)
+ }
+ }
+ openRequest.onerror = function() {
+ reject(openRequest.error)
+ }
+ })
+ }
+}
+
+
+
+
+// The rest is just Vue and UI things vvv
+//
+// new Vue({
+// el: '#app',
+// vuetify: new Vuetify(),
+// data: () => ({
+// initialized: false,
+// key1: '',
+// val1: '',
+// error1: null,
+// key2: '',
+// error2: null,
+// databaseOutput: null
+// }),
+// methods: {
+// initialize () {
+// this.initialized = true
+// SimpleIDB.initialize()
+// },
+// insertObject () {
+// this.error1 = null
+// try {
+// let jsonVal = (this.val1.includes('{')) ? JSON.parse(this.val1) : this.val1
+// SimpleIDB.set(this.key1, jsonVal)
+// } catch(e) { this.error1 = e.message }
+// },
+// removeObject () {
+// this.error2 = null
+// try {
+// SimpleIDB.remove(this.key2)
+// } catch(e) { this.error2 = e.message }
+// }
+// }
+// })
diff --git a/web/static/js/stimulus.js b/web/static/js/stimulus.js
index 322bbadd..1f4d31d9 100644
--- a/web/static/js/stimulus.js
+++ b/web/static/js/stimulus.js
@@ -4,7 +4,20 @@ REDUX (GLOBAL STATE)
const createStore = Redux.createStore
const applyMiddleware = Redux.applyMiddleware
-
+SimpleIDB.initialize()
+
+// (function() {
+// 'use strict';
+//
+// //check for support
+// if (!('indexedDB' in window)) {
+// console.log('This browser doesn\'t support IndexedDB');
+// return;
+// }
+//
+// var dbPromise = idb.open('eye-candy-db', 1);
+//
+// })();
/***********************************************
MIDDLEWARE
@@ -120,23 +133,40 @@ fetch("/get-sid", {
// console.log("COOKIE",document.cookie)
+async function loadPreRenderForStimuli(stimulusQueue) {
+ let stimulus
+ for (s in stimulusQueue) {
+ stimulus = stimulusQueue[s]
+ if (typeof(stimulus.value.image)==="number") {
+ // retrieve image from indexedDB
+ try {
+
+ stimulus.value.image = await SimpleIDB.get("render-"+stimulus.value.image)
+ } catch (err) {
+ console.warn("Failed to get preRender: " + err)
+ }
+ }
+ }
+ return stimulusQueue
+}
-
-socket.on("run", (stimulusQueue) => {
+socket.on("run", async (stimulusQueue) => {
+ // TODO load preRender images
console.log("socket 'run'")
+ stimulusQueue = await loadPreRenderForStimuli(stimulusQueue)
store.dispatch(setStimulusQueueAC(stimulusQueue))
store.dispatch(setStatusAC(STATUS.STARTED))
})
-socket.on("video", (stimulusQueue) => {
+socket.on("video", async (stimulusQueue) => {
+ // TODO load preRender images
console.log("socket 'video'")
+ stimulusQueue = await loadPreRenderForStimuli(stimulusQueue)
store.dispatch(setStimulusQueueAC(stimulusQueue))
store.dispatch(setStatusAC(STATUS.VIDEO))
})
-let renders
-
-socket.on("pre-render", (preRender) => {
+socket.on("pre-render", async (preRender) => {
// TODO dangerous, insecure
// but hey, it's science!
// also, this is client side so not *so* bad..
@@ -146,12 +176,25 @@ socket.on("pre-render", (preRender) => {
eval(preRender.func)
console.log("finished preRender func eval, about to render...")
- let renderResults = preRenderFunc(...preRender.args)
+ let renderGenerator = preRenderFunc(...preRender.args)
+ let renderItem = renderGenerator.next()
+ let render = renderItem.value
+ let n = 0
+ while ( renderItem.done===false) {
+ // note: assumes that render is a canvas
+ // localStorage.setItem("render-"+n, render.toDataURL())
+ try {
+ await SimpleIDB.set("render-"+n, render.toDataURL())
+ } catch (e) {
+ console.warn("SimpleIDB failed to set render-"+n)
+ }
+ // localStorage.setItem("render-"+n, JSON.stringify(render))
+ renderItem = renderGenerator.next()
+ render = renderItem.value
+ n++
+ }
console.log("finished render")
- renders = renderResults.renders
-
- socket.emit("renderResults", {renderResults: renderResults.yield,
- sid: localStorage.getItem('sid')})
+ socket.emit("renderResults", {sid: localStorage.getItem('sid')})
})
@@ -159,8 +202,19 @@ socket.on("reset", () => {
console.log("socket 'reset'")
// TODO next line causes TypeError: document.querySelector(...) is null on reset
store.dispatch(resetAC())
- renders = undefined
+ // remove preRenders
+ SimpleIDB.clearAll()
+ // Object.entries(localStorage).map(
+ // Object.entries(localStorage).map(
+ // x => x[0]
+ // ).filter(
+ // x => x.substring(0,7)=="render-"
+ // ).map(
+ // x => SimpleIDB.remove(x).catch(e => {
+ // console.warn("SimpleIDB failed to delete " + x)
+ // }))
+ // x => localStorage.removeItem(x))
})
socket.on("target", () => {
diff --git a/web/static/stimulus.html b/web/static/stimulus.html
index 00e39cc7..24c33376 100644
--- a/web/static/stimulus.html
+++ b/web/static/stimulus.html
@@ -5,6 +5,7 @@
+