Skip to content

Commit edda4d4

Browse files
mike-wardclaude
andcommitted
add SVG transforms, groups, strokes; code quality fixes; docs
- transforms: matrix, translate, rotate, scale, skewX, skewY - groups: nested <g> elements with style inheritance - strokes: width, linecap (butt/round/square), linejoin (miter/bevel/round) - fix: inheritance detection using sentinel values - fix: closed path detection with epsilon tolerance - fix: extract magic numbers to named constants - add tiger.v example (Ghostscript Tiger) - add docs/SVG.md documentation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 385ecc6 commit edda4d4

File tree

11 files changed

+1853
-78
lines changed

11 files changed

+1853
-78
lines changed

.claude/settings.local.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
"Bash(v:*)",
88
"Bash(glxinfo:*)",
99
"Bash(awk:*)",
10-
"Bash(find:*)"
10+
"Bash(find:*)",
11+
"Bash(wc:*)",
12+
"Bash(sort:*)",
13+
"Bash(git push)"
1114
]
1215
}
1316
}

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ V is a simple language. It deserves a simple UI framework.
5353
- SDF-based drop shadows
5454
- Gradient borders
5555
- Blur effects
56-
- SVG icons with color override
56+
- Full SVG support (paths, transforms, groups, strokes)
5757

5858
**Animation**
5959
- Tweens with easing curves
@@ -133,6 +133,7 @@ v install gui
133133
| [ARCHITECTURE.md](docs/ARCHITECTURE.md) | How v-gui works under the hood |
134134
| [LAYOUT_ALGORITHM.md](docs/LAYOUT_ALGORITHM.md) | Sizing, alignment, and positioning |
135135
| [ANIMATIONS.md](docs/ANIMATIONS.md) | Tweens, springs, and transitions |
136+
| [SVG.md](docs/SVG.md) | Vector graphics and icon rendering |
136137

137138
Generate API docs with:
138139
```bash
@@ -148,6 +149,7 @@ v run examples/get_started.v # Start here
148149
v run examples/buttons.v # Button variants
149150
v run examples/animations.v # Tweens and springs
150151
v run examples/theme_designer.v # Build custom themes
152+
v run examples/tiger.v # SVG rendering demo
151153
v run examples/snake.v # A complete game
152154
```
153155

docs/ARCHITECTURE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ Pre-built, composable UI elements.
125125
**Primitives**:
126126
- `text`: Text rendering via vglyph
127127
- `image`: Bitmap display
128-
- `svg`: Vector graphics with tessellation and color override
128+
- `svg`: Vector graphics with full SVG support (paths, transforms, groups, strokes)
129129

130130
**Composite Components**: Built from primitives:
131131
- `button`, `toggle`, `switch`: Interactive buttons

docs/GET_STARTED.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ v run examples/containers.v
212212
# Cool stuff
213213
v run examples/animations.v # Tweens, springs, hero transitions
214214
v run examples/svg_demo.v # Vector icon rendering
215+
v run examples/tiger.v # Complex SVG (Ghostscript Tiger)
215216
v run examples/theme_designer.v
216217
v run examples/snake.v # Yes, it's a game
217218
```

docs/ROADMAP.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ as a cross-platform, immediate mode GUI toolkit.
3939
- Add physics-based spring animations (more natural than linear/easing curves).
4040
- Support layout transitions (animated constraints when window resizes).
4141
- Add "hero" transitions between views.
42-
- [x] **Vector Graphics:** SVG icon support with path-based rendering, tessellation, and caching.
42+
- [x] **Vector Graphics:** Full SVG support including paths, transforms (matrix, translate, rotate,
43+
scale, skew), nested groups with style inheritance, and strokes (width, linecap, linejoin).
4344

4445
### Phase 2: Fundamental Widgets & UX
4546
*Focus: Completing the standard library of widgets expected by developers.*

docs/SVG.md

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
# SVG Support
2+
3+
v-gui includes a complete SVG rendering pipeline for vector graphics. SVGs are parsed, tessellated
4+
into triangles, cached, and rendered efficiently via the GPU.
5+
6+
## Basic Usage
7+
8+
```v ignore
9+
// From file
10+
gui.svg(file_name: 'icon.svg', width: 24, height: 24)
11+
12+
// Inline SVG data
13+
gui.svg(svg_data: '<svg viewBox="0 0 24 24">...</svg>', width: 24, height: 24)
14+
15+
// With color override (for monochrome icons)
16+
gui.svg(file_name: 'icon.svg', width: 24, height: 24, color: gui.theme().text_color)
17+
```
18+
19+
## Supported SVG Features
20+
21+
### Elements
22+
23+
| Element | Support |
24+
|---------|---------|
25+
| `<path>` | Full (all commands: M, L, H, V, C, S, Q, T, A, Z) |
26+
| `<rect>` | Full (including rx/ry for rounded corners) |
27+
| `<circle>` | Full |
28+
| `<ellipse>` | Full |
29+
| `<line>` | Full |
30+
| `<polygon>` | Full |
31+
| `<polyline>` | Full |
32+
| `<g>` | Full (nested groups with style inheritance) |
33+
34+
### Transforms
35+
36+
All SVG transform functions are supported:
37+
38+
```xml
39+
<g transform="translate(10, 20)">...</g>
40+
<g transform="rotate(45)">...</g>
41+
<g transform="rotate(45, 100, 100)">...</g> <!-- rotate around point -->
42+
<g transform="scale(2)">...</g>
43+
<g transform="scale(2, 0.5)">...</g>
44+
<g transform="skewX(30)">...</g>
45+
<g transform="skewY(30)">...</g>
46+
<g transform="matrix(a, b, c, d, e, f)">...</g>
47+
48+
<!-- Multiple transforms are composed -->
49+
<g transform="translate(50, 50) rotate(45) scale(2)">...</g>
50+
```
51+
52+
### Fills and Strokes
53+
54+
```xml
55+
<!-- Fill colors -->
56+
<path fill="#ff0000" ... />
57+
<path fill="rgb(255, 0, 0)" ... />
58+
<path fill="red" ... />
59+
<path fill="none" ... />
60+
61+
<!-- Strokes -->
62+
<path stroke="#000" stroke-width="2" ... />
63+
<path stroke-linecap="round" ... /> <!-- butt, round, square -->
64+
<path stroke-linejoin="bevel" ... /> <!-- miter, round, bevel -->
65+
```
66+
67+
### Style Inheritance
68+
69+
Styles cascade from parent groups to children:
70+
71+
```xml
72+
<g fill="blue" stroke="black" stroke-width="2">
73+
<rect ... /> <!-- inherits blue fill, black stroke -->
74+
<circle fill="red" /> <!-- red fill, inherits black stroke -->
75+
</g>
76+
```
77+
78+
## Architecture
79+
80+
### Parsing
81+
82+
SVGs are parsed into `VectorPath` structures containing:
83+
- Path segments (move, line, quadratic/cubic bezier, arc, close)
84+
- Fill color
85+
- Stroke properties (color, width, cap, join)
86+
- Transform matrix
87+
88+
### Tessellation
89+
90+
Paths are converted to triangles for GPU rendering:
91+
92+
1. **Curve flattening**: Bezier curves subdivided to polylines based on tolerance
93+
2. **Transform application**: Affine transforms applied to all coordinates
94+
3. **Fill tessellation**: Ear clipping algorithm with hole support
95+
4. **Stroke tessellation**: Polylines expanded to quads with proper joins and caps
96+
97+
### Caching
98+
99+
Tessellated SVGs are cached by source and size:
100+
101+
```v ignore
102+
// Automatic caching - same SVG at same size reuses tessellation
103+
gui.svg(file_name: 'icon.svg', width: 24, height: 24) // tessellates
104+
gui.svg(file_name: 'icon.svg', width: 24, height: 24) // cache hit
105+
106+
// Different size = new cache entry
107+
gui.svg(file_name: 'icon.svg', width: 48, height: 48) // tessellates at new scale
108+
```
109+
110+
Manual cache control:
111+
112+
```v ignore
113+
// Clear specific SVG from cache
114+
window.remove_svg_from_cache('icon.svg')
115+
116+
// Clear all cached SVGs
117+
window.clear_svg_cache()
118+
```
119+
120+
## Examples
121+
122+
### Simple Icon
123+
124+
```v ignore
125+
gui.row(
126+
content: [
127+
gui.svg(file_name: 'save.svg', width: 16, height: 16),
128+
gui.text(text: 'Save'),
129+
]
130+
)
131+
```
132+
133+
### Icon with Theme Color
134+
135+
```v ignore
136+
gui.svg(
137+
file_name: 'settings.svg',
138+
width: 24,
139+
height: 24,
140+
color: gui.theme().text_color, // overrides SVG fill colors
141+
)
142+
```
143+
144+
### Complex SVG (Ghostscript Tiger)
145+
146+
```v ignore
147+
// See examples/tiger.v for complete example
148+
gui.svg(file_name: 'tiger.svg', width: 450, height: 450)
149+
```
150+
151+
The classic Ghostscript Tiger (240 paths with transforms, groups, and strokes) renders correctly,
152+
demonstrating full SVG compatibility.
153+
154+
## Limitations
155+
156+
Currently not supported:
157+
- `<defs>` and `<use>` (symbol reuse)
158+
- `<clipPath>` and `<mask>`
159+
- `<linearGradient>` and `<radialGradient>`
160+
- CSS styling (`<style>` blocks, `class` attributes)
161+
- `opacity` attribute
162+
- Text (`<text>`, `<tspan>`)
163+
- Filters (`<filter>`)
164+
165+
For icons and illustrations, these limitations rarely matter. For complex SVGs with gradients or
166+
text, consider converting to supported features or using bitmap images.
167+
168+
## Performance Tips
169+
170+
1. **Use appropriate sizes**: Tessellation quality scales with display size. Don't render a
171+
1000x1000 SVG at 24x24.
172+
173+
2. **Reuse SVGs**: The cache is keyed by source+size. Identical SVG widgets share tessellation.
174+
175+
3. **Simplify paths**: Fewer path segments = faster tessellation. Tools like SVGO can optimize.
176+
177+
4. **Color override for icons**: Using `color:` parameter is faster than parsing colors from SVG.

0 commit comments

Comments
 (0)