diff --git a/river.mp4 b/assets/river.mp4 similarity index 100% rename from river.mp4 rename to assets/river.mp4 diff --git a/dragdrop.html b/dragdrop.html index 21c493f..da4b1bd 100644 --- a/dragdrop.html +++ b/dragdrop.html @@ -1,11 +1,11 @@ - - Cyclogram.js prototype + Cyclogram.js + -
- -

Select a video or image file

-
-
-
- - - +
+ +

Select a video or image file

+
+
+
diff --git a/dragdrop.js b/dragdrop.js new file mode 100644 index 0000000..920fe00 --- /dev/null +++ b/dragdrop.js @@ -0,0 +1,51 @@ +import { createCyclogramCanvas, drawCyclogram } from "./modules/cyclogram.js"; +import { processUploadedVideo } from "./modules/video.js"; + +let processedVideoResponse = {}; +let framesPerDay = 288/2; // remove nighttime hours, crudely +let dotSize = 6; +let i = 0; +let rowNumber = 0; +let colNumber = 0; + +new p5(function(p5) { + p5.setup = function() { + const canvasOptions = { + width: 1000, + height: 400, + element: 'canvas', + }; + createCyclogramCanvas(p5, canvasOptions); + + document.getElementById('input').addEventListener('change', async function(event) { + try { + processedVideoResponse = await processUploadedVideo(p5, event); + } catch (error) { + alert('Error loading file!'); + } + }) + } + + p5.draw = function() { + const { video, frameRate, steps, density, x, y } = processedVideoResponse; + if (video) { + const drawOptions = { + video, + frameRate, + steps, + density, + myX: x || 100, + myY: y || 400, + framesPerDay, + dotSize, + i, + rowNumber, + colNumber, + }; + const cyclogramResults = drawCyclogram(p5, drawOptions); + i = cyclogramResults.i; + rowNumber = cyclogramResults.rowNumber; + colNumber = cyclogramResults.colNumber; + } + } +}); diff --git a/modules/cyclogram.js b/modules/cyclogram.js new file mode 100644 index 0000000..7d02461 --- /dev/null +++ b/modules/cyclogram.js @@ -0,0 +1,36 @@ +function createCyclogramCanvas(p5Instance, canvasOptions) { + const { width, height, element } = canvasOptions; + const canvas = p5Instance.createCanvas(width, height); + canvas.parent(element); + return canvas; +} + +function drawCyclogram(p5Instance, drawOptions) { + const { video, frameRate, steps, density, myX, myY, framesPerDay, dotSize } = drawOptions; + let { i, rowNumber, colNumber } = drawOptions; + + // video.elt.currentTime = i/frameRate; + video.loadPixels(); + let color = [ + video.pixels[(myY * video.width * 4 * density) + (myX * 4 * density)], // get red + video.pixels[(myY * video.width * 4 * density) + (myX * 4 * density + 1)], // get green + video.pixels[(myY * video.width * 4 * density) + (myX * 4 * density + 2)] // get blue + ]; + + p5Instance.rect(i % framesPerDay * dotSize, rowNumber * dotSize, dotSize, dotSize); + p5Instance.fill(color); + colNumber += 1; + if (i % framesPerDay == 0) { + rowNumber += 1; // new row + colNumber = 0; + } + if (i == steps) i = 0; + else i++; + + return { i, rowNumber, colNumber }; +} + +export { + createCyclogramCanvas, + drawCyclogram, +} diff --git a/modules/video.js b/modules/video.js new file mode 100644 index 0000000..bef7318 --- /dev/null +++ b/modules/video.js @@ -0,0 +1,54 @@ +function getCoordinatesOfVideoClick(video, clickEvent) { + const x = parseInt(clickEvent.offsetX/video.elt.clientWidth * video.width); + const y = parseInt(clickEvent.offsetY/video.elt.clientHeight * video.height); + clickEvent.preventDefault(); + return { x, y }; +} + +function loadVideo(p5Instance, loadOptions) { + const { url, element } = loadOptions; + const video = p5Instance.createVideo(url); + video.parent(element); + + // video.elt.getVideoPlaybackQuality().totalVideoFrames // only how many frames have loaded so far + video.elt.controls = true; + video.elt.load(); + p5Instance.noStroke(); + const frameRate = 30; + const steps = parseInt(frameRate * video.elt.duration) // in seconds + const density = 1; // pixelDensity(); + + let coordinates = {}; + video.elt.onclick = function click(event) { + coordinates = getCoordinatesOfVideoClick(video, event); + }; + + return { video, frameRate, steps, density, ...coordinates }; +} + +async function processUploadedVideo(p5Instance, event) { + const file = event.target.files[0]; + const fileReader = new FileReader(); + + return new Promise((resolve, reject) => { + fileReader.onerror = () => { + fileReader.abort(); + reject(new DOMException('Problem parsing input file')); + }; + + fileReader.onload = () => { + const blob = new Blob([fileReader.result], { type: file.type }); + const url = URL.createObjectURL(blob); + const loadOptions = { url, element: 'video' }; + const response = loadVideo(p5Instance, loadOptions); + resolve(response); + }; + fileReader.readAsArrayBuffer(file); + }); +} + +export { + loadVideo, + getCoordinatesOfVideoClick, + processUploadedVideo, +}; diff --git a/prototype.html b/prototype.html index 744fcbb..9b30808 100644 --- a/prototype.html +++ b/prototype.html @@ -1,4 +1,3 @@ - @@ -6,6 +5,7 @@ Cyclogram.js prototype + -
-
- - - +
+
diff --git a/prototype.js b/prototype.js new file mode 100644 index 0000000..bf85036 --- /dev/null +++ b/prototype.js @@ -0,0 +1,47 @@ +import { createCyclogramCanvas, drawCyclogram } from "./modules/cyclogram.js"; +import { loadVideo } from "./modules/video.js"; + +let processedVideoResponse = {}; +let framesPerDay = 288/2; // remove nighttime hours, crudely +let dotSize = 6; +let i = 0; +let rowNumber = 0; +let colNumber = 0; + +new p5(function(p5) { + p5.setup = function() { + const canvasOptions = { + width: 1000, + height: 400, + element: 'canvas', + }; + createCyclogramCanvas(p5, canvasOptions); + + const url = '../assets/river.mp4'; + const loadOptions = { url, element: 'video' }; + processedVideoResponse = loadVideo(p5, loadOptions); + } + + p5.draw = function() { + const { video, frameRate, steps, density, x, y } = processedVideoResponse; + if (video) { + const drawOptions = { + video, + frameRate, + steps, + density, + myX: x || 100, + myY: y || 400, + framesPerDay, + dotSize, + i, + rowNumber, + colNumber, + }; + const cyclogramResults = drawCyclogram(p5, drawOptions); + i = cyclogramResults.i; + rowNumber = cyclogramResults.rowNumber; + colNumber = cyclogramResults.colNumber; + } + } +});