From 1129b8577a22701422727f21003eae852e10ac8b Mon Sep 17 00:00:00 2001 From: FrogAi <91348155+FrogAi@users.noreply.github.com> Date: Wed, 31 Jul 2024 20:33:49 -0700 Subject: [PATCH] Visuals - Custom Onroad UI - Compass Add a compass to the onroad UI. --- .../assets/other_images/compass_inner.png | Bin 0 -> 16603 bytes selfdrive/ui/qt/onroad/annotated_camera.cc | 114 +++++++++++++++++- selfdrive/ui/qt/onroad/annotated_camera.h | 25 ++++ selfdrive/ui/ui.cc | 7 ++ selfdrive/ui/ui.h | 2 + 5 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 selfdrive/frogpilot/assets/other_images/compass_inner.png diff --git a/selfdrive/frogpilot/assets/other_images/compass_inner.png b/selfdrive/frogpilot/assets/other_images/compass_inner.png new file mode 100644 index 0000000000000000000000000000000000000000..d6517758c05e515d7c2fe886e85fc8cd11b770e3 GIT binary patch literal 16603 zcmb`vbx@XF*EbB3(hW)~NF$|mw@8X2N(u;~bax9PN{DoWlyrAVcXx-n;`&5)$qc1(|2?d5-uG6CM8lY5TP( zd?4FBlb1p&=%?C1LZU-@A|v_SS#L-8w!rrf7hXFTmhE&h&u;HLc=FL)c3OtSK_zfp zI$CtRdFrjf$9cZ&cSm53Ve)+Zz^0&f|4$*|O50%Qey0d%@s z%o0dM)M%0c52aA)hL$M2F;kG>CnTtMYA^}CWdeMi>|0X9V z**Q2kew&SCDtp{qUulz6hrWM#Z%CcQ^TC4$^-g>9qEQhM5lL|0;j^=|EoNKVu>z^I zbmq&WKYiLX#Kgq6bmnWF_HyQGoeBekZPzJ~!q(S69i9IBmnt0?VliE2Momjg`z1j$N^WLXswfO-xKODem4?L3v0{N=n$<*T+OcLh?Q3|NFwXNh2fBz?_m z+AmX8Tg#c|$j{GzTJPh798>x-9R>Z^^>$E*?_^I`SC@$U8P`ZtlL1M2tuaq^e`~Ap zW<0;uVsosd7#`gahKsk4+w}BwO)OlWKYtz`-K~IYAfA8V!M%I;sL;4lrez^K{1^KB z`?-aL&VQz)q#)(jA|oTqS6D6VqOEQCxzS0a;qRW^+B>VToV!awK~a^#^m1>Z@us!6 z*PPLpF5iz9>(~`DNbJmr^KyMC-591nB1z}!{4bl$=4j5!)2C0DU^pUVFdQ3epAXB+ z%d9RgE>m~!-mOZ~$uBIlN)mS3oejjN&BU=~R@V;WcI@y^~_oKjj zUW*NL8QLufbR zQ46gOU_9$SK44&EoG-1YNW#YNKmIt{bm@_dVqj2%mM=#j!^O_dJ`D>ULi1?8&PBu6 z(2yrYiLoW#{1}O^X&RQM$b0C+!>Zz_@bCk})z0wgm4cwGJ+{>FBlZs9}0 zMpiiXA?2*e9~&CF6Oovh_<)6lMNu_te0;pZVQV~J*zIKd&j0?VZjF78wt_@-Nei)&a^L2MtEwV1UIy;@kU~|2~^c@|U zyUj;MfC=OK#Kmp?L(SdY{ab!MN0q1Ism8NsQjZ=z^5>sgmor~{9_6Cl?YD>b7^1BJh_=EmKJr^Vlsl9oXd_#;^G80g1~+Tn|o3s@!bkrucM~T%+oG0?yA|ED5N#bJo>veVI?FeC`gAn+^*y` z8ORUGaILSZ$S|@g<7u?i-0W;ZEaBniSnlac``371It32it2easkRcQKSAKndVK|U1 zJ`f{dv#e#}PY~2PJymL|>H#4xY;xWrRu_cCAY|44(JU82G{EPh01WMwiN6~2cRqP} z`K*|j7={1+wJ%@4u4QIswqs#q-yF727m}c&qtl$6oXq!@5f;%8+(QcLx4ZYIk4{{? z(F69zapqe4Ypb~X^z^hs0s>mT)coUlKJ_kWkl6KtP~El(s+Gs&yj(XXF3vUnf8H*0?$@th3=m9Do;@q+xci}( zdAh-)R$oHGo0HcAmKLg8V&!sk+2h=oh4aqKtwAl@TA#KaEq6z<4fRBGI{CAt#NuHO z*!#<@#i|tTtPl*~E8XEfj zZxfqCxg=Fh5GW3g+8!Pro<>7Mi>1Jk3s02ujx)z5e`p`_^($^x(FDGnL($YpcDk>^*b#-+N>ob4_4)+>wqFUSA=c`=* zzJKTG>51%1$6=0wj%9cof<0={NZZsCDgxqe(Sia3dBYPE6C`A0=yr<*7zAce%A-^v zV@RaG;DWk44>c{$VqEQZ_rL&WWo0F8a&+{>4rBa}9}Ij!yB-2wnA)eX@RAAOjtyJ?veb4m-V%V@5_0!(_iq^5wY0vzmyFd%)!@&cSs9gL zxKg2{e4oZtGXk`-Q=ODFZ(TtdNT(nzEnN!p=i=yiVl*wz70s!yqE})5Dzti~nOwoe zMetwyS4w`BBoUX2l$1r>+03m_%poRXqC~NT^YqBkUmvl__vY)}N<%{WtqBRk_mbB| zMHi>nVQ?CJ=95L)d3kw0HMrTuCoph!NaQu|^SC!L>9)`RZq+018dI?c4>&#h_s=3K zDvInTa+XuSAq-}_5K(EZU%k3>COy`lOxs zutpA;rAXMjL6ueu?a|-AKObUE;J2C|hGMA(mPWJZp4%?ZjEZk0IjIYYc z$vGI#czP2f?$uBZ`9sTU3)KMa2JjLZqRzxbM>}Qw`0*n=kx%MXqa5r(KYHV{{iW9h z1qH$r`P!9ga&nmy!YhCO{{8NDy8Cv%!Q%pM7i~z0nzNC=pPHPkPf%-__ZMB(#N-b) zJw3fzM1DaupYQqkxm;3G($=5;#1948*~}C1d9dC7VBpg*Tpe`KUOS6i-@#atFK1P- z@Apq0n4ENe_3mAEVL(8;8ZxpEjSZr3dtP63jTloH!^nw9)9p2W-u@ zi=F0`gGj)mlsEs5)|SP-f4U3l$W z^!)sMMO8PGnMeHmmHly#d-!;HdBauiEaHk=3?zxhoAf6LYHMm%bO^c|85#9JEiyXU zo+{0NH!c*vZ~rr(4}sn5y*k>81_uXGo<^}3Z<*oEL9xGvu^+-(&=L*mnyAOtw1zXAg3p3oMsc z9V`%wPOK^FoAVi|w2@F!X^xNn{_TN*fwcNX2F;^M=*}K+$;ilDLNuSkKJ_$EI16ou z9ltwKpesB_i}Ci|PSB@MRlnEQ-wmvOuKopR>Sc9JjcW)6_pOnLLT8aA11qb>qm7ZQ zm+LVlTqxu}FC@}VrOzDf?K5wV2E>_mwzrSnh?3*uFKl4m#W?lqke*P-D37&z0rWJ$ z#}8d_opCeG`T+Z>XJTX|{>wwna^b#X>U#ww(Ks_A++Cjw#C z$Pj8_$9LxD+jj7*ig~RqT)chx_Z!0*J+K>ECXr*Ilw3io2xVns%N=6uNl|0YH#0Lk z1&p+QygOH$+>cizEk11w8=qzQY4pSLsX7K01xF#|D_LFz`*CiWKfSR=)etRq5sb>`8@?TW82Do5K*Cl4o$VD%zKH&WOUZYJ ziiWoHyH!djYdAYQTRxbav%S$Rm6oC_OG|H5-9F&Ex9iutKDLBKU{hdtW}#oQxaWq* zRUgAsfBZ0Z{<}FwU}twkSY{}J`-qY<{>9kXST%e0dVX#%fC-w=JG^p*w8p2bC8?=C zhHu_*uXIH)z1kjUg9WN|{kusIb?vF}-lEec61F`J4UH*e=WX1Fqs7|%cUL9Tcxm6( z1ETeRs8{DKlK%+?URP9FYM1}>XSb1_`<|$L2vtr*Qqr}Oy81!Zi>>Ry2^Qi}V>?Gj zJ!(Qin+ow`Z??)069u9Tc55n3)BJ8wjM8D}R^;WehdHA#mKksX8OFJFlKwBOv$Jzh zHkd4S*wG8%Go_oG+l19ZL%pGj{X#wP-nBVcXvSI%`M>#hBmqr56ciS|XPb&jxotGbfUxq>(9xez%8sAuPUBPNTg_o zRAi|plOY%>QBdon_O{`LmBPmB!&A7HS|#ti&fT4_zxdVOZgN+ceD$4!x8McLT?-<9 zR4zKUfA>Vw0!lCkB6Rf8v(@0Kf<1ai6Kn`~s8HABJ6#1pE;pgJUV{c;7a&kDf@H(WxaK?e?b8Zl{ zL^0Mn?g(KL@s!9?L`VT>n)fxzrJ5ays(0LZJqZQAHlqxkr7|Hgv3_NFIfTc>BkBn% zx*os6af1<~{rT|*HtZ9FgRCuNmvIEBWdc_3tzpH7Dk-x&TODz3G*)ZXbCH8a6Uw9- z_sHzolP6C+#dFS{Vz($W$zYR4&sFd0ZFc}utHBeO^U9%PmT zN*HizO3EMy^|RNnrz3z&_5Qp3$E$0WJjpEpmC+jBWB~SgEr&|6G#@=r(;fS#PkYet zX+%Dm{-6!E_*Y>*RRZ*$E-X2@@q;$;k8}{Ato9+{O6L63By+%--VIxC#LNc~#u?~q zLUnbupaX9hNi)PVLJZ1Dpt=J^IN1>@6-J{XVc%^xiA@t)+GG#VVuS@!LlpzJA*-+C z13uH(0=Su9QC^;S1nZ-OH^XZ%Dw%e3|2c5iQkADqgTHrWa`r=5f3+8#h zg{{|zauq{%oQo57vaEdG{qNtu3}AwPdjGhjJ4u`lhTv0)JJ{OVzLi9usR2&pSqwtP zb6{s(e5uE`iG;ka-S>f1>E;x!6Jh#KgY@@W)azQrPmFv0JzCKG(>l|>xVLx6$;p|3 zHV;ps8e9B5KAyGPo-D4{akl$355G|S^=luh9z`0=e>s#89ta5H?63Vprmdd{i4nn= zotm0Dm_yM!2Ahqj!sWl5^}>xQ%`#K*@M6_JyE zem8ce(n_3&fB@;_STH0IQqTIAFJI&-a5A^@@wWdS9bI%xOhj936rBGuNV%jTx>{2> z*qN=i3oR@x#Q*#DGt>!!ETveUuL=yd=6f;IkDQ!NEnv0q7Tdp;&!h9wK}j89Cqy;- z^UIrD4CZ|u<~>?%V9d_W4jYhCCM=s{?pikaw766QNCcqekf_$b1g$|A0j>`=$Ji|M*FQG`;Qi?1BUvdfHcQh6K&lUP zk4#9YYh(`%fq1Y$+LRmBnyqozDgd2|T14XLCR-xS$u!uzuy8LHw!n?b)e00v5#1{5 z)K@^XMfp23($jeXeI3Qt)rm|1J6Ppc;P`v7*I0;s`?g>ku8y|~CuiYTz}w<79$i^b ztXvOQy8-}M(N@1DFWmSjvqwYt5yoH|0=e3GEmT-YC~U#=qVEVYlYA9v9iZlcT1dua zDJce(_MNjOfhLM}Hoy$&%1K_*C@c37-nnxp)J8z(l)ZQ4$aNYS=ELl_^cmg zvaqnuL>leZ`YqJehYD&8WbPLO!=7AU=?GOKl`@v^b=saJEHWKP%1`KBQYoAR3dIRo zK}k_j@%-m+l|lu%ncD%N>41LKm%2*BOt z4jVxK;lL;~9>%lx0V$H}-5O6fl1dpT8(YiP#)k5eiN}A`FdUFlmFk~9Q&Za?13F|V zD~tA$3`@{QlwOE#=!TYK2tqSZeD{^t4a=E1LRNKBP*9+wPbn$o=roTA3)?sEjo}t2 z{bZ0uWAgSf5S7=ETzy}g^>!PSCue5T0sfNP5lWj?#OlR$94K=` zC<}W%ABy%LwKc^;kQtyT^SE7{a8znW65`^1RfkY_XJumQ(11$hM@uDowGt{mj_?r} zDlvVU4H(j4#WPit=U-&rO_-f2dgHeTIN{W~=b_#(cF;BbT@W~q(%^!*s3ogAJUkSG z6;i$*(KR-UH4Ooqc>n(Wg2P|1ZMZOTaqA&%RU|w$#c7Cl05ZC$9M6}ZRzawvSW;TL za)v8x`884>qU45F$Q{u2HPo_AK5=|}{KjJYjc2BoyB)4BE<3{3i%3PmIR_}3DQs+P zUHG(OmxIH@Fu!X1ujJ(9%F@%N^*{@E0;%aWEUjYRYD!Ocx9iU8sxi#m)<` zZtjDqjEn~YCf$Q5VmWrUwugnFw{7$%3Ks*RM9qn6%73^|j*_5^af1aoW@&q8heKlL z2;?c1sIaj0ntb^}DWV{mBoH{O($el1kiOvJ<4Yod^25l)G!ZGBQdgH?4lDxn!k%(! zVps0%J;&a9#`76gyq<~S;cvHbaXsFfo6o*rFNSLh=#m(MvbOwD%Z)$@Vv+}8sQD8v z9ZEk!_t={*-*N^C{We<$WOpz2@fTj$Tg`_qseQHU!4*1!;7U(E`}ULJ#1fbsnL z>VgMV^5?s&k`{Nno8$Dt!op!B-#*03f>Y+v3N@ubQjswtUb8@_ z`W&Rwd!y-3L6}z8*I$_G>FIHqEP6vP3o0&LXYp}y%rhuw1)Y0wTb1%!A7 zwQE8B<;#~_AIg0Heu3Rx4}5l>#BvS;iOp3GXFK&Fj2W3OVcm7iBbaWYMi~3$TS6ziGUF zeZJG=gOUrwLH~2IKwG46hl(n}(8Po&1qh>i(JLvAL18pXC=#3?sK0=TO_V^Ui z#`a6T%b38V(~}duk$-$NJjS22AjP0&-+bS~NJx_W6a zRa(-c3z=zXyg+x$4y2+hoz~uU5LHgw+S+O>FeQ`3si_l-F@3G`2GJ`oZzA;+>`@67 zR8$&_EG_5DU*dL_8g~(ec6KThc!ne`;~O-_BU}*-e1=Pt9Y}v2dbT^ zv5Coicvx79TX{oiVQ8r8A}Dxu!Z(eeAiTG<+$9F&Dpq;+P^SthQ<$z&sKc%k+3oAs zuN|QJilxqHoE>jy5ss8#=xZ0iS5;F-vKP?x3RGyOu$0T zr=h~*XW`(L$Oa)!Sq(nx^Yg#rQ;dy|%XRhi zgZ zWaJ45)&6w;`epdu6o-XGF*i6kSlZaw7$-`rW*+=`FXN5;H+G~w;o(96f=j%C( zi6GemT(D=Omo20PNvRxaNrc(%0t%%N`H?rZ1XC3;()Rjg;m2U?=!H2-LEF_q$Rkuh zXR1}a4xHWpj^=1|_!)jVHsqq1kxZliM5jRd*4X$_()m|0r(K;3;EgjVTdfFSqER8@_7ZrG z8Nh1YRwQ{Qkdmpu%O>r?mNXnhPlaOe2s8+3^a~Fwy;rYZiNjMP0E1DPoZFR#hJ=J{ zrpt%UFIrbUO!KolLx__XODz~@a%(2m&JvXd@Vf2r3eCQKr&$0OJ(_*dyw`_x%2Xlq zyM6|4XX55oTSZqD^?;5}B2`TY6%}<_;`btmo#UWy+QNe=eCZN%5>P=ZZGq(1@%;Td zn0e8@9;fhg!>m2GK`pc~9=cGM;4tEq+UbtsB(rRNkBgp7`lpMazt^nqNvJ(RW( zng(ukUc4x0LzY!nCme#B!9x=oD6_U~@CR)y4bW>BpjU&!^5Atg%{(m|s1`l5JZezF zvT}0Ftc;B4=}3m>=R@qTvCTIx7vRD9eSLkrASBm*Gh%;L(kjZ%zHP6sZ7BMWjEIP+ z(y;Bb>~jp$R>;TVVO_?LiwH2ck#2nJkNb(=EqY;NV>`j?i-26nt3|oky+i`8&>N^n zQ{Wh7j#L8n7_PEeIS!J!{}WrNN!1CJ@Dq4Gl`x4LR8$6T;9>n6*x%n@(P210ghWMv z^>Ntr%kvj6=oOznr53h;SQ}G#^2CMf%`en|Vb!sT34V}#X1IBIOFwkqXJgxfsC3Q& z9;+%`q-BodBcavA(cAPO1;KGr8-AWcgxm)m#CkEXm7~^K(ki)m1&#h<3jG>HKmAlO zE|g=iio5uknL7)U9Zp&GbadS70AFiDX?!U+J%5*tnR&FVq9TMTqAS_6Z0QJ8c}z@9 z0Y(Of(XFAE?_C!26CkIoZf@2P=-6Ey2&=z%G1dxmT9Dr{Z;oW;#7=hMBrDs7XZeQD za#kP$CAP$3oh5=cSU;0_`TL_Tq0@FH_#K%F*`Rm@odHpCUYcLGyj%pN43Ma zE>>_G8387$xxRpphl7jDKaHCg#JZ|)3;n@OS^!P^2KDGdzYFOD5wOa=wcgm$W5^#? zLNl<{*swm@1}ib1{_Bv^(V<#=R0vw&2VqhmkikU6#DyBBu}_{pbrXW>7W+s$%tKOW z-4lOd{f0W$*2!tX0R%5gt39a4An^1qnS6dL1}}$kT&LWe-D<#9PJ-3;U5T+VP?K!hFvISlO-N$pAkaF-!mao@gulTkhfcFiLyT7Me~kB)YJn*rBTpqd5z{396kiN(1BcNH;$BDl!%}49L%>?M=KX~LRJ%U}Wb7b!H%~$}p095e z8Vm-iNZG*_TY{lg!{)JPGDkJ;FyNvENEJ0PI_f>(EGPEL2Ap|}?w+1#9x2J#elQN>7K}RII0c3aoHV>AF8eOzy#ozqlItikt{0{||ExHcM8g$O zbB|_O7n>@Lz}<1Xj{l;;*_x4+B@WQNc+t>DY25p*k=2>p4KibP45VpF0;(nwK~05PfI%X=QQotEjlR=}7snBaT7A!MR|xy8kRF zAp2cr>3ZzLi3ZhEl_<#GKk4LZNKQ#fX$MUEK22~q?7MG@+Yc!%G-s~|#R-h1(r&!I z`vYK%AmeyFx>L0lPA~t^#ntt?zs;p)GVGqTEg;E&gJo|>MIXq?$SQys8{m4g;VaaV zf(a~{xDh2m7nY?QZ}MDCZII*ZUwTa zYaaSYAYt1aQ>Pl0qEO(VM?-JhWAMc-yVy? z>yleQ7s2icz}{4Ii!aZxVhjv|0$L3od_6#cu)?odG!71bj{p?UZGN*GmH-;H2AQ;H zw0af+)xmGG06%&r@=kOF-$B|#P`a{#h|wW>9}M>Dwz{Crfcw!S@bGz9I#!rjS%2PR zWj)`8IKj!X+Eq1bmP#lqJ8lQFDg7#Ejl4v1I4m9lc&`5r0QMWubgaM}Jk-z?d^pwR z0IcrOJenxa9>ZENdBI^#UVGMx3fo0L3Ji4*GwZGp%lD8wo5(|+ zK+S#WNU1WN0hnX|cw;0G^u-TZsCh=za@zG^I0qY9ag9kr6wTa!a0A9q2`| zNQ#Z^Di_AmX-ZR#3|kkKfHYF}2b!svviGbax>nDjp6*10PE;Naog*wm^d-GZRzRH9+bPa9rko*2~GrFbD_disdPgHw>e55SJ5v$?;uCU*xX7O<*1I9aY99-^x3oj&QAP!dc(2M538 zUB?oRJIdE@^aOwzcsk7fOP!2eYcb*bchsN1eg$Gdk6coqUlS1h>nU*Z<)AU(;g?S` zhV?Mdx+*ID9uL3V4}1MHOoGn-H~X3|t}ZLAXkFc<&K7mTGL=D zF<%QNLZPVR_T(M#1L(jHIHh510Hc)#!!NMrcC3QXb51z~Ije&H z9XF6aw`$mtdDnr8 zsRW`$uCK4}UCmYeKMi5)9x_hrgQ+Pbdmg|=EnleWHMqYrx3Cbc1m!E*4@E{%Pp=^b zqJKbV@5(F!46i_FiUm?c-Jyv9&l#LLRn~4`L5$=?JbkXI$p;?Vf}p6V2OU}K_5hbd z1a8ng|L7%H_LoPXzx#|nVUehW9!9;t`};)h}go63FXI?+I~WiH_2#URbhg)l&U?S^+21@#e~wba^HGA!fFCYN^t9lMgs zM3VCc7VZSJBEMD=T|;2$!jOQ5p%tVy8)i5+Qvlo5bLs^Ucx}jo9Q4)mQ)bwmOCc>w zmoReoDV9p|SIbbRg`sK2HEb!1@((rPv#`|XXJ=D%$82nyaj>#ty-%I7rVFg~hBn0s*!-7m zt*terE(owv3<>w&{t^lkxzrR2UGxu$G+xX)s5Z@z(00Ma!%5wph8?xMz3na{B;-o& zOZOM&^V{rdND6%rKn26nI1VPo+zQmP_ruH1FWRSpIqQH@ACjA$t)L!%30+fi!oszW zp;_S`dyVf;un4~dY8!xN@DPkefd|mT7HNz1$;{Hy($U7|_NULEg$9$WTVFhX?#{-_ z>R1}Lm1r|TxIcAG7TTe@p?ag4|N8g(%8H#LSpGAv3Sj%pK#K4M9m3!>?z`XPZhv#xc-^vk+O)jKZf2iIe9jdaj57P4Tdcy@L z&v&6ur>b*e;&k*AA$?7v9I9wbOAB!nv|WFb_;wzJs;x%fbl}6IwfG-N7@klGPH#%V zFRV)xV<&ri5i(-p(n@in7oUQHaugL5E}z~H7l?bkDUavG38*Gpe0d!jq>4bmG=d_h zSa!HKTYcmRd_RN)3(Ycv8(EA&h&aW;5qaW)w3JjD4z${6=$l?0+)=f0auO$}rQL(h zR%H`OWmVmeAj9f|10P~sI;;OV0y2C~y3*F>=I~^`6IAOSQ4x`lcV=c+_PEekPu>Uu z^IOpCmEKh9m38{bm~sz8avKST&H*vUZ7to+qkZwtc_Io5=M`|{C?^xM34+@K2!mvp zRA>?amFHTRnGF@><{Fu9GN&TauvVj|M+!Lk>=pu$ni{~VYW4tMmtPBq^k=PfTIsz@ zXs=5A78Yi00Ug_~%Z#l+TSs$i(1s?#S6W)1O#N#1?sHM}20+}WC-nX;$jppFm^Qb! z50NDa4xppL8RRTCqvFZr%zL2JltaeTfs%FIv#)vgLpD4iK>wu`_$WEhESZ`v_jNe) zfgA7%ZC6*g8=9gZ0}HH6U0t0VIxy3pCSIXt zBQdQvxc_@*@Zm%40d&J{E4=n;G2n6mVRnX$gmP|!aZ`RXdkpoK<|bOT=U!9vleP z8@6FtCtqb{RaKqM0A#ELEyugww&1z<1}9bZ19E7^>O>s1K%DzX2oE13nI1c%wfeaU z4N=>Oz6KjI-bgIicP8JXqErk^u|+?lC#||) zUskh#^HoVWly>d(-VGhc5p!)FR)$TDkA$fCjP z#`7BdY(8Dk$5p)k`Dj*$tzVJ2zynx7U7j;&(}HH@+)eEA8fe8>263hd0!3Gvz<-HD z0B*7nA0O`rvSDL~I`q&<0=nSMNk;|fT)YVKr?BKN<>3LJ@ z>qT?HMVraMcfH^p#tUpogi5}3XPutwZTI&f$Vl-(@~J>clyTd93Vab!6+IDRDgo-7 znF3T4$FwvS2G^Uwe;3Om6&5og?D`F}uOZ=dys%YMWCovd9*hswwbfOmUn3d}QaHhF zp7#ha#SzfQG+By+J-xiF3>fg^Ewsi`(lu-{pF zRo!|f4=z9m6y$3e4t>N)1Tbu(A;k#%Om-r`^xp(v?2l-kbOo2iNF~F67k>dGLP^1X zKA5o)O2H+dMhisaBJ@z0eS=v#Md1e=c5?xv6@I5o^Z5N}GH6RUo1vr6AotUPT8aDa z^c2zVK)Q|B+1>rwrxXcV^f(#-@$G<M>g-P+C7R=1 zB+I-f3JT`*5nXgsZ!6_t<>jTMe0@7sdKT9Nh1K_HUqQ0-W6R3$?JdKHwm|gV?9v@V za`HU_a&ms4N%)hDBv{K>&=05MlX#`bq!eS#EG#?|k@$_90w-310q?@fCFNV9Z7O>| z;;@BF?}cnS00lRY`C`X4Pkx)7g$rSgilkqpDK`pWd@T6_fz(5A7=UDEw($gHjo597 zNW3O!QcQ$q%ELum{8Z9BI=B;-Yuy-maI=9}5nK6pFpbhuQi?zTNm%CKPkr2r_y*4V zx~V|g;5IRO0!;I+VOF4H!-IodPPvp~+IPMjmqf+?sp}&{tFsjSLS4NJbUO``HV(-A zcpzI@4~Y7b3KnthA%-nnY!LWhQA)}7)vH(*wqLNxwP6b#LwMi3{U0ZOCJlqq(w2Q7 z;5hO~^ji&!Wl?V-uUcCf2CJ)KP9RQ7xnfR!@Q#U@yoD;620B=5NPcFf$uUF=)uQsF zllFl4;TRg6*++Lbcd3z|Jn2%uyu9Ru8k&9~WG!85x6!ms~7&p?sp`h!e%2n`UA zVhHcwyEha<_sIcVW}^Rn?8-p$A@r^GM%}pwiRv#1FzFzseva9|zu0F)9P_$E9+d|~ zy0hT$&{o{!*;5u~=G7OPnjsmb&v2eP{_0PVhOS*ZEjy(MK=Py?rJlTlGn>o}i|ug% z0diVz-lXz&eEIlUz!ymx#fP3u^1-%*1i+XB>>!!V;?Ezfup~B_0rWT|vm9*t z5;!z?JG3Jd4z;x^h)+hHBIfjpg8S_~bib*6*lX!uMvdWEoa<|Kbz~x?5>8rDjqw(h zZA8!U+^54&A}}Z;XxhYpYT|nw=ijmvvT|^Sdt4E?gN_IDfWoBtKS5zZ97|vp-FXx5^AU;Ryy-K!^fW~kz?_PJrbZ#jX&rp0X+qbc zR5(T^<3jKKt)ZzY_NikEr$nkgt@Ol^uDKXr{e$9Dw5naIb z6_XLRpvDu1Z)kPJ<4n&{N{|`Oy>Tc#9207@Xwjc^ZWljHON&tt*#4!C7cZl!;g%#)s_-j$s^QD?_ z=aNMJf6j~^11&iLyMW&dhUnNXW_kj7sQO7~IiW zU_JdF6^&YUb$9oJETNpAo4b;AEhxZD|KNc`fD&V&95PO}BO#pfULq$Z)`$!XbC$m! zZk#=bhHV3Fn2m%%t-;LR$YT%TATs@>FE~GUXhLb_k20;UID=MKftEzCdn4EDAIbno$%rt+j zY8J1Su053iijXdTK9TJO-9Mic`hT$+>Wx-ac)5Ea9!Eia6`-#!lwut-xT3%(Mzz z2;MO}lult`!&J?G$iRV=$fHQE0;3T^f_!3ZYz`?W19Xc*GIDZG>F|qeM^Ykc*(Gv9 z!aLA#5exXVjf9kRV)@`qisEnabled()) { - map_settings_btn->setVisible(!hideBottomIcons); - main_layout->setAlignment(map_settings_btn, (rightHandDM ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignBottom); + map_settings_btn->setVisible(!hideBottomIcons && compass); + main_layout->setAlignment(map_settings_btn, (rightHandDM && !compass || !rightHandDM && compass ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignBottom); } } @@ -513,6 +513,9 @@ void AnnotatedCameraWidget::initializeFrogPilotWidgets() { QSpacerItem *spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); bottom_layout->addItem(spacer); + compass_img = new Compass(this); + bottom_layout->addWidget(compass_img, 0, Qt::AlignBottom | Qt::AlignRight); + map_settings_btn_bottom = new MapSettingsButton(this); bottom_layout->addWidget(map_settings_btn_bottom, 0, Qt::AlignBottom | Qt::AlignRight); @@ -546,6 +549,14 @@ void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &painter, const UISce drawStatusBar(painter); } + compass = scene.compass; + bool enableCompass = compass && !hideBottomIcons; + compass_img->setVisible(enableCompass); + if (enableCompass) { + compass_img->updateState(scene); + bottom_layout->setAlignment(compass_img, (rightHandDM ? Qt::AlignLeft : Qt::AlignRight)); + } + conditionalSpeed = scene.conditional_speed; conditionalSpeedLead = scene.conditional_speed_lead; conditionalStatus = scene.conditional_status; @@ -560,7 +571,7 @@ void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &painter, const UISce mapOpen = scene.map_open; map_settings_btn_bottom->setEnabled(map_settings_btn->isEnabled()); if (map_settings_btn_bottom->isEnabled()) { - map_settings_btn_bottom->setVisible(!hideBottomIcons); + map_settings_btn_bottom->setVisible(!hideBottomIcons && !compass); bottom_layout->setAlignment(map_settings_btn_bottom, (rightHandDM ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignBottom); } @@ -588,6 +599,103 @@ void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &painter, const UISce trafficModeActive = scene.traffic_mode_active; } +Compass::Compass(QWidget *parent) : QWidget(parent) { + setFixedSize(btn_size * 1.5, btn_size * 1.5); + + compassSize = btn_size; + circleOffset = compassSize / 2; + degreeLabelOffset = circleOffset + 25; + innerCompass = compassSize / 2; + + x = (btn_size * 1.5) / 2 + 20; + y = (btn_size * 1.5) / 2; + + compassInnerImg = loadPixmap("../frogpilot/assets/other_images/compass_inner.png", QSize(compassSize / 1.75, compassSize / 1.75)); + initializeStaticElements(); +} + +void Compass::initializeStaticElements() { + staticElements = QPixmap(size()); + staticElements.fill(Qt::transparent); + QPainter p(&staticElements); + + p.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + p.setPen(QPen(Qt::white, 2)); + p.setBrush(QColor(0, 0, 0, 100)); + + const int xOffset = x - circleOffset; + const int yOffset = y - circleOffset; + + p.drawEllipse(xOffset, yOffset, compassSize, compassSize); + p.setBrush(Qt::NoBrush); + const int innerOffset = innerCompass + 5; + p.drawEllipse(x - innerOffset, y - innerOffset, innerOffset * 2, innerOffset * 2); + p.drawEllipse(x - degreeLabelOffset, y - degreeLabelOffset, degreeLabelOffset * 2, degreeLabelOffset * 2); + + QPainterPath outerCircle, innerCircle; + outerCircle.addEllipse(x - degreeLabelOffset, y - degreeLabelOffset, degreeLabelOffset * 2, degreeLabelOffset * 2); + innerCircle.addEllipse(xOffset, yOffset, compassSize, compassSize); + p.fillPath(outerCircle.subtracted(innerCircle), Qt::black); +} + +void Compass::updateState(const UIScene &scene) { + if (bearingDeg != scene.bearing_deg) { + bearingDeg = (scene.bearing_deg + 360) % 360; + update(); + } +} + +void Compass::paintEvent(QPaintEvent *event) { + QPainter p(this); + p.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + p.drawPixmap(0, 0, staticElements); + p.translate(x, y); + p.rotate(bearingDeg); + p.drawPixmap(-compassInnerImg.width() / 2, -compassInnerImg.height() / 2, compassInnerImg); + p.resetTransform(); + + QFont font = InterFont(10, QFont::Normal); + const int halfCompassSize = compassSize / 2; + for (int i = 0; i < 360; i += 15) { + bool isBold = abs(i - bearingDeg) <= 7; + font.setWeight(isBold ? QFont::Bold : QFont::Normal); + p.setFont(font); + p.setPen(QPen(Qt::white, i % 90 == 0 ? 2 : 1)); + + p.save(); + p.translate(x, y); + p.rotate(i); + int lineLength = i % 90 == 0 ? 12 : 8; + p.drawLine(0, -(halfCompassSize - lineLength), 0, -halfCompassSize); + p.translate(0, -(halfCompassSize + 12)); + p.rotate(-i); + p.drawText(QRect(-20, -10, 40, 20), Qt::AlignCenter, QString::number(i)); + p.restore(); + } + + p.setFont(InterFont(20, QFont::Bold)); + const std::map, int, QColor>> directionInfo = { + {"N", {{292.5, 67.5}, Qt::AlignTop | Qt::AlignHCenter, Qt::white}}, + {"E", {{22.5, 157.5}, Qt::AlignRight | Qt::AlignVCenter, Qt::white}}, + {"S", {{112.5, 247.5}, Qt::AlignBottom | Qt::AlignHCenter, Qt::white}}, + {"W", {{202.5, 337.5}, Qt::AlignLeft | Qt::AlignVCenter, Qt::white}} + }; + const int directionOffset = 20; + + for (const auto &[direction, params] : directionInfo) { + const auto &[range, alignmentFlag, color] = params; + const auto &[minRange, maxRange] = range; + + bool isInRange = (minRange > maxRange) ? (bearingDeg >= minRange || bearingDeg <= maxRange) : (bearingDeg >= minRange && bearingDeg <= maxRange); + + QRect textRect(x - innerCompass + directionOffset, y - innerCompass + directionOffset, innerCompass * 2 - 2 * directionOffset, innerCompass * 2 - 2 * directionOffset); + + p.setOpacity(isInRange ? 1.0 : 0.2); + p.setPen(QPen(color)); + p.drawText(textRect, alignmentFlag, direction); + } +} + void AnnotatedCameraWidget::drawSLCConfirmation(QPainter &p) { p.save(); diff --git a/selfdrive/ui/qt/onroad/annotated_camera.h b/selfdrive/ui/qt/onroad/annotated_camera.h index be39dcb98..5297769c4 100644 --- a/selfdrive/ui/qt/onroad/annotated_camera.h +++ b/selfdrive/ui/qt/onroad/annotated_camera.h @@ -6,6 +6,29 @@ #include "selfdrive/ui/qt/onroad/buttons.h" #include "selfdrive/ui/qt/widgets/cameraview.h" +class Compass : public QWidget { + Q_OBJECT + +public: + explicit Compass(QWidget *parent = 0); + void updateState(const UIScene &scene); + +private: + void initializeStaticElements(); + void paintEvent(QPaintEvent *event) override; + + int bearingDeg; + int circleOffset; + int compassSize; + int degreeLabelOffset; + int innerCompass; + int x; + int y; + + QPixmap compassInnerImg; + QPixmap staticElements; +}; + class AnnotatedCameraWidget : public CameraWidget { Q_OBJECT @@ -51,11 +74,13 @@ private: // FrogPilot variables Params paramsMemory{"/dev/shm/params"}; + Compass *compass_img; DistanceButton *distance_btn; QHBoxLayout *bottom_layout; bool alwaysOnLateralActive; + bool compass; bool experimentalMode; bool mapOpen; bool onroadDistanceButton; diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index a099c797d..fb41cf948 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -247,6 +247,10 @@ static void update_state(UIState *s) { } if (sm.updated("liveLocationKalman")) { auto liveLocationKalman = sm["liveLocationKalman"].getLiveLocationKalman(); + auto orientation = liveLocationKalman.getCalibratedOrientationNED(); + if (orientation.getValid()) { + scene.bearing_deg = RAD2DEG(orientation.getValue()[2]); + } } if (sm.updated("liveTorqueParameters")) { auto liveTorqueParameters = sm["liveTorqueParameters"].getLiveTorqueParameters(); @@ -294,6 +298,9 @@ void ui_update_frogpilot_params(UIState *s, Params ¶ms) { scene.conditional_speed_lead = scene.conditional_experimental ? params.getInt("CESpeedLead") : 0; scene.show_cem_status_bar = scene.conditional_experimental && !params.getBool("HideCEMStatusBar"); + bool custom_onroad_ui = params.getBool("CustomUI"); + scene.compass = custom_onroad_ui && params.getBool("Compass"); + scene.disable_smoothing_mtsc = params.getBool("MTSCEnabled") && params.getBool("DisableMTSCSmoothing"); scene.disable_smoothing_vtsc = params.getBool("VisionTurnControl") && params.getBool("DisableVTSCSmoothing"); diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index b129e6265..2f6203426 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -123,6 +123,7 @@ typedef struct UIScene { // FrogPilot variables bool always_on_lateral_active; + bool compass; bool conditional_experimental; bool disable_smoothing_mtsc; bool disable_smoothing_vtsc; @@ -160,6 +161,7 @@ typedef struct UIScene { float unconfirmed_speed_limit; int alert_size; + int bearing_deg; int conditional_speed; int conditional_speed_lead; int conditional_status;