Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Geometric Pattern Generator</title>
<link rel="stylesheet" href="styles.css" />
<!-- <style>
.tile {
width: 100px;
height: 100px;
}
</style> -->
</head>
<body>
<button id="download-svg" type="button">Download SVG</button>
Expand Down
74 changes: 66 additions & 8 deletions src/Tile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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,
Expand All @@ -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");
}
Expand Down Expand Up @@ -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,
Expand Down
30 changes: 23 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -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}`);
Expand All @@ -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");

Expand Down
64 changes: 64 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
}
};