Files
cellxgene/client/src/components/graph/setupLasso.js
T
Colin Megill 4c3d783fc8 Continuous data in right sidebar (#985)
* Create right sidebar files, move histos over

* restructure continuous legend + all padding

* right sidebar file

* proper graph positioning and resize

* new lasso values since right sidebar

* fix autosave position
2019-11-05 20:29:21 -05:00

146 lines
3.5 KiB
JavaScript

// https://bl.ocks.org/pbeshai/8008075f9ce771ee8be39e8c38907570
import * as d3 from "d3";
const Lasso = () => {
const dispatch = d3.dispatch("start", "end", "cancel");
const polygonToPath = polygon =>
`M${polygon.map(d => d.join(",")).join("L")}`;
const distance = (pt1, pt2) =>
Math.sqrt((pt2[0] - pt1[0]) ** 2 + (pt2[1] - pt1[1]) ** 2);
// distance last point has to be to first point before it auto closes when mouse is released
const closeDistance = 75;
const lasso = svg => {
let lassoPolygon;
let lassoPath;
let closePath;
const handleDragStart = () => {
lassoPolygon = [d3.mouse(svg.node())]; // current x y of mouse within element
if (lassoPath) {
lassoPath.remove();
}
lassoPath = g
.append("path")
.attr("data-testid", "lasso-element")
.attr("fill", "#0bb")
.attr("fill-opacity", 0.1)
.attr("stroke", "#0bb")
.attr("stroke-dasharray", "3, 3");
closePath = g
.append("line")
.attr("x2", lassoPolygon[0][0])
.attr("y2", lassoPolygon[0][1])
.attr("stroke", "#0bb")
.attr("stroke-dasharray", "3, 3")
.attr("opacity", 0);
dispatch.call("start", lasso, lassoPolygon);
};
const handleDrag = () => {
const point = d3.mouse(svg.node());
lassoPolygon.push(point);
lassoPath.attr("d", polygonToPath(lassoPolygon));
// indicate if we are within closing distance
if (
distance(lassoPolygon[0], lassoPolygon[lassoPolygon.length - 1]) <
closeDistance
) {
closePath
.attr("x1", point[0])
.attr("y1", point[1])
.attr("opacity", 1);
} else {
closePath.attr("opacity", 0);
}
};
const handleDragEnd = () => {
// remove the close path
closePath.remove();
closePath = null;
// succesfully closed
if (
distance(lassoPolygon[0], lassoPolygon[lassoPolygon.length - 1]) <
closeDistance
) {
lassoPath.attr("d", `${polygonToPath(lassoPolygon)}Z`);
dispatch.call("end", lasso, lassoPolygon);
// otherwise cancel
} else {
lassoPath.remove();
lassoPath = null;
lassoPolygon = null;
dispatch.call("cancel");
}
};
// append a <g> with a rect
const g = svg.append("g").attr("class", "lasso-group");
const bbox = svg.node().getBoundingClientRect();
const area = g
.append("rect")
.attr("width", bbox.width)
.attr("height", bbox.height)
.attr("opacity", 0);
const drag = d3
.drag()
.on("start", handleDragStart)
.on("drag", handleDrag)
.on("end", handleDragEnd);
area.call(drag);
lasso.reset = () => {
if (lassoPath) {
lassoPath.remove();
lassoPath = null;
}
lassoPolygon = null;
if (closePath) {
closePath.remove();
closePath = null;
}
};
lasso.move = polygon => {
if (polygon !== lassoPolygon || polygon.length !== lassoPolygon.length) {
lasso.reset();
lassoPolygon = polygon;
lassoPath = g
.append("path")
.attr("data-testid", "lasso-element")
.attr("fill", "#0bb")
.attr("fill-opacity", 0.1)
.attr("stroke", "#0bb")
.attr("stroke-dasharray", "3, 3");
lassoPath.attr("d", `${polygonToPath(lassoPolygon)}Z`);
}
};
};
lasso.on = (type, callback) => {
dispatch.on(type, callback);
return lasso;
};
return lasso;
};
export default Lasso;