mirror of
https://github.com/twinksoftware/Gearslip.git
synced 2026-11-10 16:02:25 +01:00
Gearslip: phone-side Android Auto client
This commit is contained in:
commit
24cfbee0c4
98 files changed
+12370
No files matched your search
@@ -0,0 +1,176 @@
|
||||
import math
|
||||
|
||||
def pt(cx, cy, r, deg):
|
||||
a = math.radians(deg - 90)
|
||||
return (cx + r*math.cos(a), cy + r*math.sin(a))
|
||||
|
||||
def gear_points(cx, cy, teeth, outer_r, inner_r, tooth_frac=0.5):
|
||||
slice_w = 360.0 / teeth
|
||||
gap = slice_w * (1 - tooth_frac)
|
||||
pts = []
|
||||
for i in range(teeth):
|
||||
start = i * slice_w
|
||||
a1 = start + gap/2
|
||||
a2 = a1 + slice_w*tooth_frac
|
||||
pts.append(pt(cx, cy, inner_r, start))
|
||||
pts.append(pt(cx, cy, outer_r, a1))
|
||||
pts.append(pt(cx, cy, outer_r, a2))
|
||||
pts.append(pt(cx, cy, inner_r, start + slice_w))
|
||||
return pts
|
||||
|
||||
def path_from_pts(pts):
|
||||
d = f"M {pts[0][0]:.2f},{pts[0][1]:.2f} "
|
||||
for p in pts[1:]:
|
||||
d += f"L {p[0]:.2f},{p[1]:.2f} "
|
||||
return d + "Z"
|
||||
|
||||
def rotate(pts, ocx, ocy, deg):
|
||||
a = math.radians(deg)
|
||||
out = []
|
||||
for x, y in pts:
|
||||
x0, y0 = x-ocx, y-ocy
|
||||
out.append((x0*math.cos(a)-y0*math.sin(a)+ocx, x0*math.sin(a)+y0*math.cos(a)+ocy))
|
||||
return out
|
||||
|
||||
def scale_pts(pts, ocx, ocy, s):
|
||||
return [((x-ocx)*s+ocx, (y-ocy)*s+ocy) for x, y in pts]
|
||||
|
||||
cx, cy = 54, 54
|
||||
teeth = 8
|
||||
outer_r = 36
|
||||
inner_r = 27
|
||||
|
||||
all_pts = gear_points(cx, cy, teeth, outer_r, inner_r)
|
||||
broken_idx = 1
|
||||
body_pts = [p for i, p in enumerate(all_pts) if not (4*broken_idx <= i < 4*broken_idx+4)]
|
||||
|
||||
tooth_pts = all_pts[4*broken_idx:4*broken_idx+4]
|
||||
tooth_cx = sum(p[0] for p in tooth_pts)/4
|
||||
tooth_cy = sum(p[1] for p in tooth_pts)/4
|
||||
|
||||
dx, dy = 14, 2
|
||||
final_pts = rotate([(x+dx, y+dy) for x, y in tooth_pts], tooth_cx+dx, tooth_cy+dy, 26)
|
||||
final_cx, final_cy = tooth_cx+dx, tooth_cy+dy
|
||||
|
||||
trail_half_w_near = 1.2
|
||||
trail_half_w_far = 7.5
|
||||
perp = (-dy, dx)
|
||||
plen = math.hypot(*perp)
|
||||
perp = (perp[0]/plen, perp[1]/plen)
|
||||
|
||||
gap_angle = math.atan2(tooth_cy - cy, tooth_cx - cx)
|
||||
extend_r = inner_r - 6
|
||||
gap_x = cx + math.cos(gap_angle) * extend_r
|
||||
gap_y = cy + math.sin(gap_angle) * extend_r
|
||||
|
||||
near1 = (gap_x + perp[0]*trail_half_w_near, gap_y + perp[1]*trail_half_w_near)
|
||||
near2 = (gap_x - perp[0]*trail_half_w_near, gap_y - perp[1]*trail_half_w_near)
|
||||
far1 = (final_cx + perp[0]*trail_half_w_far, final_cy + perp[1]*trail_half_w_far)
|
||||
far2 = (final_cx - perp[0]*trail_half_w_far, final_cy - perp[1]*trail_half_w_far)
|
||||
trail_pts = [near1, far1, far2, near2]
|
||||
|
||||
hub_r = 12
|
||||
|
||||
# --- scale everything down to fit the adaptive-icon safe zone (66dp diameter circle) ---
|
||||
SCALE = 0.66
|
||||
def S(pts):
|
||||
return scale_pts(pts, cx, cy, SCALE)
|
||||
|
||||
body_s = S(body_pts)
|
||||
final_s = S(final_pts)
|
||||
trail_s = S(trail_pts)
|
||||
hub_r_s = hub_r * SCALE
|
||||
gap_x_s, gap_y_s = scale_pts([(gap_x, gap_y)], cx, cy, SCALE)[0]
|
||||
final_cx_s, final_cy_s = scale_pts([(final_cx, final_cy)], cx, cy, SCALE)[0]
|
||||
|
||||
body_path = path_from_pts(body_s)
|
||||
final_path = path_from_pts(final_s)
|
||||
trail_path = path_from_pts(trail_s)
|
||||
|
||||
GREY = "#8B9096"
|
||||
RED = "#8C1F1F"
|
||||
BG = "#0c0c0d"
|
||||
RING = "#131415"
|
||||
|
||||
# ===== 1. Background: full-bleed, unscaled =====
|
||||
bg_xml = f'''<?xml version="1.0" encoding="utf-8"?>
|
||||
<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="M0,0h108v108h-108z"
|
||||
android:fillColor="{BG}"/>
|
||||
<path
|
||||
android:pathData="M54,2 A52,52 0 1,1 53.99,2 Z"
|
||||
android:fillColor="{RING}"/>
|
||||
</vector>
|
||||
'''
|
||||
|
||||
# ===== 2. Foreground: gear + broken piece + trail, scaled into the safe zone =====
|
||||
# Trail uses a gradient (narrow near the gear, fading out) via an <aapt:attr> gradient,
|
||||
# same visual as the mockup.
|
||||
fg_xml = f'''<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="{trail_path}">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:type="linear"
|
||||
android:startX="{gap_x_s:.2f}"
|
||||
android:startY="{gap_y_s:.2f}"
|
||||
android:endX="{final_cx_s:.2f}"
|
||||
android:endY="{final_cy_s:.2f}">
|
||||
<item android:offset="0" android:color="#00{RED[1:]}"/>
|
||||
<item android:offset="1" android:color="{RED}"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="{body_path}"
|
||||
android:fillColor="{GREY}"/>
|
||||
<path
|
||||
android:pathData="{final_path}"
|
||||
android:fillColor="{RED}"/>
|
||||
<path
|
||||
android:pathData="M{cx},{cy-hub_r_s:.2f} A{hub_r_s:.2f},{hub_r_s:.2f} 0 1,1 {cx-0.01:.2f},{cy-hub_r_s:.2f} Z"
|
||||
android:fillColor="{BG}"/>
|
||||
</vector>
|
||||
'''
|
||||
|
||||
# ===== 3. Monochrome: same silhouette, single alpha-bearing color for Material You
|
||||
# theming. The system substitutes its own tint - the fillColor value here is
|
||||
# irrelevant, only the shape/alpha matters. Trail kept at a lower alpha so the
|
||||
# fade effect survives tinting too.
|
||||
mono_xml = f'''<?xml version="1.0" encoding="utf-8"?>
|
||||
<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="{trail_path}"
|
||||
android:fillColor="#FF000000"
|
||||
android:fillAlpha="0.35"/>
|
||||
<path
|
||||
android:pathData="{body_path}"
|
||||
android:fillColor="#FF000000"/>
|
||||
<path
|
||||
android:pathData="{final_path}"
|
||||
android:fillColor="#FF000000"/>
|
||||
<path
|
||||
android:pathData="M{cx},{cy-hub_r_s:.2f} A{hub_r_s:.2f},{hub_r_s:.2f} 0 1,1 {cx-0.01:.2f},{cy-hub_r_s:.2f} Z"
|
||||
android:fillColor="#00000000"/>
|
||||
</vector>
|
||||
'''
|
||||
|
||||
out = '/tmp/claude-1000/-home-seb-AABrowser/dadb0938-9338-4349-968f-4a89bc8de777/scratchpad/icon'
|
||||
open(f'{out}/ic_launcher_background.xml', 'w').write(bg_xml)
|
||||
open(f'{out}/ic_launcher_foreground.xml', 'w').write(fg_xml)
|
||||
open(f'{out}/ic_launcher_monochrome.xml', 'w').write(mono_xml)
|
||||
print("written")
|
||||
Reference in new issue
Block a user