Skip to content

Instantly share code, notes, and snippets.

@anotherjesse
Created September 1, 2024 12:01
Show Gist options
  • Save anotherjesse/77d20f61ddc4a6acc6921fd5545e1023 to your computer and use it in GitHub Desktop.
Save anotherjesse/77d20f61ddc4a6acc6921fd5545e1023 to your computer and use it in GitHub Desktop.
<script lang="ts">
let svgCanvas: SVGElement;
let numberOfColors = 3;
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 {
x: number;
y: number;
angle: number;
penDown: boolean;
path: string;
draw: (group: SVGElement, color: string) => void;
move: (distance: number) => void;
rotate: (angle: number) => void;
}
function newTurtle(initialX: number, initialY: number): Turtle {
return {
x: initialX,
y: initialY,
angle: 0,
penDown: true,
path: `M${initialX},${initialY} `,
draw: function(group: SVGElement, color: string) {
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttribute("d", this.path);
path.setAttribute("stroke", color);
path.setAttribute("stroke-width", "0.001");
path.setAttribute("fill", "none");
group.appendChild(path);
},
move: function(distance: number) {
const newX = this.x + distance * Math.cos(this.angle);
const newY = this.y + distance * Math.sin(this.angle);
if (this.penDown) {
this.path += `L${newX.toFixed(3)},${newY.toFixed(3)} `;
} else {
this.path += `M${newX.toFixed(3)},${newY.toFixed(3)} `;
}
this.x = newX;
this.y = newY;
},
rotate: function(angle: number) {
this.angle += angle * (Math.PI / 180);
}
};
}
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)}`);
svgCanvas.appendChild(group);
return group;
});
for (let x = 0.0; x < 0.8; x += 0.1) {
for (let y = 0.1; y < 0.9; y += 0.1) {
let turtle = newTurtle(x, y);
for (let i = 0; i < 36; i++) {
turtle.move(0.2);
turtle.rotate(170);
}
const color = colors[Math.floor(Math.random() * colors.length)];
const group = svgCanvas.querySelector(`#group-${color.substring(1)}`) as SVGElement;
if (group) {
turtle.draw(group, color);
}
}
}
}
function downloadSVG() {
const serializer = new XMLSerializer();
const svgCopy = svgCanvas.cloneNode(true) as SVGElement;
svgCopy.setAttribute("width", "6in");
svgCopy.setAttribute("height", "6in");
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="500"
height="500"
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