Created
September 1, 2024 12:01
-
-
Save anotherjesse/77d20f61ddc4a6acc6921fd5545e1023 to your computer and use it in GitHub Desktop.
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
<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