From e8f66abf1706231a1d737a4c578d541b390cbe3c Mon Sep 17 00:00:00 2001 From: tim <59872808+trhyder@users.noreply.github.com> Date: Fri, 20 Sep 2024 08:16:40 -0400 Subject: [PATCH] New and updated icons for CAD and BIM workspace * Updates to some icons, new icons added for CAD tools, and new layout for CAD tools * updates to icons * WIP changes to BIM Tool UI * Separated BimToolUI, added Draw wall icon Separated BimToolUI into AddElementUI and ModifySelectedUI classes, added Draw wall icon * Voids back in UI * WIP UI tools and icons partial support for draw wall tool added * 40d95c294 WIP UI tools and icons Minor tweaks * WIP on profile-axis-ui * WIP on profile-axis-ui * Added Merge, Split, Rotate, and Flip back into wall edit tools * Separators added * Restore commented out code in CAD workspace.py --------- Co-authored-by: tim Co-authored-by: Dion Moult --- .../bonsai/bim/data/icons/dm_3-point_arc.png | Bin 0 -> 597 bytes src/bonsai/bonsai/bim/data/icons/dm_add.png | Bin 997 -> 1046 bytes .../bonsai/bim/data/icons/dm_add_bend.png | Bin 0 -> 357 bytes .../bim/data/icons/dm_add_transition.png | Bin 0 -> 372 bytes .../bonsai/bim/data/icons/dm_add_void.png | Bin 956 -> 1026 bytes src/bonsai/bonsai/bim/data/icons/dm_apply.png | Bin 0 -> 508 bytes .../bonsai/bim/data/icons/dm_apply_axis.png | Bin 0 -> 398 bytes .../bim/data/icons/dm_apply_profile.png | Bin 0 -> 414 bytes src/bonsai/bonsai/bim/data/icons/dm_butt.png | Bin 176 -> 192 bytes .../bonsai/bim/data/icons/dm_centerline.png | Bin 329 -> 269 bytes .../bonsai/bim/data/icons/dm_centreline.png | Bin 329 -> 269 bytes .../bonsai/bim/data/icons/dm_circle.png | Bin 0 -> 880 bytes .../data/icons/dm_connect_mep_elements.png | Bin 0 -> 319 bytes src/bonsai/bonsai/bim/data/icons/dm_draw.png | Bin 0 -> 797 bytes .../bonsai/bim/data/icons/dm_edit_axis.png | Bin 183 -> 279 bytes .../bonsai/bim/data/icons/dm_edit_profile.png | Bin 191 -> 355 bytes .../bim/data/icons/dm_edit_railing_path.png | Bin 0 -> 392 bytes .../bim/data/icons/dm_edit_roof_path.png | Bin 0 -> 486 bytes .../bonsai/bim/data/icons/dm_exterior.png | Bin 248 -> 257 bytes .../bonsai/bim/data/icons/dm_fillet.png | Bin 0 -> 413 bytes src/bonsai/bonsai/bim/data/icons/dm_flip.png | Bin 405 -> 310 bytes .../bonsai/bim/data/icons/dm_interior.png | Bin 237 -> 251 bytes src/bonsai/bonsai/bim/data/icons/dm_join.png | Bin 0 -> 215 bytes src/bonsai/bonsai/bim/data/icons/dm_merge.png | Bin 1244 -> 1260 bytes .../bonsai/bim/data/icons/dm_mirror.png | Bin 495 -> 481 bytes src/bonsai/bonsai/bim/data/icons/dm_mitre.png | Bin 267 -> 278 bytes .../bonsai/bim/data/icons/dm_offset.png | Bin 0 -> 184 bytes .../icons/dm_perform_quantity_take-off.png | Bin 752 -> 448 bytes .../bonsai/bim/data/icons/dm_rectangle.png | Bin 0 -> 211 bytes .../bonsai/bim/data/icons/dm_rotate_90.png | Bin 1027 -> 985 bytes .../data/icons/dm_set_gable_roof_angle.png | Bin 0 -> 435 bytes src/bonsai/bonsai/bim/data/icons/dm_split.png | Bin 799 -> 819 bytes .../bonsai/bim/data/icons/dm_unjoin_walls.png | Bin 460 -> 485 bytes .../bonsai/bim/data/icons/lm_3-point_arc.png | Bin 0 -> 655 bytes src/bonsai/bonsai/bim/data/icons/lm_add.png | Bin 1028 -> 1102 bytes .../bonsai/bim/data/icons/lm_add_bend.png | Bin 0 -> 364 bytes .../bim/data/icons/lm_add_transition.png | Bin 0 -> 381 bytes .../bonsai/bim/data/icons/lm_add_void.png | Bin 1016 -> 1092 bytes src/bonsai/bonsai/bim/data/icons/lm_apply.png | Bin 0 -> 506 bytes .../bonsai/bim/data/icons/lm_apply_axis.png | Bin 0 -> 399 bytes .../bim/data/icons/lm_apply_profile.png | Bin 0 -> 415 bytes src/bonsai/bonsai/bim/data/icons/lm_butt.png | Bin 180 -> 200 bytes .../bonsai/bim/data/icons/lm_centerline.png | Bin 398 -> 307 bytes .../bonsai/bim/data/icons/lm_centreline.png | Bin 400 -> 307 bytes .../bonsai/bim/data/icons/lm_circle.png | Bin 0 -> 889 bytes .../data/icons/lm_connect_mep_elements.png | Bin 0 -> 378 bytes src/bonsai/bonsai/bim/data/icons/lm_draw.png | Bin 0 -> 874 bytes .../bonsai/bim/data/icons/lm_edit_axis.png | Bin 183 -> 273 bytes .../bonsai/bim/data/icons/lm_edit_profile.png | Bin 190 -> 362 bytes .../bim/data/icons/lm_edit_railing_path.png | Bin 0 -> 386 bytes .../bim/data/icons/lm_edit_roof_path.png | Bin 0 -> 534 bytes .../bonsai/bim/data/icons/lm_exterior.png | Bin 277 -> 294 bytes .../bonsai/bim/data/icons/lm_fillet.png | Bin 0 -> 415 bytes src/bonsai/bonsai/bim/data/icons/lm_flip.png | Bin 433 -> 409 bytes .../bonsai/bim/data/icons/lm_interior.png | Bin 267 -> 287 bytes src/bonsai/bonsai/bim/data/icons/lm_join.png | Bin 0 -> 217 bytes src/bonsai/bonsai/bim/data/icons/lm_merge.png | Bin 1494 -> 1473 bytes .../bonsai/bim/data/icons/lm_mirror.png | Bin 571 -> 537 bytes src/bonsai/bonsai/bim/data/icons/lm_mitre.png | Bin 270 -> 279 bytes .../bonsai/bim/data/icons/lm_offset.png | Bin 0 -> 190 bytes .../icons/lm_perform_quantity_take-off.png | Bin 1113 -> 659 bytes .../bonsai/bim/data/icons/lm_rectangle.png | Bin 0 -> 212 bytes .../bonsai/bim/data/icons/lm_rotate_90.png | Bin 1532 -> 1461 bytes .../data/icons/lm_set_gable_roof_angle.png | Bin 0 -> 444 bytes src/bonsai/bonsai/bim/data/icons/lm_split.png | Bin 925 -> 886 bytes .../bonsai/bim/data/icons/lm_unjoin_walls.png | Bin 493 -> 495 bytes src/bonsai/bonsai/bim/module/cad/__init__.py | 4 + src/bonsai/bonsai/bim/module/cad/workspace.py | 256 ++-- .../bonsai/bim/module/model/workspace.py | 1071 +++++++--------- src/bonsai/scripts/bonsai_icons.svg | 1074 ++++++++++++----- 70 files changed, 1342 insertions(+), 1063 deletions(-) create mode 100644 src/bonsai/bonsai/bim/data/icons/dm_3-point_arc.png create mode 100644 src/bonsai/bonsai/bim/data/icons/dm_add_bend.png create mode 100644 src/bonsai/bonsai/bim/data/icons/dm_add_transition.png create mode 100644 src/bonsai/bonsai/bim/data/icons/dm_apply.png create mode 100644 src/bonsai/bonsai/bim/data/icons/dm_apply_axis.png create mode 100644 src/bonsai/bonsai/bim/data/icons/dm_apply_profile.png create mode 100644 src/bonsai/bonsai/bim/data/icons/dm_circle.png create mode 100644 src/bonsai/bonsai/bim/data/icons/dm_connect_mep_elements.png create mode 100644 src/bonsai/bonsai/bim/data/icons/dm_draw.png create mode 100644 src/bonsai/bonsai/bim/data/icons/dm_edit_railing_path.png create mode 100644 src/bonsai/bonsai/bim/data/icons/dm_edit_roof_path.png create mode 100644 src/bonsai/bonsai/bim/data/icons/dm_fillet.png create mode 100644 src/bonsai/bonsai/bim/data/icons/dm_join.png create mode 100644 src/bonsai/bonsai/bim/data/icons/dm_offset.png create mode 100644 src/bonsai/bonsai/bim/data/icons/dm_rectangle.png create mode 100644 src/bonsai/bonsai/bim/data/icons/dm_set_gable_roof_angle.png create mode 100644 src/bonsai/bonsai/bim/data/icons/lm_3-point_arc.png create mode 100644 src/bonsai/bonsai/bim/data/icons/lm_add_bend.png create mode 100644 src/bonsai/bonsai/bim/data/icons/lm_add_transition.png create mode 100644 src/bonsai/bonsai/bim/data/icons/lm_apply.png create mode 100644 src/bonsai/bonsai/bim/data/icons/lm_apply_axis.png create mode 100644 src/bonsai/bonsai/bim/data/icons/lm_apply_profile.png create mode 100644 src/bonsai/bonsai/bim/data/icons/lm_circle.png create mode 100644 src/bonsai/bonsai/bim/data/icons/lm_connect_mep_elements.png create mode 100644 src/bonsai/bonsai/bim/data/icons/lm_draw.png create mode 100644 src/bonsai/bonsai/bim/data/icons/lm_edit_railing_path.png create mode 100644 src/bonsai/bonsai/bim/data/icons/lm_edit_roof_path.png create mode 100644 src/bonsai/bonsai/bim/data/icons/lm_fillet.png create mode 100644 src/bonsai/bonsai/bim/data/icons/lm_join.png create mode 100644 src/bonsai/bonsai/bim/data/icons/lm_offset.png create mode 100644 src/bonsai/bonsai/bim/data/icons/lm_rectangle.png create mode 100644 src/bonsai/bonsai/bim/data/icons/lm_set_gable_roof_angle.png diff --git a/src/bonsai/bonsai/bim/data/icons/dm_3-point_arc.png b/src/bonsai/bonsai/bim/data/icons/dm_3-point_arc.png new file mode 100644 index 0000000000000000000000000000000000000000..2344f2485117f9d58a4a77203d9e347a52887583 GIT binary patch literal 597 zcmV-b0;>IqP)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10pdwS zK~z|U?Uy}k6j2n0pR?mEtB8#swM8Hhl2kUb5=4ak7ZytJGaVMfdurjS^Zx>53 zpd=A1o6VP{MOqtEM0b;YEp~#F@$Swt>)i$Mg$u)b&z$>k=iHAmNeokkp^grmNjzsw zh4}^m_y}yKycFWj18VveYLgy+~Nc&>Gjo z@|Se1pBk84ehJhQPJjw`U7=n8goNqIDbu)PpJ&2>0JJj~SAlN{=L*Hk*9Qimv04MM znzE+`1`uPcr;I;tR$f-Y$>5f1 literal 0 HcmV?d00001 diff --git a/src/bonsai/bonsai/bim/data/icons/dm_add.png b/src/bonsai/bonsai/bim/data/icons/dm_add.png index c6c89c7d1511ca698f59d7e83b2bea443bc461bb..7c1371fe4f2557dc51a9ffcfd0ae217424d6baa8 100644 GIT binary patch delta 966 zcmV;%13CQV2bKtsUVo!WL_t(og~gX$Xxmj7#((cgW@GVU>_V{>MYPgMTRP`O!3#g; zo$JJ*H_{DPl%>os8jxv7&Yi ztX<8T=Dc1cS<@_OlQh8x4&ndiIq&o2f6jT&8&OsMPq=fP=zry#GH~qyJO)>zkJ_Je z$s^gmfhPKwyV31}Gy+Aja2V*exOcX*_HscCI5_4i)pekL;I=fhE5NSb34hqjGm*R* z@Y44%NC!}zky|)xkR~5b9$S)uZ5+PCV%`;~10+N7LpIK_+{XraZ%gC>#D%s@4WuK?lz zB?{ZGQaNuQT{j?s4Y%(J9DeNB=*cRe5tz)Q__TmCcI9Gq+t|>fGVGMxU;yhbLTDcy zoEUBk92pN|%Ygu3QzXn33UbKcUgG7>%u zx@{rxOn(f-u>z#y^W#H<{>~$(1bd&tXF&W62VK~e{;|`2fioi~5a|JK%_MQQw_!O( zkNUd?g1B7`uWhP5XfWa*`|&)McMvvYnJ?$-crCM$$J@L5o~7na16=*vBq^u?Zy7_9+L<&bX| zaDPw2*cXd`l@xz9J|S&WDvo0)y-t~Vy=@;+fgriR&{gll-75@g_TN#LyPf& zZ?R^TRFg)wdQMjSI$YO1RinQp>lY>3#J zR&9EAk+#N=H%-!1|5tg>bKY})@AGkT9#K`^r;OpeT*{?!rGH!+_s#(Xq>P4qA$V5^ zc9d~`g@B|KE7>L3j4#JP_Je*AHRM;c*hAj0BLc9Ib03ew_zqZu`OGQEnGkHMVA12W z066iEn!w~D;CtZ1glOS~z@D)@+wG#DCuM+A&My#jAMijc;uxsa}=kscUQoT?+h} z#^iqstUGk4rCD0P%`yeRf}P&JdmeA2?beO<9D!{>_b?K_66C;x&gawIftzExmAe=^ z%^V=Cv52~wZ{Kfq*ZTel(KA)yB#>;)zNlET-2s17!+)a=Kcbbu_-Lni>ug0izPIL= z-_THTsUzQ9_)VE;qm%1??3!+x}xR4OW-x&eG=^Y%+uD=-PQBr zcZ3aqC4Z5?$sV;25Op;+U0aIEmQ9YYX8=^RwOTfA!2ISAU_&86J>ZBLiTO2DSb06)>gWlEa%B zu2TE%-l*Kd0o`9j>5OsZEcz=+e5scd7m6+~S?1Mu(Q*u9P-%|G2k_#-obP^=Jf zPk(_r>n@<9KLsrTp2zbA@(33j2k;G(Y(fHeg&~ijI;vl5;qm=wC>zC#$(Ibc1N8_t z&2{xg*iAta-e~GL`2=uEZGji15||U^;Rh{N`_9OV$;|%brqD^>i~s}?YF8{njWNu& r0vg1=|wZw|w;e>`uhd|NcwMol&-MRCwuH5|Oxvo`7 zJxhc+9FFbUWomPP8HkFmai&PBNiBC|`EyYG1B0+b*xDx12|GAc-#xJuyFK0aD%+0? z`}(N2lS)eO#;=#T+K?l|e8YS literal 0 HcmV?d00001 diff --git a/src/bonsai/bonsai/bim/data/icons/dm_add_transition.png b/src/bonsai/bonsai/bim/data/icons/dm_add_transition.png new file mode 100644 index 0000000000000000000000000000000000000000..6f020d1dfa7778f27727f2ea9aa4ebf31b5262c0 GIT binary patch literal 372 zcmV-)0gL{LP)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10Rc%w zK~z|U?U%7?0znXlzd7$L0=_^Bdk;@i33&+Z@&-a)z%EC^!CD9w7B<@E5%L^CiICn` zus!3XNRU7Z?{*I_ko{p{n}6nmo!OlgRi#b^wPyDKKv-2ES!dUT`5d?d(8OE@K*$2N z`G}Ai&?@V90+9EI8;-IB`nEBw0OU2hqIq}(83PU5OkmJVpj0<`Ut_$NT2n@G0l z_lBaDLaMzDJ%9)B03N^tcz}Sf_wDS`#E2$FP=+YGDgjDKd*+2aib1Bphbw@%d`wOw znf@NhiVmetHK6fiLpQnw?t#5c4Cf+Lo{pNB6^E#U@?6&MbOcrCPqg9x2KWTz7(7>N SGdrpP0000tn zV|{rczr+ki>}KV+6uQpy`o&)>bR<8FR4egnvZ>CtMe~pkW%CR?w=#K7`5) zncLN1QIsQbjrI)n|tA2zsyBGljP}eL%{xfPcgqOK$g>>*o^hD;K<4ByhIs&R5~f&9Op$n54MVttCbZtCu}I5U1dIxW z_v;t)9pD585BgZE@O`phb}6s{!^8kSBr+rHOMmG%UINsx7dw|JxD5=%HexbByi65g z0FBHAsyxiO08zHoF5wjMIT4^Ndd;t0#ATo&x*v);3?I`Fgs|>#nV^|S&SPs!)Lxxr z|LQRTXKJ{GK@o`^ZrqXZqIjwacxtG|U4e;sx@S0Y^Rt=z|y=%gGGY) zNOR}Tad9VhGUiTZCKLUy=6-X&^Z(uNbfn^UHGg=u|lOG{MOLL!KPo?t-{9$V>5)_1L`9G@^&!F?zr%mkls8a zfTXzn;}!#fPSy^Luo3ti_zy4@S{_-f3yt#FoTLvp4isdPIJ>Q;EC&3S`q``fd>Ytk zPsjB^ihot;1WL2YNA6$45`iE5KT{PPq$*c)nB!q1@RGwfSb=jXZQi^Cq>*|n@c|y} zI^ldv&+`Q^62{Hgi}6Cbz;)#1WvqhH>E7d*AX^ zTxdkHE=W!RE0UhS#$JSnfq0_rUUG-DC7HVww?Lyhmt+CA3lRLOw2N?e>W^hZ9I38m z0{bNk0c)lNX?H-=`{`mu_yGH`@d6+AEl0@Ut}yF140pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10f|XO zK~z|U?UubS192F~KhL!#5w;RGF^EK4%wji~CH@4Hw?tEIDxxYRB$)gSVr4@70fOtI zF^ECLW)Kq9J_c>lo?czgOAYrOpS$NipL>4y@)K31kue%d_XIovPvAcY_n(JhUn_8kkz-ndUPi9>w88=7;sVlIrhhNC-#(ld5_)hE!`I}D6| z^bv0rTJ0pnjE8LDk(KFMa9aV5EN4-TTFRd!#Egd! zIdTgJkXkz;$s}fh4NLhML&qrSJs2lIr$v94aC*`S{7ZqN#5}NODL(-(Kzk+I`JFo! zs3B1q7jVY{MTu(5QKHv1_VPIX5^6ZL30NP@(yQhQUY9^oVjY+R-hezRt8T_#9>+^i y(R9Ahj$t(EKpF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10UJp~ zK~z|U?Uyl5!$1&4|0m`e6fPq;K`1~nhoH$dLS$Qt1Vy+8O%9O)3MeR(jVLK{4u?+> zQH1r_EI~3-cBF0oXlI^Qf7V_~LYHyWX?FmLm7pu^qaiPKdi%5^t;!2XkJc8Q-7yGY zyNfR%o_)Pq*K5hWuj3rl!0sW{E-Z6lQB17-PZt)&1j}4jzH<*mfMyfcMB{Y8cKa^? zcm-DPA;s#;R1ZMnPtsR9^d|i46X@In5g-CYfCvx)OaQtvqZ_CDHlQ1)bY&I{0B8;+ z!+im^H8i)AlTN{prr*gl{(|*z~Ub8nhoJtpGp< s&>x|X<5vLdZ(0gy+EUNwJNVb}CuJ?A{v%FbnE(I)07*qoM6N<$f&eO=5dZ)H literal 0 HcmV?d00001 diff --git a/src/bonsai/bonsai/bim/data/icons/dm_apply_profile.png b/src/bonsai/bonsai/bim/data/icons/dm_apply_profile.png new file mode 100644 index 0000000000000000000000000000000000000000..2d3a40e104d924ab3c345aed84c89f7a20b5081f GIT binary patch literal 414 zcmV;P0b%}$P)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10V_#F zK~z|U?Uyl1#6T2=zZc^X?8Q=8uwB-|B4@BuwDbg`lLZSGwDJgwjW@8du(7c;BepAh z0WYwdXR%IXoh;c9LJH@HK>p0j`#-{Cl8CC(rJ_#Lf!LjhO~=4yd6^z-Z**6uvAhrW zP<>vuuDwfH0QivwdGyO8TPJ*;Fr7er?O$gX(ZR1*MO7g)V={B=d|L-^=>?fl=R2Q) z0PuGRJ&8>lZ4_?Ws{wKjAo^+&wvuFe5AjQ3t9HV+pFrm`5C8%|00;m9pa2j%OvIjT zwH+w-yf4-qc>thKXnfc<07UyBH_lal4^+L)8soItJOHes#TgJCh&88P)!OVqx34C3 z8q0^-VJ0UfOa0{lkborKE&2K@Pz!*Zjd{NkDW3)CzyCY_20+B3$TZjm3;+NC07*qo IM6N<$f_^2ZrvLx| literal 0 HcmV?d00001 diff --git a/src/bonsai/bonsai/bim/data/icons/dm_butt.png b/src/bonsai/bonsai/bim/data/icons/dm_butt.png index 615187620f52506a7cc77e0460308e1bf65f7c87..8ba6e70730670e26ea63a29637965587f16d89df 100644 GIT binary patch delta 105 zcmdnMcz|(2T#~P+i(^Pc>)Y#&ybKCF&KLRYHJ)@i@rd$IVd2})v~Bv_U7OY#ENAwb z6t&BimjMD6Y;lX>@OxzUk{Qf@!0!aZgu2mvv4FO#oir BA`1Wj delta 89 zcmX@WxPfs(T#&7&i(^Pc>)UIFf(!;6ERG#-cl0PKJ>s5a%ecYW^}J83E3@A&TV4h* lSn&Gp4@Hlt{SY3*0rr+sw)Pu+_kM!}JYD@<);T3K0RUz89s~dY diff --git a/src/bonsai/bonsai/bim/data/icons/dm_centerline.png b/src/bonsai/bonsai/bim/data/icons/dm_centerline.png index 9f52ff498572bf10a918a5b899a5cb27d0bb555f..60ce5f0ce22eec54c8376cc649af75092127cb24 100644 GIT binary patch delta 183 zcmV;o07(DI0*wNYUVoZNL_t(og=75x|33pZz<9X^tHgiZ7DgBoT?^y2Y6FJ9jOVdv zyZB$X#Q>XmOjvEFBN%W8uu!1aWS|{zmKqKi(orxAjh{wu2c%NN0RtWdqYfBw2T+m- z&keK#jH%&(0gr+KcK{_(fYBZx$B+RJfl&vHI)IW~kUr23KrnBRkOhWx6ri_&$O;ik l959m_4jAw#7q7E~qC;}`${002ovPDHLkV1hW9M&~?*Jpmc}E}!BYb`RUuWoY`)2R33>~V09Hhz3Gg=sfB@jyh%)M(FV_UfTQ=-pC;a6_ t6bUZ}N-cnxhMy6Tc3sGO2?9U>cmv8vDraIXmOjvEFBN%W8uu!1aWS|{zmKqKi(orxAjh{wu2c%NN0RtWdqYfBw2T+m- z&keK#jH%&(0gr+KcK{_(fYBZx$B+RJfl&vHI)IW~kUr23KrnBRkOhWx6ri_&$O;ik l959m_4jAw#7q7E~qC;}`${002ovPDHLkV1hW9M&~?*Jpmc}E}!BYb`RUuWoY`)2R33>~V09Hhz3Gg=sfB@jyh%)M(FV_UfTQ=-pC;a6_ t6bUZ}N-cnxhMy6Tc3sGO2?9U>cmv8vDraIpF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10{uxu zK~z|U#h1%ZQ&AAce{(75USb4sp%OKwtqML9Npz#jRtRkTHxR_OE@(?l&_9D6qIIVW z;}aiI5n7F!h%OK|=*mPE_@0?0#R$aH!5(pw>FZH4l2liB1_X7|mkQ`T5Cs;*)|AR9yZ$N%8wNfwp;i*;N zDlb5+&bkS}RZA%>uk#cIO^$+EFisNSJeO9ttDOQx@A*fhuTcSRa^fOM>ohD+z!XEZQ!-^2AE zn$oYR^p$H$%l?Y>K;jW9$3dRdOs(>mT<&l=a;WxUG85@WNCJDSq<)7;ME&uJO2CQ& z4Dj^jLs)hgr3;}2*a0Z{r`bHB53oo(&v{)1{0rOg*YO{5$Sjjx#2!8X0000yKl6e`6Yy)oyHaUBaZZ;h@C)ie07; zdMbM*9tw1ORB~7=R!y8eKgxlrF6)SO-0WGK;}759+~e}iHmNtgCZRId@f2swq4d%p zBKx;9m2mlUMwD{;b4n!LH=FarxneC_ta#e~wuOJVxfU?Ill#E_KtMG$W6S9)Kz}iK My85}Sb4q9e0H-c_Gynhq literal 0 HcmV?d00001 diff --git a/src/bonsai/bonsai/bim/data/icons/dm_draw.png b/src/bonsai/bonsai/bim/data/icons/dm_draw.png new file mode 100644 index 0000000000000000000000000000000000000000..84d60b656bbaa33541dc9ceaf9de6a4cf1360914 GIT binary patch literal 797 zcmV+&1LFLNP)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10;)+w zK~z|U#h1%Z6Hye#f2Z;gSE6n)QA{AFQ%qdAHu_Jnalt|p79d0s=oBn{1i|<~<0FQZ zCdU5(m8}aCWk%vc*|-#yXk5@5j*HF=L+Q}zw4`5hb2DeoIp3Fa?wxx_RF!5T)>L+& zWj~V2irJ6w95~>Zz9Dj6z!&o$B6K0b(ZyPniO~kip0T#4<-g?^Gb)77^SIMHo~g^VG{%%UK{R$?J&Bg>?T&>(&MB&HoTY zuqW?A%+h#faYxRu0>n12q0EO?>xw9}UevSG>w$U02oO6tfl9tc@)DE}w6vbn)6<1Y zud5zLVwqzQk-XIN#UOI1vIxaW-T-bzCzoM0vJdxi3KUQxm)VHq0OK0ZRQW6i2oTFm z*GI17YGmht4xke_6k32-sTp7@Iyn<+j*oM|<3nFw(nd}}~^Yl>7!tC5{4v(5nBzVXPh zzbdm2xdFaiAmJ+@GOtGFW`#HF&Tz5e%)f!PN~wGmkZ=`HZfiU<7Rq<~&Jc8W@KqqD zJ;9rW0`LWRsR?hvH3Z&qES9OjFG{6aLuD1wPY_$9W5d={3!4%09{~;ne}M0M3TPIZ b{&)NzbBOz7T{QP|00000NkvXXu0mjfl5JqY literal 0 HcmV?d00001 diff --git a/src/bonsai/bonsai/bim/data/icons/dm_edit_axis.png b/src/bonsai/bonsai/bim/data/icons/dm_edit_axis.png index e80452746074123edff81e847dc612affcb66958..e8700e5dd79d26f75152bbb79932d9cd51901bba 100644 GIT binary patch delta 193 zcmV;y06zb>0ha=hUVo%XL_t(oh3%Hn3Bxc9MDI%0NjpL^SPX`CFlU6u11aALfkYOq zXwiKNTYkugB#H<;)oBj1PM2tL(D|WAd34k>f)>JGxRKw^% z599qG|AGJrfWH8+_JOw*Ks^i2He=033^Z4OGX!ND!U)Kq%{tTuASiNw=FR&{${^GL v?j{U?HV+UZmz4Lh?*TxD7Q-FMeJ5N2zwRe6BGCZs00000NkvXXu0mjfARbOx delta 96 zcmbQvw4HH6T%?Pqi(^Pc>)Uf1c^MQ0oGu=2ymO)A7;h{`_Gxb&dsSAOn|)a&Zf!R6 mFGanxULC#m&p%5BG|<59zm&`N{paFxkc6kJpUXO@geCywVIegD diff --git a/src/bonsai/bonsai/bim/data/icons/dm_edit_profile.png b/src/bonsai/bonsai/bim/data/icons/dm_edit_profile.png index 458cae080c3c4e22a1018ba8a9de4bf1dd674272..81e28320b7e3c803571246eea853a1897c3cf4d1 100644 GIT binary patch delta 270 zcmV+p0rCF70pkLYUVrdOL_t(oh3%I?4uUWcMduT4W4dCA8xy>di3^bsW$BGHaia&& z-iL8RA`K{m>ClDz6=CSiYo{f@U}g%bSR*NHE}9ja3bAAWn+iU_`t)7_#+^Vnw=yjW zoN5(!0f;Vaj%7QD=e{JO$pAhIHa`!36h#|e93EoO?Yx8b38Gvt%`Edn51(N{4771*T^u<(K&{chE9y)Y!ac^MRVm>qi}+Bp}dI7w*mFz6Z`UGDRE@>Dgi&8|j% zWo^tqm;&-nuW;5swB=Xfhi_~Q2q3UtlEFNI+0K#Urk%mv6CepsS3j3^P6pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10ToF^ zK~z|U?bfkM!%!5)@&7qqU%b*o=QDfC0Eitbo4~2JXx0ac14<@;f-Oj2en0CFq^{k60BQPc z>nD%-N4Yup0v6?NHzLs`?-bEblI+PgdhhcCyu(>0ao1Bj1~}~lJJka+c|)>8pzZC5 zz5*9zmlp$weOPqdoo9GCz=9i2Zk|u8uEjZk-GiL{9P+=|s8)bQy%A8|l1#>vkIeL# m#Q+k1N&_GO0w4gI27CiP*h`%&cKvVw0000@P)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10dq-2 zK~z|U-Ivcw!$1&+zbSni$w3qm+DoCnPbxw!MkzJW9>n-Q3SJZh5z)Ml>cJJ)e>741_?86tJ<2zv6GR=S{`AK{L(VmnS<3_7U)c@ZE zfFa0mO}_#dMFXhsb1AdOs%}tK&4Kf(d(0iCe)YnZ_82oMtISuO&ec-U&dAmX*;RJZ8oWHZ2 zi6DgnEP!L+71#%EfuJKh1fag)j;i-G6vB56tXl0pabhEYwyhQnVFj=RnIH%YfKP<; zh7eV?B&ELlypS?W9X18*NvUs;83Cy!(T$yyI&E$*DGk*BO-ilTv$#pA!KO7S4VBZT c?D~5C4JROw6}Lwb&Hw-a07*qoM6N<$f=MCI^Z)<= literal 0 HcmV?d00001 diff --git a/src/bonsai/bonsai/bim/data/icons/dm_exterior.png b/src/bonsai/bonsai/bim/data/icons/dm_exterior.png index 19533f7ce9e9abb21921e2fccd9e016638da2758..c436505b36f5e668353695a798ffcf32f3739597 100644 GIT binary patch delta 171 zcmV;c0960@0f7RLUVn~BL_t(oh3(eC34kyR1<*uX$CIw4F5%H7bSHappm-?~f(WAK z_mF=oEtE&ULRds7=Un745fSf`-G3BmX1+VG-dz4g;DB1KJ2YTJ0E(($HD`l2php1U z6$Ahd&>vw!16l+CUI84SE4Z8o{to;F0uTWG7mR3t9Y|bKR3rs<0&;l~4OkEWcm)9n ZzyX925L9vA+SdR8002ovPDHLkV1mD-KxF^` delta 162 zcmV;T0A2rq0{8)tUVnv2L_t(oh3(d{4S+BVMA0AQb*Nw^UORJu_j4%P)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10V+vE zK~z|U?UcPr13?r<&t0O8b~cK3f`x<>T3DsX6Qt5dQOObnmsG*WNM#^e`Urwx;ieQD z!52`Ffcsg5fIG!z*IhQv-0sdWbI!fVn$94zLGo z6xyfgGc?SId#A`E;GodrvH}+O_faweJ1GlZMD8Q7Y1lz_3$|4iJhy;N!w!i(0Bg?F z3^4)SLGTRU?`jRdiU=${f_dBZ2so8%jDmBuqPHSIH!?e5n4#XtY5NbR3pEEfWx%-v zh{H1=ec>vEL&irmub%7lsK!;oGX;plW76+@h6R^Z!m|ZL*U2YpMz8e=U~OmMzH0aq z0qv&J#GYyvU^|tqH$LhX*!-L5@anT}fm*@~cmXfq1-!s7#28eB+YYSv00000NkvXX Hu0mjfBnhnn literal 0 HcmV?d00001 diff --git a/src/bonsai/bonsai/bim/data/icons/dm_flip.png b/src/bonsai/bonsai/bim/data/icons/dm_flip.png index 4a108ee2fb89739a17852dea34e1e1827f769db3..55cc60155573f8be25707e4be28ce584d94d1333 100644 GIT binary patch delta 225 zcmV<703QF91GWN?U4H<>Nkl2H@-+$izHw1wuic z5Oexp03K+8HP96V=wiXhNB&#zS~00000NkvXXu0mjfWW!#t delta 321 zcmV-H0lxmW0+j=hU4H>0Nkl=X3c{oNc&;fw9@tht=i&$tYyqmjT`|OO zyay8Y1ksNlGJshTqwx%27DO^G08Bz;<9|Yc2_cFr04jtyt^|k> zs<;7=M5yCNz#t-y-%H~Fj^k6XApl$x?p{v4_?z(N0I;&h0r&k0oZ^l45q5wJKHR?U zqD>9#+q3u;*Z`OD#){AbF5{CBI>30`jF1EJaT7uYkjK@CJU|~;A<_VQT#Oh8Opl8Y z!+`nXDFiU|F*=$*9wT?y*KJEYj delta 151 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 diff --git a/src/bonsai/bonsai/bim/data/icons/dm_mirror.png b/src/bonsai/bonsai/bim/data/icons/dm_mirror.png index fb894b589885161c51445cd3bc5e7c35b7abf1b2..826c392b85ffa035cdb12154e281fd76e7be8854 100644 GIT binary patch delta 397 zcmV;80doHD1K|UZUVm;$L_t(oh2@vQPQpMGMel9=9B}0~Y2rfTN~J?6LRD7A#FZaq zV&Pxd`CQZl7-l;2o)j0}WEK?q&gmPbol-<#jDI36MG7nZP$}5B%Rl5-1&BAU+?CYl3Buz706^4i%Cb**w*UZ;b1t3h3!WBeGc!sg z;kg1o66M%C_TddR9Eh!gcqTHOKO}yW4F~iOqbXNqS8)~GA^-?0F2|u8l^Sjn004fi z(cXx*$J$a#FMmL(WKd57-z)&!^(zpEn33g)lVWhiq>zmQ-w!~%%4&U}zcBZ2^ rGCUyh=_(O{VfeAssQB;Sj=uql_w(YQq`Q;=0000lR zJM`~IEFV8%Gc@PZ55O0n2`UgVzniNgo0lQ3V{iAju2`w>1C&002ovPDHLk FV1n$nu=oG~ diff --git a/src/bonsai/bonsai/bim/data/icons/dm_mitre.png b/src/bonsai/bonsai/bim/data/icons/dm_mitre.png index 552906a9922d4cda4d22c26029fb692f392d71a1..3b220db2f60dee3756e28f95330c5d3528cdafe2 100644 GIT binary patch delta 169 zcmV;a09OBt0+s@hUKXQCL_t(og=75x|33qTfLsF@85kHCk#8G+)F7BA6Lc_9(8!)F zyBVku0!dHS+0TD}?jX7PsS*X0ID%?vfLupVH4&2I2&(4-QXN5)Y)FbDXjTpo>j;__ zM8r6P<|QE^N6@7x#^(sS)djd5LD$L%nmIfH{Jbk>w}=pnF!ImfCje00000NkvXXu0mjfFU?5Y diff --git a/src/bonsai/bonsai/bim/data/icons/dm_offset.png b/src/bonsai/bonsai/bim/data/icons/dm_offset.png new file mode 100644 index 0000000000000000000000000000000000000000..5b4ffb4d736a30f65ab792aea59672bff97d8f92 GIT binary patch literal 184 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4j!ywFfJby(BP*AeO zHKHUqKdq!Zu_%?Hyu4g5GcUV1Ik6yBFTW^#_B$IXpdwdK7srr@*0<*t3Nk42usAr` zmvsE$)6~e25YR8EHrqM7_l(Z;H|;xY=Ls#=|C@7vWBmV~eH=iINZhNklp=*FK#yg(MwtuA$`fgC}CKTAD> z=pyPp41xzSnj07oVAe+P1fPqv+8LNh)JcRe51J{c>Z&(gJ#VU{$~Q_(2VXVkrc^K$ zNdg-{4`>66z%lR+Tn7Iw&z3+j>j zt~Nt#OMO*$#~^SO_8zE;OLw0H+D zbH155=X`hWoo^;3N&K@EPzNjlzudP0_-en(N9g{}EsS=rBGB%mxS?a@%hr z&Bnu}2<(aZ$ABjLHNe@}SSv6RAW-AB$+-&E0IjwQF@Kk&NA~9=Wh5;cPf8;2y!YIP zk~(5}&&LZU34hd?#Bq71#yqVMTYrx?6?7 ze@7q-jFAwFB9;a&`biTB%{Xw><2m3u&~ALjxihP>B(~wDnBN)LX8p0beUfh3Uy_uS z^wD@qQdf~0dE0On*lPb3FkfN?nurt;U>ev6G#Wn#x_^Lb;2ZD;*jb=Po)k|Jod*Cn zftMu+90o@1-vjmn2aLzvR}IWL*Np--@;*Vgqy^AtCEh%4e}CfNZj-Ua@Jh{!6juloB9PpgNnFZ8v3a?Y1iFDQ zzzk3iTz>%;f!k#i9|V2?Pll#`_)HW<2cNnUEB(hgFoYLKtAh{s47y2kuTnV+a5M002ovPDHLk FV1i?1HiiHI diff --git a/src/bonsai/bonsai/bim/data/icons/dm_rectangle.png b/src/bonsai/bonsai/bim/data/icons/dm_rectangle.png new file mode 100644 index 0000000000000000000000000000000000000000..f4323dfea6ffba5323d2482a89db75d9a2cd28c3 GIT binary patch literal 211 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4j!ywFfJby(BP*AeO zHKHUqKdq!Zu_%?Hyu4g5GcUV1Ik6yBFTW^#_B$IXprQm%7srr@*5p6`|JxsSU|Pnw zb#7y0BkOdQ$4{kJv7N2IE9RE-h*^$lG2eliY!?{USsY>Q@O1_YNR%E}!R#h$@Twt@ zy~iry3gcovg0H3;+qgTSrLAb-#c>;xu&AO8;msD<{b z!|G9Wd4|ucHg$59in<=y4vYaCfFr`P!x_C5g|0`>tP z1ABm>|B}G-z~iKq?;%A%7tjWD16Ki^S)WOgqizM>2Tsq4L7c^-CfTVjDmkl8^=@^u zI_QiXRPR??=6~vhjgTmGf!e3;&4}z*SIw0G>SHnB4s~Gz1N5rC`OB?A|(SKFLb--<;ys!=UzASoxJAj*j zKY_P^0pLQQAGihh3Aiz$>a0;v=!?$CX?0qCq=fm)VSh6n?U=eYzO|~`0zaU(HF^Yh zMyD^-E7c|H>p^5qW_NSgZH>vUR)1H2HbqE5VprI`({;Mp(k@KIx09j8WYy9R{857dq+JlWCkM|jG29(m$aTQ67Z`P9|wLQUEg!*0WJgf*C7C~H`;XtkP|pga=sffYy&C& zH-BeXKd_dWAD0TrPo_wh+j{a@v@6dbSyt*$kE^HDE$WAnM&GE)UPX!>Q(sj-3cDBT z<^0fV!gg0#Dn?xqHDOpitUhJ=yKk2MhrB6c=Y(T7OEC?*MKF_Mh(L0H~R^ ztGi9|QqF!_^^7LZ0GzSpDm8U4aBbma27teTLEs3`3mgPK1HPN>8rbht87;A8a!h?y zU8&Y);|S)edm{KVvCyF|P?xE9s*kJN)Sn7qdew*3wwVB^0#T)xtDDqD4oE{?GZO$O z3j+15NO~X#WPg{sAmBJvy5{(|q<>VLcvCWd4E{^^cjh5AqEbLwsC zE9$uVX<<{l>D~{kkEu5W&8lygBv~J8e_C=yv&sA6^k~81#jxL%v#kr;6FFN=-KmbL zmzIEdGxqGZP!E`%(INF`_3XmN`N6`LoUJ?jI|{ycN0KK?0CYO>PE2|+Q5VI+hb0>c zV25&l_kXKBC4TcF&N~%x9*Xsbx!%zNv_(A*R8;#AusG(Op~p)6{s2aSB^7bD0tbLg zfCn>UOKgO61&h)OECl8N=KvRleXt_Qap1y=HGxer-vHF|01g8yfGY}{)-b_P9XPiM z0PPiPklune;%?rH)+Z;Z<_N;U>1 zFFWy8-)d?&2crNp-)q9R$MhYEdX=gFAN2$uAfZbmMqg%_tM-NQ2lcBs&q4L-k^q~- zx8KyVN1`@XHIY2)F{;1ijSEd(>qqr>bu7EuRS}?OYC4Bajj6}fjG9S~Iwt@O{|n}> V%gV$b5G()y002ovPDHLkV1i);$i@Hw diff --git a/src/bonsai/bonsai/bim/data/icons/dm_set_gable_roof_angle.png b/src/bonsai/bonsai/bim/data/icons/dm_set_gable_roof_angle.png new file mode 100644 index 0000000000000000000000000000000000000000..d3dbf2f3c64599d99f707411f2a3dfd9b186596a GIT binary patch literal 435 zcmV;k0ZjghP)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10YFJa zK~z|U?bWeMLQxdQ@$XM82!o(0T6Bu2M?*C9H&kPd&Ow7iOVm?DbPA#b3;Guvn*1Mx zhyr~GZbkG51TCd@ntFr1qR`h{-Wl&X_udbOdpR;QqKrhOw*#W(j@p7}Ohvlgn(fpF zf89Ke4$L4sV{zeHUVbeKz-O#tG}X#i)hSI!0RTuMatwNHdG6+KHfWSzpiXIye(xBO z4JwN*y3O1SSJYZV|iYRBl26_y>CK z3Z_e7Js{o3xF9*<+EVc)6aavbXdPvdPJ!8gk|(5`IpwQR06yfdG*9xa3flgrq8=*1 zzs4(U@oqIldN}tlv%s8k174RgoqWNKVHS)Okx#=I5Y{h91NTE~8Q@{KF!Bz>fEW-1 dVn7Ut0UzFtZxQj*0{#F1002ovPDHLkV1gLUsp9|u literal 0 HcmV?d00001 diff --git a/src/bonsai/bonsai/bim/data/icons/dm_split.png b/src/bonsai/bonsai/bim/data/icons/dm_split.png index e33e105c708925c84d2402ed05076ffc110470c5..03018a8681f7cdb9e5d21c0201d38cd7aace350a 100644 GIT binary patch delta 737 zcmV<70v`RJ2D1i`UVp$zL_t(oh3%J5NEC4t$3MUQQ(C$y9gHH3K+P?zkODh&C4tV`#pu7Tj87B{<9C|VhY(I4GEufxpFz8yz*H+OHo z(|g}}^ZS1I&CHt_BO(BpSg(};h#8QU+x?U_`hlpb4S=u2CVxf827m@gI++oF8N322 zPj~_Rb}})QWdP7?I$l`-VZq6b>@Nd=Ui14(07G0g95eBOsuG%418u6FTnQk&Pz@oH ziK`_9=mtWnewj&(Gl=2?J4huy0-Nn)3rKLEv_!wI)4UFN57gTA5>O9(0ks1Xj?Q6t ztWK=Oml$2AIe);YG6#5=G0y=2z%PBkPp1~nm{_E19p;yS)2d#?8p%%N07&!TEXG{V zT|@4hSYPXZm>&f0xwLLzP7BUGkR!$-V<4Sb20oEWACbdD-wHI}4vYhPmG&Gs>u&TO z#mj-qAk$eQ>IwLsm}i}ZOh`ZQ)MY*mcwS0R0BMcRBY*z$Sa)nDPSV`aTL3^Ga6;8f zz&T*vtJ(7sI0xU|x_~i{l$1u~l(Y=KTIH1Ez-z2s$V3;`li=;K7JvoOJBs0=l4eK+ z56GeD4_9jq)`Z)svAX8~KAUqNET%lkGseG6%R@ zsCiKU00d9O@|KZp%>uxVS04%F~lM7&(BBS_AVAAR)#P3=oU4DQP#|FSZ3HQDO Tpue*Y00000NkvXXu0mjf12I&y delta 717 zcmV;;0y6!x2A>9yUVp4fL_t(oh3%J3NR&Ys$A2@fE1I5!pfZRktJM|7GCC+76v9hI z(<{ta*z5G9gn&FX9CJ{Hvn;DC{sK&M-zs`uR%#T$TEK&ipzfRkjF zV5JkviO4LZF9Uy#O=`ODBHVXD zMJ-FILG`$;U;}XtJOWNxybo5LwDk=D@NFstGGftIj#u_NB_>%8InQJ;la=u#X`?6QL zANWG1-G64|J8&A9*{J@e?N;i-GoafoT#0}06=QtW5*MI-Gx3}Z07$TV6x4u4L(3bo z00>!A27u9AYUh686!ViwE6*y<5MuDF@ zZXn}A54y&RY9MH34U$LX`v9GA4^;s7Ee8fM7R4M3Ib&vwy%rML|K$ ze{j)4p>*^QsNf=klRv=CO(>L>BI4rUAgJIV1qt>zG+>jO8k?IGOAlPaxq0t>AI{Ck zQ6fUfXm4|EK=j^GWL0IH391NXqBpErOJ)*ZNVM%6PfzzL8C ziqzkb<`5CBgNv2EhJ0K^-bG~0Pp|(&G(DAX$ew^*;Mq^E;~Dtgtdvco><4NrikZ6! zvc^#5eT@XpHGhExGy$mc!5PQ~fXwA0?FZJ?-CoLNUd#@GEPI7f0Ssa%S6wrPy#)-! z1pp$JJ7?!7S(N?2f~sUp09DG&17_R+AfMZHmzw1#i5Ng_szP=YVXZR&Fig@u2U325 vIC{dRRyVt{DeVFLF95B-<94Fs75D6s-(13 zH*nHMPir~A*a9?xi{A(fMgT{^E3h_7CRB(gf)&L-~E!4haNV`@9HV1G_0brh%*Dpx^=Q@$vVBZg@R{CQqQMY9h z2G+&%XK~$0L?4Jev{gS4wju?$j8H=3Z#)YCK&vQINuG=VSRUpW+om)J$N~Njz!!|0 VsEQoDdI10c002ovPDHLkV1lImqmcjr diff --git a/src/bonsai/bonsai/bim/data/icons/lm_3-point_arc.png b/src/bonsai/bonsai/bim/data/icons/lm_3-point_arc.png new file mode 100644 index 0000000000000000000000000000000000000000..970710b2d803ca9c218289fa65947e06a5fa25a4 GIT binary patch literal 655 zcmV;A0&x9_P)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10vt(1 zK~z|U?Uzq$6Hye!fA_^o@ef24p`SnyT?pAoq)28$K7k8K6HP4?7lL4+3pYg+1Xp%v zrH#cP>Z&d!;~%Qx-i`eLUAQTND3X-Sb(1NRNt_N7U$XJQz|48)-FtZ4xpPNUm2tw1 zH9K%1xN%)K_9#5h6RQRQRP~cF=3|)iegA@pT0<>B!oQ2eQVmW3Tkaa%&g^X^JodQUYFZ`1`i3Ru<(+ZE}o%7ny(a|e~XL1>v`V6I6<@q z+-|}?Xzl4u0IAQMM$IhfJaDADyeT4|d-ST)KrSwMAWU_!UOjGB%FB-9XrWNJ9bI=_ z_pakO?fv4Ut^uvC@0@$Ibt~=u0Crok$iDHd8c@_oy>dNHBg1?RbGh6^Hk)k&uT}M3 zPhgRf3K!pAp8Acir*y8dJt#)ulm41B$+X-8i<=Q_cS&QB*A|Jwu>02 pi~i(TH9*Xa8NgZ}NQ$x7z#sP&1U$I66Au6Y002ovPDHLkV1nOUB76V< literal 0 HcmV?d00001 diff --git a/src/bonsai/bonsai/bim/data/icons/lm_add.png b/src/bonsai/bonsai/bim/data/icons/lm_add.png index 8031b0577c3a4699ef5cc877574c1f6e1e2b660a..0ecbed228f85a93382c54265e145684f8c6aad91 100644 GIT binary patch delta 1022 zcmVr;K8Xz@WDn>VvJ&_Q9_jjjU$y} z;)Ay|UZMs``ofSH1#E(~YU(u6kd{m@CxioIf5NreiGSN^%u(z^An6WT6`=a3 z)x?ohzd$kd84m1Gr~<5T_q;%-f&ErLyDx8u0l{Z@$S4IpAh0&+PvtjjGEmHiTWsw2fl@${6pw=TDpEN})vqZTD4@-~iBOF~ z4RC$x!b>nGXn%{%{M!L)uB>Q4Ks+=&E9gPswlxYb$EboYz+}6!&xv#z(TpMv+>jf`jAVgMZW^MtX!0>m1llIkt8?gsDf{> z>--6(W@|912Cj=oHsTFL87yZu^mCy6WY$=4GEcwqj=3T&1Ae_V6tFYDIey)3{`}_d#F>p&Vfm6K=b5Z^J zbj6pw7k4{Gq8Yk zAeV(SjV1m!5PkICK#XNsi-81?Hy>;der}p(dVgkS=ImEbXFP@I7t2}6Zo*Z-G2CwV zJCxppsPV^<09#fT_hNVTy2?s;Xi*Q>cXN0%v|B47~p2smbn%i3w}5 z$FlAN5E0AM@!NMm0f5`>o@3W?0|qlVTxAUf$n^Adtz}uS#uzHIEUPT?VB7XF;DrU* zFn_eE{}P6o!;%wjQFkZIRc-2@($@ZW5xp1aO2h~K7TesWyFTqr z%uOK;wPoNJ;CZRoQGz=4z&*<-BZ%&f7IwK@b+&D{r=_J`Q;tWF^1mT~cni|t##az# sD9mD&VF&ZKUol7vxM7!Z2 zAub`&2Oiwwzd2T9X6^J*le)c36O_WD*ebHQ=}a+U%S_Zbq-#-Q*oLWq%VLO1hSLg} zy0ui`9v>JDG;L{1tLN>W`@QG;xxYW>{;sGh|7RGhdANbLvVR8J%Kmcz0ddgL6vsLm z$9mpD$CCn5P_2BYz(HrVV0Hixrra7bz!k*(O-TSY;=M)S6@?vS9yt~G7|a(>@xnw= z0Pe?sO&M2O71;;ekP|Ij5oj~`^VI=7GX(=UXm1px9k??``0QlRm&P=91Vz#YOBSrK3(-lu_g0BbSKm&6><9KwBYX>!?Ol9o*s;a#8hE3y^% zxrpYI3cJD|Mf&C7wu+1Zz`e{R&J8+`RgL%mSS_#@h_AxJ0P6} zehgRHY*cQ{V5}@Ov!$^!op4nLk}RRR{jjtC=*83ZM=zdfvL#wG2}=cF2qLQ5rK-O1 zlj9kEN(BJDP08A}TWuMeSn)nMs{1jFpOV*OReuU$1z0<}R=G`_0L*i?GyoR*>W)m- z1Gg_ECabwR!c$XIU3DMG;waqA4)&!7NElyoEpLK43xs$7FB)DFbwl*Zh9G9wj-Ie)Hb4IIg-gOa*1W+mVU(!R-}9HAGPt5VOv++s2sjb;SNd7-H<^ef<$=FNT#(+fP5A{he1 zrOzBn{+Ot0jjBp87|aNr_n#4fhzQQwZ7rC(h6n6`#Mpl1LnZ%W1pyQ@O4o{WjDG<< WrCSja(&x$m0000@=2RXTg-B%L?n77XExpzp~<9&+YHKC0WM-x&uS2D={k`#F4-{Jhe==_y0U+(?O ztG+Z(TV-LV0>{2t3Vq3p4F09x(*8rW6%?r z_3-UX9mZ;o*V|X07n;zJ+3LG_r+*6L4d*3irp>hX{wH~Wbx%y^*?(WIioWW5;kt&g zOmNo2zt;b49VRqvvaF9w|8U7rI<3;n?R|T8`SDvD?g`2=1}S+Qu$~(e0t^HOPgg&e IbxsLQ0Fr@}761SM literal 0 HcmV?d00001 diff --git a/src/bonsai/bonsai/bim/data/icons/lm_add_transition.png b/src/bonsai/bonsai/bim/data/icons/lm_add_transition.png new file mode 100644 index 0000000000000000000000000000000000000000..12b8b1fdaf46a54e8119c2f97562497b8c4dc2a9 GIT binary patch literal 381 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4j!ywFfJby(BP*AeO zHKHUqKdq!Zu_%?Hyu4g5GcUV1Ik6yBFTW^#_B$IX1_nkhPZ!6Kh}O5$HfAve3b>ws zv#$36yGw^y*~&F7S8iT2YVA_e_`!UQaW_j4%cL~sE|y919&hb`Fs}@{5goBISy@qv zCB{pX1eSzU@pC3YYu&#RqtKvi)ahNtAdkbas|_ zHIu^8+f#o^SuwCbV328GE?`LdHSP3{VD~SQ8vnz^zpEPiIaKG*U-LPfga5#>E&n5q zamtwZM~7~!W2ukdG+FgS^sUI>HLQ!hriDx;7l?}Cz(qU`on(M`>ehGpS}0m=ij2L+)7xvPJb*tNTCTIEBMS9^@9zZ z&n1s!y8@+ctTBbX2=#y+AD$NI(rKDQja*w10TRS;)=;g&hrnG6gpC0E?_0+wJv)gl zn}CWA9`Tempd=%;a6y#@w~qcL38?Jg?rG(KKs6v4k|(3j@TeQC=hv(RIGy-qf+mF~ zU{wyp$(Ta<%zrn@JExoYcToWXQr^z92p<9WERlFVh82S0Q4Z@qyu_f99N~0Og3z)8 z-!^7g!&t;Pl3|d=Iov+W2N@Q-4eqOVTC;}RPaP?B~_~Y z!~*1L1OQ-x1n|6{-LPBm&*(_8KoGc*B6bSpV7`wHfl4NBH@Kk6Tfl9L5u_84z*>a1 zOG88FLVuC)Sxb0O;Y%PfwpZmemY|tLU7o z_YP8Q%Tfd^3Wc}38@7FAnx-#5KY#M+<`Y#4Uw_Y!%XJIV!tgNse*e3+ZI48w(e?%7 zR!V>xndM&2E%*xrihR@Cs-haO3WW!7!T6&uw{~JJ6uMZ3iq%@`rmWuAf(=VK(L{~k4Xb_l8 zqv2klJl={8VS>1m7Lf%Vb=q3ho(*H# zFWdLRX00001t(UVnv2L_t(og~gXmY*bYg$A9PcOQaI0ACZQI(KM7)Y2{5jB$(); zjY>kI3l>6z5||mOA5(Q@Xi{N=CXKXIZDxo;Br$QugoW-Lpeqf|ys?2yC0&S?fHh4U zV?>Ibnd71zDMR0UbwqY;a0RJhjvn9$u%;kdxFIl5zlQhDcQaQqfHlkxQH}wR6)2pQovV_jFY4#Bq5xbU z(yS>y5aIcfgn#e0^Q!dPelF$(u!iXt-{gi?vKmU9$uZ12HpQM#Rpe>K^kE*Au zw^}_a{eM>`F7LV`9I`B{4Z!PSg9_gOEk))l`8aMu3E8&Y<#EwfsyM8;*y#NZXRrb% zvV7j<0$0?K`seTgh!C#`fPXQZ`Qn|gbsCP zX5rxjk;oAT0CNtKAA$HvgnI$#$aac|XUXs)3*FV-0D7|Rci=VI9~&lRj)4~2kQ^0} zhyKl{`Y}+Yst1iR=|$h{9dNl?GT9g3fvR&$CVDvFbAiyW${7)+Gmq9y(h~@D&H>d8 z4Sx;Qp-?EbT#B5#Ku>P@iaIu-^a6k6iWT8S7K)7*cxn7WgaYmgdAFekta&|U4UwW5 zGzcvFC7Ey_oHVrbKSMI(oSvR`1S$AO)6>?kfKR&PLC`_qsVwQJ8vMSF+ZPj6?NC)2 z9UXOqF8XE!AcC+bz8k};pl=JnU!JXeP9gDKQ)vL@w2GDDLhTpF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10f$LM zK~z|U?Up?+1W^<~&zlg7?>g2k^Uwjsdz7<$6?{% zaR$DM>DUag>`1>reh5Qv$(yG@M`;rqBzGtMz<(3~kW73I)kR188F&TSO4I&V?pvUY zgd>8(eG7o+b`*-i#$Fu0zl3VpD~LD`CZKC!;eiAIG`X_`%mN=k4%LM)V=oThTTs%J wvDz?m4I1CR2@9@B;5Vb@e?*Z$Bv5mKPdit&CL7mNQvd(}07*qoM6N<$g3a&I_5c6? literal 0 HcmV?d00001 diff --git a/src/bonsai/bonsai/bim/data/icons/lm_apply_axis.png b/src/bonsai/bonsai/bim/data/icons/lm_apply_axis.png new file mode 100644 index 0000000000000000000000000000000000000000..8f9725463b9ee092780b03013a48612b63d9986b GIT binary patch literal 399 zcmV;A0dW3_P)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10USw0 zK~z|U?Uyl5!$1&4-)wRY3SYx1Kr)9wv117#u0Wd-u7EIBNWmdeKoJTqL6LL74hzy2J)-AJi30eF z7ld_?9r;an&fSEanE<)jK~*~!O{p~H+|qx$Xv#TFsg^#t2NFP^34_=<9oTM<2M~7( z?{6{1y6gA{7>ASeEyunI|M~<5_do(j00|%gBmfgY+PaW-n(f;_+G!?jT|@(*eucW( zz5uA3p?+n_(fM=nFS?x?Z5!@=KpF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10W3*G zK~z|U?Uyl5!$1&4-)wRPdc+B6qfk)z3^c(BPJzS;probZ0>lXc#0_XDXlN*?(gVr` zxB&LhU<1=%cblSs$&nag0g2zN0& z{I;&+EBOKNr3J#B)tw{@7jpnQ=@QJD?aU6)&&mRTK5KM+;%rtTjc#55YnH^}>34wE zGvEP=A+#iMSnHt3(!K&fCnS;gI%X?L(m8Y=^RntBYfzTK04D%G%m$`p7J%}WJFflh_!EshPWUmoBk=$L002ov JPDHLkV1nTsu8#l! literal 0 HcmV?d00001 diff --git a/src/bonsai/bonsai/bim/data/icons/lm_butt.png b/src/bonsai/bonsai/bim/data/icons/lm_butt.png index 73f33be177fdd97d73edd83f2fc15f2c78c6bc54..baae236e3849abd98d131c5d93c38f41d278ec89 100644 GIT binary patch delta 113 zcmdnOc!F_4Tvn*3i(^Pc>)Y!ac@G$HxLiDZO6X?0rcUKnpF?>m?DJL4|5rWeTqp00i_ I>zopr0Bu4m=Kufz delta 93 zcmX@XxP@^-T$rP$i(^Pc>)UINf(!~Au8yYChHKKocs|bkxJjSkO+ixGcaK%8G7fZY pv*%y{gM_}#o7)xM{APu48T4AK*#77LED#0pJzf1=);T3K0RT%mAc+6~ diff --git a/src/bonsai/bonsai/bim/data/icons/lm_centerline.png b/src/bonsai/bonsai/bim/data/icons/lm_centerline.png index c49427f1eeedb3bb69927c011fa77518e9f29d8e..668c8217548801ef42bae48e787d70decf15bd47 100644 GIT binary patch delta 222 zcmV<403rX51G55tx>Qo5`PK*aN;Nd+*EpjQIS0qR2t054Mj5$%@% zm`7j^P)8uW2Q>4T??44qz(3dV)f5n8?C1ZQukA(@LCV|3;+NC07*qoM6N<$f_pY%Y5)KL delta 314 zcmV-A0mc5a0*(WaU4H=^Nkl(JZ) zBW$xb+^0yE3yG7yoVscEfCZqKz701=7pPADR4VD5x7!1Mrq0NmnC^fjIM{r~^~ M07*qoM6N<$f|K}?^#A|> diff --git a/src/bonsai/bonsai/bim/data/icons/lm_centreline.png b/src/bonsai/bonsai/bim/data/icons/lm_centreline.png index 718f41601ca68396b981debf444ed6fc4f1762ea..668c8217548801ef42bae48e787d70decf15bd47 100644 GIT binary patch delta 222 zcmV<403rX71G55tx>Qo5`PK*aN;Nd+*EpjQIS0qR2t054Mj5$%@% zm`7j^P)8uW2Q>4T??44qz(3dV)f5n8?C1ZQukA(@LCV|3;+NC07*qoM6N<$f_(B~YybcN delta 316 zcmV-C0mJ^Y0+0icU4H=`Nkl70(s(E`&fL6RgHfK0h@thOdl zRaFh3_rpncLN%imh~qe7=BUe*QZ+U=%8CFpXL=`O<`n?n3BADd1bzS;PEPo<=K(qZ O0000pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10|rS% zK~z|U#h1%ZQ$ZAmzjG->7siFg_`rpas7u8cMoDy|ODaYemhve569^b}B9H~H7YLEL~((*L6=5>&T-R}J97&ZT4a`Y&iVbmxp&SzGoq@jz>az0FsEdBizC&bsZs=IqKh-_J1H1 zIY(f_E#4eJ+j?&U7aXi3Bpa)<@%UH025Y7As1p$OXJ4qWP5#3p`cPg?S zbpSk*_nHN0?HdI?`NAp@Zk7swdfiypwu>Ol1dcBKsy>pSLe$H4^Es4b~)wrrszSx@vh9*-z=Bol@(I!J|i&+nOW)~r&pGh4}eHFy$6 z2^Y?jliHeo8t_{FLn%KIIXBm#bk1L~#_qnrNG<3{3Fa^HLI>tDhvF|L4Zw9^cX7V* z5yK^orw0~{T+HQE63zQS!?}LG33MxpF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10S8G$ zK~z|U?Uy}H!$1&5zYl^a{d0=c$OXOa#&jtxiclbEI1ptbq%_D9Z~*TCDBPsu6f14( z2u+$4G=YsZiWRY2&+K~|&CF^kA{g_WjY%tUQJk*W#=m*A_QP}63+l0e~UkjF>Kp5^NFkL`rKOk0^mwS-koz# zf90rE;0JuZgWvk>1Qmgya5VvI?P3UAO#r~Ph%|@70j0OV7&9H3JNg(h{0o9W5cu~3 YTfw=2iXc1QPyhe`07*qoM6N<$f_uxDb^rhX literal 0 HcmV?d00001 diff --git a/src/bonsai/bonsai/bim/data/icons/lm_draw.png b/src/bonsai/bonsai/bim/data/icons/lm_draw.png new file mode 100644 index 0000000000000000000000000000000000000000..4b8a6717c7a03936be0eb30ac5b802629e543a92 GIT binary patch literal 874 zcmV-w1C{)VP)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10{2No zK~z|U#g|KL6IT?5zvFQ_;9Uf(iXashWktGBbsIypw*x8L-n^!|D2I3Ks`pV_0qoYU4*1d_vkORUxh)S{7oZ0G9U-VEhW$LaLjzQ?l_r8>$eI z<0!8P!1CT%}<>Y%Kc~ zaK1(Pzob(;uuOGHnIoZP=&1H=acaS{i``-iZX} z0NpswKa2ZB5&+A4LzuozU=AQ4(5HXTM+usqh8ARxVVp~Du7esZUT!9e?7#}m!R)yF zV)Jd8hz?lbjbNNgY%Y$=A3xvI)3e@joOPX@oo^;4CgvJRmvq3!vZI)io1+sNEPMx$ z$z+CI*L^WJH}_MNx~>BjcqcK2lbbUnzZmOnsULx9cVcVPN#&N93qVaan?(Q{*^#De z(~>JwOCcMUUpU*}-=CS8nQ<#=*LBwcn3>z#+uK>mXEK@j$;rvtIsiW0yamcG@#B;R z$Ik%F&d#25U3Y&2jpw?qSNRf#;Z5MPdVrKZj%{uFTDjus*z0Pd`WBeB#auh0TyeBE zQ7)H9&1`Gs1N^e^)6Lqv<2VN@Yib|DI32hkzwpib$*HNSXTajY)7RJc5r zK>IJfiCsLMz8Hq#)sBvi*HM~SH=@;uHUD?~Kfi_ zN;LfAPfK2a0L0AjR4sW-Io+b=ex$oGD+$083V;9zfB+!xQS6=p69DB@D5qkHAzDT! zYK;FsUV;D!fL{Pu=fG?Q5buJk%UE&|1Nsb10LsDZ0JPFTS~P&d{Wxq5%4yI5xHn+{ pXnUYrYrgZ|1ZbgXxHGxmgbQk0A~3FBu5JJT002ovPDHLkV1nusOke;2 delta 96 zcmbQpw4HH6T%?Pqi(^Pc>)Ue+1sN20oG(tXHPms6WI4Rx;Nzd ndFAxuCtrnW@2}@$Km!k$x}I{_&Mtmh36k)1^>bP0l+XkK`TimC diff --git a/src/bonsai/bonsai/bim/data/icons/lm_edit_profile.png b/src/bonsai/bonsai/bim/data/icons/lm_edit_profile.png index 16af577eecd72cf7323458e52aab97f4bc6ad8e5..5ca6dbe99b6353c1a79552a288e491be303a2d95 100644 GIT binary patch delta 277 zcmV+w0qXv~0qO#fUVryVL_t(oh3!{44#F@HoPqo(WoVGXD}syU4M?^Fuwqa z1|GnRmIk3*5J$?!1)0jydS}PST4Ra`d?L^wWoevNC7EU5k|3Go7{aY4FDvyYxU|-dI^U-T;9825&nNrv9nX)PE=TtVO{haju bJ%0g>OjimPpAMOb00000NkvXXu0mjfJ*Iv} delta 103 zcmaFGw2yH@T!Oc!i(^Pc>)Y!K1sN20oC6OTs!4ofI=mp2VMC0-{!MD{i%dhMOeYm? yGL|_Y-cS_n7GEW}>}Fr{c5?;pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10S`$; zK~z|U?bb0$#6TFu@&A{M9>7|#k?p*IEa*XY(G8x%##5-6P0%BF02cz=+bLV9*r=Ug zBQeiLP*>t6OG29Y+If8M2Lr>HB)XiS({>N=01xm0z%{+l&W4+q^(!Q2iqg#4>0S?~ z-O}8LZgp^eRb60x(0Fl9DUdlrZ6h6*1_LEPbwJJez5*3H1U&vaRWNC*_%9$_ebD~+ z?&qP!gB)zdlED@#)=Ia?y(K6x$${N~PRe_NX{!K5=@e+N#AaWQ%%aHv%zsKf gzymzM1G)x$0-g#|+8jXL&Hw-a07*qoM6N<$f@Zsb%7 literal 0 HcmV?d00001 diff --git a/src/bonsai/bonsai/bim/data/icons/lm_edit_roof_path.png b/src/bonsai/bonsai/bim/data/icons/lm_edit_roof_path.png new file mode 100644 index 0000000000000000000000000000000000000000..686a4e53ca1d10fef248d879196caf6ab13c6743 GIT binary patch literal 534 zcmV+x0_pvUP)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10i#Jo zK~z|U-Ivc!!axwlzX2YC*Dx4kf)~@BhalJkeF7vVz|ET+i1+}Ahnn~h-NuV1Mi0if z@GZszkd!}Vr=^9&x$e%)Z>KZcZ%IUOOskaO>FQW0R=ELnkf5Z+cZ z;{rgltmNweRyyHra*<~K?I8hDjb>MoV%LoX;ZIh8N>+!WO;rI2p7{U((xgYfzp_fw z1WFr#bpSFTnAPQZa%P@HQFL7zKyuFM$$td^>4cX6;CbGi5Dl0a*4mypu1;p2pjFQX zu*>2&9(tY!00UzTh$t0;nMWehH7~yp+KQ>BAdcgqh|Gf4HCn9xnHGm7k zm7J#S22IxGy#SUF06s+Il!(5qwY@xXasx2)2*5dj zFA+IOYs^vrlHc4~d*+&sLIJiloMuD|0oc{;`#`7wG^3Um!U>S&hRXv%BH|RKS?=}) z^u)I6-CcW@KfokFswmB)inTGz7NyRZ1&Y!QFV(L9D@wJ`vyw%rJElsM<~MgtI`;GY Y52R~9Tvw4hlmGw#07*qoM6N<$f^Dqh2LJ#7 literal 0 HcmV?d00001 diff --git a/src/bonsai/bonsai/bim/data/icons/lm_exterior.png b/src/bonsai/bonsai/bim/data/icons/lm_exterior.png index e0ef805a04bda03cea305731ff97586439445878..b10fdd3ef0a910eb56521e49f1d4ce9dbec205fa 100644 GIT binary patch delta 208 zcmV;>05AWQ0;U3xUVpPmL_t(oh3(cs4uUWc1<)D8jr0a>kKx8dJ(DclaEwlS13gm5 zg-QwuBnIhc(Z8B7guL`GD61+F5m{^B!zUubL_~Z)4xaxSJ#{>{-!1w0KLQ6d-yx-R zfC6HS4+LO46&PdMd=Wyp6a_Hzd9*76fLBlfa6psIPbfe{_DKi;yaG6&uVCsvuy)`l zPyrQ?|BkOvfcL(gdvUF7rvm5P;09FcM0$X#-bTA30C)uzPysKTV<_;GZ_&vB0000< KMNUMnLSTX>1y#}j delta 191 zcmV;w06_ny0+j-gUVoxVL_t(oh3(d{4FWL?1yCD#BiO*n7&J)0Oi<7;Mx1N_BPH~g zy9SVd<6k;Op8OlLst#Jj%$SIXAIItKn~c+wx88RD_~4ho0n&RvKmo0_7ZQN4KvnO{ zVXgJz24Lp%%G?bAuOI+$fDq9e3NW)h5&*9N4v>`6<~`sM_&5dv5CCE3D-=*lnGO=q t;48>EPuGvpF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10W3*G zK~z|U?UcPr!%!H8pEtFWZZ3*$f)}8JQwOi0lMc2;5vN{&I_vBmNYr8%EmTJ@K@hwG zaS^-$b*LntOA982V`DN&_(zhQBw=zMwa8F_wa>d8vb-cmU6#pr9xy z=pDR-3mKcig+i$=yoo~h1J6wgGo`qib{6JeCXnwV$$N>I8GR<{l^z8E;qj!`OVIlc zL;?BteIQ*PaLx_P>>2p3s_Li%Yn1;~Jhs;cg~qKCC^fXvKZi=x2XLyY_Zxz{vMhfRfK5oOg2U2V z&}c*%aM4vSP(9Ui;1>84k#Vydwl1Ofel8+oU&oneQEs5*ms3E)1x zLEXr6QIUW#cg^!OZ*0HMXd%z%i9Mg0DMKm^1x-gVuZ9k3z77PkX7K!2>o*8u?tm$)0i1K}EX z2QUzvI1k`};Kum?3&JPv2QVNO@jKTz0Ni*1CIsZi>2nc(=N#iVfeCRQhT*L32<{Of z07d(z>ihnQh#mm!0K7^m%M<`b`=%OiLJ&THSv(ZM1sLNY2oAs+_eHn@oZ>zR7XT~H zMXUk5;v9qx5LFa+MF<>AiWS#ap63T4M2+dAv;oW$*gOtgCLx5V@H2^Ve+h^ZA6MJ9 uN_!lC08!)P(%y*~AW7VeIF{;~zvtgk#P5%l3Tp`f0000CIVmLqfZZ>{|554yd#R0Ttw&XW zQp&THZ%2VKCQSRBvlj&*qRTAuKnDs2fI5KKB?RzN1xP6!T0*(H0(Af}1>-+30J;T` ze2%ZG05QhU%D1DyIp?SS=S0K+nE5t~0?>hi0WbjG0M97!JMJr(00000NkvXXu0mjf DW*bz2 delta 181 zcmV;m080O#0*eBWUVoTLL_t(oh3%HX3575aM0drGw1XsNcoEc^{0A?Vk&q6wq^$Rd z2T?K~+&NAl```t#EFyFjUb^eU8AvcX0Z#%4s7nq@DYa057~__A-%?6~F@t?r-kHO3Y>Ez j6hK7gyn`!H00lSzd^R2)I=MHU00000NkvXXu0mjfZS+YE diff --git a/src/bonsai/bonsai/bim/data/icons/lm_join.png b/src/bonsai/bonsai/bim/data/icons/lm_join.png new file mode 100644 index 0000000000000000000000000000000000000000..ff116439df1a54048dd0daf318a3cdff052c766f GIT binary patch literal 217 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 delta 487 zcmVv=5rJ0L+P;X~#bZQt zABJIHJC{+r?^%YPdbLxT2SHFXkktzfyLHWj%sg1H*XQxU%zp#T9M;+@GfxPQmjc29 zSO?M@z3JYeoUFA~5s3iQH+w)(1x%#P#IEK5u*tx$JTuX1wK@PgiST$bnT$2&08sgN zhoA~F5e>V!5XP8>h+M~GM6?8u36N$Eq?O?TmLtnN9bnH8dFI&wdx9u3&j;8M!pFQA zARA%{;59LpT7PpOS%&#{dX=cg9MBv_9?PLM*8vnjlraaAA0kgRoB7 z%fw#>6mQPV9T6GC2Qw#o#4V*|!f4HRRT9D+`=1~QVL+9OW17byoO3NBSHtVQ!PurSVxrKmpFRN_1+b?>`9x%w#%f$J6m_Ro|!4-S|ED0KwY(g3-RplTu{#}QP| z1*AHHCfSe_N6@SsAl4ByEr^J51kFoALXMzIQH;+KbgK(+IWdB+l@T^a(7iT9cf^2H m$H)9?1pt}=00001s(2$KbnU4H@~Nkl8unCsJ##iW1+DOYGZNW?OMdfD%fbNpmhpkb+_=w!b@1u+f%s&iT77; z*m*Pm%v*kbMNwolk*2BG0>p8=(bH{3!y@d+2O}dRhn#Z>@PDyXDvf4YHeqJ-z`Zn0 zZ^d!^1UT)SJ725SUKJINqUg4ySzxAADm`s98VRu9ITz*ge%OF>?kq3>oNP22$3h6x zKtFKJd%qjF2$VwzQ|%^m&dmV*W_Gn+uOA0a00SX}b1gR@Y0PZnd9Fx00AOajB;ClR z4tekAiU@=dc7IBuS!23IkALhRFteAya5G#mvq4GwBz*<$0@s`0-X=*hS5(-0KWk=t zfDdN&P|~Ge^!RB5&w~byR4RLWdU}=t1BN9HnSa?5@FGpqsg5XN5p(C92Mz;A z%xu)mCV?_AIX*t#-w`GB-jB5+An9oCJOLbBkFzYhxjmFHNs>3ME=EywLDHG#y(LMf z`2HmH_V(V%vg~pvl&~d22nq1s%-%_=Nm>L}B|WZIt7}~){9SYj{E;UtD&)P7JFbLz zPuOmfO*&D+yeDku0fC|Q!}d|ayeDku0pmCxWD}Hd!xOffq^pE~i!On09RhkhSA*BH P00000NkvXXu0mjfUkwXC delta 1035 zcmV+m1oZop1=$FYU4H}SNklE{x1D=^_xii4denc8jw! zOV)ygu7+9Bu4!nL34xdfyC8aDkXeC$z`W3IBnq-{cjuhFII@{(3SEUkAVo=3WXiJJ z%=dJ$b8PnP?pZG)Z}x?O_nYs1-shS5=6k-eD2hZLNs`#IWq-@&%$_~_@4>;r6riQ0 z<@VF3PyYqb+uQ4ghKA-T`pVz3jc;3&g-LK@hCSvg{9_ zBzv!-ocnoTVBknIV7Xil!!UdmAWhTvN~O|Wk~T{^lO)N>_V)G` z?|p})qkq+E^}9HZ*GsxP48wiJKI3))@CNWUfcJiBwOTztm4G;oJAo4bX4VOGNa_O8 zEX(EvLGV3eMpkvsZS&rL-N-SSfXl$WdHyZ%XOow(09c&omwhk!GcO69S(wzgwtwpP+xW_AEL3(PmO zCneSDxJD!DGPAkB+$_u1NqW}I4CsvG_)s$eX7&PO|ADPh6mr>~(b=mrNge1!QA;|@vP)oS&;_kI(wN75>$iTtT|nc35W zgM-(+_p5+Cz^4BG{&Qxw9@r5C!GUqO87G_(aH|FU1CcHQLW|@uIRF3v07*qoLVg diff --git a/src/bonsai/bonsai/bim/data/icons/lm_rectangle.png b/src/bonsai/bonsai/bim/data/icons/lm_rectangle.png new file mode 100644 index 0000000000000000000000000000000000000000..c87d1abba7e7499c78c74ab3fd976921ae536894 GIT binary patch literal 212 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4j!ywFfJby(BP*AeO zHKHUqKdq!Zu_%?Hyu4g5GcUV1Ik6yBFTW^#_B$IXprS-i7srr@*5p6`|JxsSU|Pnw zb#7y0BkOdQ$9p1Hv7P>0BkVRTZn6!=ybG9P3Ol$3X1B3{1Pop^1hV&7C0t=# z%qLNLUSr0jtY_D$9VDF?hQAxvXU!-aKMI}Wm3Q=jT+KL3hqGA;m(9Q1NnOyCrTdEs`zENt3 zBoSXiH{0x{o$k)ud;G97%VaaVn^u!NpYHSLoag-Rx#yned4FVPT+iXIYjhyDQV7Fv z7w~)F-fFda^lAfKl7TP`cS#zPwBVe3^=b!)whT0b9|bO?Y3e6Qa^_k9xD~-N@eE%34idInLP}g0b<}Butm})Guvur+a+yo^I4QMW@f)B7K?v4aNt1pKLMB- zQ55Y5o&d(vH2qSNB=5Ex4G#}*UR+$fT~e53*`5}VnZ53udu(7};OX}ZqE;td527{t z`}@0kdU|$C`a0k@(c zkOJQ;7K@L*muy_ph^p1wDI7u2!p4#bWVulI{cE0lq&uIXUM0{>@hk zpiY}n5Cl8>`ubijm&=P+x=<8F+ki6g3E-t%F1LGZZ0uj}86b)xU(zt}Ht@SRj$gja zNm395Uw@D^C~3aYXgr-H$w}Y$Z}dFx2rvZv&GWp0@$vC7b}&@ zYgv}<13r?^=kEY$*3JlU%FOOAl}Zn+3&2wQeQtbw{A^1om&=Q0HVO=?IRIbMv+V^s zH-9&G6nGbiT3Q^(7o2nV01IaJ_|VYMt?L2Y44l2pz`1ikU#V2OWpHq?2Pl}?CA;PH z^mH9KZDt>9*C!?>rp@eeU}Ku5KV1*tBCvU-fthUuXfzsmpbtROT!*zT03Tc_)@rpM z0DlMegkkvUwE+GK_)!#XX$ijXZvgHD@P9mSlce4z)M@}5SBg_pQ#DDC066Eqy()m2 z{R+5I(zB&fX&?;49iHbMk<<;4rfFT$#-?qj2DJ#NgU;;i>>=QcnceC8{zp3kRIAm) zz%R{grFK(eJq0M2%YC^c06?Wu*?(td zLy{hq^kAB%J6Jl9P_Nf#J3`=jTte)FHR5^aHj6|5*(nj^k6nOTe};3_o-kKoms>qbPc=-DzZG z~9hBwo=?&O0WFPJh|Baby4d{QOg9)`;Wy zdfs7i-Nl>D=D3*+0SDgc;sDm3foiqd*J`!KfDLALZxlse`+oqqce6Nvw|DQ}cda;p z1wpV6_{1WmSwqq>Gdq%H*=uI@H{dV8vvC~1_`e1?0DQDde+)QlnV2_$r_Ah;IF6rF z_cA(NfZ^fcRezr6Jp)`V>CPlc?(%*AT+j3RvMgKWoLejD5;MCBC<7OE#J>#eDwRr) zjE;^r&j>&gzVDv^v0Om4TK!0?)w&tD6j!#zdLl_z0o&s^-nC)^Gk>d8Dxa71ec-*o5#Y8sjvqf2 zfJHmu z6wU0RG)=eG>-BUwf47|BOeKn<`z2iq9F_D%&+{HS*-R{&0pIu66bgmwvn=a(&h3xm zcx<_K?tlCKdpyspNxHzy{#q;+pBNh(n{V4ImC7LSTi{$t-%XNa$4LRgFx(>Pr$A4K zeX39>d}e%n{7`355Corc&OI$@phNvU5X5mj-Qia@voYXoGrJ{g5cVAK$c~<0ISVxM;ynylO*|Fk|bMzNYV|J zO69|y1@fNQ1DtPW4|ey1tJP{nAOPNIG#WqYG`3o;U4Wc(pKI$RZ3gDjG<~2$CrLld z2Y>YG?ol?I%`XH00ydP(-CvVUo)G^ZwKj;$;ruvr0u!i_wqq60C=8P%ngn1z+B!ov$e3WQ|68LMz>Smd`;kl z_Y5#GG4TuF1z>ff(fGn~0ALzG(wgod-+%WDzz2Y{ec$it>FIfYZaT755lM$x*p8R7 z3AiVJ{Cc|=Tu%Nfa7`G7S9AtB=WYUu0L5alKg+V-JlT=}&bh-t-%>V!QmOO=@S3C# zmCNPzkgozW`x)RQ{W=W8b=7LMFAT%$oO8Q@8GtOy=7B=)zvQ=HQnT%|l#Gs!wtvj* z(fsk&;{XE#13v&Jfr}(fwpy(jNxzZwdte;Ux^?T0n%P_q(7#NLeqeSvfLwl==h$Rs z=5n&;?Cfk+(w!{qXa|6AluD(|!1=(;&Ye3mGn>grdNP2+C#xPs(M!NTfcK4zj9h+v zx~8V4-UjZTEBP66N#0M5C~+f8_fq@Qov zwCS8quQW}!0!SKf>m*G87X(3Ybyw2OdD3L1>iIDrNf)=z19hNxc6RpL?SJ(-GBOh6 zACeb`hK8PQ2Rr8;$dmlY_x)7><#PE-;11yRG)*{`gxzRGLLRpFP{85bt8%N}GtrRIEIHFm|`zh4W?)eL$z zcqM+gF}KQP#X$F2 zm1pPPX=VD+l9K*mkL`ph{=bjU-=fXHVCl<$!D9{MYll1PMXZ}voxXZHfMKn;oR05> z*KZg5{N57pc-m%fPR71(5?l-p0tYu5D_Ad3wwqXOqxr(?tB!~R_amJ?$2A;h$~&$& zv2Ial|0biQs&Mn(@9D7%c0RVMHR9X!m-l*WtiTGT`)zCjr*t>yXlp8&$IoAxb*l94 k?Qc0xgbf&&A*h1=lWoD}e{&vg0){+;r>mdKI;Vst0Q+9BhyVZp literal 0 HcmV?d00001 diff --git a/src/bonsai/bonsai/bim/data/icons/lm_split.png b/src/bonsai/bonsai/bim/data/icons/lm_split.png index d3709d08eed0a3381d906afc6c6be32f8afc332c..0fb0f5110bd906c00df0b9485839906e1d10a4e0 100644 GIT binary patch delta 805 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 DXb8j( diff --git a/src/bonsai/bonsai/bim/module/cad/__init__.py b/src/bonsai/bonsai/bim/module/cad/__init__.py index 3f0ac8e336..cf4d5297a0 100644 --- a/src/bonsai/bonsai/bim/module/cad/__init__.py +++ b/src/bonsai/bonsai/bim/module/cad/__init__.py @@ -40,8 +40,12 @@ def register(): bpy.utils.register_tool(workspace.CadTool, after={"builtin.transform"}, separator=True, group=False) bpy.types.Scene.BIMCadProperties = bpy.props.PointerProperty(type=prop.BIMCadProperties) + workspace.load_custom_icons() + def unregister(): if not bpy.app.background: bpy.utils.unregister_tool(workspace.CadTool) del bpy.types.Scene.BIMCadProperties + + workspace.unload_custom_icons() \ No newline at end of file diff --git a/src/bonsai/bonsai/bim/module/cad/workspace.py b/src/bonsai/bonsai/bim/module/cad/workspace.py index 501fbdb11f..6b3723cf72 100644 --- a/src/bonsai/bonsai/bim/module/cad/workspace.py +++ b/src/bonsai/bonsai/bim/module/cad/workspace.py @@ -24,11 +24,48 @@ import ifcopenshell.util.unit from bpy.types import WorkSpaceTool from bonsai.bim.module.model.data import AuthoringData, RailingData, RoofData +def check_display_mode(): + global display_mode + try: + theme = bpy.context.preferences.themes['Default'] + text_color = theme.user_interface.wcol_menu_item.text + background_color = theme.user_interface.wcol_menu_item.outline + print(f"text_color = {text_color}") + print(f"background_color = {background_color}") + if sum(text_color) < 2.6: + display_mode = "lm" + else: + display_mode = "dm" + except: + display_mode = "dm" + + +def load_custom_icons(): + global custom_icon_previews + if display_mode is None: + check_display_mode() + + icons_dir = os.path.join(os.path.dirname(__file__), "..", "..", "data", "icons") + custom_icon_previews = bpy.utils.previews.new() + + prefix = f"{display_mode}_" + + for entry in os.scandir(icons_dir): + if entry.name.endswith(".png") and entry.name.startswith(prefix): + name = os.path.splitext(entry.name)[0].replace(prefix, "", 1) + custom_icon_previews.load(name.upper(), entry.path, "IMAGE") + + +def unload_custom_icons(): + global custom_icon_previews + if custom_icon_previews: + bpy.utils.previews.remove(custom_icon_previews) + custom_icon_previews = None + class CadTool(WorkSpaceTool): bl_space_type = "VIEW_3D" bl_context_mode = "EDIT_MESH" - bl_idname = "bim.cad_tool" bl_label = "CAD Tool" bl_description = "Gives you CAD authoring related superpowers" @@ -47,93 +84,47 @@ class CadTool(WorkSpaceTool): ) def draw_settings(context, layout, workspace_tool): + ui_context = str(context.region.type) obj = context.active_object if not obj or not obj.data: return if hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.subshape_type == "PROFILE": - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_Q") element = tool.Ifc.get_entity(obj) if element: if element.is_a("IfcProfileDef"): - row.operator("bim.edit_arbitrary_profile", text="Save Profile") - row.operator("bim.align_view_to_profile", text="", icon="AXIS_FRONT") - row.operator("bim.disable_editing_arbitrary_profile", text="", icon="CANCEL") + add_header_apply_button(layout, "Edit Profile", "bim.edit_arbitrary_profile", "bim.disable_editing_arbitrary_profile", ui_context) elif element.is_a("IfcRelSpaceBoundary"): - row.operator("bim.edit_boundary_geometry", text="Save Profile") - row.operator("bim.disable_editing_boundary_geometry", text="", icon="CANCEL") + add_header_apply_button(layout, "Edit Boundary Geometry", "bim.edit_boundary_geometry", "bim.disable_editing_boundary_geometry", ui_context) else: - row.operator("bim.edit_extrusion_profile", text="Save Profile") - row.operator("bim.align_view_to_profile", text="", icon="AXIS_FRONT") - row.operator("bim.disable_editing_extrusion_profile", text="", icon="CANCEL") + add_header_apply_button(layout, "Edit Profile", "bim.edit_extrusion_profile", "bim.disable_editing_extrusion_profile", ui_context) - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_E") - row.operator("bim.cad_hotkey", text="Extend").hotkey = "S_E" - - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_T") - row.operator("bim.cad_hotkey", text="Mitre").hotkey = "S_T" - - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_F") - row.operator("bim.cad_hotkey", text="Fillet").hotkey = "S_F" - - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_O") - row.operator("bim.cad_hotkey", text="Offset").hotkey = "S_O" - - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_R") - row.operator("bim.add_rectangle", text="Rectangle") - - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_C") - row.operator("bim.add_ifccircle", text="Circle") - - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_V") - row.operator("bim.set_arc_index", text="3-Point Arc") - - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_X") - row.operator("bim.reset_vertex", text="Reset Vertex") + row = layout.row(align=True) + add_layout_hotkey_operator(row, "Extend", "S_E", "Extends/reduces element to 3D cursor", ui_context) + row = row if ui_context == "TOOL_HEADER" else layout.row(align=True) + add_layout_hotkey_operator(row, "Join", "S_T", "Joins two non-parallel paths at their intersection", ui_context) + row = row if ui_context == "TOOL_HEADER" else layout.row(align=True) + add_layout_hotkey_operator(row, "Fillet", "S_V", "Fillet", ui_context) + row = row if ui_context == "TOOL_HEADER" else layout.row(align=True) + add_layout_hotkey_operator(row, "Offset", "S_O", "Offset", ui_context) + row = row if ui_context == "TOOL_HEADER" else layout.row(align=True) + add_layout_hotkey_operator(row, "Rectangle", "S_R", "Rectangle", ui_context) + row = row if ui_context == "TOOL_HEADER" else layout.row(align=True) + add_layout_hotkey_operator(row, "Circle", "S_C", "Circle", ui_context) + row = row if ui_context == "TOOL_HEADER" else layout.row(align=True) + add_layout_hotkey_operator(row, "3-Point Arc", "S_V", "3-Point Arc", ui_context) + row = row if ui_context == "TOOL_HEADER" else layout.row(align=True) + add_layout_hotkey_operator(row, "Reset Vertex", "S_X", "Reset Vertex", ui_context) elif hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.subshape_type == "AXIS": - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_Q") - row.operator("bim.edit_extrusion_axis", text="Save Axis") - row.operator("bim.disable_editing_extrusion_axis", text="", icon="CANCEL") - - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_E") - row.operator("bim.cad_hotkey", text="Extend").hotkey = "S_E" - - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_T") - row.operator("bim.cad_hotkey", text="Mitre").hotkey = "S_T" - - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_F") - row.operator("bim.cad_hotkey", text="Fillet").hotkey = "S_F" - - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_O") - row.operator("bim.cad_hotkey", text="Offset").hotkey = "S_O" + add_header_apply_button(layout, "Edit Axis", "bim.set_arc_index", "bim.set_arc_index", ui_context) + row = layout.row(align=True) + add_layout_hotkey_operator(row, "Extend", "S_E", "Extends/reduces element to 3D cursor", ui_context) + row = row if ui_context == "TOOL_HEADER" else layout.row(align=True) + add_layout_hotkey_operator(row, "Join", "S_T", "Joins two non-parallel paths at their intersection", ui_context) + row = row if ui_context == "TOOL_HEADER" else layout.row(align=True) + add_layout_hotkey_operator(row, "Fillet", "S_F", "Fillet", ui_context) + row = row if ui_context == "TOOL_HEADER" else layout.row(align=True) + add_layout_hotkey_operator(row, "Offset", "S_O", "Offset", ui_context) else: if ( @@ -141,54 +132,29 @@ class CadTool(WorkSpaceTool): and RailingData.data["pset_data"] and context.active_object.BIMRailingProperties.is_editing_path ): - row = layout.row(align=True) - row.label(text="", icon=f"EVENT_TAB") - row.operator("bim.finish_editing_railing_path") - row.operator("bim.cancel_editing_railing_path", icon="CANCEL", text="") + add_header_apply_button(layout, "Edit Railing Path", "bim.finish_editing_railing_path", "bim.cancel_editing_railing_path", ui_context) elif ( (RoofData.is_loaded or not RoofData.load()) and RoofData.data["pset_data"] and context.active_object.BIMRoofProperties.is_editing_path ): - row = layout.row(align=True) - row.label(text="", icon=f"EVENT_TAB") - row.operator("bim.finish_editing_roof_path") - row.operator("bim.cancel_editing_roof_path", icon="CANCEL", text="") - - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_R") - row.operator("bim.cad_hotkey", text="Set Gable Roof Angle").hotkey = "S_R" - - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_E") - row.operator("bim.cad_hotkey", text="Extend").hotkey = "S_E" - - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_T") - row.operator("bim.cad_hotkey", text="Mitre").hotkey = "S_T" - - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_F") - row.operator("bim.cad_hotkey", text="Fillet").hotkey = "S_F" - - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_O") - row.operator("bim.cad_hotkey", text="Offset").hotkey = "S_O" - - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="2-Point Arc", icon="EVENT_C") - - row = layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_V") - row.operator("bim.cad_hotkey", text="3-Point Arc").hotkey = "S_V" + add_header_apply_button(layout, "Edit Roof Path", "bim.finish_editing_roof_path", "bim.cancel_editing_roof_path", ui_context) + row = layout.row(align=True) + add_layout_hotkey_operator(row, "Set Gable Roof Angle", "S_R", "Set Gable Roof Angle", ui_context) + + row = layout.row(align=True) + add_layout_hotkey_operator(row, "Extend", "S_E", "Extends/reduces element to 3D cursor", ui_context) + row = row if ui_context == "TOOL_HEADER" else layout.row(align=True) + add_layout_hotkey_operator(row, "Join", "S_T", "Joins two non-parallel paths at their intersection", ui_context) + row = row if ui_context == "TOOL_HEADER" else layout.row(align=True) + add_layout_hotkey_operator(row, "Fillet", "S_V", "Fillet", ui_context) + row = row if ui_context == "TOOL_HEADER" else layout.row(align=True) + add_layout_hotkey_operator(row, "Offset", "S_O", "Offset", ui_context) + row = row if ui_context == "TOOL_HEADER" else layout.row(align=True) + add_layout_hotkey_operator(row, "2-Point Arc", "S_C", "2-Point Arc", ui_context) + row = row if ui_context == "TOOL_HEADER" else layout.row(align=True) + add_layout_hotkey_operator(row, "3-Point Arc", "S_V", "3-Point Arc", ui_context) class CadHotkey(bpy.types.Operator): @@ -196,6 +162,11 @@ class CadHotkey(bpy.types.Operator): bl_label = "CAD Hotkey" bl_options = {"REGISTER", "UNDO"} hotkey: bpy.props.StringProperty() + description: bpy.props.StringProperty() + + @classmethod + def description(cls, context, operator): + return operator.description or "" def execute(self, context): self.props = context.scene.BIMCadProperties @@ -306,3 +277,52 @@ class CadHotkey(bpy.types.Operator): and hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.subshape_type == "PROFILE" ) + + +def add_header_apply_button(layout, text, apply_operator, cancel_operator, ui_context=""): + custom_icon = custom_icon_previews.get(text.upper().replace(" ", "_"), custom_icon_previews["IFC"]).icon_id + row = layout.row(align=True) + row.label(text=f"{text} Mode", icon_value=custom_icon) + if apply_operator in ("bim.edit_arbitrary_profile", "bim.edit_extrusion_profile"): + row.operator("bim.align_view_to_profile", text="Align View" if ui_context!="TOOL_HEADER" else "", icon="AXIS_FRONT") + + row = layout.row(align=True) + row.operator(apply_operator, text="Apply" if ui_context!="TOOL_HEADER" else "", icon_value=custom_icon_previews["APPLY"].icon_id) + row.label(text="", icon="EVENT_TAB") if ui_context!="TOOL_HEADER" else row + + row = layout.row(align=True) + row.label(text="Tools") + + +def add_layout_hotkey_operator(layout, text, hotkey, description, ui_context=""): + parts = hotkey.split("_") + modifier, key = parts + op_text = "" if ui_context == "TOOL_HEADER" else text + custom_icon = custom_icon_previews.get(text.upper().replace(" ", "_"), custom_icon_previews["IFC"]).icon_id + modifier_icon, modifier_str = MODIFIERS.get(modifier, ("NONE", "")) + + row = layout if ui_context == "TOOL_HEADER" else layout.row(align=True) + op = row.operator("bim.cad_hotkey", text=op_text, icon_value=custom_icon) + + if ui_context != "TOOL_HEADER" and len(parts) == 2: + layout = layout.row(align=True) # Create a new line for hotkey display + layout.label(text="", icon=modifier_icon) + layout.label(text="", icon=f"EVENT_{key}") + + hotkey_description = f"Hotkey: {modifier_str} {key}" + description = "\n\n".join(filter(None, [hotkey_description if description else ""])) + op.hotkey = hotkey + op.description = description or hotkey_description + + return op + + + + + +custom_icon_previews = None +display_mode = None + +MODIFIERS = { + "S": ("EVENT_SHIFT", "⇧"), +} diff --git a/src/bonsai/bonsai/bim/module/model/workspace.py b/src/bonsai/bonsai/bim/module/model/workspace.py index 7802e64301..0c3a9f3f9b 100644 --- a/src/bonsai/bonsai/bim/module/model/workspace.py +++ b/src/bonsai/bonsai/bim/module/model/workspace.py @@ -73,7 +73,7 @@ class BimTool(WorkSpaceTool): bl_space_type = "VIEW_3D" bl_context_mode = "OBJECT" bl_idname = "bim.bim_tool" - bl_label = "BIM Tool" + bl_label = "Multi Object Tool" bl_description = "Create and edit elements by construction class" bl_icon = os.path.join(os.path.dirname(__file__), "ops.authoring.bim") bl_widget = None @@ -95,6 +95,7 @@ class BimTool(WorkSpaceTool): ("bim.hotkey", {"type": "Q", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_Q")]}), ("bim.hotkey", {"type": "R", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_R")]}), ("bim.hotkey", {"type": "T", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_T")]}), + ("bim.hotkey", {"type": "U", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_U")]}), ("bim.hotkey", {"type": "V", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_V")]}), ("bim.hotkey", {"type": "X", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_X")]}), ("bim.hotkey", {"type": "Y", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_Y")]}), @@ -107,7 +108,9 @@ class BimTool(WorkSpaceTool): def draw_settings(context, layout, ws_tool): # Unlike operators, Blender doesn't treat workspace tools as a class, so we'll create our own. - BimToolUI.draw(context, layout, ifc_element_type="all") + CreateObjectUI.draw(context, layout, ifc_element_type="all") + if context.active_object and context.selected_objects: + EditObjectUI.draw(context, layout) class WallTool(BimTool): @@ -122,7 +125,9 @@ class WallTool(BimTool): @classmethod def draw_settings(cls, context, layout, ws_tool): - BimToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + CreateObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + if context.active_object and context.selected_objects: + EditObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) class SlabTool(BimTool): @@ -137,7 +142,9 @@ class SlabTool(BimTool): @classmethod def draw_settings(cls, context, layout, ws_tool): - BimToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + CreateObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + if context.active_object and context.selected_objects: + EditObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) class DoorTool(BimTool): @@ -152,7 +159,9 @@ class DoorTool(BimTool): @classmethod def draw_settings(cls, context, layout, ws_tool): - BimToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + CreateObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + if context.active_object and context.selected_objects: + EditObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) class WindowTool(BimTool): @@ -167,7 +176,9 @@ class WindowTool(BimTool): @classmethod def draw_settings(cls, context, layout, ws_tool): - BimToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + CreateObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + if context.active_object and context.selected_objects: + EditObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) class ColumnTool(BimTool): @@ -182,7 +193,9 @@ class ColumnTool(BimTool): @classmethod def draw_settings(cls, context, layout, ws_tool): - BimToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + CreateObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + if context.active_object and context.selected_objects: + EditObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) class BeamTool(BimTool): @@ -197,7 +210,9 @@ class BeamTool(BimTool): @classmethod def draw_settings(cls, context, layout, ws_tool): - BimToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + CreateObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + if context.active_object and context.selected_objects: + EditObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) class DuctTool(BimTool): @@ -212,7 +227,9 @@ class DuctTool(BimTool): @classmethod def draw_settings(cls, context, layout, ws_tool): - BimToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + CreateObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + if context.active_object and context.selected_objects: + EditObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) class CableCarrierTool(BimTool): @@ -227,7 +244,9 @@ class CableCarrierTool(BimTool): @classmethod def draw_settings(cls, context, layout, ws_tool): - BimToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + CreateObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + if context.active_object and context.selected_objects: + EditObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) class PipeTool(BimTool): @@ -242,7 +261,9 @@ class PipeTool(BimTool): @classmethod def draw_settings(cls, context, layout, ws_tool): - BimToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + CreateObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + if context.active_object and context.selected_objects: + EditObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) class CableTool(BimTool): @@ -257,31 +278,28 @@ class CableTool(BimTool): @classmethod def draw_settings(cls, context, layout, ws_tool): - BimToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + CreateObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) + if context.active_object and context.selected_objects: + EditObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) def add_layout_hotkey_operator(layout, text, hotkey, description, ui_context=""): - modifier, key = hotkey.split("_") + parts = hotkey.split("_") if hotkey else [] + modifier, key = (parts + ["", ""])[:2] - try: - custom_icon = custom_icon_previews[text.upper().replace(" ", "_")].icon_id - except KeyError: - custom_icon = custom_icon_previews["IFC"].icon_id + op_text = "" if ui_context == "TOOL_HEADER" else text + custom_icon = custom_icon_previews.get(text.upper().replace(" ", "_"), custom_icon_previews["IFC"]).icon_id + modifier_icon, modifier_str = MODIFIERS.get(modifier, ("NONE", "")) - modifier_icon, modifier_str = MODIFIERS[modifier] + row = layout.row(align=True) + op = row.operator("bim.hotkey", text=op_text, icon_value=custom_icon) - if ui_context == "TOOL_HEADER": - op = layout.operator("bim.hotkey", text="", icon_value=custom_icon) - else: - row = layout.row(align=True) - op = row.operator("bim.hotkey", text=text, icon_value=custom_icon) + if ui_context != "TOOL_HEADER": row.label(text="", icon=modifier_icon) - row.label(text="", icon=f"EVENT_{key}") + row.label(text="", icon=f"EVENT_{key}" if key else "BLANK1") - hotkey_description = f"Hotkey: {modifier_str} {key}" - if description: - description += "\n\n" - description += hotkey_description + hotkey_description = f"Hotkey: {modifier_str} {key}".strip() + description = "\n\n".join(filter(None, [description, hotkey_description])) op.hotkey = hotkey if ui_context == "TOOL_HEADER": @@ -291,7 +309,12 @@ def add_layout_hotkey_operator(layout, text, hotkey, description, ui_context="") return op -class BimToolUI: +def format_ifc_camel_case(string): + string = string.replace("Ifc", "") + return ''.join(' ' + char if char.isupper() else char for char in string).strip() + + +class CreateObjectUI: @classmethod def draw(cls, context, layout, ifc_element_type=None): cls.layout = layout @@ -312,51 +335,71 @@ class BimToolUI: elif AuthoringData.data["ifc_element_type"] != ifc_element_type: AuthoringData.load(ifc_element_type) - if context.region.type == "TOOL_HEADER": - cls.draw_container(context) - cls.draw_type_selection_interface(context) - if context.active_object and context.selected_objects: - cls.draw_edit_object_header_interface(context) + if ifc_element_type and context.region.type == "TOOL_HEADER": + tool_name = "Multi Object Tool" if ifc_element_type == "all" else format_ifc_camel_case(ifc_element_type.removesuffix('Type')) + ' Tool' + cls.layout.label(text=tool_name, icon="TOOL_SETTINGS") - elif context.region.type in ("UI", "WINDOW"): - cls.draw_container(context) - cls.draw_thumbnail() - cls.draw_type_selection_interface(context) - if context.active_object and context.selected_objects: - cls.draw_edit_object_panel_interface(context) + cls.draw_type_manager_launcher(context) + cls.draw_thumbnail() if context.region.type != "TOOL_HEADER" else cls + cls.draw_add_object(context) + + @classmethod + def draw_container_info(cls, context): + text = AuthoringData.data["default_container"] + if context.region.type == "UI": + text = f"Container: {text}" - if not context.selected_objects: - cls.draw_create_object_interface() + cls.layout.row(align=True).label(text=text, icon="OUTLINER_COLLECTION") @classmethod - def draw_create_object_interface(cls): + def draw_type_manager_launcher(cls, context): + ui_context = str(context.region.type) + row = cls.layout.row(align=True) + if not AuthoringData.data["ifc_classes"]: + if AuthoringData.data["ifc_element_type"]: + row.label(text=f"No {AuthoringData.data['ifc_element_type']} Found", icon="ERROR") + row = cls.layout.row(align=True) + op = row.operator( + "bim.add_default_type", + icon_value=custom_icon_previews["ADD_TYPE"].icon_id, + text=f"Create default {AuthoringData.data['ifc_element_type']}", + ) + op.ifc_element_type = AuthoringData.data["ifc_element_type"] + else: + row.label(text="No Element Types Found", icon="ERROR") + row = cls.layout.row(align=True) + row.operator("bim.launch_type_manager", icon=tool.Blender.TYPE_MANAGER_ICON, text="Launch Type Manager") + + @classmethod + def draw_add_object(cls, context): + ui_context = str(context.region.type) + row = cls.layout.row(align=True) if not AuthoringData.data["relating_type_id"]: return + if cls.props.ifc_class == "IfcWallType": - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="rl1", text="RL") - - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="extrusion_depth", text="Height") - - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="length", text="Length") - - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="x_angle", text="Angle") + row.prop(data=cls.props, property="rl1", text="Relative Level" if ui_context != "TOOL_HEADER" else "RL") + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + row.prop(data=cls.props, property="extrusion_depth", text="Height" if ui_context != "TOOL_HEADER" else "H") + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + row.prop(data=cls.props, property="length", text="Length" if ui_context != "TOOL_HEADER" else "L") + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + row.prop(data=cls.props, property="x_angle", text="Slope") if ui_context != "TOOL_HEADER" else "A" + elif cls.props.ifc_class in ("IfcSlabType", "IfcRampType", "IfcRoofType"): - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="x_angle", text="Angle") - elif cls.props.ifc_class in ("IfcColumnType", "IfcBeamType", "IfcMemberType"): - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="cardinal_point", text="Axis") + row.prop(data=cls.props, property="x_angle", text="Slope" if ui_context != "TOOL_HEADER" else "A", icon="FILE_3D") + + elif cls.props.ifc_class in ("IfcColumnType", "IfcMemberType"): + row.prop(data=cls.props, property="cardinal_point", text="Axis") + row.prop(data=cls.props, property="extrusion_depth", text="Height" if ui_context !="TOOL_HEADER" else "H") + + elif cls.props.ifc_class in ("IfcBeamType"): + row.prop(data=cls.props, property="cardinal_point", text="Axis") + row.prop(data=cls.props, property="extrusion_depth", text="Length" if ui_context !="TOOL_HEADER" else "L") - row = cls.layout.row(align=True) - label = "Height" if cls.props.ifc_class == "IfcColumn" else "Length" - row.prop(data=cls.props, property="extrusion_depth", text=label) elif cls.props.ifc_class in ("IfcDoorType", "IfcDoorStyle"): - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="rl1", text="RL") + row.prop(data=cls.props, property="rl1", text="Relative Level" if ui_context != "TOOL_HEADER" else "RL") + elif cls.props.ifc_class in ( "IfcWindowType", "IfcWindowStyle", @@ -367,604 +410,30 @@ class BimToolUI: "IfcCableCarrierSegmentType", "IfcCableSegmentType", ): - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="rl2", text="RL") + row.prop(data=cls.props, property="rl2", text="Relative Level (rl2)" if ui_context != "TOOL_HEADER" else "RL") + + ### this neeeds to move elif cls.props.ifc_class in ("IfcSpaceType"): - add_layout_hotkey_operator(cls.layout, "Generate", "S_G", "Generate a space at the cursor position") + add_layout_hotkey_operator(cls.layout, "Generate", "S_G", bpy.ops.bim.generate_space.__doc__, ui_context) + ### else: row = cls.layout.row(align=True) - row.prop(data=cls.props, property="rl_mode", text="RL") + row.prop(data=cls.props, property="rl_mode", text="RL Mode" if ui_context != "TOOL_HEADER" else "RL") - @classmethod - def draw_edit_object_panel_interface(cls, context): - ui_context = str(context.region.type) - if AuthoringData.data["active_material_usage"] == "LAYER2": - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="extrusion_depth", text="Height:") - op = row.operator("bim.change_extrusion_depth", icon="FILE_REFRESH", text="") - op.depth = cls.props.extrusion_depth - - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="length", text="Length:") - op = row.operator("bim.change_layer_length", icon="FILE_REFRESH", text="") - op.length = cls.props.length - - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="x_angle", text="Angle:") - op = row.operator("bim.change_extrusion_x_angle", icon="FILE_REFRESH", text="") - op.x_angle = cls.props.x_angle - - cls.layout.separator() - - add_layout_hotkey_operator(cls.layout, "Regen", "S_G", "Regenerate selected Element", ui_context) - - cls.layout.separator() - - add_layout_hotkey_operator( - cls.layout, - "Extend", - "S_E", - "Extends/ reduces Element to 3D cursor", - ui_context, - ) - add_layout_hotkey_operator( - cls.layout, - "Butt", - "S_T", - "Intersects two non-parallel Elements to a butt corner junction", - ui_context, - ) - add_layout_hotkey_operator( - cls.layout, - "Mitre", - "S_Y", - "Intersects two non-parallel Elements to a mitred corner junction", - ui_context, - ) - - row = cls.layout.row(align=True) - row.operator( - "bim.unjoin_walls", text="Unjoin Walls", icon_value=custom_icon_previews["UNJOIN_WALLS"].icon_id - ) - cls.layout.separator() - - add_layout_hotkey_operator(cls.layout, "Merge", "S_M", "Merge selected Elements", ui_context) - add_layout_hotkey_operator( - cls.layout, - "Split", - "S_K", - "Split selected Element into two Elements at the cursor location", - ui_context, - ) - add_layout_hotkey_operator( - cls.layout, "Rotate 90", "S_R", "Rotate the selected Element by 90 degrees", ui_context - ) - add_layout_hotkey_operator( - cls.layout, "Flip", "S_F", "Flip Element about its local axes, keep the position", ui_context - ) - - # row.operator("bim.unjoin_walls", icon="X", text="") - - elif AuthoringData.data["active_material_usage"] == "LAYER3": - if len(context.selected_objects) == 1: - add_layout_hotkey_operator( - cls.layout, "Edit Profile", "S_E", "Edit associated Profile Element", ui_context - ) - elif "LAYER2" in AuthoringData.data["selected_material_usages"]: - add_layout_hotkey_operator( - cls.layout, - "Extend Wall To Slab", - "S_E", - "Extend the selected Wall to the selected Slab", - ui_context, - ) - - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="x_angle", text="Angle") - op = row.operator("bim.change_extrusion_x_angle", icon="FILE_REFRESH", text="") - op.x_angle = cls.props.x_angle - - elif AuthoringData.data["active_material_usage"] == "PROFILE": - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="cardinal_point", text="Axis") - op = row.operator("bim.change_cardinal_point", icon="FILE_REFRESH", text="") - op.cardinal_point = int(cls.props.cardinal_point) - - row = cls.layout.row(align=True) - label = ( - "Height" if AuthoringData.data["active_class"] in ("IfcColumn", "IfcColumnStandardCase") else "Length" - ) - row.prop(data=cls.props, property="extrusion_depth", text=label) - op = row.operator("bim.change_profile_depth", icon="FILE_REFRESH", text="") - op.depth = cls.props.extrusion_depth - - add_layout_hotkey_operator( - cls.layout, - "Extend", - "S_E", - "Extends/ reduces element to 3D cursor", - ui_context, - ) - add_layout_hotkey_operator( - cls.layout, "Flip", "S_F", "Flip object about its local axes, keep the position", ui_context - ) - - if AuthoringData.data["active_class"] in ( - "IfcCableCarrierSegment", - "IfcCableSegment", - "IfcDuctSegment", - "IfcPipeSegment", - ): - - add_layout_hotkey_operator( - cls.layout, - "Add Fitting", - "S_Y", - "Add a Fitting based on currently selected Elements and cursor", - ui_context, - ) - cls.layout.operator("bim.mep_add_bend") - cls.layout.operator("bim.mep_add_transition") - cls.layout.operator("bim.mep_add_obstruction") - - else: - add_layout_hotkey_operator( - cls.layout, - "Edit Axis", - "A_E", - "Edit axis of element", - ui_context, - ) - add_layout_hotkey_operator( - cls.layout, - "Butt", - "S_T", - "Intersects two non-parallel Elements to a butt corner junction", - ui_context, - ) - add_layout_hotkey_operator( - cls.layout, - "Mitre", - "S_Y", - "Intersects two non-parallel Elements to a mitred corner junction", - ui_context, - ) - add_layout_hotkey_operator( - cls.layout, "Rotate 90", "S_R", "Rotate the selected element by 90 degrees", ui_context - ) - add_layout_hotkey_operator(cls.layout, "Regen", "S_G", "Regenerate selected element", ui_context) - row.operator("bim.extend_profile", icon="X", text="").join_type = "" - - elif ( - tool.Model.is_parametric_railing_active() and not context.active_object.BIMRailingProperties.is_editing_path - ): - # NOTE: should be above "active_representation_type" = "SweptSolid" check - # because it could be a SweptSolid too - row = cls.layout.row(align=True) - row.label(text="", icon="EVENT_TAB") - row.operator("bim.enable_editing_railing_path", text="Edit Railing Path") - - elif AuthoringData.data["active_class"] in ( - "IfcWindow", - "IfcWindowStandardCase", - "IfcDoor", - "IfcDoorStandardCase", - ): - if AuthoringData.data["active_class"] in ("IfcWindow", "IfcWindowStandardCase"): - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="rl2", text="RL") - elif AuthoringData.data["active_class"] in ("IfcDoor", "IfcDoorStandardCase"): - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="rl1", text="RL") - - add_layout_hotkey_operator(cls.layout, "Regen", "S_G", "Regenerate selected Element", ui_context) - add_layout_hotkey_operator( - cls.layout, "Flip", "S_F", "Flip object about its local axes, keep the position", ui_context - ) - - elif AuthoringData.data["active_representation_type"] == "SweptSolid": - if not tool.Model.is_parametric_window_active() and not tool.Model.is_parametric_door_active(): - add_layout_hotkey_operator( - cls.layout, "Edit Profile", "S_E", "Edit assosiated Profile Element", ui_context - ) - - elif AuthoringData.data["active_class"] in ("IfcSpace",): - add_layout_hotkey_operator(cls.layout, "Regen", "S_G", "Regenerate selected Space", ui_context) - - elif tool.Model.is_parametric_roof_active() and not context.active_object.BIMRoofProperties.is_editing_path: - row = cls.layout.row(align=True) - row.label(text="", icon="EVENT_TAB") - row.operator("bim.enable_editing_roof_path", text="Edit Roof Path") - - if context.region.type != "TOOL_HEADER" and PortData.data["total_ports"] > 0: - add_layout_hotkey_operator( - cls.layout, - "Regen MEP", - "S_G", - "Regenerates the positions and segment lengths of a distribution element and all connected elements", - ui_context, - ) - cls.layout.operator("bim.mep_connect_elements") - - row = cls.layout.row(align=True) - if len(context.selected_objects) > 1: - row.operator("bim.add_opening", text="Apply Opening", icon_value=custom_icon_previews["VOID"].icon_id) - else: - row.operator( - "bim.add_potential_opening", text="Add Opening", icon_value=custom_icon_previews["ADD_VOID"].icon_id - ) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_O") - - if AuthoringData.data["is_voidable_element"]: - if AuthoringData.data["has_visible_openings"]: - row = cls.layout.row(align=True) - row.operator("bim.edit_openings", icon="CHECKMARK", text="") - row.operator("bim.hide_openings", icon="CANCEL", text="") - - if AuthoringData.data["active_class"] in ("IfcOpeningElement",): - row.operator("bim.edit_openings", icon="CHECKMARK", text="") - row.operator("bim.hide_openings", icon="CANCEL", text="") - if len(context.selected_objects) == 2: - row = cls.layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_L") - row.operator("bim.clone_opening", text="Clone Opening") - - cls.layout.row(align=True).label(text="Align") - add_layout_hotkey_operator( - cls.layout, - "Exterior", - "S_X", - "Align selected Elements to the Exterior of the last selected object", - ui_context, - ) - add_layout_hotkey_operator( - cls.layout, - "Centerline", - "S_C", - "Align selected Elements to the Centerline of the last selected object", - ui_context, - ) - add_layout_hotkey_operator( - cls.layout, - "Interior", - "S_V", - "Align selected Elements to the Interior of the last selected object", - ui_context, - ) - add_layout_hotkey_operator( - cls.layout, - "Mirror", - "S_M", - "Mirrors the selected Elements along a mirror plane set by the last selected object", - ui_context, - ) - - cls.layout.row(align=True).label(text="Mode") - add_layout_hotkey_operator( - cls.layout, "Opening", "A_O", "Toggle display of Openings in the selected Elements", ui_context - ) - add_layout_hotkey_operator(cls.layout, "Decomposition", "A_D", "Select decomposition", ui_context) - - cls.layout.row(align=True).label(text="Aggregation") - add_layout_hotkey_operator(cls.layout, "Assign", "C_P", "Assign element to Aggregation", ui_context) - add_layout_hotkey_operator(cls.layout, "Unassign", "A_P", "Unassign element from Aggregation", ui_context) - - cls.layout.row(align=True).label(text="Qto") - add_layout_hotkey_operator( - cls.layout, - "Perform Quantity Take-off", - "S_Q", - "Perform a quantity take off on selected objects based of a QTO rule configuration", - ui_context, - ) - - @classmethod - def draw_edit_object_header_interface(cls, context): - ui_context = str(context.region.type) - if AuthoringData.data["active_material_usage"] == "LAYER2": - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="extrusion_depth", text="Height:") - op = row.operator("bim.change_extrusion_depth", icon="FILE_REFRESH", text="") - op.depth = cls.props.extrusion_depth - - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="length", text="Length:") - op = row.operator("bim.change_layer_length", icon="FILE_REFRESH", text="") - op.length = cls.props.length - - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="x_angle", text="Angle:") - op = row.operator("bim.change_extrusion_x_angle", icon="FILE_REFRESH", text="") - op.x_angle = cls.props.x_angle - - row = cls.layout.row() - add_layout_hotkey_operator(row, "Regen", "S_G", "Regenerate selected Element", ui_context) - - row = cls.layout.row(align=True) - add_layout_hotkey_operator( - row, - "Extend", - "S_E", - "Extends/reduces Element to 3D cursor", - ui_context, - ) - add_layout_hotkey_operator( - row, - "Butt", - "S_T", - "Intersects two non-parallel Elements to a butt corner junction", - ui_context, - ) - add_layout_hotkey_operator( - row, - "Mitre", - "S_Y", - "Intersects two non-parallel Elements to a mitred corner junction", - ui_context, - ) - row.operator("bim.unjoin_walls", text="", icon_value=custom_icon_previews["UNJOIN_WALLS"].icon_id) - - row = cls.layout.row(align=True) - add_layout_hotkey_operator(row, "Merge", "S_M", "Merge selected Elements", ui_context) - add_layout_hotkey_operator( - row, "Split", "S_K", "Split selected Element into two Elements at the cursor location", ui_context - ) - add_layout_hotkey_operator(row, "Rotate 90", "S_R", "Rotate the selected Element by 90 degrees", ui_context) - add_layout_hotkey_operator( - row, "Flip", "S_F", "Flip Element about its local axes, keep the position", ui_context - ) - - elif AuthoringData.data["active_material_usage"] == "LAYER3": - if len(context.selected_objects) == 1: - add_layout_hotkey_operator( - cls.layout, "Edit Profile", "S_E", "Edit associated Profile Element", ui_context - ) - elif "LAYER2" in AuthoringData.data["selected_material_usages"]: - add_layout_hotkey_operator( - cls.layout, - "Extend Wall To Slab", - "S_E", - "Extend the selected Wall to the selected Slab", - ui_context, - ) - - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="x_angle", text="Angle") - op = row.operator("bim.change_extrusion_x_angle", icon="FILE_REFRESH", text="") - op.x_angle = cls.props.x_angle - - elif AuthoringData.data["active_material_usage"] == "PROFILE": - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="cardinal_point", text="Axis") - op = row.operator("bim.change_cardinal_point", icon="FILE_REFRESH", text="") - op.cardinal_point = int(cls.props.cardinal_point) - - row = cls.layout.row(align=True) - label = ( - "Height" if AuthoringData.data["active_class"] in ("IfcColumn", "IfcColumnStandardCase") else "Length" - ) - row.prop(data=cls.props, property="extrusion_depth", text=label) - op = row.operator("bim.change_profile_depth", icon="FILE_REFRESH", text="") - op.depth = cls.props.extrusion_depth - - add_layout_hotkey_operator( - cls.layout, - "Extend", - "S_E", - "Extends/ reduces element to 3D cursor", - ui_context, - ) - add_layout_hotkey_operator( - cls.layout, "Flip", "S_F", "Flip object about its local axes, keep the position", ui_context - ) - - if AuthoringData.data["active_class"] in ( - "IfcCableCarrierSegment", - "IfcCableSegment", - "IfcDuctSegment", - "IfcPipeSegment", - ): - - add_layout_hotkey_operator( - cls.layout, - "Add Fitting", - "S_Y", - "Add a Fitting based on currently selected Elements and cursor", - ui_context, - ) - if context.region.type != "TOOL_HEADER": - cls.layout.operator("bim.mep_add_bend") - cls.layout.operator("bim.mep_add_transition") - cls.layout.operator("bim.mep_add_obstruction") - - else: - add_layout_hotkey_operator( - cls.layout, - "Edit Axis", - "A_E", - "Edit axis of Element", - ui_context, - ) - add_layout_hotkey_operator( - cls.layout, - "Butt", - "S_T", - "Intersects two non-parallel Elements to a butt corner junction", - ui_context, - ) - add_layout_hotkey_operator( - cls.layout, - "Mitre", - "S_Y", - "Intersects two non-parallel Elements to a mitred corner junction", - ui_context, - ) - add_layout_hotkey_operator( - cls.layout, "Rotate 90", "S_R", "Rotate the selected element by 90 degrees", ui_context - ) - add_layout_hotkey_operator(cls.layout, "Regen", "S_G", "Regenerate selected element", ui_context) - row.operator("bim.extend_profile", icon="X", text="").join_type = "" - - elif ( - tool.Model.is_parametric_railing_active() and not context.active_object.BIMRailingProperties.is_editing_path - ): - # NOTE: should be above "active_representation_type" = "SweptSolid" check - # because it could be a SweptSolid too - row = cls.layout.row(align=True) - row.label(text="", icon="EVENT_TAB") - row.operator("bim.enable_editing_railing_path", text="Edit Railing Path") - - elif AuthoringData.data["active_class"] in ( - "IfcWindow", - "IfcWindowStandardCase", - "IfcDoor", - "IfcDoorStandardCase", - ): - if AuthoringData.data["active_class"] in ("IfcWindow", "IfcWindowStandardCase"): - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="rl2", text="RL") - elif AuthoringData.data["active_class"] in ("IfcDoor", "IfcDoorStandardCase"): - row = cls.layout.row(align=True) - row.prop(data=cls.props, property="rl1", text="RL") - - add_layout_hotkey_operator(cls.layout, "Regen", "S_G", "Regenerate selected Element", ui_context) - add_layout_hotkey_operator( - cls.layout, "Flip", "S_F", "Flip object about its local axes, keep the position", ui_context - ) - - elif AuthoringData.data["active_representation_type"] == "SweptSolid": - if not tool.Model.is_parametric_window_active() and not tool.Model.is_parametric_door_active(): - add_layout_hotkey_operator( - cls.layout, "Edit Profile", "S_E", "Edit assosiated Profile Element", ui_context - ) - - elif AuthoringData.data["active_class"] in ("IfcSpace",): - add_layout_hotkey_operator(cls.layout, "Regen", "S_G", "Regenerate selected Space", ui_context) - - elif tool.Model.is_parametric_roof_active() and not context.active_object.BIMRoofProperties.is_editing_path: - row = cls.layout.row(align=True) - row.label(text="", icon="EVENT_TAB") - row.operator("bim.enable_editing_roof_path", text="Edit Roof Path") - - if context.region.type != "TOOL_HEADER" and PortData.data["total_ports"] > 0: - add_layout_hotkey_operator( - cls.layout, - "Regen MEP", - "S_G", - "Regenerates the positions and segment lengths of a distribution element and all connected elements", - ui_context, - ) - cls.layout.operator("bim.mep_connect_elements") - - row = cls.layout.row(align=True) - if len(context.selected_objects) > 1: - row.operator("bim.add_opening", text="", icon_value=custom_icon_previews["VOID"].icon_id) - else: - row.operator("bim.add_potential_opening", text="", icon_value=custom_icon_previews["ADD_VOID"].icon_id) - - if AuthoringData.data["is_voidable_element"]: - if AuthoringData.data["has_visible_openings"]: - row = cls.layout.row(align=True) - row.operator("bim.edit_openings", icon="CHECKMARK", text="") - row.operator("bim.hide_openings", icon="CANCEL", text="") - - if AuthoringData.data["active_class"] in ("IfcOpeningElement",): - row.operator("bim.edit_openings", icon="CHECKMARK", text="") - row.operator("bim.hide_openings", icon="CANCEL", text="") - if len(context.selected_objects) == 2: - row = cls.layout.row(align=True) - row.label(text="", icon="EVENT_SHIFT") - row.label(text="", icon="EVENT_L") - row.operator("bim.clone_opening", text="Clone Opening") - - row = cls.layout.row(align=True) - add_layout_hotkey_operator( - row, "Exterior", "S_X", "Align selected Elements to the Exterior of the last selected object", ui_context - ) - add_layout_hotkey_operator( - row, - "Centerline", - "S_C", - "Align selected Elements to the Centerline of the last selected object", - ui_context, - ) - add_layout_hotkey_operator( - row, "Interior", "S_V", "Align selected Elements to the Interior of the last selected object", ui_context - ) - add_layout_hotkey_operator( - row, - "Mirror", - "S_M", - "Mirrors the selected Elements along a mirror plane set by the last selected object", - ui_context, - ) - - row = cls.layout.row(align=True) - add_layout_hotkey_operator( - row, "Opening", "A_O", "Toggle display of Openings in the selected Elements", ui_context - ) - add_layout_hotkey_operator(row, "Decomposition", "A_D", "Select decomposition", ui_context) - - row = cls.layout.row(align=True) - add_layout_hotkey_operator(row, "Assign", "C_P", "Assign element to Aggregation", ui_context) - add_layout_hotkey_operator(row, "Unassign", "A_P", "Unassign element from Aggregation", ui_context) - - row = cls.layout.row() - add_layout_hotkey_operator( - row, - "Perform Quantity Take-off", - "S_Q", - "Perform a quantity take off on selected objects based of a QTO rule configuration", - ui_context, - ) - - @classmethod - def draw_container(cls, context): - text = AuthoringData.data["default_container"] - if context.region.type == "UI": - text = f"Container: {text}" - - cls.layout.row(align=True).label(text=text, icon="OUTLINER_COLLECTION") - - @classmethod - def draw_type_selection_interface(cls, context): - # shared by both sidebar and header - ui_context = str(context.region.type) - row = cls.layout.row(align=True) if AuthoringData.data["ifc_classes"]: - if ui_context == "UI": - text = "Add" - else: - text = "" - if not AuthoringData.data["ifc_element_type"]: - prop_with_search(row, cls.props, "ifc_class", text="") - - row = cls.layout.row(align=True) + if not AuthoringData.data["ifc_element_type"]: + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + prop_with_search(row, cls.props, "ifc_class", text="Type Class" if ui_context != "TOOL_HEADER" else "") if AuthoringData.data["relating_type_id"]: - prop_with_search(row, cls.props, "relating_type_id", text="") - row.operator("bim.add_constr_type_instance", text=text, icon_value=custom_icon_previews[f"ADD"].icon_id) - + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + prop_with_search(row, cls.props, "relating_type_id", text="Relating Type" if ui_context != "TOOL_HEADER" else "") + row.operator("bim.launch_type_manager", icon=tool.Blender.TYPE_MANAGER_ICON, text="") + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Add", "S_A", bpy.ops.bim.add_constr_type_instance.__doc__, ui_context) + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Draw", "S_P", bpy.ops.bim.draw_polyline_wall.__doc__, ui_context) if cls.props.ifc_class == "IfcWallType" else row else: row.label(text="No Construction Type", icon="FILE_3D") - row.operator("bim.launch_type_manager", icon=tool.Blender.TYPE_MANAGER_ICON, text="") - else: - if AuthoringData.data["ifc_element_type"]: - row.label(text=f"No {AuthoringData.data['ifc_element_type']} Found", icon="ERROR") - row = cls.layout.row() - row.prop(cls.props, "type_name") - row = cls.layout.row(align=True) - op = row.operator( - "bim.add_default_type", - icon_value=custom_icon_previews["ADD_TYPE"].icon_id, - text=f"Create {AuthoringData.data['ifc_element_type']}", - ) - op.ifc_element_type = AuthoringData.data["ifc_element_type"] - row.operator("bim.launch_type_manager", icon=tool.Blender.TYPE_MANAGER_ICON, text="") - else: - row.label(text="No Element Types Found", icon="ERROR") - row.operator("bim.launch_type_manager", icon=tool.Blender.TYPE_MANAGER_ICON, text="Launch Type Manager") @classmethod def draw_thumbnail(cls): @@ -978,9 +447,289 @@ class BimToolUI: op.ifc_class = cls.props.ifc_class +class EditObjectUI: + @classmethod + def draw(cls, context, layout, ifc_element_type=None): + cls.layout = layout + cls.props = context.scene.BIMModelProperties + + row = cls.layout.row(align=True) + row.separator() + if not tool.Ifc.get(): + row.label(text="No IFC Project", icon="ERROR") + return + + if not PortData.is_loaded: + PortData.load() + + if not AuthoringData.is_loaded: + AuthoringData.load(ifc_element_type) + elif ifc_element_type == "all" and AuthoringData.data["ifc_element_type"] is not None: + AuthoringData.load("all") + elif AuthoringData.data["ifc_element_type"] != ifc_element_type: + AuthoringData.load(ifc_element_type) + + if context.region.type == "TOOL_HEADER": + text = format_ifc_camel_case(AuthoringData.data["active_class"]) + layout.label(text=f"{text} Edit Tools:", icon="RESTRICT_SELECT_OFF") + cls.draw_parameter_adjustments(context) + row = cls.draw_operations(context) + cls.draw_void(context, row) + cls.draw_align(context) + cls.draw_aggregation(context) + cls.draw_qto(context) + cls.draw_modes(context) + + if context.region.type in ("UI", "WINDOW"): + text = format_ifc_camel_case(AuthoringData.data["active_class"]) + layout.label(text=f"{text} Edit Tools:", icon="RESTRICT_SELECT_OFF") + cls.draw_parameter_adjustments(context) + row = cls.draw_operations(context) + cls.draw_void(context, row) + cls.draw_align(context) + cls.draw_aggregation(context) + cls.draw_qto(context) + cls.draw_modes(context) + + @classmethod + def draw_parameter_adjustments(cls, context): + ui_context = str(context.region.type) + row = cls.layout.row(align=True) + row.label(text="Parameter Adjustments") if ui_context != "TOOL_HEADER" else row + row = cls.layout.row(align=True) + + if AuthoringData.data["active_material_usage"] == "LAYER2": + row.prop(data=cls.props, property="extrusion_depth", text="Height" if ui_context != "TOOL_HEADER" else "H") + op = row.operator("bim.change_extrusion_depth", icon="FILE_REFRESH", text="") + op.depth = cls.props.extrusion_depth + + row = cls.layout.row(align=True)if ui_context != "TOOL_HEADER" else row + row.prop(data=cls.props, property="length", text="Length" if ui_context != "TOOL_HEADER" else "L") + op = row.operator("bim.change_layer_length", icon="FILE_REFRESH", text="") + op.length = cls.props.length + + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + row.prop(data=cls.props, property="x_angle", text="Slope" if ui_context != "TOOL_HEADER" else "A") + op = row.operator("bim.change_extrusion_x_angle", icon="FILE_REFRESH", text="") + op.x_angle = cls.props.x_angle + + elif AuthoringData.data["active_material_usage"] == "LAYER3": + row.prop(data=cls.props, property="x_angle", text="Angle" if ui_context != "TOOL_HEADER" else "A") + op = row.operator("bim.change_extrusion_x_angle", icon="FILE_REFRESH", text="") + op.x_angle = cls.props.x_angle + + elif AuthoringData.data["active_material_usage"] == "PROFILE": + row.prop(data=cls.props, property="cardinal_point", text="Axis" if ui_context != "TOOL_HEADER" else "") + op = row.operator("bim.change_cardinal_point", icon="FILE_REFRESH", text="") + op.cardinal_point = int(cls.props.cardinal_point) + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + label = ("Height" if AuthoringData.data["active_class"] in ("IfcColumn", "IfcColumnStandardCase") else "Length") + row.prop(data=cls.props, property="extrusion_depth", text=label if ui_context != "TOOL_HEADER" else label[0]) + op = row.operator("bim.change_profile_depth", icon="FILE_REFRESH", text="") + op.depth = cls.props.extrusion_depth + + elif AuthoringData.data["active_class"] in ("IfcWindow","IfcWindowStandardCase","IfcDoor","IfcDoorStandardCase"): + if AuthoringData.data["active_class"] in ("IfcWindow", "IfcWindowStandardCase"): + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + row.prop(data=cls.props, property="rl2", text="Relative Level (rl2)" if ui_context != "TOOL_HEADER" else "RL") + elif AuthoringData.data["active_class"] in ("IfcDoor", "IfcDoorStandardCase"): + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + row.prop(data=cls.props, property="rl1", text="Relative Level (rl1)" if ui_context != "TOOL_HEADER" else "RL") + + @classmethod + def draw_operations(cls, context): + ui_context = str(context.region.type) + + row = cls.layout.row(align=True) + row.separator() + row.label(text="Operations") if ui_context != "TOOL_HEADER" else row + cls.draw_regen_operations(row) + + if AuthoringData.data["active_material_usage"] == "LAYER2": + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Extend", "S_E", "Extends/reduces element to 3D cursor", ui_context) + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Butt", "S_T", "Intersects two non-parallel elements to a butt corner junction", ui_context) + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Mitre", "S_Y", "Intersects two non-parallel elements to a mitred corner junction", ui_context) + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Unjoin Walls", "S_U", "", ui_context) + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Merge", "S_M", "Merge selected Elements", ui_context) + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Split", "S_K", "Split selected Element into two Elements at the cursor location", ui_context) + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Rotate 90", "S_R", "Rotate the selected Element by 90 degrees", ui_context) + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Flip", "S_F", "Flip Element about its local axes, keep the position", ui_context) + + elif AuthoringData.data["active_material_usage"] == "LAYER3": + if "LAYER2" in AuthoringData.data["selected_material_usages"]: + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(cls.layout, "Extend Wall To Slab", "S_E", "", ui_context) + + elif AuthoringData.data["active_material_usage"] == "PROFILE": + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Extend", "S_E", "", ui_context) + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Flip", "S_F", bpy.ops.bim.flip_object.__doc__, ui_context) + + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + if AuthoringData.data["active_class"] in ("IfcCableCarrierSegment","IfcCableSegment","IfcDuctSegment","IfcPipeSegment"): + add_layout_hotkey_operator(row, "Add Fitting", "S_Y", "", ui_context) + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + row.operator("bim.mep_add_bend", text="Add Bend" if ui_context != "TOOL_HEADER" else "", icon_value=custom_icon_previews["ADD_BEND"].icon_id) + row.label(text="", icon="BLANK1") if ui_context != "TOOL_HEADER" else row + row.label(text="", icon="BLANK1") if ui_context != "TOOL_HEADER" else row + + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + row.operator("bim.mep_add_transition", text="Add Transition" if ui_context != "TOOL_HEADER" else "", icon_value=custom_icon_previews["ADD_TRANSITION"].icon_id) + row.label(text="", icon="BLANK1") if ui_context != "TOOL_HEADER" else row + row.label(text="", icon="BLANK1") if ui_context != "TOOL_HEADER" else row + + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + row.operator("bim.mep_add_obstruction", text="Add Obstruction" if ui_context != "TOOL_HEADER" else "", icon_value=custom_icon_previews["IFC"].icon_id) + row.label(text="", icon="BLANK1") if ui_context != "TOOL_HEADER" else row + row.label(text="", icon="BLANK1") if ui_context != "TOOL_HEADER" else row + + else: + add_layout_hotkey_operator( row, "Edit Axis", "A_E", "", ui_context) + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Butt", "S_T", "", ui_context) + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Mitre", "S_Y", "", ui_context) + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Rotate 90", "S_R", bpy.ops.bim.rotate_90.__doc__, ui_context) + + if PortData.data["total_ports"] > 0: + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + row.operator("bim.mep_connect_elements", text="Connect MEP Elements" if ui_context != "TOOL_HEADER" else "", icon_value=custom_icon_previews["CONNECT_MEP_ELEMENTS"].icon_id) + row.label(text="", icon="BLANK1") if ui_context != "TOOL_HEADER" else row + row.label(text="", icon="BLANK1") if ui_context != "TOOL_HEADER" else row + + return row + + @classmethod + def draw_regen_operations(cls, row): + custom_icon = custom_icon_previews.get("REGEN", custom_icon_previews["IFC"]).icon_id + + if AuthoringData.data["active_material_usage"] == "LAYER2": + op = row.operator("bim.hotkey", text="", icon_value=custom_icon) + description = f"{bpy.ops.bim.recalculate_wall.__doc__}\n\nHotkey: S G" + op.hotkey = "S_G" + op.description = description.strip() + elif AuthoringData.data["active_material_usage"] == "PROFILE": + if AuthoringData.data["active_class"] in ("IfcCableCarrierSegment","IfcCableSegment","IfcDuctSegment","IfcPipeSegment"): + pass + else: + op = row.operator("bim.hotkey", text="", icon_value=custom_icon) + description = f"{bpy.ops.bim.recalculate_profile.__doc__}\n\nHotkey: S G" + op.hotkey = "S_G" + op.description = description.strip() + + if PortData.data["total_ports"] > 0: + op = row.operator("bim.hotkey", text="", icon_value=custom_icon) + description = f"{bpy.ops.bim.regenerate_distribution_element.__doc__}\n\nHotkey: S G" + op.hotkey = "S_G" + op.description = description.strip() + + @classmethod + def draw_void(cls, context, row): + ui_context = str(context.region.type) + + if len(context.selected_objects) > 1: + op_text = "Apply Void" if ui_context != "TOOL_HEADER" else "" + op_icon = custom_icon_previews["APPLY_VOID"].icon_id + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + row.operator("bim.add_opening", text=op_text, icon_value=op_icon) + else: + op_text = "Add Void" if ui_context != "TOOL_HEADER" else "" + op_icon = custom_icon_previews["ADD_VOID"].icon_id + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + row.operator("bim.add_potential_opening", text=op_text, icon_value=op_icon) + if ui_context != "TOOL_HEADER": + row.label(text="", icon="EVENT_SHIFT") + row.label(text="", icon="EVENT_O") + + if AuthoringData.data["is_voidable_element"]: + if AuthoringData.data["has_visible_openings"]: + row = cls.layout.row(align=True) + row.operator("bim.edit_openings", icon="CHECKMARK", text="") + row.operator("bim.hide_openings", icon="CANCEL", text="") + + if AuthoringData.data["active_class"] in ("IfcOpeningElement",): + row = cls.layout.row(align=True) + row.operator("bim.edit_openings", icon="CHECKMARK", text="") + row.operator("bim.hide_openings", icon="CANCEL", text="") + if len(context.selected_objects) == 2: + row = cls.layout.row(align=True) + row.label(text="", icon="EVENT_SHIFT") + row.label(text="", icon="EVENT_L") + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + row.operator("bim.clone_opening", text="Clone Opening") + + @classmethod + def draw_align(cls, context): + ui_context = str(context.region.type) + row = cls.layout.row(align=True) + row.separator() + row.label(text="Align") if ui_context != "TOOL_HEADER" else row + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Exterior", "S_X", "", ui_context) + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Centreline", "S_C", "", ui_context) + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Interior", "S_V", "", ui_context) + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Mirror", "S_M", bpy.ops.bim.mirror_elements.__doc__, ui_context) + + @classmethod + def draw_aggregation(cls, context): + ui_context = str(context.region.type) + row = cls.layout.row(align=True) + row.separator() + row.label(text="Aggregation") if ui_context != "TOOL_HEADER" else row + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Assign", "C_P", bpy.ops.bim.aggregate_assign_object.__doc__, ui_context) + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Unassign", "A_P", bpy.ops.bim.aggregate_unassign_object.__doc__, ui_context) + + @classmethod + def draw_qto(cls, context): + ui_context = str(context.region.type) + row = cls.layout.row(align=True) + row.separator() + row.label(text="Quantity Take-off") if ui_context != "TOOL_HEADER" else row + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Perform Quantity Take-off", "S_Q", bpy.ops.bim.perform_quantity_take_off.__doc__, ui_context) + + @classmethod + def draw_modes(cls, context): + ui_context = str(context.region.type) + row = cls.layout.row(align=True) + row.separator() + row.label(text="Mode") if ui_context != "TOOL_HEADER" else row + + if AuthoringData.data["active_material_usage"] == "LAYER3": + if len(context.selected_objects) == 1: + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Edit Profile", "S_E", "", ui_context) + elif (tool.Model.is_parametric_railing_active() and not context.active_object.BIMRailingProperties.is_editing_path): + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + row.operator("bim.enable_editing_railing_path", text="Edit Path" if ui_context != "TOOL_HEADER" else "", icon_value=custom_icon_previews["EDIT_RAILING_PATH"].icon_id) + elif AuthoringData.data["active_material_usage"] == "PROFILE": + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Edit Axis", "A_E", "", ui_context) + + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Toggle Openings", "A_O", "Toggle openings", ui_context) + row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row + add_layout_hotkey_operator(row, "Decomposition", "A_D", "Select decomposition", ui_context) + + class Hotkey(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.hotkey" - bl_label = "" + bl_label = "BIM Hotkey" bl_options = {"REGISTER", "UNDO"} hotkey: bpy.props.StringProperty() description: bpy.props.StringProperty() @@ -1055,9 +804,9 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator): return if self.active_material_usage == "LAYER2": if bpy.ops.bim.align_wall.poll(): - bpy.ops.bim.align_wall(align_type="CENTERLINE") + bpy.ops.bim.align_wall(align_type="Centreline") else: - bpy.ops.bim.align_product(align_type="CENTERLINE") + bpy.ops.bim.align_product(align_type="Centreline") def hotkey_S_E(self): if not bpy.context.selected_objects: @@ -1227,7 +976,7 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator): def hotkey_S_V(self): if not bpy.context.selected_objects: return - if self.active_material_usage == "LAYER2": + elif self.active_material_usage == "LAYER2": bpy.ops.bim.align_wall(align_type="INTERIOR") else: bpy.ops.bim.align_product(align_type="POSITIVE") @@ -1276,6 +1025,11 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator): if AuthoringData.data["active_class"] in ("IfcOpeningElement",): if len(bpy.context.selected_objects) == 2: bpy.ops.bim.clone_opening() + + def hotkey_S_U(self): + if not bpy.context.selected_objects: + return + bpy.ops.bim.unjoin_walls() def hotkey_A_D(self): if not bpy.context.selected_objects: @@ -1306,4 +1060,5 @@ MODIFIERS = { "A": ("EVENT_ALT", "OPTION" if sys.platform == "Darwin" else "ALT"), "C": ("EVENT_CTRL", "CTRL"), "S": ("EVENT_SHIFT", "⇧"), + "E": ("EVENT_PADENTER", "ENTER" if sys.platform == "Darwin" else "RETURN"), } diff --git a/src/bonsai/scripts/bonsai_icons.svg b/src/bonsai/scripts/bonsai_icons.svg index 75f412a65b..6c9278e70a 100644 --- a/src/bonsai/scripts/bonsai_icons.svg +++ b/src/bonsai/scripts/bonsai_icons.svg @@ -25,17 +25,18 @@ inkscape:pageopacity="0" inkscape:pagecheckerboard="true" inkscape:deskcolor="#d1d1d1" - inkscape:document-units="mm" + inkscape:document-units="px" showgrid="true" - inkscape:zoom="16.000001" - inkscape:cx="26.874999" - inkscape:cy="-2.1562499" - inkscape:window-width="1842" + inkscape:zoom="19.333334" + inkscape:cx="12.775862" + inkscape:cy="21.284482" + inkscape:window-width="1839" inkscape:window-height="1421" - inkscape:window-x="1212" - inkscape:window-y="25" + inkscape:window-x="1064" + inkscape:window-y="106" inkscape:window-maximized="0" - inkscape:current-layer="g32883">