#!/usr/bin/env python3 """Generate the RuView README visual system. Deterministic, no network.""" from pathlib import Path from html import escape import math,json,re from readme_visuals_xml import parse_svg ROOT=Path(__file__).resolve().parents[1];OUT=ROOT/'assets/readme';OUT.mkdir(parents=True,exist_ok=True) C='#78cfff';BLUE='#438fff';INK='#071124';WHITE='#f3f7ff';MUTED='#b0c2db' LIME='#b8f36c';AMBER='#ffb45e' def text(x,y,value,size=16,color=MUTED,anchor='start',extra=''): weight='font-weight="650" letter-spacing="-0.6"' if size>=25 else 'font-weight="500"' return f'{escape(value)}' def path(d,color=C,width=1.5,extra=''): return f'' def circle(x,y,r,color=C,extra=''): fill = '' if 'fill=' in extra else 'fill="none"' return f'' def icon(kind,x,y,scale=1): s='' if kind in ['signal','start','presence']: for r in [18,32,46]:s+=f'' s+=path('M-46 0H46M0 -23V23',BLUE,.7) s+=''+path('M0 0L40 -18',LIME,2)+circle(34,-15,3,LIME,extra=f'fill="{LIME}"')+'' for px,py in [(-20,8),(12,13),(23,-8)]:s+=circle(px,py,3,AMBER,extra=f'fill="{AMBER}" class="spark"') elif kind=='pose': # Authored anatomical mesh silhouette; a conceptual figure, not captured pose data. s+='' s+='' # Far arm and leg provide occlusion and depth. s+='' s+='' s+='' # Near arm: shoulder, upper arm, elbow, forearm and closed hand. s+='' # Shoulder and chest taper into abdomen and pelvis. s+='' # Three-quarter head with jaw, nose and ear contour. s+='' s+='' # Surface contours make volume legible at README scale. s+=path('M-9 -23Q-2 -18 10 -23M-7 -15Q0 -11 9 -16M-6 -8Q1 -5 8 -9M-7 1Q1 5 9 1M0 -26L2 9M-4 -40Q0 -42 7 -40M2 -32L6 -34',LIME,.55,extra='opacity=".65"') s+=path('M-5 -45Q1 -49 6 -46M-10 -22L-6 -8',C,.7,extra='opacity=".8"') s+='' # Joint markers remain attached to the anatomical surface. for px,py in [(-9,-24),(10,-23),(1,-12),(2,9)]: s+=circle(px,py,3.5,AMBER,extra='stroke-opacity=".25"')+circle(px,py,1.5,AMBER,extra=f'fill="{AMBER}" class="spark"') s+='' s+=path('M-42 -42V-53H-30M30 -53H42V-42M42 45V55H30M-30 55H-42V45',BLUE,.8) elif kind in ['vitals','models']: for j in range(3): s+=path(f'M-49 {j*12-14}C-34 {j*12-14} -30 {j*12-35} -17 {j*12-35}S0 {j*12+7} 14 {j*12+7}S34 {j*12-14} 49 {j*12-14}',C if j!=1 else LIME,1.5,extra=f'class="trace" style="animation-delay:-{j}s" opacity="{1-j*.2}"') s+=''+path('M0 -42V40',AMBER,1)+circle(0,-8,3,AMBER,extra=f'fill="{AMBER}"')+'' elif kind in ['edge','hardware','seed','evidence']: for j in range(3): yy=j*13 s+=f'' s+=path('M-12 -31L0 -37L12 -31L0 -25Z',LIME,2) for px in [-26,0,26]:s+=path(f'M{px} 22V39',AMBER,1,extra='class="flow"') elif kind in ['home','automation']: s+='' s+=path('M-32 17V-16L0 -35L32 -16V17M-32 -16L0 3L32 -16M0 3V34',C,1.5) s+=path('M-22 20L-10 13L12 25M-10 13V-5',LIME,1.5,extra='class="flow"') for px,py in [(-22,20),(-10,-5),(12,25)]:s+=circle(px,py,4,AMBER,extra=f'fill="#102749" class="breathe"') elif kind in ['agent','agents']: for px,py in [(0,-36),(-38,-10),(-23,31),(30,28),(39,-17)]: s+=path(f'M0 0L{px} {py}',BLUE,1,extra='class="flow"') s+=f'' s+=circle(0,0,15,LIME)+path('M-6 0L-1 5L8 -6',LIME,2) else:s+=path('M-35 0L0 -25L35 0L0 25Z',C,2) base='' if kind=='pose': base='' return f'{base}{s}' return f'{base}{s}' def spatial(x,y,scale=1): """Projected geometry gives depth without scripts or CSS 3D dependencies.""" s='' s+='' s+='' for px,py in [(-40,6),(0,-15),(40,6),(-20,30),(20,30),(0,49)]: s+=circle(px,py,2,BLUE,extra='fill="#70b6ff" class="spark"') s+='' s+='' return f'{s}' def _frame(w,h,title,desc,body): return f'''{escape(title)}{escape(desc)}{body}''' MOTION='''''' def frame(w,h,title,desc,body): value=_frame(w,h,title,desc,body) motion=MOTION for name in ['float','flow','breathe','trace','scan','orbit','spark','wave','sweep','limb','stride','layer','cursor','humanTurn','humanBody','armFar','armNear','legFar','legNear','humanLight','gaitShadow']: if f'class="{name}"' not in body: motion=re.sub(r'\.'+name+r'\{[^}]*\}', '', motion) for name in ['float','flow','breathe','trace','scan','spark','wave','sweep','limb','layer','cursor','humanTurn','humanBody','armSwing','legSwing','humanLight','gaitShadow']: if f'animation:{name} ' not in motion: marker='@keyframes '+name+'{'; start=motion.find(marker) if start>=0: pos=start+len(marker); depth=1 while depth: depth+=(motion[pos]=='{')-(motion[pos]=='}');pos+=1 motion=motion[:start]+motion[pos:] value=value[:value.index('')+8:] palette={'#0b303a':'#102749','#06171e':'#071429','#030c13':'#03091a', '#245360':'#2d4e77','#91f6df':'#9ee7ff','#26bad7':'#348bff', '#35d4db':'#348bff','#72eee0':C,'#70b6ff':BLUE,'#041117':INK, '#133a45':'#142c50','#285c68':'#2e527d','#0a2733':'#0b1b35', '#286171':'#2e5b8e','#366d80':'#4473a5','#5fe3d8':'#438fff', '#0a2630':'#0c2242','#12343d':'#162f50','#4b9ba4':'#5490c5', '#071b23':'#091b35','#285360':'#315685','#214451':'#233f63','#3c8897':'#437cb2'} for old,new in palette.items():value=value.replace(old,new) rim=f'' return value.replace('',rim+'') manifest=[] def save(name,w,h,title,desc,body,target): name+='-spatial-v6' if name in ['hero','feature-pose'] else '-spatial-v4' s=frame(w,h,title,desc,body);raw=(s+'\n').encode('utf-8');parse_svg(raw);(OUT/(name+'.svg')).write_bytes(raw);manifest.append({'file':'assets/readme/'+name+'.svg','title':title,'target':target,'width':w,'height':h}) # Cinematic RF stage: authored projected planes, not a live measurement. s=text(48,45,'RUVIEW / SPATIAL INTELLIGENCE',12,C,extra='class="mono"') s+=text(44,140,'Invisible signals.',59,WHITE)+text(44,204,'Visible possibility.',55,LIME) s+=text(48,256,'Explore the space between sensing and understanding.',18) s+=text(48,284,'WiFi sensing. Edge models. Local automation.',18) s+=''+text(72,358,'Enter the field',18,INK)+path('M246 351H266M259 344L266 351L259 358',INK,2) s+=text(49,418,'RF SENSING / OPEN RESEARCH / EDGE COMPUTE',10,MUTED,extra='class="mono"') s+='' # Ground perspective and an exploded room volume. for i in range(7):s+=path(f'M{785+i*24} 270L{648+i*91} 432','#1b3558',.8) for j in range(5):s+=path(f'M{768-j*27} {286+j*31}H{1110+j*16}','#1b3558',.8) s+='' s+='' s+='' s+='' for i in range(3):s+=f'' s+=icon('pose',959,236,1.8)+icon('hardware',771,324,.72) for j in range(3):s+=path(f'M795 {309+j*8}Q854 {245+j*14} 939 {223+j*17}',C,1.2,extra=f'class="flow" style="animation-delay:-{j}s"') for px,py in [(780,153),(963,70),(1148,162),(1148,303),(960,400)]:s+=circle(px,py,3,AMBER,extra=f'fill="{AMBER}" class="spark"') s+=text(1148,419,'CONCEPTUAL RF FIELD',10,MUTED,'end',extra='class="mono"') save('hero',1200,450,'RuView: invisible signals, visible possibility','Animated conceptual RF room with an anatomical human silhouette, moving scan plane and propagating waves. Illustration only, not a sensor measurement.',s,'docs/user-guide.md') # Visual navigation icons, each receives a real outer link in Markdown. nav=[('start','Start here','docs/user-guide.md'),('hardware','Hardware','firmware/esp32-csi-node/README.md'),('models','Models','https://huggingface.co/ruvnet/wifi-densepose-pretrained'),('agents','AI toolkit','harness/ruview/README.md'),('home','Smart home','docs/integrations/home-assistant.md'),('seed','Cognitum','https://cognitum.one/seed')] for k,label,target in nav:save('nav-'+k,180,116,label,'Open '+label+'. Animated navigation icon.',icon(k,90,45,.65)+text(90,98,label,16,WHITE,'middle'),target) # Capability cards. No unverified performance claims. cards=[('presence','01','Presence & motion','Explore changes in room occupancy.','Inspect motion features from CSI.','SENSING GUIDE','docs/user-guide.md'),('vitals','02','Breathing & vital signals','Inspect phase-derived signal trends.','Validate estimates on your hardware.','SIGNAL PIPELINE','v2/crates/wifi-densepose-signal/README.md'),('pose','03','Pose research','Train and compare body keypoints.','Live accuracy differs from benchmarks.','READ THE EVIDENCE','docs/benchmarks/pose-estimation-cog.md'),('edge','04','Edge sensor mesh','Provision, capture and calibrate.','Explore ESP32 firmware and runbooks.','HARDWARE GUIDE','firmware/esp32-csi-node/README.md'),('automation','05','Local automation','Connect rooms to your smart home.','Home Assistant, MQTT and Matter.','INTEGRATION GUIDE','docs/integrations/home-assistant.md'),('agents','06','A guided AI operator','Source-cited setup and verification.','CLI, MCP and agent workflows.','TOOLKIT GUIDE','harness/ruview/README.md')] for k,num,title,l1,l2,cta,target in cards: s=text(30,39,'RUVIEW / '+num,11,C,extra='class="mono"')+text(30,89,title,25,WHITE)+text(30,126,l1,14)+text(30,151,l2,14) s+=''+icon(k,480,144,1.12)+path('M30 204H550','#245360',1)+path('M30 204H550',C,1,extra='class="flow" opacity=".5"') s+=text(30,245,cta,12,C,extra='class="mono"')+path('M510 239H540M532 231L540 239L532 247',C,1.5) save('feature-'+k,580,276,title,l1+' '+l2+' Conceptual animation; follow the linked source for capability limits.',s,target) # Overview uses three clearly labelled stages rather than fabricated telemetry. s=text(36,44,'SIGNAL TO CONTEXT',12,C,extra='class="mono"')+text(36,89,'A sensing loop you can inspect.',34,WHITE) for i,(label,sub,kind,tail) in enumerate([('Capture RF','CSI from supported hardware','signal','Provision and calibrate'),('Extract context','Signal processing and models','models','Compare against real data'),('Act locally','Events, APIs and integrations','automation','Apply rules and review outputs')]): x=36+i*396;s+=f'' s+=text(x+24,153,f'0{i+1}',12,C)+text(x+24,188,label,26,WHITE)+text(x+24,216,sub,15)+spatial(x+269,278,.35)+icon(kind,x+269,270,.5)+text(x+24,311,tail,13) if i<2:s+=path(f'M{x+344} 229H{x+384}M{x+376} 221L{x+384} 229L{x+376} 237',C,2,extra='class="flow"') s+=text(36,373,'CONCEPTUAL PIPELINE / HARDWARE AND ENVIRONMENT VALIDATION MATTER',11,MUTED,extra='class="mono"') save('overview',1200,396,'RuView sensing overview','Conceptual sequence: capture RF, extract context, act locally. Links to architecture and user documentation.',s,'docs/user-guide.md') # Chapter headings act as visual landmarks, all remain links. headers=[('features','Explore the capabilities','SENSING / MODELS / AUTOMATION','presence','docs/user-guide.md'),('edge','Intelligence at the edge','SENSORS / FIRMWARE / MODULES','edge','firmware/esp32-csi-node/README.md'),('learning','From recordings to models','CAPTURE / TRAIN / EVALUATE','models','docs/user-guide.md'),('evidence','Evidence before accuracy claims','BENCHMARKS / REPRODUCERS / LIMITS','evidence','docs/benchmarks/pose-estimation-cog.md'),('docs','Choose your next step','GUIDES / HARDWARE / INTEGRATIONS','agents','docs/user-guide.md')] for k,title,sub,kind,target in headers: s=icon(kind,76,75,.75)+text(140,63,title,34,WHITE)+text(142,99,sub,12,C,extra='class="mono"') s+=spatial(1012,84,.55) s+=path('M1140 70H1160M1152 62L1160 70L1152 78',C,2) save('header-'+k,1200,148,title,'Animated section heading. '+sub,s,target) # Brand destination; original ruOS promotion remains in the README. s=text(44,42,'COGNITUM ONE',12,C,extra='class="mono"')+text(44,99,'Ambient Intelligence.',42,WHITE)+text(44,147,'At the edge of the physical world.',25,C)+text(44,190,'Explore the platform behind the ecosystem.',16) s+=''+text(65,254,'Explore Cognitum',18,INK)+path('M263 247H284M277 240L284 247L277 254',INK,2) s+=''+spatial(945,173,1.55)+icon('seed',945,145,1.65) for j in range(3):s+=f'' save('cognitum',1200,304,'Explore Cognitum One','Ambient Intelligence at the edge of the physical world. Open cognitum.one.',s,'https://cognitum.one') (OUT/'manifest.json').write_bytes((json.dumps({'schema_version':1,'style':'RuView spatial v4: navy glass, electric blue, lime and amber','motion':'Decorative CSS; prefers-reduced-motion supported; no script or remote dependencies','assets':manifest},indent=2)+'\n').encode('utf-8')) print(f'Generated {len(manifest)} SVGs, {sum((ROOT/x["file"]).stat().st_size for x in manifest):,} bytes')