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