Skip to content

Instantly share code, notes, and snippets.

@anotherjesse
Created September 5, 2024 12:33
Show Gist options
  • Save anotherjesse/9fc8ed64ea89145bcd7c5948c362c216 to your computer and use it in GitHub Desktop.
Save anotherjesse/9fc8ed64ea89145bcd7c5948c362c216 to your computer and use it in GitHub Desktop.
<script lang="ts">
import { perlinNoise2dFactory } from "squeaker";
let svgCanvas: SVGElement;
let numberOfColors = 1;
const colorOptions = [3, 4, 5];
const palletes: Record<string, string[]> = {
reds: ["#ff0000", "#ff4040", "#ff8080", "#ffc0c0", "#ffe0e0"],
greens: ["#00ff00", "#40ff40", "#80ff80", "#c0ffc0", "#e0ffe0"],
blues: ["#0000ff", "#4040ff", "#8080ff", "#c0c0ff", "#e0e0ff"],
yellows: ["#ffff00", "#ffff40", "#ffff80", "#ffffc0", "#ffffe0"],
purples: ["#ff00ff", "#ff40ff", "#ff80ff", "#ffc0ff", "#ffe0ff"],
oranges: ["#ff8000", "#ffa040", "#ffc080", "#ffe0c0", "#fff0e0"],
pinks: ["#ff00ff", "#ff40ff", "#ff80ff", "#ffc0ff", "#ffe0ff"],
grays: ["#808080", "#a0a0a0", "#c0c0c0", "#e0e0e0", "#f0f0f0"],
rainbow: ["#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff"],
};
let colorPalette = "rainbow";
interface Turtle {
path: string;
draw: (group: SVGElement) => void;
drawTo: (x: number, y: number) => void;
moveTo: (x: number, y: number) => void;
relativeDrawTo: (x: number, y: number) => void;
}
function newTurtle(): Turtle {
return {
path: ``,
draw: function (group: SVGElement) {
if (this.path.includes("M")) {
const path = document.createElementNS(
"http://www.w3.org/2000/svg",
"path",
);
path.setAttribute("d", this.path);
group.appendChild(path);
}
},
moveTo: function (x: number, y: number) {
this.path += `M${x.toFixed(4)},${y.toFixed(4)} `;
},
drawTo: function (x: number, y: number) {
if (this.path.length === 0) {
this.path += `M${x.toFixed(4)},${y.toFixed(4)} `;
} else {
this.path += `L${x.toFixed(4)},${y.toFixed(4)} `;
}
},
relativeDrawTo: function (x: number, y: number) {
this.path += `l${x.toFixed(4)},${y.toFixed(4)} `;
},
};
}
function drawTurtlePath(
selectedNumberOfColors: number,
selectedColorPalette: string,
) {
svgCanvas.innerHTML = "";
const colors = palletes[selectedColorPalette].slice(
0,
selectedNumberOfColors,
);
let groups = colors.map((color) => {
const group = document.createElementNS(
"http://www.w3.org/2000/svg",
"g",
);
group.setAttribute("id", `group-${color.substring(1)}`);
group.setAttribute("stroke", color);
group.setAttribute("stroke-width", "0.001");
group.setAttribute("fill", "none");
svgCanvas.appendChild(group);
return group;
});
let seed = 4311214 * 1.125;
const noise = perlinNoise2dFactory({ seed });
let scaleX = 20;
let scaleY = 20;
let stepX = 0.01;
let stepY = 0.01;
let lineLength = 0.00025;
let seen = {};
let pointCount = 0;
for (let x = 0; x <= 1; x += stepX) {
for (let y = 0; y <= 1; y += stepY) {
let turtle = newTurtle();
turtle.moveTo(x, y);
let sx = x;
let sy = y;
for (let i = 0; i < 100; i++) {
let noiseValue = noise(sx * scaleX, sy * scaleY);
let angle = noiseValue * Math.PI * 2;
sx += Math.cos(angle) * lineLength;
sy += Math.sin(angle) * lineLength;
let r = Math.sqrt(
Math.pow(sx - 0.5, 2) + Math.pow(sy - 0.5, 2),
);
if (r > 0.48) {
break;
}
let key = `${sx.toFixed(4)},${sy.toFixed(4)}`;
if (seen[key]) {
console.log("break");
break;
}
seen[key] = true;
turtle.drawTo(sx, sy);
pointCount++;
}
turtle.draw(groups[0]);
}
}
console.log("pointCount", pointCount);
}
function downloadSVG() {
const serializer = new XMLSerializer();
const svgCopy = svgCanvas.cloneNode(true) as SVGElement;
svgCopy.setAttribute("width", "4in");
svgCopy.setAttribute("height", "4in");
svgCopy.setAttribute("viewBox", "0 0 1 1");
const source = serializer.serializeToString(svgCopy);
const blob = new Blob([source], { type: "image/svg+xml" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "lines.svg";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
$: if (svgCanvas) drawTurtlePath(numberOfColors, colorPalette);
</script>
<div class="controls">
<button
id="redrawButton"
on:click={() => drawTurtlePath(numberOfColors, colorPalette)}
>Redraw SVG</button
>
<select id="colorCount" bind:value={numberOfColors}>
{#each colorOptions as option}
<option value={option}>{option}</option>
{/each}
</select>
<select id="colorPalette" bind:value={colorPalette}>
{#each Object.keys(palletes) as palette}
<option value={palette}>{palette}</option>
{/each}
</select>
<button id="downloadButton" on:click={downloadSVG}>Download SVG</button>
</div>
<div id="svgContainer">
<svg
bind:this={svgCanvas}
id="svgCanvas"
width="1000"
height="1000"
viewBox="0 0 1 1"
></svg>
</div>
<style>
#svgContainer {
border: 1px solid #ccc;
margin: 20px 0;
}
.controls {
margin-bottom: 20px;
display: flex;
gap: 10px;
align-items: center;
}
.controls select {
padding: 5px;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment