From 1764301ab30c860045e092f3585c7bab53c17068 Mon Sep 17 00:00:00 2001 From: Jake Read <jake.read@cba.mit.edu> Date: Thu, 10 Oct 2019 10:18:29 -0400 Subject: [PATCH] readpng looks good --- README.md | 2 + hunks/hunks.js | 2 +- hunks/image/displayimagedata.js | 45 +++++ hunks/image/readpng.js | 155 +++++++++++------- hunks/image/readpng_leo.js | 107 ------------ ...{thresholdrgba_leo.js => thresholdrgba.js} | 0 hunks/link.js | 24 ++- hunks/manager.js | 2 +- hunks/view.js | 4 +- processes/vfpts.js | 9 + scratch/readpng.js | 78 +++++++++ {hunks/image => scratch}/threshold.js | 0 test_files/ATP.8E5.traces.png | Bin 0 -> 17477 bytes {scratch => test_files}/test.png | Bin typeset.js | 13 ++ 15 files changed, 265 insertions(+), 176 deletions(-) create mode 100644 hunks/image/displayimagedata.js delete mode 100644 hunks/image/readpng_leo.js rename hunks/image/{thresholdrgba_leo.js => thresholdrgba.js} (100%) create mode 100644 scratch/readpng.js rename {hunks/image => scratch}/threshold.js (100%) create mode 100644 test_files/ATP.8E5.traces.png rename {scratch => test_files}/test.png (100%) diff --git a/README.md b/README.md index 244fec4..5272066 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ Cuttlefish runs programs that are made of 'hunks' - each hunk is a small block o Hunks interface with the rest of the world through data ports - inputs and outputs - and their state variables. Each of these interfaces *is typed* - this helps us when we serialize messages (Cuttlefish is made for networks) - and helps everyone understand what hunks are meant to do. +Typing is rendered in `/typeset.js` - we can only connect outputs to inputs when a function exists here to copy data from the output-type to the input-type. These functions are directional! + ### Flowcontrolled Runtime When Cuttlefish is running, it ferries data between hunk outputs and outputs, and gives each hunk some time to operate on those inputs, producing outputs. By connecting different hunks in graphs, we can develop programs. diff --git a/hunks/hunks.js b/hunks/hunks.js index f77d7ce..1cf8a74 100644 --- a/hunks/hunks.js +++ b/hunks/hunks.js @@ -14,7 +14,7 @@ function Hunkify(hunk) { // we set this during load, only once, this avoids some confusion hunk.type = null hunk.name = null - // we keep a copy of our position-in-the-array ... + // we keep a copy of our position-in-the-array ... of our parent ... our pip hunk.ind = null hunk.log = function(msg) { diff --git a/hunks/image/displayimagedata.js b/hunks/image/displayimagedata.js new file mode 100644 index 0000000..15227c0 --- /dev/null +++ b/hunks/image/displayimagedata.js @@ -0,0 +1,45 @@ +import { Hunkify, Input, Output, State } from '../hunks.js' + +export default function DisplayImageData() { + Hunkify(this) + + const imageIn = new Input('ImageData', 'image', this) + this.inputs.push(imageIn) + + this.init = () => { + this.dom = $('div').get(0) + } + + let uid = `${this.name}_canvas_dom` + + this.onload = () => { + // I would say these implementations could use some clarity / nice-ness + // there's a real hairball to dig up in the hunks-getting-dom-elements 'system' + // that is fair game to anyone willing to wrastle with the dom and force-layout for 1wk + const target = $('<div>').get(0); + $(this.dom).append(target); + // these methods are rad ! + const view = html ` + <style> + #${uid} { + border: 1px solid black; + margin: 5px; + } + </style> + <canvas + id=${uid} + width=100 + height=100> + </canvas> + ` + render(view, target); + } + + this.loop = () => { + if(imageIn.io()){ + let imageData = imageIn.get() + console.log($(this.dom).children(`#${uid}`)) + } + } + +} diff --git a/hunks/image/readpng.js b/hunks/image/readpng.js index 2ba2af8..478edac 100644 --- a/hunks/image/readpng.js +++ b/hunks/image/readpng.js @@ -1,78 +1,119 @@ -/* - -hunk template - -*/ +/* hunk template */ // these are ES6 modules -import { - Hunkify, - Input, - Output, - State -} from '../hunks.js' +import { Hunkify, Input, Output, State } from '../hunks.js' + +import { html, svg, render } from 'https://unpkg.com/lit-html?module'; -function ReadPNG() { +export default function UploadPNG() { // this fn attaches handles to our function-object, Hunkify(this) - let imageOutput = new Output('rgba', 'image', this) - let buttonOutput = new Output('string', 'button', this) - this.outputs.push(imageOutput, buttonOutput) + // keeping these local globals, + let idealWidth = 400 + let localImageInfo = null + let imageUpdated = false - let xSize = new State('number', 'xSize', 400) - let ySize = new State('number', 'ySize', 400) - this.states.push(xSize, ySize) + // it looks like 'RGBA' types are actually ImageData objects, + // https://developer.mozilla.org/en-US/docs/Web/API/ImageData + // since we will be using these in canvas-contexts, makes sense to just use these types + const imageOut = new Output('ImageData', 'image', this) + this.outputs.push(imageOut) + + // as a hack, we can use boolean state variables as a button: they have handy callbacks... + const trig = new State('boolean', 'release', false) + this.states.push(trig) + trig.onChange = (value) => { + // I'll set this flag so that we will release image info on the next loop + // if any is available, + imageUpdated = true + } - // State items also have change handlers, - xSize.onChange = (value) => { - // at this point, something external (probably a human) - // has requested that we change this state variable, - // we can reject that, by doing nothing here, or we can - stateItem.set(value) + this.init = () => { + this.dom = $('<div>').get(0) } - // hunks can choose to- or not- have init code. - // at init, the module has been loaded and state variables have been - // recalled from any program save - so this is a good point - // to check any of those, and setup accordingly ... - // as is tradition, - this.dom = {} + //import + const png_read_handler = (e) => { + const reader = new FileReader(); - let button + const files = e.target.files; + const file = files[0]; - this.init = () => { - // manager calls this once - // it is loaded and state is updated (from program) - console.log('HELLO Read PNG') - this.dom = document.createElement('div') - // + const img = new Image(); + img.file = file; + + const loader = (aImg) => (e) => { + aImg.src = e.target.result; + aImg.onload = () => { + // OK: sometimes this image is *way too big* to dump into the DOM, + // so we write it into a new and virtual canvas that we won't render: + let virtualCanvas = $('<canvas>').get(0) + virtualCanvas.width = aImg.width + virtualCanvas.height = aImg.height + let vContext = virtualCanvas.getContext("2d") + vContext.drawImage(aImg, 0, 0) + // great, now we can use this to pull normalized ImageData information, this is what we pass around: + localImageInfo = vContext.getImageData(0, 0, aImg.width, aImg.height) + imageUpdated = true + + // now we can draw something: + const canvas = document.getElementById(`${this.name}_canvas_dom`); + // we'll want to shrink the whole thing ... or enlarge, + let scale = idealWidth / aImg.width + // the height and width of the actual image, + canvas.width = aImg.width * scale + canvas.height = aImg.height * scale + // and then we draw into it, + const ctx = canvas.getContext("2d"); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.scale(scale, scale) + ctx.drawImage(aImg, 0, 0); + } + }; + reader.onload = loader(img); + reader.readAsDataURL(file); } this.onload = () => { - let contact = $('<div>').addClass('btn').append('! read png !').get(0) - $(this.dom).append(contact) - contact.addEventListener('click', (evt) => { - buttonOutput.put('anything') - }) + const target = $('<div>').get(0); + $(this.dom).append(target); + // to name unique DOM elements, if we want to access them later using 'get element' methods, + // we can prepend the hunk's name (which is unique) + const view = html ` + <style> + #${this.name}_canvas_dom { + border: 1px solid black; + margin: 5px; + } + #${this.name}_dom { + position: absolute; + left: 10px; + top: 350px; + } + </style> + <div id=${this.name}_canvas_wrappper> + <canvas + id=${this.name}_canvas_dom + width=100 + height=100> + </canvas> + </div> + <input + id=${this.name}_dom + type="file" + accept="image/png" + @input=${ (e) => png_read_handler(e)}></input> + ` + render(view, target); } - // to divide time between hunks, each has a loop function - // this is the hunks' runtime: a manager calls this once-per-round - // here is where we check inputs, put to outputs, do work, etc this.loop = () => { - // typically we check inputs and outputs first, - // making sure we are clear to run, - /* - if (inA.io() && !outB.io()) { - // an input is occupied, and the exit path is empty - let output = internalFunc(this.inputs.a.get()) - // put 'er there - outB.put(output) + // we release data if we have any, if it's been updated, and if the output is clear: + if(localImageInfo && imageUpdated && !imageOut.io()){ + console.log(`${this.name} puts:` , localImageInfo) + imageUpdated = false + imageOut.put(localImageInfo) } - */ } } - -// the hunk is also an ES6 module, this is how we export those: -export default ReadPNG diff --git a/hunks/image/readpng_leo.js b/hunks/image/readpng_leo.js deleted file mode 100644 index 2426ce6..0000000 --- a/hunks/image/readpng_leo.js +++ /dev/null @@ -1,107 +0,0 @@ -/* - -hunk template - -*/ - -// these are ES6 modules -import { - Hunkify, - Input, - Output, - State -} from '../hunks.js' - -import { - html, - svg, - render -} from 'https://unpkg.com/lit-html?module'; - -export default function UploadPNG() { - // this fn attaches handles to our function-object, - Hunkify(this) - - const imageOut = new Output('RGBA', 'Image', this) - this.outputs.push(imageOut) - - this.init = () => { - this.dom = $('<div>').get(0) - } - - //import - const png_read_handler = (e) => { - const reader = new FileReader(); - - const files = e.target.files; - const file = files[0]; - - const img = new Image(); - img.file = file; - - const loader = (aImg) => (e) => { - aImg.src = e.target.result; - - aImg.onload = () => { - const canvas = document.getElementById("wackyAndTotallyUniqueID2"); - const ctx = canvas.getContext("2d"); - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.drawImage(aImg, 0, 0); - - // string other functions here - const data = getImageDataRGBA(); - // console.log("data", data); - - imageOut.put(data) - // console.log(data); - // console.log("copied", JSON.stringify(data)); - } - - }; - reader.onload = loader(img); - reader.readAsDataURL(file); - } - - // get image data: RGBA - const getImageDataRGBA = () => { //should add dpi term here - const canvas = document.getElementById("wackyAndTotallyUniqueID2"); - const ctx = canvas.getContext("2d"); - const imageRGBA = ctx.getImageData(0, 0, canvas.width, canvas.height); - return imageRGBA; - } - - this.onload = () => { - const target = $('<div>').get(0); - $(this.dom).append(target); - - const view = html ` - <style> - #wackyAndTotallyUniqueID2 { - border: 1px solid black; - margin: 5px; - } - - #wackyAndTotallyUniqueID { - position: absolute; - left: 10px; - top: 350px; - } - </style> - <div>Upload!</div> - <canvas - id="wackyAndTotallyUniqueID2" - width=300 - height=300> - </canvas> - <input - id="wackyAndTotallyUniqueID" - type="file" - accept="image/png" - @input=${(e) => png_read_handler(e)}></input> - ` - render(view, target); - } - - - this.loop = () => {} -} diff --git a/hunks/image/thresholdrgba_leo.js b/hunks/image/thresholdrgba.js similarity index 100% rename from hunks/image/thresholdrgba_leo.js rename to hunks/image/thresholdrgba.js diff --git a/hunks/link.js b/hunks/link.js index bcd415f..dc1e86b 100644 --- a/hunks/link.js +++ b/hunks/link.js @@ -16,7 +16,8 @@ import { import { TSET, LK, - MSGS + MSGS, + findPhy } from '../typeset.js' function Link() { @@ -93,9 +94,15 @@ function Link() { } else { // the object doesn't already exist, if (input) { - this.inputs[kp + 1] = new Input(nks[kp].typeKey, nks[kp].nameKey, this, true) + // we can only make types we have serialization routines for: + let phy = findPhy(nks[kp].typeKey) + if(phy.key && phy.read){ + this.inputs[kp + 1] = new Input(nks[kp].typeKey, nks[kp].nameKey, this, true) + } } else { - this.outputs[kp + 1] = new Output(nks[kp].typeKey, nks[kp].nameKey, this, true) + if(phy.key && phy.write){ + this.outputs[kp + 1] = new Output(nks[kp].typeKey, nks[kp].nameKey, this, true) + } } } } @@ -135,9 +142,6 @@ function Link() { /* --------------------------- ---------------------------- */ this.init = () => { - // manager calls this once - // it is loaded and state is updated (from program) - this.log('LINK INIT BEGINS ->') // since we know this needs to default to nc, and many programs // will save with these states set 'true', we reset them now. //otherLink.set(0) @@ -147,12 +151,16 @@ function Link() { // just add in order let ipKeys = getTypeAndNameKeys(inputList.value) for (let kp of ipKeys) { - this.inputs.push(new Input(kp.typeKey, kp.nameKey, this, true)) + if(getPhy(kp.typeKey).key && getPhy(kp.typeKey).read){ + this.inputs.push(new Input(kp.typeKey, kp.nameKey, this, true)) + } } let opKeys = getTypeAndNameKeys(outputList.value) for (let kp of opKeys) { - this.outputs.push(new Output(kp.typeKey, kp.nameKey, this, true)) + if(getPhy(kp.typeKey).key && getPhy(kp.typeKey).write){ + this.outputs.push(new Output(kp.typeKey, kp.nameKey, this, true)) + } } } diff --git a/hunks/manager.js b/hunks/manager.js index ca36041..2839aaa 100644 --- a/hunks/manager.js +++ b/hunks/manager.js @@ -301,7 +301,7 @@ function Manager() { for (let ind in hunks) { // alright look, we keep a copy of this if (hunks[ind].ind !== parseInt(ind)) { - console.log(`swapping ${hunks[ind].ind} for ${parseInt(ind)}`) + // console.log(`swapping ${hunks[ind].ind} for ${parseInt(ind)}`) hunks[ind].ind = parseInt(ind) } } diff --git a/hunks/view.js b/hunks/view.js index 871bed9..fef8a94 100644 --- a/hunks/view.js +++ b/hunks/view.js @@ -504,7 +504,7 @@ function View() { /* --------------------------- ---------------------------- */ let removeDef = (index) => { - console.log('removeDef, also many floop likely errors') + // console.log('removeDef, also many floop likely errors') // ok ok ok let od = defs[index] if (od === undefined) throw new Error('no hunk to delete!') @@ -526,7 +526,7 @@ function View() { let onDefReorder = () => { for (let ind in defs) { if (defs[ind].ind !== parseInt(ind)) { - console.log(`swapping ${defs[ind].ind} for ${parseInt(ind)}`) + // console.log(`swapping ${defs[ind].ind} for ${parseInt(ind)}`) defs[ind].newInd(parseInt(ind)) } } diff --git a/processes/vfpts.js b/processes/vfpts.js index b11ee8f..03895cf 100644 --- a/processes/vfpts.js +++ b/processes/vfpts.js @@ -193,3 +193,12 @@ let openPort = () => { } findSerialPort() + +// this causes node to req. time from the OS more often (as often as possible) +// meaning that our events are handled more often, and we drop ring times by some ms +// does burn cycles though, + +let reminders = () => { + setImmediate(reminders) +} +reminders() diff --git a/scratch/readpng.js b/scratch/readpng.js new file mode 100644 index 0000000..2ba2af8 --- /dev/null +++ b/scratch/readpng.js @@ -0,0 +1,78 @@ +/* + +hunk template + +*/ + +// these are ES6 modules +import { + Hunkify, + Input, + Output, + State +} from '../hunks.js' + +function ReadPNG() { + // this fn attaches handles to our function-object, + Hunkify(this) + + let imageOutput = new Output('rgba', 'image', this) + let buttonOutput = new Output('string', 'button', this) + this.outputs.push(imageOutput, buttonOutput) + + let xSize = new State('number', 'xSize', 400) + let ySize = new State('number', 'ySize', 400) + this.states.push(xSize, ySize) + + // State items also have change handlers, + xSize.onChange = (value) => { + // at this point, something external (probably a human) + // has requested that we change this state variable, + // we can reject that, by doing nothing here, or we can + stateItem.set(value) + } + + // hunks can choose to- or not- have init code. + // at init, the module has been loaded and state variables have been + // recalled from any program save - so this is a good point + // to check any of those, and setup accordingly ... + // as is tradition, + this.dom = {} + + let button + + this.init = () => { + // manager calls this once + // it is loaded and state is updated (from program) + console.log('HELLO Read PNG') + this.dom = document.createElement('div') + // + } + + this.onload = () => { + let contact = $('<div>').addClass('btn').append('! read png !').get(0) + $(this.dom).append(contact) + contact.addEventListener('click', (evt) => { + buttonOutput.put('anything') + }) + } + + // to divide time between hunks, each has a loop function + // this is the hunks' runtime: a manager calls this once-per-round + // here is where we check inputs, put to outputs, do work, etc + this.loop = () => { + // typically we check inputs and outputs first, + // making sure we are clear to run, + /* + if (inA.io() && !outB.io()) { + // an input is occupied, and the exit path is empty + let output = internalFunc(this.inputs.a.get()) + // put 'er there + outB.put(output) + } + */ + } +} + +// the hunk is also an ES6 module, this is how we export those: +export default ReadPNG diff --git a/hunks/image/threshold.js b/scratch/threshold.js similarity index 100% rename from hunks/image/threshold.js rename to scratch/threshold.js diff --git a/test_files/ATP.8E5.traces.png b/test_files/ATP.8E5.traces.png new file mode 100644 index 0000000000000000000000000000000000000000..aeda5d3836e904dbd2694caf869c6e35f2f0fa2d GIT binary patch literal 17477 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 UKlh%E1vRKVUHx3vIVCg!077q56aWAK literal 0 HcmV?d00001 diff --git a/scratch/test.png b/test_files/test.png similarity index 100% rename from scratch/test.png rename to test_files/test.png diff --git a/typeset.js b/typeset.js index c4ad614..fe4d974 100644 --- a/typeset.js +++ b/typeset.js @@ -431,6 +431,19 @@ const TSET = [ return intBounds(num, -2147483647, 2147483647) } } + }, + { // cuttlefish only, so no key, read or write fn's + // this is : https://developer.mozilla.org/en-US/docs/Web/API/ImageData + name: 'ImageData', + copy: { + ImageData: function(imageData){ + return new ImageData( + new Uint8ClampedArray(imageData.data), + imageData.width, + imageData.height + ) // + } + } } // etc ] // end TSET -- GitLab