Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save Cristianoaf81GIT/9cf41b330d3d42fe570d884dcfb8a10a to your computer and use it in GitHub Desktop.

Select an option

Save Cristianoaf81GIT/9cf41b330d3d42fe570d884dcfb8a10a to your computer and use it in GitHub Desktop.
D3js-Draw paths using circles
<svg></svg><br />
<button>clear</button>
import * as d3 from "https://cdn.skypack.dev/d3@7.2.1";
import * as lodash from "https://cdn.skypack.dev/lodash@4.17.21";
const WIDTH = 400;
const HEIGHT = 400;
const RADIUS = 10;
const POINT_COLOR = `rgb(255,50,50,0.25)`;
const pointsCoords = [];
// get svg reference and set its css attributes
var svg = d3.select("svg").attr("width", WIDTH).attr("height", HEIGHT);
// append group element
var group = svg.append("g");
// draw a green background
var background = group
.append("rect")
.attr("width", WIDTH)
.attr("height", HEIGHT)
.attr("fill", "grey");
/*
get collisions between points
@param {d3.BaseType} node - point node
@param {number} layerX - x coord
@param {number} layerY - y coord
*/
const getPointCollisions = (node, layerX, layerY) => {
const selection = d3.select(node);
const x = Number(selection.attr("cx"));
const y = Number(selection.attr("cy"));
const diameter = RADIUS * 2 + 2;
const collisionX = lodash.inRange(layerX, x - diameter, x + diameter);
const collisionY = lodash.inRange(layerY, y - diameter, y + diameter);
return { x: collisionX, y: collisionY };
};
// draws a path between points
const drawPath = () => {
pointsCoords.length = 0;
d3.selectAll("polyline").remove();
for (let [index, value] of group.selectAll("circle").nodes().entries()) {
value = d3.select(value);
pointsCoords.push(`${value.attr("cx")} ${value.attr("cy")}`);
}
console.log(pointsCoords.toString());
group
.append("polyline")
.attr("points", pointsCoords.toString())
.attr("fill", POINT_COLOR);
};
/*
* add events on new point
* @param {d3.Selection<SVGCircleElement, unknown, HTMLElement, any>} point
*/
const addPointEvents = (point) => {
const pointDragHandler = d3.drag().on("drag", (e) => {
const currentPointId = point.attr("id");
const newPointId = `${e.x}-${e.y}`;
const xStartPoint = e.x > RADIUS && e.x < WIDTH - RADIUS;
const xEndPoint = e.x < WIDTH - RADIUS;
const yStartPoint = e.y > RADIUS && e.x < WIDTH - RADIUS;
const yEndPoint = e.y < WIDTH - RADIUS;
let collisions = [];
const boundary = {
x: xStartPoint && xEndPoint,
y: yStartPoint && yEndPoint
};
group
.selectAll("circle")
.nodes()
.forEach((node) => collisions.push(getPointCollisions(node, e.x, e.y)));
if (collisions && collisions.length > 0) {
const collisionsInBothAxis = collisions.filter(
(coll) => coll.x && coll.y && coll
);
if (collisionsInBothAxis && collisionsInBothAxis.length === 0) {
if (point && !point.empty()) {
if (boundary.x && boundary.y) {
point.attr("cx", e.x).attr("cy", e.y);
point.attr("id", newPointId);
drawPath();
}
}
}
}
});
pointDragHandler(point);
point.on("dblclick", () => {
point.remove();
drawPath();
});
drawPath();
};
/*
* draws a single point
* @param {string} id - css id attribute
* @param {number} x - x coord
* @param {number} y - y coord
*/
const drawPoint = (id, x, y) => {
const r = Math.round(Math.random() * 255);
const g = Math.round(Math.random() * 255);
const b = Math.round(Math.random() * 255);
const COLOR = `rgb(${r},${g},${b})`;
// pointsCoords.push(`${x} ${y}`)
return group
.append("circle")
.attr("cx", x)
.attr("cy", y)
.attr("id", id)
.attr("r", RADIUS)
.attr("fill", COLOR)
.attr("stroke", "black")
.attr("stroke-width", 2)
.raise();
};
// when click on group add new point
group.on("click", (e) => {
const id = `${e.layerX}-${e.layerY}`;
let point;
const collisions = [];
let circleNodes = group.selectAll("circle").nodes();
circleNodes.forEach((node) => {
collisions.push(getPointCollisions(node, e.layerX, e.layerY));
});
if (collisions && collisions.length > 0) {
const collisionsInBothAxis = collisions.filter(
(coll) => coll.x && coll.y && coll
);
if (collisionsInBothAxis && collisionsInBothAxis.length === 0) {
point = drawPoint(id, e.x, e.y);
circleNodes = group.selectAll("circle").nodes();
addPointEvents(point);
}
} else {
point = drawPoint(id, e.x, e.y);
circleNodes = group.selectAll("circle").nodes();
addPointEvents(point);
}
});
var button = document.getElementsByTagName("button")[0];
if (button)
button.addEventListener("click", function (evt) {
group.selectAll("circle").remove();
group.selectAll("polyline").remove();
pointsCoords.length = 0;
});
body {
margin: 2px;
padding: 0px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment