diff --git a/hunks/adhoc/saturn.js b/hunks/adhoc/saturn.js index 1c5f9378cc312df68de97538c2b1da200f48fb1a..3ee468fb6eaebb7ffff7015e613dcb95b205de1b 100644 --- a/hunks/adhoc/saturn.js +++ b/hunks/adhoc/saturn.js @@ -37,7 +37,6 @@ export default function Saturn() { let outy = this.output('number', 'outy') let outz = this.output('number', 'outz') let outp = this.output('array', 'posn') - let period = 0.100 // his.state('number', 'period', 50) // in ms, let allClear = () => { return (!outx.io() && !outy.io() && !outz.io()) @@ -53,6 +52,7 @@ export default function Saturn() { let deviation = 0.1 // virtual radius to junction about let accel = 980 // units/s/s (9.8m/s/s, 9800mm/s/s is 1G) let minSpeed = 0.01 // conspicuous, to debug for tails (indexing) + let period = 0.050 // his.state('number', 'period', 50) // in ms, // current states, let cruise = 200 // target, (units/s) @@ -107,19 +107,10 @@ export default function Saturn() { if (debug) console.log(`reverse pass for ${i}\n`, positions[i], positions[i + 1]) if (debug) console.log(`current entrance to calculate is`, speeds[i]) if (debug) console.log(`the constraining exit is`, speeds[i + 1]) - // to calcluate the maximum entrance, given our exit, with pure acceleration: let d = vLen(math.subtract(positions[i + 1], positions[i])) - // with given period, how fast can we possibly go in one period? for small moves this is constraining let maxEntranceByAccel = Math.sqrt(Math.pow(speeds[i + 1], 2) + 2 * accel * d) - - // to calculate the maximum entrance, given our exit, to make within one period of time: - // let t = 2 * d / (speeds[i] + speeds[i + 1]) // current t ... - // v2, where t = period (1.5 is a safety factor for later rounding) - let maxEntranceByPeriod = (period * 1.5) / (2 * d) - speeds[i] - - // set the entrance speed to the min of jd or our Max Entrance, but no lower than the minspeed - let max = Math.max(minSpeed, Math.min(speeds[i], maxEntranceByAccel, maxEntranceByPeriod)) + let max = Math.max(minSpeed, Math.min(speeds[i], maxEntranceByAccel)) // just for logging let temp = speeds[i] // stay safe w/ current state at zero @@ -132,6 +123,35 @@ export default function Saturn() { } } + // OK: *i think* the move might be to do this *first* and then never increase moves + + let periodPass = (speeds, debug) => { + for (let i = positions.length - 2; i > 0; i--) { + // distance to make, + let d = vDist(positions[i], positions[i+1]) + // 'initial' velocity (final) + let vi = speeds[i] + let vf = speeds[i + 1] + // with maximum acceleration, and this distance, this takes this long: + let tMin = (2 * d) / (vi + vf) + if(tMin < period){ + console.warn('vi, vf, distance, tMin') + console.log(speeds[i].toFixed(3), speeds[i + 1].toFixed(3)) + console.log(d.toFixed(3)) + console.log(tMin.toFixed(3)) + // this is a reverse pass, so we actually want to modify vi + // such that ... + let nvi = period / (2 * d) - vf + console.log('propose', nvi.toFixed(3)) + // ok then: given the d, what v for one period? + let v = d / period + console.log('flat at', v.toFixed(3)) + // however: this might drive vf into the -ves - in that case, we need to walk forwards... + // this might actually be that sorting algo... + } + } + } + let forwardPass = (speeds, debug) => { // link, walk forwards: can we accel to these velocities in time? for (let i = 0; i < positions.length - 2; i++) { @@ -139,8 +159,8 @@ export default function Saturn() { if (debug) console.log(`current exit to calculate is`, speeds[i + 1]) if (debug) console.log(`the constraining entrance is`, speeds[i]) let d = vLen(math.subtract(positions[i + 1], positions[i])) - let maxExit = Math.sqrt(Math.pow(speeds[i], 2) + 2 * accel * d) - let max = Math.max(minSpeed, Math.min(speeds[i + 1], maxExit)) + let maxExitByAccel = Math.sqrt(Math.pow(speeds[i], 2) + 2 * accel * d) + let max = Math.max(minSpeed, Math.min(speeds[i + 1], maxExitByAccel)) let temp = speeds[i + 1] if (i === positions.length - 2) { // tail should always be minspeed, if not, trouble @@ -154,292 +174,6 @@ export default function Saturn() { // here is assuming positions[0] is current position, for which speed is the current velocity } - let rampPass = (speeds, debug) => { - let ramps = [] - for (let i = 0; i < positions.length - 2; i++) { - if (debug) console.log(`ramp pass for ${i}`) - let pi = positions[i] - let pf = positions[i + 1] - let vi = speeds[i] - let vf = speeds[i + 1] - let d = vDist(pi, pf) - let maxEntry = Math.sqrt(Math.pow(speeds[i + 1], 2) + 2 * accel * d) - let maxExit = Math.sqrt(Math.pow(speeds[i], 2) + 2 * accel * d) - if (debug) console.log(`entrance speed is ${vi}`) - if (debug) console.log(`exit speed is ${vf}`) - if (debug) console.log(`d is ${d}, maxEntry ${maxEntry}, maxExit ${maxExit}`) - // these are common and useful: - let writeUpTick = () => { - ramps.push({ - vi: vi, - vf: vf, - t: (vf - vi) / accel, - pi: pi, - pf: pf - }) - } - let writeDownTick = () => { - ramps.push({ - vi: vi, - vf: vf, - t: (vi - vf) / accel, - pi: pi, - pf: pf - }) - } - // big switch - if (maxExit <= vf) { - // the all-up and all-down segments should always be clear: - // since we already swept for these cases in the revpass - if (debug) console.log(`/`) - writeUpTick() - } else if (maxEntry <= vi) { - if (debug) console.log('\\') - writeDownTick() - } else if (vi === cruise && vf === cruise) { - // similarely, since we're not segmenting cruise any farther, it should also be OK - if (debug) console.log('--') - ramps.push({ - vi: vi, - vf: vf, - t: d / vi, - pi: pi, - pf: pf - }) - } else if (vi === cruise) { - if (debug) console.log('--\\') - let dcDist = (Math.pow(cruise, 2) - Math.pow(vf, 2)) / (2 * accel) - let pInter = math.add(pf, vScalar(vUnitBetween(pf, pi), dcDist)) - // now, we need to tune accel / cruise phases so that neither t is < 1 period - let tSeg1 = (d - dcDist) / cruise - let tSeg2 = (cruise - vf) / accel - if(tSeg1 < period || tSeg2 < period){ - // hopeless, write downtick - // there are other options here: adjust one to suit other, but we're not here for it - // and in these cases, the small segments, being small, can just get washed into a big ramp, ok - writeDownTick() - } else { - // and if we can't do that, we backtrack to '\\' type - // seg1 - ramps.push({ - vi: vi, - vf: cruise, - t: tSeg1, - pi: pi, - pf: pInter - }) - // seg 2, - ramps.push({ - vi: cruise, - vf: vf, - t: tSeg2, - pi: pInter, - pf: pf - }) - } - } else if (vf === cruise) { - if (debug) console.log('/--') - let acDist = (Math.pow(cruise, 2) - Math.pow(vi, 2)) / (2 * accel) - let pInter = math.add(pi, vScalar(vUnitBetween(pi, pf), acDist)) - // I feel the same about this as I did above - let tSeg1 = (cruise - vi) / accel - let tSeg2 = (d - acDist) / cruise - if(tSeg1 < period || tSeg2 < period){ - writeDownTick() - } else { - // seg1 - ramps.push({ - vi: vi, - vf: cruise, - t: tSeg1, - pi: pi, - pf: pInter - }) - // seg2 - ramps.push({ - vi: cruise, - vf: vf, - t: tSeg2, - pi: pInter, - pf: pf - }) - } - } else { - let dcDist = (Math.pow(cruise, 2) - Math.pow(vf, 2)) / (2 * accel) - let acDist = (Math.pow(cruise, 2) - Math.pow(vi, 2)) / (2 * accel) - let writeTriangle = () => { - if (debug) console.log('/\\') - let vPeak = Math.sqrt(((2 * accel * d + Math.pow(vi, 2) + Math.pow(vf, 2)) / 2)) - let acDist = (Math.pow(vPeak, 2) - Math.pow(vi, 2)) / (2 * accel) - let pInter = math.add(pi, vScalar(vUnitBetween(pi, pf), acDist)) - // finally, we have to check here if either / or side is too small, then default to smallticks - let tSeg1 = (vPeak - vi) / accel - let tSeg2 = (vPeak - vf) / accel - if(tSeg1 < period || tSeg2 < period){ - // bail hard, - if(vf > vi){ - writeUpTick() - } else { - writeDownTick() - } - } else { - ramps.push({ - vi: vi, - vf: vPeak, - t: tSeg1, - pi: pi, - pf: pInter - }) - ramps.push({ - vi: vPeak, - vf: vf, - t: tSeg2, - pi: pInter, - pf: pf - }) - } - } - if (acDist + dcDist < d) { - if (debug) console.log('/--\\') - let pa = math.add(pi, vScalar(vUnitBetween(pi, pf), acDist)) - let pb = math.add(pf, vScalar(vUnitBetween(pf, pi), dcDist)) - // ok, - let tSeg1 = (cruise - vi) / accel - let tSeg2 = (d - acDist - dcDist) / cruise - let tSeg3 = (cruise - vf) / accel - if(tSeg2 < period){ - // for this case, contencating into a triangle is fine... it will be within ~ 50ms of extra accel time: not much - writeTriangle() - } else if (tSeg1 < period && tSeg3 < period){ - // contencate to slow-up or slow-down - if(vf > vi){ - writeUpTick() - } else { - writeDownTick() - } - } else if (tSeg1 < period){ - // check that enough space to make slow triangle, - if(tSeg1 + tSeg2 < period){ - // sweet lord there must be a better way - if(vf > vi){ - writeUpTick() - } else { - writeDownTick() - } - } else { - // slow-triangle up, - ramps.push({ - vi: vi, - vf: cruise, - t: 2 * vDist(pi, pb) / (vi + cruise), - pi: pi, - pf: pb - }) - // and last seg. as normal - ramps.push({ - vi: cruise, - vf: vf, - t: tSeg3, - pi: pb, - pf: pf - }) - } - } else if (tSeg3 < period){ - if(tSeg2 + tSeg3 < period){ - // ibid - if(vf > vi){ - writeUpTick() - } else { - writeDownTick() - } - } else { - // write first seg as normal, - ramps.push({ - vi: vi, - vf: cruise, - t: tSeg1, - pi: pi, - pf: pa - }) - // second as slow-loss from pa -> vf - ramps.push({ - vi: cruise, - vf: vf, - t: 2 * vDist(pa, pf) / (cruise + vf), - pi: pa, - pf: pf - }) - } - } else { - // 3 segs - ramps.push({ - vi: vi, - vf: cruise, - t: tSeg1, - pi: pi, - pf: pa - }) - ramps.push({ - vi: cruise, - vf: cruise, - t: tSeg2, - pi: pa, - pf: pb - }) - ramps.push({ - vi: cruise, - vf: vf, - t: tSeg3, - pi: pb, - pf: pf - }) - } - } else { - // the actual triangle case - writeTriangle() - } - } // end BIGSWITCH - } // end for-over-positions - return ramps - } - - let roundPass = (ramps, debug) => { - for (let i = 0; i < ramps.length; i++) { - // try a forward walk through these things. adjust so that distance is equal for each, - let r = ramps[i] - // first, given current d, entrance and exit speeds, the time: - let d = vDist(r.pi, r.pf) - let t = 2 * d / (r.vi + r.vf) - // does this calc match the ramp time? yes - // so ... - } - } - - let blockPass = (ramps, debug) => { - let blocks = [] - for(let i = 0; i < ramps.length; i++){ - let r = ramps[i] - let d = vDist(r.pi, r.pf) - // how many blocks are we going to split it to? - let count = r.t / period - let integer = Math.round(count) - if(integer < 1) { - console.warn(`small ramp during blockPass at ${i}`) - integer = 1 - } - // the pos'ns to split to: - let vu = vUnitBetween(r.pi, r.pf) - // now just... - for(let b = 0; b < integer; b ++){ - // percentage through, - let start = b / integer - let finish = (b + 1) / integer - blocks.push([math.add(r.pi, vScalar(vu, start)), math.add(r.pi, vScalar(vu, finish))]) - } - } - return blocks - } - let blockOnce = true let blocks = [] let posNow = [] @@ -465,6 +199,9 @@ export default function Saturn() { // first we get all move final v's by jd: // we jd, if (positions.length > positionsBufferSize - 1 && blockOnce) { + for(let i = 0; i < positions.length - 2; i ++){ + //console.log(vDist(positions[i], positions[i + 1])) + } blockOnce = false // ok, here's the lookahead routine: console.time('lookahead') @@ -474,41 +211,13 @@ export default function Saturn() { // jd runs an algorithm that calculates maximum allowable // instantaneous accelerations at corners jd(speeds) - // we occasionally (rather, often) need to start decelerating (or accelerating) a few segments before - // the actual constraint: these passes link segments to one another by ensuring that a pass through - // the whole path does not require any accelerations outside of our range. - // the reversepass also sets minimum speeds such that we don't find any moves impossible to execute - // within one network period + // revpass to link by max. accel: reversePass(speeds) + // reverse again, checking that times are within periods... + periodPass(speeds) forwardPass(speeds) console.timeLog('lookahead') - // now have allowable maximum speeds at the junctions - the corners - between segments - // but we still need to consider how much acceleration we can do in between these minimum speeds - // i.e. we will now generate the individual accel- deccel- and cruise phases of each segment - // *now* since we are operating on a period basis, we also tune these segments such that - // (1) none are smaller than one period of time and (2) all take some integer division of periods to complete - let ramps = rampPass(speeds) - // or, - //let ramps = timeSegPass(speeds) - // we do one last check: - for (let r of ramps) { - //console.log(`${r.t.toFixed(3)}`) - if (r.vi < minSpeed || r.vf < minSpeed || r.vi > (cruise * 1.1) || r.vf > (cruise * 1.1) || r.t < period) { - console.warn(`troublesome ramp found on final check`, r) - } - } - // now we're done, - console.timeLog('lookahead') - console.log(`have ${ramps.length} ramps for ${positions.length} positions`) - // should see about writing out motion blocks: - blocks = blockPass(ramps, true) - console.log(`have ${blocks.length} blocks for ${ramps.length} ramps`) - console.timeEnd('lookahead') // 49ms ... to write ~ 3k blocks from 145 ramps from 64 positions - // OK: I think this is it... I'll wrap the loop again to do this once, then write those blocks out at the - // flowcontrol'd interval... and see how it looks. this is maybe even enough: if steppers just make these counts - // of steps, distance is preserved, but some jerk every 50ms... interpolation next - // run once, - //throw new Error('halt') + // re-hash from here } if(blocks.length > 0 && allClear()){ @@ -520,7 +229,7 @@ export default function Saturn() { outx.put(1) outy.put(1) outz.put(1) - // and, + // output in steps-space... posNow = vScalar(bl[0], spmm) posUpdated = true } diff --git a/hunks/adhoc/tpath.js b/hunks/adhoc/tpath.js index 5ed840add1856dcecd6fd2da5110c1323346ac01..651d610de479c813eb1056210466434a343cd24b 100644 --- a/hunks/adhoc/tpath.js +++ b/hunks/adhoc/tpath.js @@ -17,7 +17,7 @@ import { import * as expath from '../../test_files/example-path-sl2.js' import { vScalar } from '../../libs/smallvectors.js' -let dpi = 600 +let dpi = 72 export default function TPFCOUT(){ Hunkify(this) @@ -43,7 +43,9 @@ export default function TPFCOUT(){ this.loop = () => { if(path.length > 0 && go){ if(!outPosn.io()){ - outPosn.put(vScalar(path.shift(), 600 / 25.4)) + let op = vScalar(path.shift(), (1/dpi)*25.4) + //console.log(op[1]) + outPosn.put(op) } } } diff --git a/hunks/image/readpng.js b/hunks/image/readpng.js index a1d5358a9feffafbe596998690cbef385a7b06ac..c71825d134d6f332d566c0b1eebafdb146623891 100644 --- a/hunks/image/readpng.js +++ b/hunks/image/readpng.js @@ -125,6 +125,11 @@ export default function UploadPNG() { } localDpi = ppx * 25.4 / 1000 dpiUpdated = true + if(localImageInfo){ + let width = localImageInfo.width * ((dpi*1000) / 25.4) + let height = localImageInfo.height * ((dpi*1000) / 25.4) + console.warn(`size in mm is w: ${width.toFixed(3)}, h: ${height.toFixed(3)}`) + } console.log('dpi', localDpi) } let headerReader = new FileReader() diff --git a/scratch/ramppass.js b/scratch/ramppass.js new file mode 100644 index 0000000000000000000000000000000000000000..a9a205b4999419c66f70b871e3e95b7d189599be --- /dev/null +++ b/scratch/ramppass.js @@ -0,0 +1,274 @@ +let rampPass = (speeds, debug) => { + let ramps = [] + for (let i = 0; i < positions.length - 2; i++) { + if (debug) console.log(`ramp pass for ${i}`) + let pi = positions[i] + let pf = positions[i + 1] + let vi = speeds[i] + let vf = speeds[i + 1] + let d = vDist(pi, pf) + let maxEntry = Math.sqrt(Math.pow(speeds[i + 1], 2) + 2 * accel * d) + let maxExit = Math.sqrt(Math.pow(speeds[i], 2) + 2 * accel * d) + if (debug) console.log(`entrance speed is ${vi}`) + if (debug) console.log(`exit speed is ${vf}`) + if (debug) console.log(`d is ${d}, maxEntry ${maxEntry}, maxExit ${maxExit}`) + // these are common and useful: + let writeUpTick = () => { + ramps.push({ + vi: vi, + vf: vf, + t: (vf - vi) / accel, + pi: pi, + pf: pf + }) + } + let writeDownTick = () => { + ramps.push({ + vi: vi, + vf: vf, + t: (vi - vf) / accel, + pi: pi, + pf: pf + }) + } + // big switch + if (maxExit <= vf) { + // the all-up and all-down segments should always be clear: + // since we already swept for these cases in the revpass + if (debug) console.log(`/`) + writeUpTick() + } else if (maxEntry <= vi) { + if (debug) console.log('\\') + writeDownTick() + } else if (vi === cruise && vf === cruise) { + // similarely, since we're not segmenting cruise any farther, it should also be OK + if (debug) console.log('--') + ramps.push({ + vi: vi, + vf: vf, + t: d / vi, + pi: pi, + pf: pf + }) + } else if (vi === cruise) { + if (debug) console.log('--\\') + let dcDist = (Math.pow(cruise, 2) - Math.pow(vf, 2)) / (2 * accel) + let pInter = math.add(pf, vScalar(vUnitBetween(pf, pi), dcDist)) + // now, we need to tune accel / cruise phases so that neither t is < 1 period + let tSeg1 = (d - dcDist) / cruise + let tSeg2 = (cruise - vf) / accel + if (tSeg1 < period || tSeg2 < period) { + // hopeless, write downtick + // there are other options here: adjust one to suit other, but we're not here for it + // and in these cases, the small segments, being small, can just get washed into a big ramp, ok + writeDownTick() + } else { + // and if we can't do that, we backtrack to '\\' type + // seg1 + ramps.push({ + vi: vi, + vf: cruise, + t: tSeg1, + pi: pi, + pf: pInter + }) + // seg 2, + ramps.push({ + vi: cruise, + vf: vf, + t: tSeg2, + pi: pInter, + pf: pf + }) + } + } else if (vf === cruise) { + if (debug) console.log('/--') + let acDist = (Math.pow(cruise, 2) - Math.pow(vi, 2)) / (2 * accel) + let pInter = math.add(pi, vScalar(vUnitBetween(pi, pf), acDist)) + // I feel the same about this as I did above + let tSeg1 = (cruise - vi) / accel + let tSeg2 = (d - acDist) / cruise + if (tSeg1 < period || tSeg2 < period) { + writeDownTick() + } else { + // seg1 + ramps.push({ + vi: vi, + vf: cruise, + t: tSeg1, + pi: pi, + pf: pInter + }) + // seg2 + ramps.push({ + vi: cruise, + vf: vf, + t: tSeg2, + pi: pInter, + pf: pf + }) + } + } else { + let dcDist = (Math.pow(cruise, 2) - Math.pow(vf, 2)) / (2 * accel) + let acDist = (Math.pow(cruise, 2) - Math.pow(vi, 2)) / (2 * accel) + let writeTriangle = () => { + if (debug) console.log('/\\') + let vPeak = Math.sqrt(((2 * accel * d + Math.pow(vi, 2) + Math.pow(vf, 2)) / 2)) + let acDist = (Math.pow(vPeak, 2) - Math.pow(vi, 2)) / (2 * accel) + let pInter = math.add(pi, vScalar(vUnitBetween(pi, pf), acDist)) + // finally, we have to check here if either / or side is too small, then default to smallticks + let tSeg1 = (vPeak - vi) / accel + let tSeg2 = (vPeak - vf) / accel + if (tSeg1 < period || tSeg2 < period) { + // bail hard, + if (vf > vi) { + writeUpTick() + } else { + writeDownTick() + } + } else { + ramps.push({ + vi: vi, + vf: vPeak, + t: tSeg1, + pi: pi, + pf: pInter + }) + ramps.push({ + vi: vPeak, + vf: vf, + t: tSeg2, + pi: pInter, + pf: pf + }) + } + } + if (acDist + dcDist < d) { + if (debug) console.log('/--\\') + let pa = math.add(pi, vScalar(vUnitBetween(pi, pf), acDist)) + let pb = math.add(pf, vScalar(vUnitBetween(pf, pi), dcDist)) + // ok, + let tSeg1 = (cruise - vi) / accel + let tSeg2 = (d - acDist - dcDist) / cruise + let tSeg3 = (cruise - vf) / accel + if (tSeg2 < period) { + // for this case, contencating into a triangle is fine... it will be within ~ 50ms of extra accel time: not much + writeTriangle() + } else if (tSeg1 < period && tSeg3 < period) { + // contencate to slow-up or slow-down + if (vf > vi) { + writeUpTick() + } else { + writeDownTick() + } + } else if (tSeg1 < period) { + // check that enough space to make slow triangle, + if (tSeg1 + tSeg2 < period) { + // sweet lord there must be a better way + if (vf > vi) { + writeUpTick() + } else { + writeDownTick() + } + } else { + // slow-triangle up, + ramps.push({ + vi: vi, + vf: cruise, + t: 2 * vDist(pi, pb) / (vi + cruise), + pi: pi, + pf: pb + }) + // and last seg. as normal + ramps.push({ + vi: cruise, + vf: vf, + t: tSeg3, + pi: pb, + pf: pf + }) + } + } else if (tSeg3 < period) { + if (tSeg2 + tSeg3 < period) { + // ibid + if (vf > vi) { + writeUpTick() + } else { + writeDownTick() + } + } else { + // write first seg as normal, + ramps.push({ + vi: vi, + vf: cruise, + t: tSeg1, + pi: pi, + pf: pa + }) + // second as slow-loss from pa -> vf + ramps.push({ + vi: cruise, + vf: vf, + t: 2 * vDist(pa, pf) / (cruise + vf), + pi: pa, + pf: pf + }) + } + } else { + // 3 segs + ramps.push({ + vi: vi, + vf: cruise, + t: tSeg1, + pi: pi, + pf: pa + }) + ramps.push({ + vi: cruise, + vf: cruise, + t: tSeg2, + pi: pa, + pf: pb + }) + ramps.push({ + vi: cruise, + vf: vf, + t: tSeg3, + pi: pb, + pf: pf + }) + } + } else { + // the actual triangle case + writeTriangle() + } + } // end BIGSWITCH + } // end for-over-positions + return ramps +} + + +let blockPass = (ramps, debug) => { + let blocks = [] + for (let i = 0; i < ramps.length; i++) { + let r = ramps[i] + let d = vDist(r.pi, r.pf) + // how many blocks are we going to split it to? + let count = r.t / period + let integer = Math.round(count) + if (integer < 1) { + console.warn(`small ramp during blockPass at ${i}`) + integer = 1 + } + // the pos'ns to split to: + let vu = vUnitBetween(r.pi, r.pf) + // now just... + for (let b = 0; b < integer; b++) { + // percentage through, + let start = b / integer + let finish = (b + 1) / integer + blocks.push([math.add(r.pi, vScalar(vu, start)), math.add(r.pi, vScalar(vu, finish))]) + } + } + return blocks +} diff --git a/typeset.js b/typeset.js index 087e48d059c848c6cc1e719528736c0aea27d940..ca65b80927cf550921413ba46132d56be9881e2c 100644 --- a/typeset.js +++ b/typeset.js @@ -498,6 +498,9 @@ const TSET = [ imageData.width, imageData.height ) // + }, + reference: function(imageData){ + return imageData } } },