Published
Edited
Jul 8, 2019
1 fork
Importers
5 stars
Also listed in…
Forks
Insert cell
Insert cell
Insert cell
Insert cell
function polygonInput(config = {}) {
let { value = [], title, description, width = 400, height = Math.round((210 / 400) * width) } = config;
if (Array.isArray(config)) value = config;
let [x, y] = [null, null];
if (value.length > 0) {
x = value[value.length - 1][0];
y = value[value.length - 1][1];
}
const formEl = html`<form style="width: ${width}px;"></form>`;
const context = DOM.context2d(width, height);
const canvas = context.canvas;
canvas.style.margin = "10px 0 0";
formEl.append(canvas);

function draw() {
context.fillStyle = "#f5f5f5";
context.fillRect(0, 0, width, height);
if (x != null && y != null) {
const n = value.length;

context.fillStyle = "#aaa";
context.strokeStyle = 'black';
// Draw the polygon
if (n > 1){
context.beginPath();
context.moveTo(...value[0]);
for (let i = 1; i < n; i++){
context.lineTo(...value[i]);
}
if (value.length > 2){
context.lineTo(...value[0]);
}
context.fill('evenodd');
context.stroke();
// highlight last segment
context.strokeStyle = 'red';
context.beginPath();
context.moveTo(...value[n-1]);
context.lineTo(...value[0]);
context.stroke();
}
context.strokeStyle = 'black';
context.fillStyle = "#4a4a4a";
// Draw the vertices
for (let i = 0; i < n; i++){
context.beginPath();
context.arc(...value[i], 4, 0, 2 * Math.PI);
context.stroke();
context.fill();
}
}
}

function dist(p1, p2) {
const dx = p1[0]-p2[0];
const dy = p1[1]-p2[1];
return dx*dx + dy*dy;
}
function add(p1, p2) {
const x = p1[0]+p2[0];
const y = p1[1]+p2[1];
return [x, y];
}
function sub(p1, p2) {
const dx = p1[0]-p2[0];
const dy = p1[1]-p2[1];
return [dx, dy];
}

let selected;
let dragged;
let mouse;
canvas.onmousedown = function (event) {
dragged = false;
mouse = [event.offsetX,event.offsetY];
selected = -1;
let i = 0;
for (let p of value) {
if (dist(p,mouse) < 100) {
selected = i;
break;
}
i++;
}
if (selected < 0) {
value.push(mouse);
x = mouse[0];
y = mouse[1];
}
draw();
canvas.dispatchEvent(new CustomEvent("input", { bubbles: true }));
}
canvas.onmousemove = function (event) {
if (selected<0) return;
if (!mouse) return;
dragged = true;
let newMouse = [event.offsetX,event.offsetY];
value[selected] = add(value[selected], sub(newMouse, mouse));
x = value[selected][0];
y = value[selected][1];
draw();
canvas.dispatchEvent(new CustomEvent("input", { bubbles: true }));
mouse = newMouse;
}
canvas.onmouseup = function (event) {
if (!dragged && selected >= 0) {
value.splice(selected,1);
}
selected = -1;
draw();
canvas.dispatchEvent(new CustomEvent("input", { bubbles: true }));
}

draw();

const form = input({
type: "cartesianCoordinates",
title,
description,
display: v =>
html`<div style="position: absolute; width: ${width}px; white-space: nowrap; color: #444; text-align: center; font: 13px sans-serif; margin-top: -18px;">
<span style="color: #777;">Last <em>x</em>:</span> ${x != null ? x : ""}
&nbsp; &nbsp;
<span style="color: #777;">Last <em>y</em>:</span> ${y != null ? y : ""}
</div>`,
getValue: () => value,
form: formEl
});
return form;
}
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more