function vectorInput(options = {}) {
let {
label = options.label,
value = [0, 0],
max = 10,
step = 0.1,
arrowSize = 8,
arrowAngle = Math.PI / 3,
size = 100
} = options;
const div = html`<div style="margin:2px;display:flex" >`;
const ctx = DOM.context2d(size, size);
const canvas = ctx.canvas;
if (label) {
const labelDiv = htl.html`<div>${label}`;
Object.assign(labelDiv.style, {
font: "12px sans-serif",
display: "inline-block",
"min-width": "120px"
});
div.append(labelDiv);
}
div.append(canvas);
const draw = () => {
ctx.clearRect(0, 0, size, size);
ctx.fillStyle = "rgb(200,200,200)";
ctx.fillRect(0, 0, size, size);
ctx.strokeStyle = ctx.fillStyle = "black";
ctx.beginPath();
if (value[0] == 0 && value[1] == 0) {
ctx.arc(size / 2, size / 2, 2, 0, Math.PI * 2);
ctx.fill();
} else {
let ang = Math.atan2(value[1], value[0]);
ctx.moveTo(size / 2, size / 2);
let [x, y] = [
((value[0] / max) * size + size) / 2,
((value[1] / max) * size + size) / 2
];
ctx.lineTo(x, y);
const a = ang + arrowAngle / 2 + Math.PI,
b = ang - arrowAngle / 2 + Math.PI;
const r = arrowSize;
ctx.lineTo(x + r * Math.cos(a), y + r * Math.sin(a));
ctx.lineTo(x + r * Math.cos(b), y + r * Math.sin(b));
ctx.lineTo(x, y);
ctx.fill();
}
ctx.stroke();
};
draw();
const xy = xyInput([-max, max], { step });
xy.style.marginLeft = "2px";
div.append(xy);
Object.defineProperty(div, "value", {
get() {
return value;
},
set(v) {
value = v;
draw();
}
});
div.value = value;
Inputs.bind(xy, div);
canvas.onmousedown = canvas.onmousemove = (e) => {
if (e.buttons) {
value = [e.offsetX, e.offsetY].map(
(x) => Math.round((((x - size / 2) / size) * 2 * max) / step) * step
);
draw();
set(div, value);
}
};
return div;
}