Two copies for two jobs. rippr-src/ is a browsable git archive export of the tracked tree at 2f76983 - no build outputs, no local.properties, no nested .git - which is convenient to read in gitea but carries no history and will drift. rippr-full-history.bundle is the real backup: all 18 commits, verified as "records a complete history" and test-cloned before committing. This matters because ~/dojo/rippr has no git remote and otherwise exists only on one machine. rippr-src/SNAPSHOT.md explains the difference and how to restore. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
174 lines
5.7 KiB
Python
174 lines
5.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Emits the Android vector drawables for the chosen Route mark.
|
|
|
|
This is a real conversion, not a copy: VectorDrawable understands a subset of SVG.
|
|
Three things in the source SVG have no direct equivalent and are translated here.
|
|
|
|
<circle> -> two arc commands, since only <path> exists
|
|
stroke-dasharray -> trimPathStart / trimPathEnd, which cut one segment out of
|
|
a path; the dash offset becomes the trim start, expressed
|
|
as a fraction of the circle's circumference
|
|
opacity -> strokeAlpha / fillAlpha
|
|
|
|
Run after gen_logos.py; writes straight into app/src/main/res/drawable/.
|
|
"""
|
|
|
|
import math
|
|
import pathlib
|
|
|
|
import gen_logos
|
|
|
|
HERE = pathlib.Path(__file__).parent
|
|
RES = HERE.parent / "app" / "src" / "main" / "res" / "drawable"
|
|
|
|
CX = CY = 54.0
|
|
|
|
# The standalone logo draws its ring at r=43, which is outside the adaptive-icon safe
|
|
# circle (r=33) — a launcher mask crops it clean off, taking the accent segment with
|
|
# it. The launcher build therefore shrinks the whole mark to fit inside the safe zone.
|
|
DESIGN_RING_R = 43.0
|
|
RING_R = 32.0
|
|
GLYPH_SIZE = 40.0
|
|
|
|
# Must match concept_route() in gen_logos.py.
|
|
DASH_LEN = 34.0
|
|
DASH_OFFSET = 100.0
|
|
|
|
# Trim fractions describe an angular span, so they are derived from the design radius
|
|
# and stay correct at any ring size.
|
|
DESIGN_CIRCUMFERENCE = 2 * math.pi * DESIGN_RING_R
|
|
|
|
# Starts at due east and sweeps clockwise, matching how a browser renders <circle>,
|
|
# so the trim fractions below land the accent in the same place as the preview.
|
|
RING_PATH = (
|
|
f"M{CX + RING_R},{CY} "
|
|
f"A{RING_R},{RING_R} 0 1 1 {CX - RING_R},{CY} "
|
|
f"A{RING_R},{RING_R} 0 1 1 {CX + RING_R},{CY}"
|
|
)
|
|
|
|
LAUNCHER = """<?xml version="1.0" encoding="utf-8"?>
|
|
<!-- Generated by design/to_vector_drawable.py — do not edit by hand. -->
|
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
android:width="108dp"
|
|
android:height="108dp"
|
|
android:viewportWidth="108"
|
|
android:viewportHeight="108">
|
|
|
|
<!-- Track ring. -->
|
|
<path
|
|
android:pathData="{ring}"
|
|
android:strokeColor="{dim}"
|
|
android:strokeWidth="3.5"
|
|
android:strokeAlpha="0.6" />
|
|
|
|
<!-- Material Symbols `route`, Apache-2.0. Scaled from its 960 box onto the icon canvas. -->
|
|
<group
|
|
android:translateX="{tx}"
|
|
android:translateY="{ty}"
|
|
android:scaleX="{scale}"
|
|
android:scaleY="{scale}">
|
|
<path
|
|
android:pathData="{glyph}"
|
|
android:fillColor="{ink}" />
|
|
</group>
|
|
|
|
<!-- Recording-progress segment: the SVG's dash pattern, cut with trimPath. -->
|
|
<path
|
|
android:pathData="{ring}"
|
|
android:strokeColor="{accent}"
|
|
android:strokeWidth="3.5"
|
|
android:strokeLineCap="round"
|
|
android:trimPathStart="{trim_start:.4f}"
|
|
android:trimPathEnd="{trim_end:.4f}" />
|
|
</vector>
|
|
"""
|
|
|
|
# Status-bar icons are flattened to a single color with only alpha preserved, so the
|
|
# ring and accent are dropped — at 24dp they would collapse into a grey smudge.
|
|
MONO = """<?xml version="1.0" encoding="utf-8"?>
|
|
<!-- Generated by design/to_vector_drawable.py — do not edit by hand. -->
|
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
android:width="24dp"
|
|
android:height="24dp"
|
|
android:viewportWidth="24"
|
|
android:viewportHeight="24"
|
|
android:tint="#FFFFFFFF">
|
|
<group
|
|
android:translateX="{tx}"
|
|
android:translateY="{ty}"
|
|
android:scaleX="{scale}"
|
|
android:scaleY="{scale}">
|
|
<path
|
|
android:pathData="{glyph}"
|
|
android:fillColor="#FFFFFFFF" />
|
|
</group>
|
|
</vector>
|
|
"""
|
|
|
|
# Themed ("monochrome") launcher icons keep the 108 canvas and are tinted by the
|
|
# system, so this one keeps the ring for a fuller shape at launcher size.
|
|
THEMED = """<?xml version="1.0" encoding="utf-8"?>
|
|
<!-- Generated by design/to_vector_drawable.py — do not edit by hand. -->
|
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
android:width="108dp"
|
|
android:height="108dp"
|
|
android:viewportWidth="108"
|
|
android:viewportHeight="108">
|
|
<path
|
|
android:pathData="{ring}"
|
|
android:strokeColor="#FF000000"
|
|
android:strokeWidth="3.5" />
|
|
<group
|
|
android:translateX="{tx}"
|
|
android:translateY="{ty}"
|
|
android:scaleX="{scale}"
|
|
android:scaleY="{scale}">
|
|
<path
|
|
android:pathData="{glyph}"
|
|
android:fillColor="#FF000000" />
|
|
</group>
|
|
</vector>
|
|
"""
|
|
|
|
|
|
def main():
|
|
glyph = gen_logos.glyph_path("route")
|
|
|
|
launcher_scale = GLYPH_SIZE / gen_logos.GLYPH_BOX
|
|
common = dict(
|
|
ring=RING_PATH,
|
|
glyph=glyph,
|
|
dim=gen_logos.DIM,
|
|
ink=gen_logos.INK,
|
|
accent=gen_logos.ACCENT,
|
|
tx=f"{CX - GLYPH_SIZE / 2:g}",
|
|
ty=f"{CY + GLYPH_SIZE / 2:g}",
|
|
scale=f"{launcher_scale:.6f}",
|
|
trim_start=DASH_OFFSET / DESIGN_CIRCUMFERENCE,
|
|
trim_end=(DASH_OFFSET + DASH_LEN) / DESIGN_CIRCUMFERENCE,
|
|
)
|
|
|
|
RES.mkdir(parents=True, exist_ok=True)
|
|
(RES / "ic_launcher_foreground.xml").write_text(LAUNCHER.format(**common))
|
|
(RES / "ic_launcher_monochrome.xml").write_text(THEMED.format(**common))
|
|
|
|
# The status-bar glyph fills a 24dp box edge to edge, minus a little padding.
|
|
mono_size = 20.0
|
|
mono_scale = mono_size / gen_logos.GLYPH_BOX
|
|
(RES / "ic_stat_rippr.xml").write_text(
|
|
MONO.format(
|
|
glyph=glyph,
|
|
tx=f"{(24 - mono_size) / 2:g}",
|
|
ty=f"{(24 + mono_size) / 2:g}",
|
|
scale=f"{mono_scale:.6f}",
|
|
)
|
|
)
|
|
|
|
for name in ("ic_launcher_foreground", "ic_launcher_monochrome", "ic_stat_rippr"):
|
|
print(f"wrote {RES / (name + '.xml')}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|