diff --git a/docs/straws/index.html b/docs/straws/index.html
index a4c8569..55d51ec 100644
--- a/docs/straws/index.html
+++ b/docs/straws/index.html
@@ -19,6 +19,10 @@
back
Straws UI Prototype
+ Add component
+
+
+
HPSF from the graph
diff --git a/docs/straws/sketch.js b/docs/straws/sketch.js
index 58ef3a9..40ce7ab 100644
--- a/docs/straws/sketch.js
+++ b/docs/straws/sketch.js
@@ -3,6 +3,7 @@ let bg = 0;
let zoomLevel = 1;
let components = [];
let connections = [];
+let componentKinds = new Map();
let partialConnection = null;
let dragComponent = null;
let dragPort = null;
@@ -17,7 +18,7 @@ function verticalCurve(p1, p2) {
c1.y = (p1.y + p2.y) / 2;
c2.y = (p1.y + p2.y) / 2;
noFill();
- stroke(0);
+ stroke(240);
strokeWeight(2);
bezier(p1.x, p1.y, c1.x, c1.y, c2.x, c2.y, p2.x, p2.y);
// line(p1.x, p1.y, p2.x, p2.y);
@@ -44,9 +45,10 @@ class Shape {
}
class Port {
- constructor(parent, type, label, col) {
- this.parent = parent;
+ constructor(parent, type, direction, label, col) {
this.type = type;
+ this.parent = parent;
+ this.direction = direction;
this.label = label;
this.col = col;
this.x = 0;
@@ -60,7 +62,7 @@ class Port {
draw() {
let align = BOTTOM;
let offset = this.portRadius;
- if (this.type === 'input') {
+ if (this.direction === 'input') {
align = TOP;
offset = -this.portRadius;
}
@@ -78,7 +80,7 @@ class Port {
// ask if x, y is inside the port
hit(x, y) {
let offset = this.portRadius;
- if (this.type === 'input') {
+ if (this.direction === 'input') {
offset = -this.portRadius;
}
return dist(x, y, this.x, this.y + offset) <= this.portRadius;
@@ -86,9 +88,10 @@ class Port {
yaml(indent) {
let spaces = ' '.repeat(indent) + '- ';
- let yaml = spaces + "Name: " + this.label + '\n';
+ let yaml = spaces + 'Name: ' + this.label + '\n';
spaces = ' '.repeat(indent+2);
- yaml += spaces + "Direction: " + this.type + '\n';
+ yaml += spaces + 'Direction: ' + this.direction + '\n';
+ yaml += spaces + 'Type: ' + this.type + '\n';
return yaml;
}
}
@@ -105,13 +108,13 @@ class ComponentShape extends Shape {
this.textFill = textCol;
}
- addInput(parent, label, col) {
- this.inputs.set(label, new Port(parent, "input", label, col));
+ addInput(parent, type, label, col) {
+ this.inputs.set(label, new Port(parent, type, 'input', label, col));
this._layoutPorts();
}
- addOutput(parent, label, col) {
- this.outputs.set(label, new Port(parent, "output", label, col));
+ addOutput(parent, type, label, col) {
+ this.outputs.set(label, new Port(parent, type, 'output', label, col));
this._layoutPorts();
}
@@ -183,31 +186,81 @@ class ComponentShape extends Shape {
}
}
+class ComponentKind {
+ constructor(kind, ports, w, h, col) {
+ this.kind = kind;
+ this.ports = ports;
+ this.w = w;
+ this.h = h;
+ this.col = col;
+ this.labelNumber = 0;
+ this.properties = new Map();
+ componentKinds.set(kind, this);
+ }
+
+ addProperty(name, value) {
+ this.properties.set(name, value);
+ }
+
+ addPortsTo(component) {
+ for (let port of this.ports) {
+ if (port.direction === 'input') {
+ component.addInput(port.type, port.label, port.col);
+ } else {
+ component.addOutput(port.type, port.label, port.col);
+ }
+ }
+ }
+}
+
+function addComponentKind(kind, ports, w=150, h=50, col=200) {
+ let k = new ComponentKind(kind, ports, w, h, col);
+ button = createButton(kind);
+ buttondiv = select('#button-holder');
+ button.parent(buttondiv);
+ let createComponent = () => {
+ let c = new Component(kind, 250+10*(k.labelNumber+1), 200+10*(k.labelNumber+1));
+ components.push(c);
+ changed = true;
+ }
+ button.mousePressed(createComponent);
+ return k;
+}
+
class Component {
- constructor(label, x, y) {
+ constructor(kind, x, y, label='') {
+ let ckind = componentKinds.get(kind);
+ this.kind = kind;
this.label = label;
+ if (label === '') {
+ ckind.labelNumber += 1;
+ this.label = kind + '_' + ckind.labelNumber;
+ }
this.x = x;
this.y = y;
- this.shape = new ComponentShape(x, y, 150, 50, label);
+ this.shape = new ComponentShape(x, y, ckind.w, ckind.h, this.label);
+ this.properties = ckind.properties;
this.dragging = false;
this.hovering = false;
this.dragX = 0;
this.dragY = 0;
+ ckind.addPortsTo(this);
}
- addInput(label, col) {
- this.shape.addInput(this, label, col);
+ addInput(type, label, col) {
+ this.shape.addInput(this, type, label, col);
}
- addOutput(label, col) {
- this.shape.addOutput(this, label, col);
+ addOutput(type, label, col) {
+ this.shape.addOutput(this, type, label, col);
}
draw() {
+ let ckind = componentKinds.get(this.kind);
if (this.hovering) {
- this.shape.bodyfill = color(255, 255, 0);
+ this.shape.bodyfill = lerpColor(ckind.col, color(255), 0.5);
} else {
- this.shape.bodyfill = color(255);
+ this.shape.bodyfill = ckind.col;
}
this.shape.draw();
}
@@ -275,17 +328,46 @@ class Component {
}
}
+ select() {
+ let container = select('#selected-component');
+ let html = '' + this.label + '
';
+ html += '';
+ // add the table to the container
+ container.html(html);
+ // set up the event handlers
+ for (let [name, value] of this.properties) {
+ let input = select('#prop-' + name);
+ input.changed(() => {
+ this.properties.set(name, input.value());
+ changed = true;
+ });
+ }
+ }
+
yaml(indent) {
let spaces = ' '.repeat(indent) + '- ';
- let yaml = spaces + "Name: " + this.label + '\n';
+ let yaml = spaces + 'Name: ' + this.label + '\n';
spaces = ' '.repeat(indent+2);
- yaml += spaces + "Ports:\n";
+ yaml += spaces + 'Kind: ' + this.kind + '\n';
+ yaml += spaces + 'Ports:\n';
for (let input of this.shape.inputs.values()) {
yaml += input.yaml(indent+2);
}
for (let output of this.shape.outputs.values()) {
yaml += output.yaml(indent+2);
}
+ yaml += spaces + 'Properties:\n';
+ let sp = '- ';
+ for (let [name, value] of this.properties) {
+ yaml += spaces + sp + name + ': ' + value + '\n';
+ sp = ' ';
+ }
return yaml;
}
}
@@ -294,7 +376,8 @@ class Connection {
constructor(frComp, frPort, toComp, toPort) {
// always go from the output to the input
let p = frComp.port(frPort);
- if (frComp.port(frPort).type == 'input') {
+ print(frComp);
+ if (frComp.port(frPort).direction == 'input') {
// print('reversing connection');
this.frComp = toComp;
this.frPort = toPort;
@@ -332,22 +415,42 @@ class Connection {
function restart() {
- bg = 128;
- zoomLevel = 2;
- inputCol = color(128, 128, 255);
- outputCol = color(255, 128, 128);
-
- c1 = new Component('TraceGRPC', 100, 100);
- c1.addOutput('TraceOut', outputCol);
- c2 = new Component('DeterministicSampler', 100, 300);
- c2.addInput('Input', inputCol);
- c2.addOutput('Kept', outputCol);
- c2.addOutput('Dropped', outputCol);
- c3 = new Component('HoneycombExporter', 100, 500);
- c3.addInput('Traces', inputCol);
+ bg = 80;
+ zoomLevel = 2;
+ inputCol = color(128, 255, 128);
+ outputCol = color(255, 192, 192);
+ buttondiv = select('#button-holder');
+ buttondiv.html('');
+
+ let k = addComponentKind('TraceGRPC', [
+ {direction: 'output', type: 'OtelTraces', label: 'TraceOut', col: outputCol}
+ ], 100, 70, color(128, 255, 128));
+ k.addProperty('Port', 4317);
+ k = addComponentKind('DeterministicSampler', [
+ {direction: 'input', type: 'OtelTraces', label: 'Input', col: inputCol},
+ {direction: 'output', type: 'OtelTraces', label: 'Kept', col: outputCol},
+ {direction: 'output', type: 'OtelTraces', label: 'Dropped', col: outputCol}
+ ], 150, 100, color(192, 192, 255));
+ k.addProperty('SampleRate', 10);
+ k = addComponentKind('HoneycombExporter', [
+ {direction: 'input', type: 'OtelTraces', label: 'Traces', col: inputCol}
+ ], 180, 80, color(255, 255, 128));
+ k.addProperty('Dataset', 'mydataset');
+ k.addProperty('APIKey', '$HONEYCOMB_APIKEY');
+ k = addComponentKind('EMAThroughputSampler', [
+ {direction: 'input', type: 'OtelTraces', label: 'Input', col: inputCol},
+ {direction: 'output', type: 'OtelTraces', label: 'Kept', col: outputCol},
+ {direction: 'output', type: 'OtelTraces', label: 'Dropped', col: outputCol}
+ ], 150, 100, color(192, 192, 255));
+ k.addProperty('TPS', 100);
+ k.addProperty('KeyFields', 'key1, key2, key3');
+
+ c1 = new Component('TraceGRPC', 300, 100);
+ c2 = new Component('DeterministicSampler', 300, 300);
+ c3 = new Component('HoneycombExporter', 300, 500);
components = [c1, c2, c3];
let connection = new Connection(c1, 'TraceOut', c2, 'Input');
- connections.push(connection);
+ connections = [connection];
generateYaml();
}
@@ -397,6 +500,15 @@ function mousePressed() {
}
}
+function mouseClicked() {
+ // print('clicked');
+ for (let component of components) {
+ if (component.bodyHit(mouseX, mouseY)) {
+ component.select();
+ }
+ }
+}
+
function mouseReleased() {
if (dragComponent) {
dragComponent.endDrag();
@@ -404,10 +516,12 @@ function mouseReleased() {
}
partialConnection = null;
if (dragPort) {
+ // print('dragPort', dragPort);
dragPort.hovering = false;
for (let component of components) {
let port = component.portHit(mouseX, mouseY);
- if (port && port !== dragPort && port.parent !== dragPort.parent && port.type !== dragPort.type) {
+ // print('port', port);
+ if (port && port !== dragPort && port.parent !== dragPort.parent && port.direction !== dragPort.direction) {
// print('connecting', dragPort, 'to', port);
let connection = new Connection(dragPort.parent, dragPort.label, component, port.label);
connections.push(connection);
diff --git a/docs/straws/style.css b/docs/straws/style.css
index 275fb84..969e679 100644
--- a/docs/straws/style.css
+++ b/docs/straws/style.css
@@ -31,4 +31,30 @@ pre {
padding: 20px;
border-radius: 5px;
overflow-x: auto;
+}
+
+table {
+ margin-left: 2rem;
+ border-collapse: collapse;
+ width: 30rem;
+ margin-bottom: 20px;
+ background-color: beige;
+}
+
+.propname {
+ color: #000080;
+ width: 35%;
+}
+
+.propvalue {
+ text-align: left;
+ color: #1e90ff;
+}
+
+.textfield {
+ width: 90%;
+ padding: 5px;
+ margin: 5px 0;
+ border: 1px solid #ccc;
+ border-radius: 4px;
}
\ No newline at end of file
diff --git a/p5/straws/index.html b/p5/straws/index.html
index a4c8569..55d51ec 100644
--- a/p5/straws/index.html
+++ b/p5/straws/index.html
@@ -19,6 +19,10 @@
back
Straws UI Prototype
+ Add component
+
+
+
HPSF from the graph
diff --git a/p5/straws/sketch.js b/p5/straws/sketch.js
index 58b0ee3..40ce7ab 100644
--- a/p5/straws/sketch.js
+++ b/p5/straws/sketch.js
@@ -3,6 +3,7 @@ let bg = 0;
let zoomLevel = 1;
let components = [];
let connections = [];
+let componentKinds = new Map();
let partialConnection = null;
let dragComponent = null;
let dragPort = null;
@@ -17,7 +18,7 @@ function verticalCurve(p1, p2) {
c1.y = (p1.y + p2.y) / 2;
c2.y = (p1.y + p2.y) / 2;
noFill();
- stroke(0);
+ stroke(240);
strokeWeight(2);
bezier(p1.x, p1.y, c1.x, c1.y, c2.x, c2.y, p2.x, p2.y);
// line(p1.x, p1.y, p2.x, p2.y);
@@ -185,16 +186,65 @@ class ComponentShape extends Shape {
}
}
+class ComponentKind {
+ constructor(kind, ports, w, h, col) {
+ this.kind = kind;
+ this.ports = ports;
+ this.w = w;
+ this.h = h;
+ this.col = col;
+ this.labelNumber = 0;
+ this.properties = new Map();
+ componentKinds.set(kind, this);
+ }
+
+ addProperty(name, value) {
+ this.properties.set(name, value);
+ }
+
+ addPortsTo(component) {
+ for (let port of this.ports) {
+ if (port.direction === 'input') {
+ component.addInput(port.type, port.label, port.col);
+ } else {
+ component.addOutput(port.type, port.label, port.col);
+ }
+ }
+ }
+}
+
+function addComponentKind(kind, ports, w=150, h=50, col=200) {
+ let k = new ComponentKind(kind, ports, w, h, col);
+ button = createButton(kind);
+ buttondiv = select('#button-holder');
+ button.parent(buttondiv);
+ let createComponent = () => {
+ let c = new Component(kind, 250+10*(k.labelNumber+1), 200+10*(k.labelNumber+1));
+ components.push(c);
+ changed = true;
+ }
+ button.mousePressed(createComponent);
+ return k;
+}
+
class Component {
- constructor(label, x, y) {
+ constructor(kind, x, y, label='') {
+ let ckind = componentKinds.get(kind);
+ this.kind = kind;
this.label = label;
+ if (label === '') {
+ ckind.labelNumber += 1;
+ this.label = kind + '_' + ckind.labelNumber;
+ }
this.x = x;
this.y = y;
- this.shape = new ComponentShape(x, y, 150, 50, label);
+ this.shape = new ComponentShape(x, y, ckind.w, ckind.h, this.label);
+ this.properties = ckind.properties;
this.dragging = false;
this.hovering = false;
this.dragX = 0;
this.dragY = 0;
+ ckind.addPortsTo(this);
}
addInput(type, label, col) {
@@ -206,10 +256,11 @@ class Component {
}
draw() {
+ let ckind = componentKinds.get(this.kind);
if (this.hovering) {
- this.shape.bodyfill = color(255, 255, 0);
+ this.shape.bodyfill = lerpColor(ckind.col, color(255), 0.5);
} else {
- this.shape.bodyfill = color(255);
+ this.shape.bodyfill = ckind.col;
}
this.shape.draw();
}
@@ -277,10 +328,33 @@ class Component {
}
}
+ select() {
+ let container = select('#selected-component');
+ let html = '' + this.label + '
';
+ html += '';
+ // add the table to the container
+ container.html(html);
+ // set up the event handlers
+ for (let [name, value] of this.properties) {
+ let input = select('#prop-' + name);
+ input.changed(() => {
+ this.properties.set(name, input.value());
+ changed = true;
+ });
+ }
+ }
+
yaml(indent) {
let spaces = ' '.repeat(indent) + '- ';
let yaml = spaces + 'Name: ' + this.label + '\n';
spaces = ' '.repeat(indent+2);
+ yaml += spaces + 'Kind: ' + this.kind + '\n';
yaml += spaces + 'Ports:\n';
for (let input of this.shape.inputs.values()) {
yaml += input.yaml(indent+2);
@@ -288,6 +362,12 @@ class Component {
for (let output of this.shape.outputs.values()) {
yaml += output.yaml(indent+2);
}
+ yaml += spaces + 'Properties:\n';
+ let sp = '- ';
+ for (let [name, value] of this.properties) {
+ yaml += spaces + sp + name + ': ' + value + '\n';
+ sp = ' ';
+ }
return yaml;
}
}
@@ -335,19 +415,39 @@ class Connection {
function restart() {
- bg = 128;
+ bg = 80;
zoomLevel = 2;
- inputCol = color(128, 128, 255);
- outputCol = color(255, 128, 128);
-
- c1 = new Component('TraceGRPC', 100, 100);
- c1.addOutput('OtelTraces', 'TraceOut', outputCol);
- c2 = new Component('DeterministicSampler', 100, 300);
- c2.addInput('OtelTraces', 'Input', inputCol);
- c2.addOutput('OtelTraces', 'Kept', outputCol);
- c2.addOutput('OtelTraces', 'Dropped', outputCol);
- c3 = new Component('HoneycombExporter', 100, 500);
- c3.addInput('OtelTraces', 'Traces', inputCol);
+ inputCol = color(128, 255, 128);
+ outputCol = color(255, 192, 192);
+ buttondiv = select('#button-holder');
+ buttondiv.html('');
+
+ let k = addComponentKind('TraceGRPC', [
+ {direction: 'output', type: 'OtelTraces', label: 'TraceOut', col: outputCol}
+ ], 100, 70, color(128, 255, 128));
+ k.addProperty('Port', 4317);
+ k = addComponentKind('DeterministicSampler', [
+ {direction: 'input', type: 'OtelTraces', label: 'Input', col: inputCol},
+ {direction: 'output', type: 'OtelTraces', label: 'Kept', col: outputCol},
+ {direction: 'output', type: 'OtelTraces', label: 'Dropped', col: outputCol}
+ ], 150, 100, color(192, 192, 255));
+ k.addProperty('SampleRate', 10);
+ k = addComponentKind('HoneycombExporter', [
+ {direction: 'input', type: 'OtelTraces', label: 'Traces', col: inputCol}
+ ], 180, 80, color(255, 255, 128));
+ k.addProperty('Dataset', 'mydataset');
+ k.addProperty('APIKey', '$HONEYCOMB_APIKEY');
+ k = addComponentKind('EMAThroughputSampler', [
+ {direction: 'input', type: 'OtelTraces', label: 'Input', col: inputCol},
+ {direction: 'output', type: 'OtelTraces', label: 'Kept', col: outputCol},
+ {direction: 'output', type: 'OtelTraces', label: 'Dropped', col: outputCol}
+ ], 150, 100, color(192, 192, 255));
+ k.addProperty('TPS', 100);
+ k.addProperty('KeyFields', 'key1, key2, key3');
+
+ c1 = new Component('TraceGRPC', 300, 100);
+ c2 = new Component('DeterministicSampler', 300, 300);
+ c3 = new Component('HoneycombExporter', 300, 500);
components = [c1, c2, c3];
let connection = new Connection(c1, 'TraceOut', c2, 'Input');
connections = [connection];
@@ -400,6 +500,15 @@ function mousePressed() {
}
}
+function mouseClicked() {
+ // print('clicked');
+ for (let component of components) {
+ if (component.bodyHit(mouseX, mouseY)) {
+ component.select();
+ }
+ }
+}
+
function mouseReleased() {
if (dragComponent) {
dragComponent.endDrag();
@@ -407,10 +516,12 @@ function mouseReleased() {
}
partialConnection = null;
if (dragPort) {
+ // print('dragPort', dragPort);
dragPort.hovering = false;
for (let component of components) {
let port = component.portHit(mouseX, mouseY);
- if (port && port !== dragPort && port.parent !== dragPort.parent && port.type !== dragPort.type) {
+ // print('port', port);
+ if (port && port !== dragPort && port.parent !== dragPort.parent && port.direction !== dragPort.direction) {
// print('connecting', dragPort, 'to', port);
let connection = new Connection(dragPort.parent, dragPort.label, component, port.label);
connections.push(connection);
diff --git a/p5/straws/style.css b/p5/straws/style.css
index 275fb84..969e679 100644
--- a/p5/straws/style.css
+++ b/p5/straws/style.css
@@ -31,4 +31,30 @@ pre {
padding: 20px;
border-radius: 5px;
overflow-x: auto;
+}
+
+table {
+ margin-left: 2rem;
+ border-collapse: collapse;
+ width: 30rem;
+ margin-bottom: 20px;
+ background-color: beige;
+}
+
+.propname {
+ color: #000080;
+ width: 35%;
+}
+
+.propvalue {
+ text-align: left;
+ color: #1e90ff;
+}
+
+.textfield {
+ width: 90%;
+ padding: 5px;
+ margin: 5px 0;
+ border: 1px solid #ccc;
+ border-radius: 4px;
}
\ No newline at end of file