From 9a4ac98917c0ab5d57637f5acb31ebcf9d59c5ae Mon Sep 17 00:00:00 2001 From: tungnguyentu Date: Fri, 15 May 2026 02:28:42 +0700 Subject: [PATCH 1/2] docs: add tutorial for drawing letters with LEDs using arc-length sampling Adds a step-by-step tutorial that teaches readers how to place LEDs along the outline of any capital letter using SVG path data from @tscircuit/alphabet and a custom arc-length parameterization function (samplePath). Co-Authored-By: Claude Sonnet 4.6 --- docs/tutorials/draw-any-letter-with-leds.mdx | 396 +++++++++++++++++++ 1 file changed, 396 insertions(+) create mode 100644 docs/tutorials/draw-any-letter-with-leds.mdx diff --git a/docs/tutorials/draw-any-letter-with-leds.mdx b/docs/tutorials/draw-any-letter-with-leds.mdx new file mode 100644 index 0000000..ea5cadc --- /dev/null +++ b/docs/tutorials/draw-any-letter-with-leds.mdx @@ -0,0 +1,396 @@ +--- +title: Draw Any Letter with LEDs +description: >- + Learn how to use tscircuit and the @tscircuit/alphabet package to + automatically place 0402 LEDs along the outline of any capital letter, + producing a reusable LedLetter component. +--- + +## Overview + +In this tutorial you will build a reusable `` component that draws +any capital letter (A–Z) by placing 0402 LEDs along the letter's outline path. +The layout is fully automatic — you supply a letter and a size, and math does +the rest. + +import TscircuitIframe from "@site/src/components/TscircuitIframe" + +## Step 1: Sample points along an SVG path + +The `@tscircuit/alphabet` package exports `svgAlphabet`, a map from each +character to a normalized SVG path string (coordinates in `[0, 1]`). We need +to convert that path into a list of evenly-spaced `{x, y}` points so we know +where to place each LED. + + ( + + {pts.map((p, i) => ( + + ))} + +) +`} /> + +## Step 2: Add current-limiting resistors + +Each LED string needs a resistor (typically 68–100 Ω for a red 0402 LED at +5 V). We add one resistor per LED wired in series. + + { + const pts = samplePath(LETTER_A, 10) + const W = 20 // letter width in mm + const H = 25 // letter height in mm + + return ( + + {pts.map((p, i) => { + const px = (p.x - 0.5) * W + const py = (0.5 - p.y) * H + return ( + <> + + + + ) + })} + + ) +} +`} /> + +## Step 3: Build the reusable LedLetter component + +Now we wrap everything in a component that accepts any capital letter and a +scale factor. All 26 letters (A–Z) are supported using the path data from +`@tscircuit/alphabet`. + + { + const path = svgAlphabet[letter.toUpperCase()] + if (!path) return null + const pts = samplePath(path, nLeds) + + return ( + <> + {pts.map((p, i) => { + const px = offsetX + (p.x - 0.3) * width + const py = offsetY + (0.6 - p.y) * height + const ledName = namePrefix + letter + \`_LED\${i + 1}\` + const resName = namePrefix + letter + \`_R\${i + 1}\` + const midNet = \`net.\${ledName}_A\` + return ( + <> + + + + ) + })} + + ) +} + +export default () => ( + + + + + + + +) +`} /> + +## Step 4: Complete usage example + +Here is the complete `` component with all 26 letters supported, +ready to copy into your own project. + +```tsx +import { svgAlphabet } from "@tscircuit/alphabet" + +function samplePath(pathStr: string, n: number) { + const points: { x: number; y: number }[] = [] + const tokens = pathStr.trim().split(/\s+/) + let i = 0 + while (i < tokens.length) { + const cmd = tokens[i++] + if (cmd === "M" || cmd === "L") { + points.push({ x: parseFloat(tokens[i]), y: parseFloat(tokens[i + 1]) }) + i += 2 + } + } + if (points.length < 2) return points + const arcLen = [0] + for (let j = 1; j < points.length; j++) { + const dx = points[j].x - points[j - 1].x + const dy = points[j].y - points[j - 1].y + arcLen.push(arcLen[j - 1] + Math.sqrt(dx * dx + dy * dy)) + } + const total = arcLen.at(-1)! + const result: { x: number; y: number }[] = [] + for (let k = 0; k < n; k++) { + const target = (k / (n - 1)) * total + let seg = 0 + while (seg < arcLen.length - 1 && arcLen[seg + 1] < target) seg++ + const segLen = arcLen[seg + 1] - arcLen[seg] + const t = segLen === 0 ? 0 : (target - arcLen[seg]) / segLen + result.push({ + x: points[seg].x + t * (points[seg + 1].x - points[seg].x), + y: points[seg].y + t * (points[seg + 1].y - points[seg].y), + }) + } + return result +} + +interface LedLetterProps { + letter: string // Any capital A–Z letter + power: string // Net name for VCC (e.g. "net.PWR") + gnd: string // Net name for GND (e.g. "net.GND") + offsetX?: number // PCB X offset in mm + offsetY?: number // PCB Y offset in mm + width?: number // Letter width in mm (default 18) + height?: number // Letter height in mm (default 24) + nLeds?: number // Number of LEDs (default 14) + namePrefix?: string // Prefix to avoid name collisions +} + +export const LedLetter = ({ + letter, + power, + gnd, + offsetX = 0, + offsetY = 0, + width = 18, + height = 24, + nLeds = 14, + namePrefix = "", +}: LedLetterProps) => { + const path = svgAlphabet[letter.toUpperCase() as keyof typeof svgAlphabet] + if (!path) return null + const pts = samplePath(path, nLeds) + + return ( + <> + {pts.map((p, i) => { + const px = offsetX + (p.x - 0.3) * width + const py = offsetY + (0.6 - p.y) * height + const ledName = `${namePrefix}${letter}_LED${i + 1}` + const resName = `${namePrefix}${letter}_R${i + 1}` + const midNet = `net.${ledName}_A` + return ( + <> + + + + ) + })} + + ) +} +``` + +Usage: + +```tsx +export default () => ( + + + + +) +``` From 3f82484dd4639760dc0d055f5f455469ac2979e0 Mon Sep 17 00:00:00 2001 From: tungnguyentu Date: Fri, 15 May 2026 23:18:27 +0700 Subject: [PATCH 2/2] docs: add components table and convert final example to live TscircuitIframe Co-Authored-By: Claude Sonnet 4.6 --- docs/tutorials/draw-any-letter-with-leds.mdx | 88 +++++++++++++++++++- 1 file changed, 85 insertions(+), 3 deletions(-) diff --git a/docs/tutorials/draw-any-letter-with-leds.mdx b/docs/tutorials/draw-any-letter-with-leds.mdx index ea5cadc..ba6e477 100644 --- a/docs/tutorials/draw-any-letter-with-leds.mdx +++ b/docs/tutorials/draw-any-letter-with-leds.mdx @@ -15,6 +15,15 @@ the rest. import TscircuitIframe from "@site/src/components/TscircuitIframe" +## Components + +| Ref | Part | Value | Footprint | +|-----|------|-------|-----------| +| LED1–LEDn | Red LED | 0402 SMD LED | 0402 | +| R1–Rn | Resistor | 68–100 Ω (current limit) | 0402 | + +*Quantity scales with the number of LEDs per letter (12–20 typical per character).* + ## Step 1: Sample points along an SVG path The `@tscircuit/alphabet` package exports `svgAlphabet`, a map from each @@ -384,13 +393,86 @@ export const LedLetter = ({ } ``` -Usage: +Here's the complete runnable example rendering two letters side by side: + + { + const px = offsetX + (p.x - 0.5) * size + const py = -(p.y - 0.5) * size * 1.2 + const midNet = \`net.\${namePrefix}MID\${i}\` + const resName = \`\${namePrefix}R\${i + 1}\` + const ledName = \`\${namePrefix}LED\${i + 1}\` + return ( + <> + + + + ) + }) +} -```tsx export default () => ( ) -``` +`} />