Created
August 15, 2023 15:53
-
-
Save gabonator/634b441c0dc35fe0f34a85dc4d20b72a to your computer and use it in GitHub Desktop.
rete experiment with OOK signal processing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/rete.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/vue-render-plugin.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/connection-plugin.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/alight.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/area-plugin.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/context-menu-plugin.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/comment-plugin.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/history-plugin.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/connection-mastery-plugin.min.js"></script> | |
<script src="https://code.highcharts.com/highcharts.js"></script> | |
<script src="https://code.highcharts.com/modules/histogram-bellcurve.js"></script> | |
<style> | |
html, body { | |
height: 100%; | |
width: 100%; | |
margin: 0; | |
overflow: hidden; | |
} | |
.node .control input, .node .input-control input { | |
width: 140px; | |
} | |
select, input { | |
width: 100%; | |
border-radius: 30px; | |
background-color: white; | |
padding: 2px 6px; | |
border: 1px solid #999; | |
font-size: 110%; | |
width: 170px; | |
} | |
.note { | |
position: absolute; | |
top: 0; | |
left: 0; | |
background: #7ca2ba; | |
color: white; | |
width: 100%; | |
text-align: center; | |
padding: 0.5em; | |
font-family: sans-serif; | |
z-index: 1; | |
a { | |
color: #eee; | |
} | |
} | |
#chart { | |
width: 800px; | |
height: 400px; | |
position: absolute; | |
left: -650px; | |
top: 400px; | |
border:1px #f00 solid; | |
display: none; | |
} | |
</style> | |
<div id="rete"></div> | |
<div id="chart"></div> | |
<script> | |
var numSocket = new Rete.Socket('Number value'); | |
var arrSocket = new Rete.Socket('Array value'); | |
var VueNumControl = { | |
props: ['readonly', 'defaultVal', 'emitter', 'ikey', 'getData', 'putData'], | |
template: '<input type="number" :readonly="readonly" :value="value" @input="change($event)" @dblclick.stop="" @pointerdown.stop="" @pointermove.stop=""/>', | |
data() { | |
return { | |
value: 0, | |
} | |
}, | |
methods: { | |
change(e){ | |
this.value = +e.target.value; | |
this.update(); | |
}, | |
update() { | |
if (this.ikey) | |
this.putData(this.ikey, this.value) | |
this.emitter.trigger('process'); | |
} | |
}, | |
mounted() { | |
// this.value = this.getData(this.ikey); | |
let val = this.getData(this.ikey); | |
this.value = val === undefined ? this.defaultVal : val; | |
this.update(); | |
} | |
} | |
var VueStrControl = { | |
props: ['readonly', 'defaultVal', 'emitter', 'ikey', 'getData', 'putData'], | |
template: '<input :readonly="readonly" :value="value" @input="change($event)" @dblclick.stop="" @pointerdown.stop="" @pointermove.stop=""/>', | |
data() { | |
return { | |
value: "nic", | |
} | |
}, | |
methods: { | |
change(e){ | |
console.log("neda sa"); | |
// this.value = +e.target.value; | |
this.update(); | |
}, | |
update() { | |
if (this.ikey) | |
this.putData(this.ikey, this.value) | |
this.emitter.trigger('process'); | |
} | |
}, | |
mounted() { | |
let val = this.getData(this.ikey); | |
this.value = val === undefined ? this.defaultVal : val; | |
this.update(); | |
} | |
} | |
var VueCheckControl = { | |
props: ['readonly', 'defaultVal', 'message', 'label', 'emitter', 'ikey', 'getData', 'putData'], | |
template: '<div style="font-family: sans-serif;"><input style="width:32px" type="checkbox" :readonly="readonly" @input="change($event)" @dblclick.stop="" @pointerdown.stop="" @pointermove.stop=""/>{{message}}</div>', | |
// TODO: checked | |
data() { | |
return { | |
value: false, | |
message: "ahoj", | |
} | |
}, | |
methods: { | |
change(e){ | |
this.value = e.target.checked; | |
this.update(); | |
}, | |
update() { | |
if (this.ikey) | |
this.putData(this.ikey, this.value) | |
// this.checked = this.value ? "checked" : ""; | |
this.emitter.trigger('process'); | |
} | |
}, | |
mounted() { | |
let val = this.getData(this.ikey); | |
this.value = val === undefined ? this.defaultVal : val; | |
this.update(); | |
} | |
} | |
var VueOptionControl = { | |
props: ['readonly', 'defaultVal', 'message', 'label', 'emitter', 'ikey', 'getData', 'putData'], | |
template: '<div style="font-family: sans-serif;"><input style="width:32px" type="checkbox" :readonly="readonly" @input="change($event)" @dblclick.stop="" @pointerdown.stop="" @pointermove.stop=""/>{{message}}</div>', | |
// TODO: checked | |
data() { | |
return { | |
value: false, | |
message: "ahoj", | |
} | |
}, | |
methods: { | |
change(e){ | |
this.value = e.target.checked; | |
this.update(); | |
}, | |
update() { | |
if (this.ikey) | |
this.putData(this.ikey, this.value) | |
// this.checked = this.value ? "checked" : ""; | |
this.emitter.trigger('process'); | |
} | |
}, | |
mounted() { | |
let val = this.getData(this.ikey); | |
this.value = val === undefined ? this.defaultVal : val; | |
this.update(); | |
} | |
} | |
var VueTextControl = { | |
props: ['readonly', 'emitter', 'ikey', 'getData', 'putData'], | |
//template: '<input type="text" :readonly="readonly" :value="value" @input="change($event)" @dblclick.stop="" @pointerdown.stop="" @pointermove.stop=""/>', | |
template: '<textarea style="width:100%; height:80px" :readonly="readonly" :value="value" @input="change($event)" @dblclick.stop="" @pointerdown.stop="" @pointermove.stop=""/>', | |
data() { | |
return { | |
value: "init text", | |
} | |
}, | |
methods: { | |
change(e){ | |
this.value = e.target.value; | |
this.update(); | |
}, | |
update() { | |
if (this.ikey) | |
this.putData(this.ikey, this.value) | |
this.emitter.trigger('process'); | |
} | |
}, | |
mounted() { | |
this.value = this.getData(this.ikey); | |
this.update(); | |
} | |
} | |
class NumControl extends Rete.Control { | |
constructor(emitter, key, readonly, defaultVal) { | |
super(key); | |
this.component = VueNumControl; | |
this.props = { emitter, ikey: key, readonly, defaultVal }; | |
} | |
setValue(val) { | |
this.vueContext.value = val; | |
} | |
} | |
class StrControl extends Rete.Control { | |
constructor(emitter, key, readonly, defaultVal) { | |
super(key); | |
this.component = VueStrControl; | |
this.props = { emitter, ikey: key, readonly, defaultVal }; | |
} | |
setValue(val) { | |
this.vueContext.value = val; | |
} | |
} | |
class CheckControl extends Rete.Control { | |
constructor(emitter, key, readonly, defaultVal/*, message*/) { | |
super(key); | |
this.component = VueCheckControl; | |
this.props = { emitter, ikey: key, readonly, defaultVal/*, message:message*/ }; | |
} | |
setValue(val) { | |
this.vueContext.value = val; | |
} | |
} | |
class OptionControl extends Rete.Control { | |
constructor(emitter, key, readonly, defaultVal, options) { | |
super(key); | |
this.component = VueOptionControl; | |
this.props = { emitter, ikey: key, readonly, defaultVal, options }; | |
} | |
setValue(val) { | |
this.vueContext.value = val; | |
} | |
} | |
class TextControl extends Rete.Control { | |
constructor(emitter, key, readonly) { | |
super(key); | |
this.component = VueTextControl; | |
this.props = { emitter, ikey: key, readonly}; | |
} | |
setValue(val) { | |
this.vueContext.value = val; | |
} | |
} | |
class NumConstant extends Rete.Component { | |
constructor(){ | |
super("Input signal"); | |
} | |
builder(node) { | |
this.out1 = new Rete.Output('data', "Pulse", arrSocket); | |
this.mydata = {data:[300,600,280,600,320,560,320,600,320,600,320,580,320,600,280,600,320,580,320,580,300,640,280,600,320,580,280,340,580,320,600,580,300,620,300,600,300,600,300,320,600,580,320,580,320,600,320,300,560,22580,300,580,280,600,320,620,300,580,320,580,300,600,320,600,300,600,300,600,300,600,300,620,300,600,300,600,300,300,620,280,640,560,300,620,280,620,280,620,280,340,600,580,320,580,340,560,320,300,600,23000,280,580,300,620,260,640,280,600,300,600,300,600,320,600,300,600,320,580,320,600,280,620,280,600,300,640,280,320,580,320,580,600,300,620,260,620,300,600,300,340,580,600,300,600,320,580,320,320,580,22540,320,580,280,620,300,600,300,620,300,580,300,600,300,620,300,600,300,600,280,600,320,620,300,580,300,600,340,280,600,300,620,580,300,620,300,580,320,600,280,320,580,620,320,600,280,600,320,300,580,498500,280,620,280,580,340,600,300,600,300,600,300,600,320,580,340,580,320,560,300,620,280,620,300,600,300,580,340,300,600,320,580,580,320,600,300,600,300,620,300,300,580,620,320,560,320,320,580,600,320,22600,260,600,300,620,300,580,320,600,300,620,280,620,280,620,260,620,300,600,340,560,320,600,300,600,300,620,300,300,600,320,600,580,300,600,280,620,300,620,280,340,580,600,280,620,280,340,580,580,320,23060,280,600,280,600,300,600,320,560,320,600,320,580,320,580,300,620,300,600,300,600,320,580,320,600,280,620,280,340,600,280,600,600,320,560,320,600,320,580,320,300,600,600,320,560,320,340,580,600,280,22580,280,600,280,620,300,600,300,600,320,580,300,620,280,620,280,600,300,600,320,580,340,580,300,620,280,620,300,320,580,340,560,600,300,600,320,600,280,600,300,340,600,600,280,600,300,320,580,620,300]}; | |
return node.addOutput(this.out1); | |
} | |
worker(node, inputs, outputs) { | |
outputs['data'] = this.mydata; | |
this.out1.name = `Pulse ${this.mydata.data.length}p / ${(this.mydata.data.reduce((a,b)=>a+b, 0)/1000).toFixed(1)}ms`; | |
this.out1.node.update(); | |
} | |
} | |
class Quantize extends Rete.Component { | |
constructor(){ | |
super("Quantize"); | |
} | |
builder(node) { | |
var inp = new Rete.Input('in', "Pulse", arrSocket); | |
this.out = new Rete.Output('out', "Pulse", arrSocket); | |
var len = new NumControl(this.editor, 'len'); | |
len.props.defaultVal = 300; | |
return node | |
.addInput(inp) | |
.addControl(len) | |
.addOutput(this.out); | |
} | |
worker(node, inputs, outputs) { | |
var arr = inputs['in'].length ? [...inputs['in'][0].data] : []; | |
var q = node.data.len; | |
for (var i=0; i<arr.length; i++) | |
arr[i] = Math.floor((arr[i]+q/2)/q)*q; | |
outputs['out'] = {data:arr}; | |
this.out.name = `Pulse ${arr.length}p / ${(arr.reduce((a,b)=>a+b, 0)/1000).toFixed(1)}ms`; | |
this.out.node.update(); | |
} | |
} | |
class Filter extends Rete.Component { | |
constructor(){ | |
super("Filter"); | |
} | |
builder(node) { | |
var inp = new Rete.Input('in', "Pulse", arrSocket); | |
this.out = new Rete.Output('out', "Pulse", arrSocket); | |
var condition = new StrControl(this.editor, 'condition'); | |
condition.props.defaultVal = "x < 5000"; | |
var breakOn = new CheckControl(this.editor, 'break'); | |
breakOn.props.defaultVal = false; | |
breakOn.props.message = "break on condition"; | |
return node | |
.addInput(inp) | |
.addControl(condition) | |
.addControl(breakOn) | |
.addOutput(this.out); | |
} | |
worker(node, inputs, outputs) { | |
var arr = inputs['in'].length ? [...inputs['in'][0].data] : []; | |
var q = node.data.len; | |
var newarr = []; | |
console.log(node.data.condition, node.data.break); | |
var filterCode = eval(`(x, i) => ${node.data.condition}`); | |
for (var i=0; i<arr.length; i++) | |
{ | |
if (filterCode(arr[i], i)) | |
newarr.push(arr[i]) | |
else if (node.data.break) | |
break; | |
} | |
outputs['out'] = {data:newarr}; | |
this.out.name = `${node.data.break} Pulse ${newarr.length}p / ${(newarr.reduce((a,b)=>a+b, 0)/1000).toFixed(1)}ms`; | |
this.out.node.update(); | |
} | |
} | |
class Histogram extends Rete.Component { | |
constructor(){ | |
super("Histogram"); | |
} | |
builder(node) { | |
var inp1 = new Rete.Input('arr', "Pulse", arrSocket); | |
return node | |
.addInput(inp1); | |
} | |
worker(node, inputs, outputs) { | |
var n1 = inputs['arr'].length ? inputs['arr'][0].data : []; | |
ShowHistogram(n1); | |
} | |
} | |
class SubArray extends Rete.Component { | |
constructor(){ | |
super("SubArray"); | |
} | |
builder(node) { | |
var inp = new Rete.Input('in', "Pulse", arrSocket); | |
this.out = new Rete.Output('out', "Pulse", arrSocket); | |
var begin = new NumControl(this.editor, 'begin'); | |
this.len = new NumControl(this.editor, 'len'); | |
var tillEnd = new CheckControl(this.editor, 'tillEnd'); | |
tillEnd.props.defaultVal = false; | |
tillEnd.props.message = "all till end"; | |
this.len.props.readonly = true; | |
begin.props.defaultVal = 1; | |
this.len.props.defaultVal = 10; | |
return node | |
.addInput(inp) | |
.addControl(begin) | |
.addControl(this.len) | |
.addControl(tillEnd) | |
.addOutput(this.out); | |
} | |
worker(node, inputs, outputs) { | |
var arr = inputs['in'].length ? [...inputs['in'][0].data] : []; | |
if (node.data.tillEnd) | |
arr = arr.slice(node.data.begin) | |
else | |
arr = arr.slice(node.data.begin, node.data.begin + node.data.len) | |
outputs['out'] = {data:arr}; | |
this.out.name = `Pulse ${arr.length}p / ${(arr.reduce((a,b)=>a+b, 0)/1000).toFixed(1)}ms`; | |
this.out.node.update(); | |
} | |
} | |
class Pairs extends Rete.Component { | |
constructor(){ | |
super("Pairs"); | |
} | |
builder(node) { | |
var inp = new Rete.Input('in', "Pulse", arrSocket); | |
this.out = new Rete.Output('out', "Pulse", arrSocket); | |
return node | |
.addInput(inp) | |
.addOutput(this.out); | |
} | |
worker(node, inputs, outputs) { | |
var arr = inputs['in'].length ? [...inputs['in'][0].data] : []; | |
if (arr.length % 2 != 0) | |
{ | |
outputs['out'] = {data:[]} | |
this.out.name = `Error!`; | |
this.out.node.update(); | |
throw "not pairs!" | |
} | |
var arrout = [] | |
for (var i=0; i<arr.length; i+=2) | |
arrout.push(`${arr[i]},${arr[i+1]}`); | |
outputs['out'] = {data:arrout}; | |
this.out.name = `Array of ${arrout.length} pairs`; | |
this.out.node.update(); | |
} | |
} | |
class LookupTable extends Rete.Component { | |
constructor(){ | |
super("Lookup table"); | |
} | |
builder(node) { | |
var inp = new Rete.Input('in', "Pulse", arrSocket); | |
this.out = new Rete.Output('out', "Pulse", arrSocket); | |
return node | |
.addInput(inp) | |
.addOutput(this.out); | |
} | |
worker(node, inputs, outputs) { | |
var arr = inputs['in'].length ? [...inputs['in'][0].data] : []; | |
if (arr.length % 2 != 0) | |
{ | |
outputs['out'] = {data:[]} | |
this.out.name = `Error!`; | |
this.out.node.update(); | |
throw "not pairs!" | |
} | |
var arrout = [] | |
for (var i=0; i<arr.length; i++) | |
if (arr[i] == "300,600") | |
arrout.push("1") | |
else if (arr[i] == "600,300") | |
arrout.push("0") | |
else | |
arrout.push("?") | |
outputs['out'] = {data:arrout}; | |
this.out.name = `Array of ${arrout.length} symbols`; | |
this.out.node.update(); | |
} | |
} | |
class ToInteger extends Rete.Component { | |
constructor(){ | |
super("To integer"); | |
// TODO: uint8_t, uint16_t, uint32_t | |
// TODO: LSB/MSB | |
} | |
builder(node) { | |
var inp = new Rete.Input('in', "Pulse", arrSocket); | |
this.out = new Rete.Output('out', "Pulse", arrSocket); | |
var firstbit = new OptionControl(this.editor, 'msblsbfirst'); | |
firstbit.props.title = "First bit" | |
firstbit.props.options = ["MSB", "LSB"]; | |
firstbit.props.defaultVal = "MSB"; | |
var datatype = new OptionControl(this.editor, 'datatype'); | |
datatype.props.title = "Data type" | |
datatype.props.options = ["uint8_t", "uint16_t", "uint32_t"]; | |
datatype.props.defaultVal = "uint32_t"; | |
return node | |
.addInput(inp) | |
.addControl(firstbit) | |
.addControl(datatype) | |
.addOutput(this.out); | |
} | |
worker(node, inputs, outputs) { | |
var arr = inputs['in'].length ? [...inputs['in'][0].data] : []; | |
var uint = 0; | |
for (var i=0; i<arr.length; i++) | |
{ | |
if (arr[i] === "0" || arr[i] === 0) | |
{ | |
} else if (arr[i] === "1" || arr[i] === 1) | |
{ | |
uint |= 1<<(arr.length-1-i) | |
} else { | |
outputs['out'] = {data:[]} | |
this.out.name = `Error!`; | |
this.out.node.update(); | |
throw "wrong data" | |
} | |
} | |
var digits = (arr.length+3)>>2; | |
var arrout = ["0x"+(("00000000"+uint.toString(16)).substr(-digits))] | |
outputs['out'] = {data:arrout}; | |
this.out.name = `uint32_t [${arrout.length}]`; | |
this.out.node.update(); | |
} | |
} | |
class Print extends Rete.Component { | |
constructor(){ | |
super("Print"); | |
} | |
builder(node) { | |
var inp = new Rete.Input('in', "Pulse", arrSocket); | |
this.print = new TextControl(this.editor, 'print'); | |
return node.addInput(inp).addControl(this.print); | |
} | |
worker(node, inputs, outputs) { | |
var arr = inputs['in'].length ? [...inputs['in'][0].data] : []; | |
console.log(arr); | |
this.editor.nodes.find(n => n.id == node.id).controls.get('print').setValue(arr.join(", ")); | |
} | |
} | |
class NumComponent extends Rete.Component { | |
constructor(){ | |
super("Number"); | |
} | |
builder(node) { | |
var out1 = new Rete.Output('num', "Number", numSocket); | |
return node.addControl(new NumControl(this.editor, 'num')).addOutput(out1); | |
} | |
worker(node, inputs, outputs) { | |
outputs['num'] = node.data.num; | |
} | |
} | |
class AddComponent extends Rete.Component { | |
constructor(){ | |
super("Add"); | |
} | |
builder(node) { | |
var inp1 = new Rete.Input('num',"Number", numSocket); | |
var inp2 = new Rete.Input('num2', "Number2", numSocket); | |
var out = new Rete.Output('num', "Number", numSocket); | |
this.inp1 = inp1 | |
inp1.addControl(new NumControl(this.editor, 'num')) | |
inp2.addControl(new NumControl(this.editor, 'num2')) | |
return node | |
.addInput(inp1) | |
.addInput(inp2) | |
.addControl(new NumControl(this.editor, 'preview', true)) | |
.addOutput(out); | |
} | |
worker(node, inputs, outputs) { | |
var n1 = inputs['num'].length?inputs['num'][0]:node.data.num1; | |
var n2 = inputs['num2'].length?inputs['num2'][0]:node.data.num2; | |
var sum = n1 + n2; | |
this.inp1.name = "ahoj" + sum; | |
this.inp1.node.update(); | |
this.editor.nodes.find(n => n.id == node.id).controls.get('preview').setValue(sum); | |
outputs['num'] = sum; | |
} | |
} | |
(async () => { | |
var container = document.querySelector('#rete'); | |
var components = [new NumComponent(), new AddComponent(), new NumConstant(), new SubArray(), new Quantize(), | |
new Histogram(), new Filter(), new Pairs(), new LookupTable(), new Print(), new ToInteger()]; | |
var editor = new Rete.NodeEditor('[email protected]', container); | |
editor.use(ConnectionPlugin.default); | |
editor.use(VueRenderPlugin.default); | |
editor.use(ContextMenuPlugin.default); | |
editor.use(AreaPlugin); | |
editor.use(CommentPlugin.default); | |
editor.use(HistoryPlugin); | |
editor.use(ConnectionMasteryPlugin.default); | |
var engine = new Rete.Engine('[email protected]'); | |
components.map(c => { | |
editor.register(c); | |
engine.register(c); | |
}); | |
var nd = await components[2].createNode() | |
var nf = await components[6].createNode({break:true}) | |
var nq = await components[4].createNode() | |
var nh = await components[5].createNode() | |
var ns = await components[3].createNode({tillEnd:true}) // subarray | |
var np = await components[7].createNode() // pairs | |
var nl = await components[8].createNode() // loopkup | |
var nx = await components[9].createNode() // print | |
var ni = await components[10].createNode() // to integer | |
nd.position = [100, 200]; | |
nf.position = [350, 200]; | |
nq.position = [600, 200]; | |
nh.position = [850, 200]; | |
ns.position = [100, 400]; | |
np.position = [350, 400]; | |
nl.position = [600, 400]; | |
nx.position = [1100, 400]; | |
ni.position = [850, 400]; | |
editor.addNode(nd); | |
editor.addNode(nf); | |
editor.addNode(nq); | |
editor.addNode(nh); | |
editor.addNode(ns); | |
editor.addNode(np); | |
editor.addNode(nl); | |
editor.addNode(nx); | |
editor.addNode(ni); | |
editor.connect(nd.outputs.get('data'), nf.inputs.get('in')); | |
editor.connect(nf.outputs.get('out'), nq.inputs.get('in')); | |
editor.connect(nq.outputs.get('out'), nh.inputs.get('arr')); | |
editor.connect(nq.outputs.get('out'), ns.inputs.get('in')); | |
editor.connect(ns.outputs.get('out'), np.inputs.get('in')); | |
editor.connect(np.outputs.get('out'), nl.inputs.get('in')); | |
editor.connect(nl.outputs.get('out'), ni.inputs.get('in')); | |
editor.connect(ni.outputs.get('out'), nx.inputs.get('in')); | |
editor.on('process nodecreated noderemoved connectioncreated connectionremoved', async () => { | |
console.log('process'); | |
await engine.abort(); | |
await engine.process(editor.toJSON()); | |
}); | |
editor.view.resize(); | |
AreaPlugin.zoomAt(editor); | |
editor.trigger('process'); | |
})(); | |
function ShowHistogram(data) | |
{ | |
if (data.length == 0) | |
{ | |
document.querySelector("#chart").style.display = "none"; | |
return; | |
} else { | |
document.querySelector("#chart").style.display = "block"; | |
} | |
var data1 = []; | |
var data2 = []; | |
for (var i=0; i<data.length; i++) | |
{ | |
if (i%2 == 0) | |
data1.push(data[i]); | |
else | |
data2.push(data[i]); | |
} | |
Highcharts.chart('chart', { | |
title: { | |
text: 'Highcharts Histogram' | |
}, | |
xAxis: [{ | |
title: { text: 'Data' }, | |
alignTicks: false, | |
}, { | |
title: { text: 'Histogram' }, | |
alignTicks: false, | |
opposite: true, | |
// min:minVal | |
}], | |
yAxis: [{ | |
title: { text: 'Data' } | |
}, { | |
title: { text: 'Histogram' }, | |
opposite: true | |
}], | |
/* | |
plotOptions: { | |
histogram: { | |
accessibility: { | |
point: { | |
valueDescriptionFormat: '{index}. {point.x:.3f} to {point.x2:.3f}, {point.y}.' | |
} | |
} | |
} | |
}, | |
*/ | |
series: [{ | |
name: 'Histogram', | |
type: 'histogram', | |
xAxis: 1 , | |
yAxis: 1, | |
baseSeries: "s1", | |
zIndex: -1, | |
color: "rgba(255, 0, 0, 0.5)", | |
binsNumber:20 | |
}, { | |
name: 'Histogram', | |
type: 'histogram', | |
xAxis: 1, | |
yAxis: 1, | |
baseSeries: "s2", | |
zIndex: -1, | |
color: "rgba(0, 0, 255, 0.5)", | |
binsNumber:20 | |
}, | |
{ | |
name: 'Data', | |
type: 'scatter', | |
data: data1, | |
id: 's1', | |
marker: { | |
radius: 1.5 | |
}, visible:false | |
},{ | |
name: 'Data', | |
type: 'scatter', | |
data: data2, | |
id: 's2', | |
marker: { | |
radius: 1.5 | |
}, visible:false | |
}] | |
}); | |
(function(H) { | |
H.Pointer.prototype.drag = function(e) { | |
var container = this.chart.container.parentElement; | |
container.style.left = container.offsetLeft + e.movementX + 'px'; | |
container.style.top = container.offsetTop + e.movementY + 'px'; | |
} | |
})(Highcharts); | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment