Py.Cafe

AIPHeX/

solara-svg-compass

Dynamic SVG Compass

DocsPricing
  • VERA_compass_pin.svg
  • VERAcompass.svg
  • VERAcompass_needle_new.svg
  • app.py
  • merged.svg
  • requirements.txt
  • sw_iframe.html
  • testing.html
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import solara
import asyncio
import base64
from pathlib import Path

# — reactive quarter‐index (0…3) —
index = solara.reactive(0)

def svg_data_uri(path):
    raw = Path(path).read_text(encoding="utf-8")
    b64 = base64.b64encode(raw.encode("utf-8")).decode("ascii")
    return f"data:image/svg+xml;base64,{b64}"

# Inline all three SVGs
BASE_URI   = svg_data_uri("VERAcompass.svg")
NEEDLE_URI = svg_data_uri("VERAcompass_needle_new.svg")
PIN_URI    = svg_data_uri("VERA_compass_pin.svg")

# Layout constants
SIZE         = 320       # px for overall compass
PIN_SIZE     = SIZE * 0.15    # 10% pin
# Pivot for CSS transform-origin in percent
PIVOT_X_PCT  = 0.50        
PIVOT_Y_PCT  = 0.495      

@solara.component
def Page():
    def go_prev():
        index.value = (index.value - 1) % 4

    def go_next():
        index.value = (index.value + 1) % 4

    def do_reset():
        index.value = 0

    # Compute angle: 0°, 90°, 180°, 270°
    angle = index.value * 90

    # Build a small block of HTML + CSS that layers the three images
    html = f"""
<div style="position:relative;
            width:{SIZE}px;height:{SIZE}px;
            margin:auto;border:1px solid #ccc;">
  <!-- Base -->
  <img src="{BASE_URI}"
       style="position:absolute;top:0;left:0;
              width:100%;height:100%;pointer-events:none;" />
  <!-- Needle -->
  <img src="{NEEDLE_URI}"
       style="position:absolute;
            top:16%;left:24%;
              width:160px;
              transform:translate(50%,50%);
              transform:rotate({angle}deg);
              pointer-events:none;" />
  <!-- Pin -->
  <img src="{PIN_URI}"
       style="position:absolute;
              width:{PIN_SIZE}px;height:{PIN_SIZE}px;
              top:50%;left:50%;
              transform:translate(-50%,-50%);
              pointer-events:none;" />
</div>
"""
    # Render it
    solara.HTML(unsafe_innerHTML=html)

    # Controls
    with solara.Row():
        solara.Button("◀ Prev", on_click=go_prev)
        solara.Button("Next ▶", on_click=go_next)
        solara.Button("Reset", on_click=do_reset)

    # Autoplay first +90° after 1s
    def startup():
        async def _d():
            await asyncio.sleep(1)
            go_next()
        asyncio.create_task(_d())
    solara.use_effect(startup, [])

# Mount
app = Page