A Pen by Cristiano Alexandre de Faria on CodePen.
Created
January 5, 2022 19:59
-
-
Save Cristianoaf81GIT/9cf41b330d3d42fe570d884dcfb8a10a to your computer and use it in GitHub Desktop.
D3js-Draw paths using circles
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <svg></svg><br /> | |
| <button>clear</button> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | |
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| body { | |
| margin: 2px; | |
| padding: 0px; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment