Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • jakeread/displacementexercise
  • rcastro/ussm
  • palomagr/displacementexercise
3 results
Show changes
Showing
with 24160 additions and 0 deletions
<!DOCTYPE html>
<html>
<head>
<title>dex-tools</title>
<!-- these three disable caching, or like, should? -->
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="-1" />
</head>
<body>
<link href="style.css" rel="stylesheet">
<script src="libs/jquery.min.js"></script>
<script src="libs/math.js" type="text/javascript"></script>
<script src="libs/d3.js"></script>
<script type="module" src="dex-client.js"></script>
<div id="wrapper">
<!-- bootloop puts first view inside of this div -->
</div>
</body>
</html>
jquery.min.js
vue.js
lit-html/
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
domain.js
click-to-go, other virtual machine dwg
Jake Read at the Center for Bits and Atoms
(c) Massachusetts Institute of Technology 2020
This work may be reproduced, modified, distributed, performed, and
displayed for any purpose, but must acknowledge the open systems assembly protocol (OSAP) project.
Copyright is retained and must be preserved. The work is provided as is;
no warranty is provided, and users accept all liability.
*/
'use strict'
import dt from './drawing/domtools.js'
export default function Pad() {
// machine size : pixel size
let psize = [500, 500]
let msize = [200, 200]
let scale = [msize[0] / psize[0], msize[1] / psize[1]]
// setup the pad
let dom = $('.plane').get(0)
let pad = $('<div>').addClass('pad').get(0)
$(pad).css('background-color', '#c9e5f2').css('width', `${psize[0]}px`).css('height', `${psize[1]}px`)
$(dom).append(pad)
let dft = { s: 1, x: -550, y: -550, ox: 0, oy: 0 }
dt.writeTransform(pad, dft)
// drawing lines,
let segs = [] // ramps: have .pi and .pf
let drawSegs = () => {
$(pad).children('.svgcont').remove() // rm all segs
for(let seg of segs){
let del = [seg.pf[0] - seg.pi[0], seg.pf[1] - seg.pi[1]]
$(pad).append(dt.svgLine(seg.pi[0] / scale[0], seg.pi[1] / scale[1], del[0]/scale[1], del[1]/scale[1]))
}
}
this.onNewTarget = (pos) => {
console.warn('bind this')
}
this.addSeg = (ramp) => {
//console.warn('draw', ramp.pi, ramp.pf)
segs.push(ramp)
if(segs.length > 10) segs.shift()
drawSegs()
}
// handle clicks
pad.addEventListener('click', (evt) => {
if(evt.target != pad) return
// scaled to machine spec, and invert y pixels -? logical
let pos = [evt.layerX * scale[0], evt.layerY * scale[1]]
//console.warn(`X: ${pos[0].toFixed(2)}, Y: ${pos[1].toFixed(2)}`)
this.onNewTarget(pos)
})
}
\ No newline at end of file
/*
saturn.js
js acceleration controller
Jake Read at the Center for Bits and Atoms
(c) Massachusetts Institute of Technology 2020
This work may be reproduced, modified, distributed, performed, and
displayed for any purpose, but must acknowledge the open systems assembly protocol (OSAP) project.
Copyright is retained and must be preserved. The work is provided as is;
no warranty is provided, and users accept all liability.
*/
'use strict'
import {
vDist,
vSum,
vLen,
vUnitBetween,
vScalar,
deg
} from './utes/smallvectors.js'
// should do input is
export default function Saturn() {
// move settings
let minSpeed = 1.0 // units / s
let cruise = 400 // units / s
let accel = 1500 // units / s / s
let deviation = 0.5 // units of junction deviation: bigger for more instant accel at corners
let period = 0.050 // minimum move time (WARN: this probably conflicts with minspeed, no?)
console.warn(`SATURN minimum segment size is ${minSpeed * period}`)
// our state,
let positions = [[0, 0]]
let speed = minSpeed
let streaming = false
let LOGRUNTIME = false
let LOGTIME = false
this.onRampOut = (ramp) => {
console.warn('bind this')
}
this.ship = () => {
streaming = true
//console.warn('vi', speed.toFixed(2), 'positions', JSON.parse(JSON.stringify(positions)))
// calculate new ramps, given current position and speed
let ramps = runSaturn(positions, speed)
//console.log('ramps', JSON.parse(JSON.stringify(ramps)))
// now we can ship one,
if (ramps.length < 1) return
this.onRampOut(JSON.parse(JSON.stringify(ramps[0])))
// 'current' speed as far as our scope is concerned is the speed at the end of the output ramp
speed = ramps[0].vf
// and position is .pf, but we need to see if we've walked past a point in the stream
let dtp = vDist(ramps[0].pf, positions[1])
//console.log(dtp, positions[1])
if (dtp < 0.001) {
// cleared last position, shift it out and update speed,
//console.log('CLEAR', positions.length)
positions.shift()
} else {
// are mid-segment, only shift ramp, update position
//console.log('MID', positions.length)
positions[0] = ramps[0].pf
}
// don't need to shift ramps because we'll recalc next time,
// if that wasn't the last ramp, set a timer to ship next
let delay = Math.max(0, ramps[0].t * 1000 - 100) // 100ms before this should finish, or *now*
//console.log(`delay is ${delay}`)
if (ramps.length > 1) {
setTimeout(this.ship, delay)
} else {
streaming = false
// setTimeout(() => { streaming = false }, delay)
}
}
this.checkStream = () => {
if (!streaming && positions.length > 1) {
this.ship()
}
}
// entry pt: we always accept points ? no flowcontrol yet
this.newTarget = (pos) => {
// array of nums in unit space,
// console.log('gotem', pos)
try {
if (vDist(pos, positions[positions.length - 1]) < (minSpeed * period)) {
console.warn('not accepting this xtremely small segment')
} else {
positions.push(pos)
this.checkStream()
}
} catch (err) {
console.warn('err at saturn input', err)
}
}
this.reset = () => {
positions = [[0, 0]]
speed = minSpeed
}
// ---------------------------------------------------- Period Pass
let periodPass = (posns, speeds, debug) => {
for (let i = posns.length - 2; i > 0; i--) {
// distance to make,
let d = vDist(posns[i], posns[i + 1])
// the *fastest* we could go if we go flat out, in one period, is
let v = d / period
// set self,
speeds[i] = v
// traceback:
if (speeds[i + 1] > speeds[i]) {
speeds[i + 1] = v
}
}
}
// ---------------------------------------------------- Junction Deviation
let jd = (posns, speeds, debug) => {
//console.log('posns', posns)
let calcJunctionSpeed = (p0, p1, p2, debug) => {
// junction speed at p1, arrival from p0 exit to p2
let v0 = math.subtract(p1, p0)
let v1 = math.subtract(p2, p1)
if (debug) console.log('for\n', v0, v1)
let dotprod = math.dot(v0, v1) / (vLen(v0) * vLen(v1))
if (debug) console.log('dotprod', dotprod)
// block for floating pt errors that drive this term past +/- 1
if (dotprod < 1) dotprod = -1
if (dotprod > 1) dotprod = 1
let omega = Math.PI - Math.acos(dotprod)
if (debug) console.log('angle between', deg(omega))
let r = deviation / (1 / Math.cos(Math.PI / 2 - omega / 2) - 1)
if (debug) console.log('rad', r)
let v = Math.sqrt(accel * r)
if (debug) console.log('permissible', v)
return v
}
// the ops,
for (let m = 0; m < posns.length; m++) {
if (m === 0) continue // noop for start: this is our current speed, should already be in speeds arr
if (m === posns.length - 1) continue // noop for last move, nothing to junction into, exit should be minspeed
let jd = calcJunctionSpeed(posns[m - 1], posns[m], posns[m + 1])
if (Number.isNaN(jd)) {
console.warn(`after jd, NaN for move at ${m}`, posns[m - 1], posns[m], posns[m + 1])
// run again w/ debug
calcJunctionSpeed(posns[m - 1], posns[m], posns[m + 1], true)
}
if (jd < speeds[m]) {
speeds[m] = jd
}
}
// walk for minspeeds
for (let s in speeds) {
if (speeds[s] < minSpeed) speeds[s] = minSpeed
if (speeds[s] > cruise) speeds[s] = cruise
}
// that's it for us
return speeds
}
// ---------------------------------------------------- Reverse Pass
let reversePass = (posns, speeds, debug) => {
// link, walking back from last
// this makes sure we can completely decelerate, through moves, to the last point at zero
for (let i = posns.length - 2; i > 0; i--) {
if (debug) console.log(`reverse pass for ${i}\n`, posns[i], posns[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(posns[i + 1], posns[i]))
let maxEntranceByAccel = Math.sqrt(Math.pow(speeds[i + 1], 2) + 2 * accel * d)
let max = Math.max(minSpeed, Math.min(speeds[i], maxEntranceByAccel))
// just for logging
let temp = speeds[i]
// stay safe w/ current state at zero
if (i === 0) {
// only the future can be modified
} else {
speeds[i] = max
}
if (debug) console.log(`entrance was ${temp}, now ${speeds[i]}`)
}
}
// ---------------------------------------------------- Forward Pass
let forwardPass = (posns, speeds, debug) => {
// link, walk forwards: can we accel to these velocities in time?
for (let i = 0; i < posns.length - 2; i++) {
if (debug) console.log(`forwards pass for ${i}\n`, posns[i], posns[i + 1])
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(posns[i + 1], posns[i]))
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 === posns.length - 2) {
// tail should always be minspeed, if not, trouble
if (max > minSpeed) console.warn('trouble halting early')
} else {
speeds[i + 1] = max
}
if (debug) console.log(`exit was ${temp}, now ${speeds[i + 1]}`)
}
// link forwards, now making sure we can accel from our start speed up to the exit
// here is assuming posns[0] is current position, for which speed is the current velocity
}
// ---------------------------------------------------- Check Segs are all < minTime
let posnsCheck = (posns, speeds) => {
for (let i = 0; i < posns.length - 1; i++) {
let d = vDist(posns[i], posns[i + 1])
let vi = speeds[i]
let vf = speeds[i + 1]
let t = 2 * d / (vi + vf)
if (false) console.log(`ap, ${t.toFixed(3)}`)
if (t < (period - 0.001)) console.warn('small link in posns check')
}
}
// ---------------------------------------------------- Seg -> Ramps
let writeSeg = (ramps, vi, vf, pi, pf) => {
let d = vDist(pi, pf)
ramps.push({
vi: vi,
vf: vf,
t: 2 * d / (vi + vf),
pi: pi,
pf: pf
})
// to trace errors, turn these on, to see which seg-writing moves might be missing steps
//console.error('pi, pf')
//console.log(pi, pf)
// check gap
if (ramps.length > 2) {
let sep = vDist(ramps[ramps.length - 2].pf, ramps[ramps.length - 1].pi)
if (sep > 0.001) throw new Error('HERE')
}
}
// ---------------------------------------------------- Triangle -> Seg
let writeTriangle = (ramps, vi, vf, pi, pf) => {
let d = vDist(pi, pf)
// not sure when I wrote this eqn, seems to work tho
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, write one seg only
writeSeg(ramps, vi, vf, pi, pf)
} else {
// write two segs,
writeSeg(ramps, vi, vPeak, pi, pInter)
writeSeg(ramps, vPeak, vf, pInter, pf)
}
}
// ---------------------------------------------------- Ramp Pass
// turn posns, speeds into segments, writing accelerations between
let rampPass = (posns, speeds, debug) => {
let rmps = []
for (let i = 0; i < posns.length - 1; i++) {
let numRampsBefore = rmps.length
if (debug) console.log(`ramp pass for ${i}`)
let pi = posns[i]
let pf = posns[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}`)
// 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(`/`)
writeSeg(rmps, vi, vf, pi, pf)
} else if (maxEntry <= vi) {
if (debug) console.log('\\')
writeSeg(rmps, vi, vf, pi, pf)
} else if (vi === cruise && vf === cruise) {
// similarely, since we're not segmenting cruise any farther, it should also be OK
if (debug) console.log('--')
writeSeg(rmps, vi, vf, pi, p)
} else if (vi === cruise) {
if (debug) console.log('--\\')
let dcDist = (Math.pow(vi, 2) - Math.pow(vf, 2)) / (2 * accel) // distance to deccelerate
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) / vi
let tSeg2 = (vi - vf) / accel
if (tSeg1 < period || tSeg2 < period) {
// small segs, just write as one downtick,
writeSeg(rmps, vi, vf, pi, pf)
} else {
// if these are both > one period, we can write 'em
writeSeg(rmps, vi, vi, pi, pInter)
writeSeg(rmps, vi, vf, pInter, 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) {
writeSeg(rmps, vi, vf, pi, pf)
} else {
writeSeg(rmps, vi, vf, pi, pInter)
writeSeg(rmps, vf, vf, pInter, pf)
}
} else {
// here we will handle triangles '/\' and 'full trapezoids' '/--\'
let dcDist = (Math.pow(cruise, 2) - Math.pow(vf, 2)) / (2 * accel)
let acDist = (Math.pow(cruise, 2) - Math.pow(vi, 2)) / (2 * accel)
if (dcDist + dcDist >= d) {
if (debug) console.log('/\\')
writeTriangle(rmps, vi, vf, pi, pf)
} else { // BEGIN TRAP SELECTIONS
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
// here we go
if (tSeg2 < period) {
// for this case, contencating into a triangle is fine... it will be within ~ 50ms of extra accel time: not much
if (debug) console.log('/\\')
writeTriangle(rmps, vi, vf, pi, pf)
} else if (tSeg1 < period && tSeg3 < period) {
// contencate into one ramp
writeSeg(rmps, vi, vf, pi, pf)
} else if (tSeg1 < period) {
// first segment smaller: second larger, third larger
// contencate first, second into one, then write last
writeSeg(rmps, vi, cruise, pi, pb)
writeSeg(rmps, cruise, vf, pb, pf)
} else if (tSeg3 < period) {
// last segment smaller: second larger, first larger
// write first, then contencate second, third into one
writeSeg(rmps, vi, cruise, pi, pa)
writeSeg(rmps, cruise, vf, pa, pf)
} else {
// forgot the genuine full cruiser, here it is
writeSeg(rmps, vi, cruise, pi, pa)
writeSeg(rmps, cruise, cruise, pa, pb)
writeSeg(rmps, cruise, vf, pb, pf)
}
} // end TRAP SELECTIONS
} // end BIGSWITCH
if (rmps.length === numRampsBefore) console.warn('zero ramps written for', pi, pf, vi, vf)
} // end for-over-posns
return rmps
}
// ---------------------------------------------------- Check Ramps are all in spec
let rampCheck = (ramps) => {
for (let i = 0; i < ramps.length; i++) {
let r = ramps[i]
let d = vDist(r.pi, r.pf)
let t = 2 * d / (r.vi + r.vf)
if (t < (period - 0.001)) console.warn('troublesome ramp, small time', r)
// more than 10% over speed is probably not cool,
let cruiseAbsMax = cruise + 0.15 * cruise
if (r.vi > cruiseAbsMax || r.vf > cruiseAbsMax) console.warn('troublesome ramp, high speed', r)
// check that ramps are continuous
if (i < ramps.length - 2) {
let sep = vDist(r.pf, ramps[i + 1].pi)
if (sep > 0.001) console.warn('disconnected ramp junction', r, ramps[i + 1])
}
}
}
// should return ramps for given posns, where p[0] is current pos, and speed is vi
let runSaturn = (posns, speed) => {
if (LOGRUNTIME) console.log('runSaturn')
if (LOGTIME) console.time('lookahead')
// posns[] is global, generate speeds for
let speeds = new Array(posns.length)
speeds[0] = speed // begin at current speed,
speeds[speeds.length - 1] = minSpeed // end at target min velocity (stopped)
// first, set all speeds such that moves can be made within single periods
periodPass(posns, speeds)
// juction deviation calculates maximum allowable instantaneous acceleration through corners
jd(posns, speeds)
// reverse pass, links through moves such that we can decelerate successfully to the end
reversePass(posns, speeds)
// forward pass accelerates through,
forwardPass(posns, speeds)
// check speeds are all sound after these passes (for mintime / period)
posnsCheck(posns, speeds)
// re-generate the ramps,
let ramps = rampPass(posns, speeds, false)
// check ramps are all sequential
rampCheck(ramps)
if (LOGTIME) console.timeLog('lookahead')
if (LOGTIME) console.timeEnd('lookahead')
return ramps
}
}
\ No newline at end of file
html {
height: 100%;
/*
margin: 0px;
padding: 0px;
*/
}
body {
height: 100%;
overflow: hidden;
margin: 0px;
padding: 0px;
font-family: Palatino, serif;
font-size: 11px;
}
#wrapper {
overflow: hidden;
width: 100%;
height: 100%;
padding: 0px;
margin: 0px;
background: #fff;
/*background-image:url("background.png");*/
/*background-origin: content-box;*/
background-image:
linear-gradient(rgba(225, 225, 225, .2) 1px, transparent 1px),
linear-gradient(90deg, rgba(225, 225, 225, .2) 1px, transparent 1px);
background-size: 10px 10px;
}
.plane {
position: absolute;
width: 10px;
height: 10px;
}
.pad {
position: absolute;
}
.flag {
position: absolute;
width: 20px;
height: 20px;
}
.button {
position: absolute;
width: 100px;
height: 20px;
padding: 5px;
background-color: #dbdbdb;
color: #000;
font-size: 15px;
text-align: center;
}
.button:hover{
background-color: #e6e6e6;
cursor: pointer;
}
.inputwrap{
position:absolute;
width: 100px;
height: 20px;
padding: 5px;
border: none;
background-color: #dbdbdb;
}
.chart{
position: absolute;
background-color: #dbdbdb;
}
/* svg bs */
.svgcont {
position: absolute;
overflow: visible;
width: 5px;
height: 5px;
}
/* nodes */
.node {
position:absolute;
background-color: #f5f5f5;
}
.nodename {
font-size: 11px;
font-family: Helvetica, sans-serif;
width: 100px;
padding-left: 5px;
padding-top: 5px;
transform-origin: 0 0;
transform: rotate(-90deg);
}
/* vPorts */
.vPort {
position: absolute;
background-color: #ebebeb;
border-left: 2px solid black;
border-right: 2px solid black;
}
.vPortname {
font-size: 11px;
font-family: Helvetica, sans-serif;
width: 120px;
padding-left: 5px;
padding-top: 3px;
transform-origin: 0 0;
transform: rotate(-90deg) translate(-110px, 0px);
}
/* s/o https://brm.io/dat-gui-light-theme/ */
.dg.main.taller-than-window .close-button {
border-top: 1px solid #ddd;
}
.dg.main .close-button {
background-color: #ccc;
}
.dg.main .close-button:hover {
background-color: #ddd;
}
.dg {
color: #555;
text-shadow: none !important;
}
.dg.main::-webkit-scrollbar {
background: #fafafa;
}
.dg.main::-webkit-scrollbar-thumb {
background: #bbb;
}
.dg li:not(.folder) {
background: #fafafa;
border-bottom: 1px solid #ddd;
}
.dg li.save-row .button {
text-shadow: none !important;
}
.dg li.title {
background: #e8e8e8 url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;
}
.dg .cr.function:hover,.dg .cr.boolean:hover {
background: #fff;
}
.dg .c input[type=text] {
background: #e9e9e9;
}
.dg .c input[type=text]:hover {
background: #eee;
}
.dg .c input[type=text]:focus {
background: #eee;
color: #555;
}
.dg .c .slider {
background: #e9e9e9;
}
.dg .c .slider:hover {
background: #eee;
}
/*
lineChart.js
draws data to line chart
Jake Read at the Center for Bits and Atoms
(c) Massachusetts Institute of Technology 2019
This work may be reproduced, modified, distributed, performed, and
displayed for any purpose, but must acknowledge the squidworks and cuttlefish projects.
Copyright is retained and must be preserved. The work is provided as is;
no warranty is provided, and users accept all liability.
*/
// using https://bl.ocks.org/d3noob/402dd382a51a4f6eea487f9a35566de0
export default function LineChart() {
// dom elements
this.de = $('<div>').addClass('chart')
let uid = `lineChart_${parseInt(Math.random()*1000)}_uid`
$(this.de).attr('id', uid)
let cwidth = 500
let cheight = 500
$(this.de).css('width', `${cwidth}px`).css('height', `${cheight}px`)
// our vars,
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 90
}
let width = cwidth - margin.left - margin.right
let height = cheight - margin.top - margin.bottom
var x = d3.scaleLinear().range([0, width])
var y = d3.scaleLinear().range([height, 0])
var thesvg = null
// make ah function
this.draw = (data) => {
var valueline = d3.line()
.x(function(d) {
return x(d[0])
})
.y(function(d) {
return y(d[1])
})
// scale
x.domain([d3.min(data, function(d) {
return d[0]
}), d3.max(data, function(d) {
return d[0];
})])
y.domain([d3.min(data, function(d) {
return d[1]
}), d3.max(data, function(d) {
return d[1];
})])
if (thesvg) {
d3.select(`#${uid}`).selectAll("*").remove()
}
thesvg = d3.select(`#${uid}`).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// write it?
thesvg.append("path")
.data([data])
.attr("class", "line")
.attr("stroke-width", "2px")
.attr("stroke", "black")
.attr("fill", "none")
.attr("d", valueline)
// write the x axis
thesvg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
// the y axis
thesvg.append("g")
.call(d3.axisLeft(y))
}
}
/*
lsq.js
input previous system measurements as state (lists)
make predictions for y based on input at x, with lsq. from old data
*/
import smallmath from './smallmath.js'
export default function LeastSquares() {
// internal state
let observations = []
let m = 1
let b = 0
// setup
this.setObservations = (xy) => {
observations = JSON.parse(JSON.stringify(xy))
if(observations[0].length > 2){
let lsqr = smallmath.lsq(observations[0], observations[1])
m = lsqr.m
b = lsqr.b
}
}
// to generate human-readable interp of model
this.printFunction = () => {
if (b >= 0) {
return `${m.toExponential(2)} x + ${b.toExponential(2)}`
} else {
return `${m.toExponential(2)} x ${b.toExponential(2)}`
}
}
this.predict = (x) => {
return m * x + b
}
}
// least squares from https://medium.com/@sahirnambiar/linear-least-squares-a-javascript-implementation-and-a-definitional-question-e3fba55a6d4b
// mod to return object of m, b, values.x, values.y,
var smallmath = {
lsq: function(values_x, values_y) {
var x_sum = 0;
var y_sum = 0;
var xy_sum = 0;
var xx_sum = 0;
var count = 0;
/*
* The above is just for quick access, makes the program faster
*/
var x = 0;
var y = 0;
var values_length = values_x.length;
if (values_length != values_y.length) {
throw new Error('The parameters values_x and values_y need to have same size!');
}
/*
* Above and below cover edge cases
*/
if (values_length === 0) {
return [
[],
[]
];
}
/*
* Calculate the sum for each of the parts necessary.
*/
for (let i = 0; i < values_length; i++) {
x = values_x[i];
y = values_y[i];
x_sum += x;
y_sum += y;
xx_sum += x * x;
xy_sum += x * y;
count++;
}
/*
* Calculate m and b for the line equation:
* y = x * m + b
*/
var m = (count * xy_sum - x_sum * y_sum) / (count * xx_sum - x_sum * x_sum);
var b = (y_sum / count) - (m * x_sum) / count;
/*
* We then return the x and y data points according to our fit
*/
var result_values_x = [];
var result_values_y = [];
for (let i = 0; i < values_length; i++) {
x = values_x[i];
y = x * m + b;
result_values_x.push(x);
result_values_y.push(y);
}
return {
m: m,
b: b,
values: {
x: result_values_x,
y: result_values_y
}
}
}
}
export default smallmath
let vDist = (v1, v2) => {
// takes v1, v2 to be arrays of same length
// computes cartesian distance
var sum = 0
for (let i = 0; i < v1.length; i++) {
sum += (v1[i] - v2[i]) * (v1[i] - v2[i])
}
return Math.sqrt(sum)
}
let vSum = (v1, v2) => {
let ret = []
for(let i = 0; i < v1.length; i ++){
ret.push(v1[i] + v2[i])
}
return ret
}
let vLen = (v) => {
let sum = 0
for(let i = 0; i < v.length; i ++){
sum += Math.pow(v[i], 2)
}
return Math.sqrt(sum)
}
// from v1 to v2,
let vUnitBetween = (v1, v2) => {
let dist = vDist(v1, v2)
let ret = []
for(let i = 0; i < v1.length; i ++){
ret[i] = (v2[i] - v1[i]) / dist
}
return ret
}
let vScalar = (v, s) => {
let ret = []
for(let i = 0; i < v.length; i ++){
ret[i] = v[i] * s
}
return ret
}
let deg = (rad) => {
return rad * (180 / Math.PI)
}
export { vDist, vSum, vLen, vUnitBetween, vScalar, deg }
/*
ts.js // typeset
serialization, keys for DEX
Jake Read at the Center for Bits and Atoms
(c) Massachusetts Institute of Technology 2020
This work may be reproduced, modified, distributed, performed, and
displayed for any purpose, but must acknowledge the open systems assembly protocol (OSAP) project.
Copyright is retained and must be preserved. The work is provided as is;
no warranty is provided, and users accept all liability.
*/
let TS = {}
let decoder = new TextDecoder()
let tempDataView = {}
TS.read = (type, buffer, start, keyless) => {
if (!keyless) {
throw new Error('need code here for key checking')
}
switch (type) {
case 'uint8':
return buffer[start]
case 'uint16':
// little endian: lsb is at the lowest address
return (buffer[start] & 255) | (buffer[start + 1] << 8)
case 'uint32':
return (buffer[start] & 255) | (buffer[start + 1] << 8) | (buffer[start + 2] << 16) | (buffer[start + 3] << 24)
case 'int32':
tempDataView = new DataView(buffer.buffer) // the actual buffer underlying the uint8array...
return tempDataView.getInt32(start, true)
case 'boolean':
if (buffer[start] > 0) {
return true
} else {
return false
}
break;
case 'string':
let length = (buffer[start] & 255) | (buffer[start + 1] << 8) | (buffer[start + 2] << 16) | (buffer[start + 3] << 24)
let pckSlice = buffer.slice(start + 4, start + 4 + length)
return {
value: decoder.decode(pckSlice),
inc: length + 4
}
default:
console.error('no code for this type read')
return null
break;
}
}
let encoder = new TextEncoder()
let tempArr = {}
let tempBytes = {}
TS.write = (type, value, buffer, start, keyless) => {
if (!keyless) {
throw new Error('need code here for key checking')
}
switch (type) {
case 'uint8':
buffer[start] = value & 255
return 1
case 'uint16':
// little endian: lsb is at the lowest address
buffer[start] = value & 255
buffer[start + 1] = (value >> 8) & 255
return 2
case 'uint32':
buffer[start] = value & 255
buffer[start + 1] = (value >> 8) & 255
buffer[start + 2] = (value >> 16) & 255
buffer[start + 3] = (value >> 24) & 255
return 4
case 'int32':
tempArr = Int32Array.from([value])
tempBytes = new Uint8Array(tempArr.buffer)
buffer.set(tempBytes, start)
return 4
case 'float32':
tempArr = Float32Array.from([value])
tempBytes = new Uint8Array(tempArr.buffer)
buffer.set(tempBytes, start)
return 4
case 'char':
// console.log('char', value.charCodeAt(0))
buffer[start] = value.charCodeAt(0)
return 1
case 'string': // so, would be good to send long strings (i.e. dirty old gcodes), so 32b base
let stringStream = encoder.encode(value)
//console.log("WRITING STRING", value)
buffer[start] = stringStream.length & 255
buffer[start + 1] = (stringStream.length >> 8) & 255
buffer[start + 2] = (stringStream.length >> 16) & 255
buffer[start + 3] = (stringStream.length >> 24) & 255
buffer.set(stringStream, start + 4)
return 4 + stringStream.length
case 'boolean':
if (value) {
buffer[start] = 1
} else {
buffer[start] = 0
}
return 1
default:
console.error('no code for this type write')
return null
break;
}
}
TS.logPacket = (buffer) => {
// log a pretty buffer
// buffers should all be Uint8Array views,
let pert = []
for (let i = 0; i < buffer.length; i++) {
pert.push(buffer[i])
}
console.log(pert)
}
export {
TS
}
This diff is collapsed.
/*
dex-server
serves dex tools
Jake Read at the Center for Bits and Atoms
(c) Massachusetts Institute of Technology 2020
This work may be reproduced, modified, distributed, performed, and
displayed for any purpose, but must acknowledge the open systems assembly protocol (OSAP) project.
Copyright is retained and must be preserved. The work is provided as is;
no warranty is provided, and users accept all liability.
*/
// new year new bootstrap
const express = require('express')
const app = express()
// this include lets us read data out of put requests,
const bodyparser = require('body-parser')
// our fs tools,
const fs = require('fs')
//const filesys = require('./filesys.js')
// and we occasionally spawn local pipes (workers)
const child_process = require('child_process')
// will use these to figure where tf we are
let ownIp = ''
const os = require('os')
// serve everything: https://expressjs.com/en/resources/middleware/serve-static.html
app.use(express.static(__dirname))
// accept post bodies as json,
app.use(bodyparser.json())
app.use(bodyparser.urlencoded({extended: true}))
// redirect traffic to /client,
app.get('/', (req, res) => {
res.redirect('/client')
})
// we also want to institute some pipes: this is a holdover for a better system
// more akin to nautilus, where server-side graphs are manipulated
// for now, we just want to dive down to a usb port, probably, so this shallow link is OK
let processes = []
app.get('/startLocal/:file', (req, res) => {
// launches another node instance at this file w/ these args,
let args = ''
if(req.query.args){
args = req.query.args.split(',')
}
console.log(`attempt to start ${req.params.file} with args ${args}`)
// startup, let's spawn,
const process = child_process.spawn('node', ['-r', 'esm', `local/${req.params.file}`])
// add our own tag,
process.fileName = req.params.file
let replied = false
let pack = ''
process.stdout.on('data', (buf) => {
// these emerge as buffers,
let msg = buf.toString()
// can only reply once to xhr req
if(msg.includes('OSAP-wss-addr:') && !replied){
res.send(msg)
replied = true
}
// ok, dealing w/ newlines
pack = pack.concat(msg)
let index = pack.indexOf('\n')
while(index >= 0){
console.log(`${process.fileName} ${process.pid}: ${pack.substring(0, index)}`)
pack = pack.slice(index + 1)
index = pack.indexOf('\n')
}
})
process.stderr.on('data', (err) => {
if(!replied){
res.send('err in local script')
replied = true
}
console.log(`${process.fileName} ${process.pid} err:`, err.toString())
})
process.on('close', (code) => {
console.log(`${process.fileName} ${process.pid} closes:`, code)
if(!replied){
res.send('local process closed')
replied = true
}
})
console.log(`started ${process.fileName} w/ pid ${process.pid}`)
})
// finally, we tell the express server to listen here:
let port = 8080
app.listen(port)
// once we're listening, report our IP:
let ifaces = os.networkInterfaces()
// this just logs the processes IP's to the termina
Object.keys(ifaces).forEach(function(ifname) {
var alias = 0;
ifaces[ifname].forEach(function(iface) {
if ('IPv4' !== iface.family){//} || iface.internal !== false) {
// skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses
return;
}
ownIp = iface.address
if (alias >= 1) {
console.log('dex-tool available on: \t' /*ifname + ':' + alias,*/ + iface.address + `:${port}`);
// this single interface has multiple ipv4 addresses
// console.log('serving at: ' ifname + ':' + alias + iface.address + `:${port}`);
} else {
console.log('dex-tool available on:\t' /*ifname + ':' + alias,*/ + iface.address + `:${port}`);
// this interface has only one ipv4 adress
//console.log(ifname, iface.address);
}
++alias;
});
});
controller/js/favicon.ico

14.7 KiB

This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.