diff --git a/index.html b/index.html index 4ad59ad..99b498c 100644 --- a/index.html +++ b/index.html @@ -5,12 +5,6 @@ Geometric Pattern Generator - diff --git a/src/Tile.ts b/src/Tile.ts index b0c4b90..46d61a2 100644 --- a/src/Tile.ts +++ b/src/Tile.ts @@ -53,7 +53,7 @@ class Tile { // Draw the lines and add them to the linesGroup for (const radius of radii) { - const lines = this.createLine(radius, direction); + const lines = this.createPath(radius, direction); linesGroup.appendChild(lines); } @@ -63,17 +63,22 @@ class Tile { const largestRadius = radii[radii.length - 1]; // Convert the linesGroup.lines into an array - Array.from(linesGroup.querySelectorAll("line")).forEach((line) => { + Array.from(linesGroup.querySelectorAll("path")).forEach((line) => { // Extract the line start and end for the current line const lineStart = { - x: parseFloat(line.getAttribute("x1") as string), - y: parseFloat(line.getAttribute("y1") as string), + x: parseFloat(line.getAttribute("d")?.split(" ")[1] as string), + y: parseFloat(line.getAttribute("d")?.split(" ")[2] as string), + // x: parseFloat(line.getAttribute("x1") as string), + // y: parseFloat(line.getAttribute("y1") as string), }; const lineEnd = { - x: parseFloat(line.getAttribute("x2") as string), - y: parseFloat(line.getAttribute("y2") as string), + x: parseFloat(line.getAttribute("d")?.split(" ")[4] as string), + y: parseFloat(line.getAttribute("d")?.split(" ")[5] as string), + // x: parseFloat(line.getAttribute("x2") as string), + // y: parseFloat(line.getAttribute("y2") as string), }; + console.log("Line start:", lineStart, "Line end:", lineEnd); // Determine if there is an intersection for the current line const intersection = this.lineArcIntersection( lineStart, @@ -84,8 +89,29 @@ class Tile { if (intersection) { console.log("Intersection found", intersection); - line.setAttribute("x1", String(intersection.x)); - line.setAttribute("y1", String(intersection.y)); + console.log( + "Modify lines 'd' attribute '", + line.getAttribute("d"), + "'" + ); + console.log( + "Updating arrtibute with this: `", + `M ${intersection.x} ${intersection.y} ${line + .getAttribute("d") + ?.split(" ") + .slice(4, 6) + .join(" ")}`, + "'" + ); + line.setAttribute( + "d", + `M ${intersection.x} ${intersection.y} ${line + .getAttribute("d") + ?.split(" ") + .slice(4, 6) + .join(" ")}` + ); + // line.setAttribute("y1", String(intersection.y)); } else { console.log("No intersection found"); } @@ -138,6 +164,38 @@ class Tile { return lines; } + // Maybe the line is causing problem? (I don't seem to be able to join a line to a path so make the lines paths?) + createPath(radius: number, direction: "horizontal" | "vertical"): SVGElement { + console.log( + "Creating a single path with direction", + direction, + "and length", + radius + ); + const paths = document.createElementNS("http://www.w3.org/2000/svg", "g"); + const pathOffset = 0; + + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + + // Horizontal Lines start at x1=0 y1=radius and finish at x2=100, y2=radius + // Vertical Lines start at x1=radius y1=0 and finish at x2=radius, y2=100 + let d; + if (direction === "horizontal") { + d = `M 0 ${radius} L 100 ${radius}`; + } else { + d = `M ${radius} 0 L ${radius} 100`; + } + + path.setAttribute("d", d); + path.setAttribute("stroke", "black"); + path.setAttribute("stroke-width", "1"); + path.setAttribute("fill", "none"); + + paths.appendChild(path); + + return paths; + } + // return an SVG arc for the given start coordinates and radius between the start and end angle createArc( cx: number, diff --git a/src/index.ts b/src/index.ts index f8c4e95..7ea3ccd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,10 @@ import Tile from "./Tile"; +import { joinClosePaths } from "./utils"; // Create a grid of patterned tiles const grid = document.getElementById("grid"); -const numRows = 10; -const numCols = 12; +const numRows = 2; +const numCols = 2; const outerSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg"); outerSVG.setAttribute("viewBox", `0 0 ${numCols * 100} ${numRows * 100}`); @@ -27,15 +28,30 @@ for (let row = 0; row < numRows; row++) { // Define the SVG element using the tile content const tileGroup = tile.element; // Apply a grid offset and rotation to the tile - tileGroup.setAttribute( - "transform", - `translate(${col * 100} ${row * 100}) rotate(${rotation} 50 50)` - ); + // tileGroup.setAttribute( + // "transform", + // `translate(${col * 100} ${row * 100}) rotate(${rotation} 50 50)` + // ); // Append the SVG element 'tileGroup' to the SVG - outerSVG?.appendChild(tileGroup); + // outerSVG?.appendChild(tileGroup); + // Iterate through the children of tileGroup and append them directly to outerSVG + while (tileGroup.firstChild) { + const child = tileGroup.firstChild; + // Check if the child is an SVGElement + if (child instanceof SVGElement) { + // Apply a grid offset and rotation to the child element + child.setAttribute( + "transform", + `translate(${col * 100} ${row * 100}) rotate(${rotation} 50 50)` + ); + } + outerSVG?.appendChild(child); + } } } +joinClosePaths(outerSVG, 5); // Add this line after creating the grid + // SVG Export (Save (Download) an SVG when the download button is clicked) const downloadButton = document.getElementById("download-svg"); diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..3dd0555 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,64 @@ +export const joinClosePaths = (outerSVG: SVGSVGElement, threshold: number) => { + const paths = Array.from(outerSVG.querySelectorAll("path")); + + for (let i = 0; i < paths.length; i++) { + // console.log("Picking a path"); + for (let j = i + 1; j < paths.length; j++) { + // console.log("Comparing to all other paths"); + const path1 = paths[i]; + const path2 = paths[j]; + + const path1D = path1.getAttribute("d") as string; + const path2D = path2.getAttribute("d") as string; + + const path1Start = path1D.split(" ").slice(1, 3); + const path1End = path1D.split(" ").slice(-2); + + const path2Start = path2D.split(" ").slice(1, 3); + const path2End = path2D.split(" ").slice(-2); + + const start1 = { + x: parseFloat(path1Start[0]), + y: parseFloat(path1Start[1]), + }; + const end1 = { x: parseFloat(path1End[0]), y: parseFloat(path1End[1]) }; + const start2 = { + x: parseFloat(path2Start[0]), + y: parseFloat(path2Start[1]), + }; + const end2 = { x: parseFloat(path2End[0]), y: parseFloat(path2End[1]) }; + + const combinations = [ + { a: start1, b: start2 }, + { a: start1, b: end2 }, + { a: end1, b: start2 }, + { a: end1, b: end2 }, + ]; + + for (const combination of combinations) { + // console.log("Comparing combinations..."); + const dist = Math.hypot( + combination.a.x - combination.b.x, + combination.a.y - combination.b.y + ); + if (threshold >= dist) { + console.log("too far to join"); + } + if (dist < threshold) { + console.log("Found close path ends..."); + path1.setAttribute( + "d", + `${path1D} L ${combination.b.x} ${combination.b.y} ${path2D + .split(" ") + .slice(3) + .join(" ")}` + ); + outerSVG.removeChild(path2); + paths.splice(j, 1); + j--; + break; + } + } + } + } +};