From ed84e06560f327d7f59f66b92723f6c5457652d5 Mon Sep 17 00:00:00 2001 From: LordBaryhobal Date: Tue, 18 Jun 2024 21:31:13 +0200 Subject: [PATCH] restructured code in separated files --- gallery/example1.pdf | Bin 31488 -> 31493 bytes gallery/example1.typ | 14 +-- gallery/example2.pdf | Bin 18349 -> 19402 bytes gallery/example2.typ | 13 ++- src/consts.typ | 5 + src/diagram.typ | 75 ++------------ src/group.typ | 52 ++++++++++ src/lib.typ | 7 +- src/participant.typ | 17 +++ src/renderer.typ | 240 +++++++++---------------------------------- src/separator.typ | 46 +++++++++ src/sequence.typ | 152 +++++++++++++++++++++++++++ 12 files changed, 351 insertions(+), 270 deletions(-) create mode 100644 src/consts.typ create mode 100644 src/group.typ create mode 100644 src/participant.typ create mode 100644 src/separator.typ create mode 100644 src/sequence.typ diff --git a/gallery/example1.pdf b/gallery/example1.pdf index 672bd3e72ce29aed744e82c6f715feac6395707d..b1f22ec1d8295280304faa572d12e004665d6314 100644 GIT binary patch delta 2059 zcmZ{kYdq771IEvkIvdR;L~UzFD);?%!M2dgki%${D2Z}iuA{lMj4og!C^SN0?&) zBf8Sep$qrpg@>d()_*KwB>je=P9yxp5h!`1r`C}LLR1zvS8hjIyemjm_OMEYm8D#Y zHOy5_tQ%hpJBZUzV<0fz}V%x8#i39h_SW#~}^7wUIn~P0Li)O}& zfdhG3C`422{Fat?IAohCpH(2oJorI758~9QnwmG(v$Rp{dRw(k&8yU%6=+$THmUr& z-P#7sNzT>}c5%+9*fTsqF(vWzT9~bY!uVB6^{C8|!x;r;uah>jJ(CHD#?p^{&(GofA?O}a zsdsts7}hT#pOyIWEHYst)k4lSJ5Xi=%$Yroo;W7JK;Dmkn;O-#C7yKIGY!ZO*_sK; z@S^HGOQkz;h$*N-2U8m~XOPs>eD7JB#bEGyn0F~)!A{%xnO9R&j&J%ll^YPr<#*^+ z?5LoWrQZ!jY_v3q#;GNYNmFw9NgKs#Mzf|qyKZe{&hR{OxS_s?bXNWQaCot}CZV%Z zA-7zGCDXy3U{{h<)fQO0PqfMhi!c(5{DeW9J2}9^oNhF8%B+>j503GtHOiWnq{%fx z#eY`3P*mFEyiDh^?F7*Wo5R6I*I|>ZaR#i=$()^__+`oU0N}w<`x+UCp{+ksePc5Ko7w&%zgSbdb%p** zrzBb(b(t=TZPx)RSSM=5MeF&zv-fL)qaKx?dc~7XdMvT3gtnf;_t}Q&aB7?vKG6ej zYK~qDwGKuL78IiVM(rFCgn{;Z-M=*SX&e;4wcvC}r`IGR4wmrsS|W@mR=2DeWORZ# zUi=}lP+)EVC1kzI#%5Ss&2?5dG%bGmcJzZXfq9X?R2KQtG(rZdv3r4>Rl(m6gFd+# zV0d9$IJRSEr;rtbv_v+i^ig?pmZBuShqP;Z$W_Wo=>*I#ZYeE~*6FX`$4k5#wWAg8 zo|X4T)r(|f!DXM`;(y+=A?+m5t$t&(F0%{3B+Wc{v%<|wHl5b`z2RyWeqK&OSbxqV zl=(p};0#FOebABqrra1t-d^GNkbI<@QO0cstm0juvP$))G=iJ;0#0yiEH8d;UM^XAqPx@-3F6Uj81ji{x*P$O=`S2| zfK?Y|thy4pcoQ942={6xJM@7SyaikCFQ9P*>n*%|N)HU)3gSRZ9~ok0;7kDsb81pW z@0DX*J#4hbOiF3&hYb##h-Gs}VHW5Pa1ooIJcC1vJDjCy$H#1w1A<;AO+L&qe3`hg zS7S}^77(LT{G-vyro?uqq+O#`Uebd~h(;KZE!#f(J9 z1mQ`YxA3bl^>~7{)D)BZvSC5Cpqr(wba&0lqT;g+gQMH9+yft$EO2k(diTCCLA8kY zh$*Ll;|E$JPoLveq?$aQ4%$D{P4P+aSP@1)9589_-!bwQwe@U9`_FZJ8+@F%@w;!& zMsMhda?D;1uCYtZTfP&uID6zcO}0w)@)Wg0_pR_nX#;msCW9FgYXCg5%Wu6@E7@@_vVU;D|85W2fz_H{vqU4&TukP>{eja)pZVr>2A?2BMI3hZN7{g%tzCSC4Q#3%hK_+3Zrqrv$R}`JuvaK)qY*Sr0%KhA9r}JVtpyy8DRYU z<$5vdf%m@04m9wF&MDC{hJDsIrP964r+FWJb9Si^umDc?tQrJ~M3DpBNp2w?NL3OM z17L7~E*g*i3-j&=+lcKZ5U~Ier<=ZdMf-nVgI=cKfwDI}Y3HWnuVGeu42GZs;Qp5BVu1h4uz)T> tC*3!~+~%Ka7#OG_RsR*8hZq_{fg151ZMV{|8dPz9Rqt delta 2055 zcmZ{kYd8~(0>@J>GcP8uG{lr;n#(peHrHtsa#=|3q|K6YpL^4=TyjfMT63vXEGnys zk=st>8maT9O>>!)EPCzjxOJZMKIb_f&N=V@)9?BJzWhJ^bEPs_Qkh4~BpHkHnsZQE zU}($~21I*zZ|v|NhN`5L&C$wm$^^CleZBh!DrNlA2p{#!w=A?ABXeUPsc^s3s|p?L z3GJ7FOdN4{(u>)WWCLX=fAnxy4*>GFo!90wh@0LHN%F4l`NV<&=dOvD)mM4jn=3C? zSDf&Q+{tPqGu;<`U$#vssRci28kIcSoZdInY9O%_R$MI@TV1c;fxkn(deIoKeh@ZW z7c@6ndxh8o%Nl_%@SVn@v{?RO5ufXWO{cuM{K*g>Xp3x44V^oeT4q1NLEckpj^c0* zvosS60|rNVHBjZ^ZGttg*ccz-pm*Hyi+zr|8(4cV+d5hL{Kn5<9ech~EseP)`YNS7 zYtw5}>C|rwL~}b?K3V4uNuWVZ8Pgl zdp{jgzVP*)cUolarB3xKT14fJwPh!KZ&i1x(-M|vYms#Q_7x@iCi}7xd^n~lWuYJx zE{WrxZ9e7K9=s&#FdhgB$nCcDNAhOmh+1x)4Bl*a~Tn=%RtEe+X*lv4C=K+E5{3GG#%2`gAmj zaa{hy51dX{L`GSgW5V#%(__$lzE#%a-4_Yglvfd@86UwrI`WF!j_v!PmB(?+4l4W! zJe-_vHVzmE#Z}=5QxTgh&W3<>dPr5cjo~*dW@Hylot}mw6(bsoHIw_)oWrbhua%1d z+9ySUu_Z*XEaWE5>~c56LAh4zu@Iik8@VgJtlv(2=(A)FH*5=jqDsj2imR{w6y-h~ z^mUdRuZQ@Msd_8{+xJyI>s-%q9bK^R`%lA0JfFtBP|#F|Gpw#PRs{=M+_-`zD<>{s zXR@wtcP4djk@Jhvb;Loq^bsF&z_`~1^m~wqO8`cx%%5=NR&C7I93n|LGtjP@--1U* zHno+J6=?VsDm=^%H*#m4t5hoap0i=M3&up$90y`N-mt+Ux)$|l$=j*9VmgA$e$e_P zN+%JoA3kG1-v=;Lsc^(K<@KeOO;{=q->17B<-_3fSt0`X|;;O<%oUg)xcozm;&QW?3BwK=;)~B~-jal+ocfDAjXBF2>({aaN zrv3h2;|P>8Z(GZ3vYLrveRN$5su0$iB^iubwW;;8=VlE@na(Z=iJw&1296HKFKLU& zrUAwJhZxlTxV+!?+;mU;qkT-)qsoQUC)NRS{7nRmc$`u0FD84{vul6WCv!;;&#d5tv zIS~w`D6#5hbCjEOvaDHZ!`eqcVd{+pI>d`KjE7l`eyA*<2ccz@Q#H}WuF(V-dK3cgPP7s zPbE7G_ikmwyEG2T&f^Py(>huzW^87~S!M3r&6%pSk=d4gC$2R;=?Usv<@HyKo-vI6 zeRx#18f#JW5hoNCkFHBy^LrjtovTWehzm$TFZ)?Uf=P$bZD8|?tC(9Tqxz(Q;79V! zub}q(-nVQ$L95KvI|l%~1M`kdS3K|RV4|0Q@VlL+OTALEW1{t=Pw6oaQ4>ZwbL~yA zm+~hqM`;S`g30SY52-9&&;<$Tr?~6BD<^Mv43wObG5 znMTezDq5%1$#KDN1Q?>l(~bf%zoEo(y%QVy&aR|3U^D-0{7^wu$bQg*lZOP0oRrnH zcp7&149xg;58Qj+fMPztig|kJkXs$+^Haa@X-7B7e!B-LwBlalug z%cM>FLO&dXd2*okNCC=5=|7_$LE4Y_qybHd3g=+X0 zs>L+XvjZk1hQ}wS#FNNCbA*)@0tkgVc{=|Sgn+lg diff --git a/gallery/example1.typ b/gallery/example1.typ index 94676cb..48be250 100644 --- a/gallery/example1.typ +++ b/gallery/example1.typ @@ -10,7 +10,7 @@ Alice <-- Bob: Another authentication Response #chronos.diagram({ - import "/src/diagram.typ": * + import chronos: * _seq("Alice", "Bob", comment: "Authentication Request") _seq("Bob", "Alice", comment: "Authentication Response", dashed: true) @@ -19,19 +19,19 @@ Alice <-- Bob: Another authentication Response }) #chronos.diagram({ - import "/src/diagram.typ": * + import chronos: * _seq("Bob", "Alice", comment: "bonjour", color: red) _seq("Alice", "Bob", comment: "ok", color: blue) }) #chronos.diagram({ - import "/src/diagram.typ": * + import chronos: * _seq("Alice", "Bob", comment: "This is a test") _seq("Alice", "Callum", comment: "This is another test with a long text") }) #chronos.diagram({ - import "/src/diagram.typ": * + import chronos: * _seq("Alice", "Bob", comment: "Authentication Request") _seq("Bob", "Alice", comment: "Authentication Failure") @@ -45,7 +45,7 @@ Alice <-- Bob: Another authentication Response }) #chronos.diagram({ - import "/src/diagram.typ": * + import chronos: * _sep("Initialization") _seq("Alice", "Bob", comment: "Authentication Request") _seq("Bob", "Alice", comment: "Authentication Response", dashed: true) @@ -56,7 +56,7 @@ Alice <-- Bob: Another authentication Response }) #chronos.diagram({ - import "/src/diagram.typ": * + import chronos: * _seq("Alice", "Bob", comment: "message 1") _seq("Bob", "Alice", comment: "ok", dashed: true) _gap() @@ -68,7 +68,7 @@ Alice <-- Bob: Another authentication Response }) #chronos.diagram({ - import "/src/diagram.typ": * + import chronos: * _seq("Alice", "Alice", comment: "On the\nright") _seq("Alice", "Alice", flip: true, comment: "On the\nleft") }) \ No newline at end of file diff --git a/gallery/example2.pdf b/gallery/example2.pdf index 364a2eea2447d33dadc38c2d2a73bb77043156ff..d9f55c1fb012765885f5b5495d6bdf8aa86844cd 100644 GIT binary patch delta 12531 zcmbWeWmF!`(k_e>T!XtOI5+MtL4!+hcXt^G8r17o4J|thS0`oB}UC z7a0qgvMwSS8MCUrnw_Pwy@@Fq&p%j#7c^BI9ZK8`!T-{}@`B_}u!R(WWT`EMT!l;s zhe3@aK}^Vk#`~Wd32o5r2&^psSR)DwCJcWdgJNf`&4Yo2f@Di5fFu3DE=b1D!UAHg z?SR9EO5g^eAHm@EV631Qp_GGus|R3cq-c}dDJ;$QlQ^-Y zPb>w>>)RiMY3@7kvVty(SDeq!-OtZo%xpWfHuSs&I&Rw+vtrS|d_bUW#LDuhXaQ(I z=08%db9cr3&6j`O&HJ`e)F9;C28jz)o$QHlD;4#69K>z8Tk zH{zLb2^DB5Wd7+9mahYZ!v{R5rB%-%XP%k)8O7V%@x3vRRPe8JVh1tHAfRJIslS-~ zXWY2_5|IML9kn{ffIg-pns&*;1pEpt2Ky}E6kQzOkkDnsgP$McyW)%b8`GQNn?_rV z&?qpFjkSQ#h(H*ejo67U2x>F&=)ZvP-H6|gx0lw-YUA?MzXBC_yStCSw{ z&fXU4$Pda1ab&W$n?Kba0`@eW1i8F(qV;m+N~LqWx1VomZtYwvTl59;1%JIs3mEN< z=Pp}Bvq*uB$1e8{_e^T78d6opQEONEM}3kWb@t%L;m1P9V!+v;P7A+9OfyFvM21>M zXU1y=B!=>2RN0hW$x-=a`DB4)=ww<(W}P=1YV~yjOAa041?1y$U}3d9MoY0!TZOAA zdX=O>z|MQ-F5^^UwX|W;_IzpJ1g>>D%R;fHXo>hv?wP|a#uwbax^BN=x?wD2@g7IH zz&ajxqIuRdeu*^*@Oo#yPPp0y>NqO$Ih=Oy1nvdu_JTrdL+pw4ID9rPF1PkWH;CR$ z9%{Fo1&<=Gz`h{BSF3|Uzoc@aaj9``@oe?a^V#9V?7QnzrTJW~gn72$^UHF|{i^46+p`UEcVkshs ztn#3eLIM>rVK}Z-J1%0bcB7~I!^A`5S*@hvIIBWl*@SMuXUL$Qw|G5IFqcFX_&MyK ziV`bQOcTiMoa-qm4CgjPM#|PS;G-xEH<_l-(rHJY943Q5Ck#g-TVz^LFzFO(nzD;A z>NvEi7z+uHU##BBp_|Yhn1;+>GHCm3BV{{iJGr7))o%3Me5#Q$Y>-S7$mAVE6DV~O zqcE~*He>*d9Xj6ez^`TKxX}_>gdnR9`6Kg%I+F#?_uiuk!Q;0T4f{X9Y_RL6(F9^{ zKy8R_)CASr_2srpmJ_u~P$Jk$Yo(dfTYnPC>fhBF)hE?utoNzb48}HlnopW5sirmqsq8e7ew%je_4mu0@^PuRVv}Qi3G-`1mBAzz!E>CyA%`&d$&%CA`#K%n3oW{myz^ zIVGW#9sVKVA#t-~zInb?zLCj-bjLX(uu!fUT|&&iGwzB{3PKGQ)98KX8QKJv*g1D3r)%{5v(qLv!ij}ng4=h5FcyE zg`1RLgi@4R`xW>hE4^Y?WK6`j9hKt29!j@RCtyDZPEmf)UqA4P8-gDLI2Vs;NGC;NTI9TFFogCe+;9&Na%9Rp>I~^NM;rA`WvHiU@3igQ7krx z9Pu~Sw+cq3xGPXUBr{@i75s_H{pY7w5^B6Z!DEIOmctImXBWBPb!p5aZ-~uu6Oq>s z20{)EG)~O7=nfYmuxm$oKv%W~B`k)BkI#ptG=ygnzm}<&4 zL?V^aU(9~|E3b5HGmj)Zf;Wp;$ezb$ESLv_g_!%!BxC|iu3`ykr3R1i%&y}SYhb>o zN=!AhWCQOXifJ=;vLNz(Z2S@8;%f`Rts1ZNcrm#w$VTqFLTWb_0J0HX`TpdOI>R&< z>Jsag=O+h`P{S#NX^x!(|DIM0)CzY$+urKUKN&bWZF}Y{Z zEW{5Gx5N-j#WzxOz*0JFo$SssQhh=0=Ac z{3FU-Y;8Ls!2MF%4)4!R@lJT8`}eZxpGk;K4w8tk>U$eZ50Wyc5w%So3t+ z<>B$YovDt=#LS6CU1J^1Tz(@9N5;Nm-731o=73N+;cVG_GO%uVy#vI6j41(H3($8d zg$GhvqbQ8GL6l6#yzvhk`ynT5v9%a?TaqV^fpEqEJ2|{THoQTKk;G30p3-QY#e)l()aHfyXwrp+q5&m)w);amlmU+6-v;b=xNSOc3-(cw`lLc-9|V#-cOVhnxwpVVkmeY?&KfKnzC)pQdJs51 zvYQll(Pn>jRoe8P#5w%gRX8CUGfAON|8o~Ox#p#dom@_FRT4(h{JSzKA}-(yRR%gi z=h)*y{+vZb{cWEDfsUnF@#Mw>(V>4Rsf*H;HWF2A4SBIV@|nU3Q8t&ZnW45I2+4rx z@hUfF=ZAl7dsuCYTqJjQD48NsuXnB0mmwm>S%b{29# zJbi1RoGNK4ER$QBK!z411Gf&-6qarh&k!%~B+WET`IzsI9Z$g)ft`TKmYx{T#s+Us zycdtCEc0uKUs--HfuP2JE}mCe5hq?^=kttk1x?fr!HhUgf|(PeC3$&%)HkW**yCYk zC+JJ`p(LMl8ycM<&YFZ1aBXOjCMKRVo-}SSEQN?XCV_T{ z)hQ!`OflYk7_`H4)y6Hk5UVgF;nla1aJyqzllD7o$rGgYW5|)#Q}_uVKUP83e8{Or z;VI`P@&+47xZymc-q>6nBEMpPq|uUm%FmvmIZSj3Dx+u&C#48&5454UpVb5iXglcN zB#AH-BRW3gT$Bml@)x&Q?;-c0 zH51$bDVqDZ_MO%pkt>Zeg)?#|tb2%ivc{C(J-#u|OpUvkV6Se8$~!INVuJUcs`L%+ zH;qou&u7#M^|Pg3E#-PMQ!~K)(UmfL@j5O++xPZy`mxrEej5&2TidVgC@73(_MX=9 zM~p_AU#&}z)b{G#y=Irn*%WWhSg9gzJ#um+PSqRjY^72{aL}|>%dvV9@zghEuId^Z zXgp;kt*s4g876qzue^t=xMeUx>&PWC)( zLsd)~rI)LVLq5j%TID(CsZ)Mx16o<3`Z2w)mQWS?<70c<_=;X|HQYW%pkY=@12k@? z-qBaJ%IccrTPUM)i`&m96&UV_XW#UOOR~_V;h!}_)Y{0CNL)*$ZbLNb%a|%p_(etO zaT|#cP|ExAKk9wTOR^0CYSm!w9Q!`JgndV2j%xfx22+<4YEZh=qGk#T2aT&oW_Be& zZ9}g3(jTOw{o3gI8#q-VDTFts`a3e9RGruE7XG7EqmGck@(-^P&0jA=I;Tj z;w(+5<}6kDdzM!y!`b^v-z|Y=O#G(1W~64my>JwDmKJBiX{B2O@NsY~_r484?%@F7>YEn4?B#+zc~Ywj)Rjc;|h}POnxn`bLkhMKov& z&FFpz72?~ef$I;XKSx2UxfSseV#{Gda9A)qc@itQ_t>OxogyldznC7xp}xg=q#vp8 zTQ~GA5x{~w7411(K2Gla)gW75mj1HmF6ytn84V(5i}P6q?5K9 z*QzJS%lL`|}T^q-2~2_rAj@TT7Eo0a%27ARQ7IIIS1E>j#S3VOHdgjoVN+ zYctnfRGSsf$0F%xI5vLR@Z(COppV4DPUNXtm%!%M@9?za%^bofM3Od;u}+cO$I1CrD55< zGK#UTy@S_Ug08@cpVA26V)iyZXE!>0v04n`ddAN$0kq zOCd~-(?Bu1P(1GO@%-{dlCi{NjF62giR2VFSu)zj`eRJpI#02DjW}xIr1I!mKMVlmhI!q8tW`{h;7L2@20fOTtUE(L&#`k)YEbJk^zQrX)_cN*tSg z>d^R}?A3-bMg`uTVE=r{JU0OCUpYJXq6A#j43}M` zGB&lfx8C=A7wtEk!|v4G-6cnohWBjV9?$kWo`p{b3Oy^&F4^x#S^1(7LMoT9GVU{EvtsPG{_VRT%n&9Did0LRgddOsc6t0qd=ay})OhLeP4sKl1i@m1(} zcQQx~g7dI?^Tgw@79I(0WY{$NnR|0wY78p zi=)!K1}t?iBiHR{soH0S_0=u;Z_SEd;r3X3H)vg+&tJCh><9Q>+d3PzkGwmZpJC6^ zzrXK>JkJE*7_VB{5f;y>0e9%xLaG!)JmEi&eP17d&>qVJ1;kIEm=Oa(2bN z7sN`gE$E+NPPaU3ri7QdUQH9nq`%08Mfd4|vC}9%C2Fd)o_#5~^KdX-@M%b{;3Z zXBQKIW^e=1#;MSA4j?X z7)gF=B3=Gl5o=Eqo1L*6)ly!88RFwFwbD6b**klQj*p&p(&Q7}Fk-nz%?@6sV0K#_ zZyS4+!^XGc94Vc#PV0Qsmiq;+bsa8t0(TLB=$VvpZ2ZLA`o26*sI&RnfAjqEIr?$Z zmZ|w0OyX%oX;;UXNkvB~4D_yNPBaVS+>3tZ*?auRc9_5Ug>*zwL!%(R3O@WIoupL- zYw(wpgv@l6usa6=DXx^txROh8vF#rSM*0~WKFIRW&P+~mDq+}(HhD^HF!)rW8qov* zA_m%*A$rB8Vo};iI%vLt(@zm7{XtFr@Ra(2;695;_GIdLPhGrt zSa*p&SX`98RS%h`*6op6);?j0-akJ?$ZzNe>Kc+K{YmMWhDIl9#7>4mn175ZM8%N# z$23ucg=+gGSkoi(Q$mvv)>-efx-uF8aTN=@HW=Eo&9%9aW>j|~?xRXL*r zjMlBG-)d6!KYh%uGcKlfw9v}^n5SzM9z61djIY01J&C^YjiELsxd~Oh77uSU6_bAw zeBnNoYqWGD;C;(6M7_y>pb*HpSWI*55y$CKrv#hFh!Lt_hda3D5PQX^$5sykr~)}( zt3P48rKt=Ur`y?yN>u>|(usR9zkgG~L!lK*O$?v>p7xxiNWo0g`Sxja(ro`cX?H@a zB7NQK=jq!TBlW0>=SB{$sb{h15dXWQ^E+xvXfkuNyD#Odi$anZ2JPG@mj&(4W4?3# zh2(|X0j3H^u|sT$UcKN;wES}epb{ltZKD*yF+6bfR4LZiz&(G0PE^;WL<%k!ydEep z$MPlloT11$;eVS6rZ4Bqkj9Q^h>M5J&ky#(F2Q`f?zQ0HH;mb_F0ju$B*0J$( zF81mcLm55NX@>a&76ftNj<3mF3k^idi93f(JQU$HUwR4;cA6ZtcA8`w5|cBo_hu>1 zrpkU_M+MhOTUb?nGe}Fj?&poRmf@0UJtK;xN#h}B>n`xS6!`1PE0<`GuP>5)T@=bH zg3Rz0Ux)ID!0~^S;)-(N0h;{a`Y`41c*wGF@UE}%E@p)*<(8Jon(0KDBGjw_&67Rf zU%2T{pyHwn_#fl62cS`m5fsU4vTudTXnu43an2- ztC5K0OB{0C?AWqrBgu!?^ThW{SeKeltKcuM_Z(NqN^2ExW*@B|14HOWCGl}_M3`x* zv0AHbYd{ykM%n6-#=};gm3(yWPB|#FiADyX^tUb~?QSWI7`ns2jUn-8sPor->4)o; z)sh^PgL(cK#KM`TKO{#V-;Y5bZ?Nac$8^eDpkdw2%0t6DAD{MF!c>Ws+=^mIhf08z z>H^p8tI-!V^s2@l0E$s3D!d?5y~Sev?wNCKYI(+Y^={)y3av7mI0+r!kW&Qm+mB#` zkxP;tVbWB@*FNW`;mk^BSsy8>*NzMa0%B7Im$Dr|pLMYh0pacf>=>2)l5Q?H;3|l9 zr9fVB1%`3VoS-4Pn?N9rt-_hDC*BUO!I@F{s>x|R3#IP|kW5RPS48(qBzLkMUV=F` zblzV061{i$LPu0eP07vd9PbBwtsCxBv-R9h_4R?k&+X&*TJ;sb$FhkoI%>71Os4&t z$c1s@h7?<~9F*6){z$Tk$0AA~EjAyD@Khk z=CoQiC7kI&fOPRAQw9=L5H3qmlx{ULtRR1p!!Nviy0v>Tan#5f2j<}ZNi9KDwVLMg zEgI*Zv!odQ_SbYN`TECi>`C8d!!bour7fnF#h)am9jBpi_lwfmh=0`AwH_`TGrLo{ zqrUDI+&7>}NcAf@{&MNKU{4$gAIB4rO17@S9w4MC0ubLgmkPipDXj?R=qMRO6ND$U zSXy_2-RNKE+)$!wwQZ6$OA%cos&_5+BOIc8YErXUhskT}Rt)^6#Ds`hrYDZ_+n(5% z`aat~-Y-7$t1d~bp6ml$ji;;QrA>&A11r%E8rd7e1w?C0?>()mcjxtpF%>JrelUqm z4oqJ{ffFZb{Y)+>3R&7*DrqCa+=@Ng9@!`cLoGTL$=!bZQMqPEEaB{?0Gv4Ot4nvO@4xRDCrQ4gWp-SZpEh!=hI~7G z1b-X0)d4`ICDgN>t$wHRgwE>fd{^b_L!r!a^6XL#D5u;RaXMyM@U4RoqKwS72&mYJi;Ht`g0l9Sc zW@(=a2GB933kxki3-u57zF$-eyQqunW*M_J$WYiuJ5R7Fn{KW&j7u6 z94kXp)tJ>6>tn1>93?p9K3DP|uIpJf#)_aF&&BJKeeR&npu9-N&*$i8sZ1zN|_CnRYj(! z9slrXr|hRuO6EN#tle3ycbUR)|514b*VjlPQ=zG}S{O9k;G(7HWsD^{5KTbTNMX@b z?iZcQ&e=*}n&X(VoS}MD90Z(ex8z&a=GK||`Bh_2;^HA8+9S$;k^i%Ef-cP>g<1({ zqpBFa2#vAB=3AY;gF)Rx#A`}U-Mt?{g3Dz3cTDzhGR%$iL*IWc!W_vrp~`pG*Q&Jp ziAR@2#zcvbC~-^Z%@l+`D?hmgkfrViDCljpiGAhw{P|dD<47^b@eOe4ArH``nAmFE z)Um=jq+6$)2#NYr{~e=-a;$y}MYWyEAx*o!eqgR(V6Kq4N(%$k!Y9WMb@mrM7_+z1 z#+U(RDu!P}btuTAB}jIu8JIr$AT}4BVrM&H5w{CDIkG!8^+nd*FHqJ#cK6J*EQiO} zc`(*Bt+vmC$m$4BG!+;g{=RZ{7|H(mcS(Lebj1F^z;EDy{M}ha71uGO;y}~K{13xB zsEkKtbkeliBg{=uo@=S*b-lm-GS{p>;H;%^ke33>r2~O_QwJaXHR}Th4brkK%3f66 z6j+(Bb^$qVy7Gc;C1G>}?SMf#tbnlNdfPt*Uwef=-xFWzwj5~G1`%BP85|s)*$>sw z(qC))Hs(ON9|^mjPF9?#5rO^A#h)b_B8aK&w&MuuX+2ohA(ERm+$yI25+TVjZ`X2|tl{3VUS)auL;vYuVzzK5 z;HkmZ``~Sn>lApHn1uK99fT-A+eoHymFRf%~M)!>^vkc)&wA^7^}IV}g5*4sH|)s&eeyJ(wH zUnlUK)`myT#Kc)&^?a+6&0lLPBWFXTw>LvHj?cN#n*osDJFZRM-3$HRkyiebaL5<^ z!hEEd-sF)q^8j5rJG4wbf-H5`tPvU>EJI0;x`gMB_&j3N5bayzZa|y{;i%yJNGfaS1he$H6DVMMlNlo8lbL;-5_2JA&9J2!*s0kQKSP2l+| z-dlrpS0T1q-7e<%d9Q%@W4nAKs;f{7XRWbX6F^39Co#-08OdzZ?sqGtMxSEt&KXME zpGGIIykk3RwXeppw^yOFyNsRHReq$lGb3qz@Uo1wKWXDzvp3LVdHLOg@-8XDw;UZc z4xInX$FAuNxLlS&=8nY=LX~m;o-qaslYdID+-I#GI&Q^TKt(Gb5%U8Pi5he$mluzb z07OXlPq4MPD-;IYaD5TVt9BM9)3Vb-)HwQ5uPI8gTN}4*Ps^9QkcLc1;$sombI*Ll z*&mMafqa_1<#JV{y6*mEKeN0)!}Hqu8`{ht|H(Hc>3#T%^^&HxSm_5V$v zk5hDnKe)qK%pnnorwyvkU)Yej_~pp^>i|4#!dM6ePuvnx-+E{8_SN1eDOS~+)iYqV&A&doinx3u8PSA_My+lG%n9_aGAwAhw%5cL^%aak&8N$0vil%z6#cYu#p1#@D#q zh3gkTR(NgB=qqaJ9UJYPGP~qA_ZN|GJ3Wr78_T)G$KLcaWETA5|KMJ_XCTgXzUJ?~ zY}BL>ESK}&?yujxA17}=J zL=3Kki$ClfllfD|{#Ao)ghL{g!bGF+`v%@jpg5U+-UYl2<2x?q@O``&c_RA-pNxPz zlB1ymZ1fqZ2|2#9oHdHd85AbpMlhKM$!0mTBB9*tEBVa_@oEJhjaRe zRVMZ<);?~MfY34O6DUbT6sQqZ-}Vx!gh}}IV1*)6toQxQ2{J4IAte68@98eOA7*lI z!Kq+&T7RU2S}K3Su;6vPE(y#pjqncmzHb1}-%Vjs#7%N4upoYySirkC1RCYz4KpNv zhN_d>ooqTRWfT~@IV{1G`m>fW&)_VYOT#5%a)c3a|hVH140+q za{YyRud1R>MceaZ)yiK3KanC7ZUA@sB$*vw(^}zYhB?8Ax=F1Q5TESxnA;!9eav-sH%9%{ z)sDTM7M?SPdd>F4z<#Uf(ka*A%W++6xFwwHwPrlcTvSxI#!BK7{pmn~LSQXpH!CR` zGNGM0u7|oYfVfhpabj&${V)@656)Eo2_4h1L?bzj*Zu29omj)s60e6*+{NzzL}yw{A_(~!0gd5)C_{2_()(9eaH;0Dwh z$ZQmrQ<51Z%_v!Fj=u8xJ0Pz-h5LtSg{U{~Kyf}nxo@gSf0^$Jsem?gn@p)j9OUL} zK4JD+!1Sw0X2Xh;?Fqq+@v&asEkR$cTe*CZ9k^itLU6hA>(?QlDWw>5%dQso@Wr1% zrR1vi)Ou-Dvx^O{_EK8lQj!9E-eNHAalKw?{p-DTtL5U|Pa|MH07$B+o0Z*n4{Z1n zb>_x@K4aEY^{0I2TNW~X)r%HfZij9gVl2nI^qHhcqUxr2zdb3bW<)29bfQ1ju{ zn|#ig8-tkvuj&nPJv9eT7^&G7gOGCjGZHT zUzt*5IP@$ZHA5ijY;>egzaX7%V${o`vl?zsqUy)!=q`{HE%#XJVltv%@Z7Si-}&d! z5R;+%zt8VtU)B(=Y6p@j6W7AX=zFEu z@H^ZYgDq|XxiB^)Ya9kvf;@=kFa6a8WCzKauniK2WUZxR4}*pTB~)`^k+J=YEc=&z zD`ID7@8V3Rqx+XRi)dNi4MRrRzzR6+F*J|!+niwNaETcw#w8Bu`E2^EXHT=V}u?bzXwcp zyzz8!cjmYX;ayli0fzMBx6r3oL$RI4*NfJ`rlK`Xy^^|-VG~1;s1ku^(!3k5eQ@Ju z?XP%7%(hr$)u7z|x)lgZk)UCWOEZ;l9L@o=A-i(X|U;ZR@iiL~f?B$KYI$<4Tq z1#^Bo_crKH5(jzJeA+#sYgVJOGWuK%aPdsW6ue>vDm}*k`3^erqK>>(*it<@LZ_?O za3f1$5Sr2AE0Wb9#&^BOSwV6=hIpotTt|Do;KT7G3@U2lPR7O3Ek`ii*UukfI_UmH z8o3)N#%pb4bxE~dVl+~Yp8^?MnJ&5YOvzSvLb}}} zC0_i9`x_jb)NKEd&Sfo4{yP1?T$+sIe^Al?|NhUB;3g~#%L8Iz=SgT4CiwWTlJ=+n z5+j_oN5XDM|ADb{|E1yoSG~W@|0UA@7c4?V=l}o! delta 11630 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 diff --git a/gallery/example2.typ b/gallery/example2.typ index 8f0e5fb..1496b46 100644 --- a/gallery/example2.typ +++ b/gallery/example2.typ @@ -2,7 +2,7 @@ #chronos.diagram({ - import "/src/diagram.typ": * + import chronos: * _seq("User", "A", comment: "DoWork", enable-dst: true) _seq("A", "B", comment: [#sym.quote.angle.l createRequest #sym.quote.angle.r], enable-dst: true) _seq("B", "C", comment: "DoWork", enable-dst: true) @@ -12,7 +12,7 @@ }) #chronos.diagram({ - import "/src/diagram.typ": * + import chronos: * _seq("User", "A", comment: "DoWork", enable-dst: true) _seq("A", "A", comment: "Internal call", enable-dst: true) _seq("A", "B", comment: [#sym.quote.angle.l createRequest #sym.quote.angle.r], enable-dst: true) @@ -21,7 +21,7 @@ }) #chronos.diagram({ - import "/src/diagram.typ": * + import chronos: * _seq("alice", "bob", comment: "hello", enable-dst: true) _seq("bob", "bob", comment: "self call", enable-dst: true) _seq("bill", "bob", comment: "hello from thread 2", enable-dst: true) @@ -30,4 +30,11 @@ _seq("bob", "bob", comment: "rc", disable-src: true, dashed: true) _seq("bob", "george", comment: "delete", destroy-dst: true) _seq("bob", "alice", comment: "success", disable-src: true, dashed: true) +}) + +#chronos.diagram({ + import chronos: * + _seq("alice", "bob", comment: "hello1", enable-dst: true) + _seq("bob", "charlie", comment: "hello2", enable-dst: true, disable-src: true) + _seq("charlie", "alice", comment: "ok", dashed: true, disable-src: true) }) \ No newline at end of file diff --git a/src/consts.typ b/src/consts.typ new file mode 100644 index 0000000..e393728 --- /dev/null +++ b/src/consts.typ @@ -0,0 +1,5 @@ +#let Y-SPACE = 10 +#let PAR-PAD = (5pt, 3pt) +#let PAR-SPACE = 10 +#let COMMENT-PAD = 8 +#let LIFELINE-W = 10 \ No newline at end of file diff --git a/src/diagram.typ b/src/diagram.typ index cd4c168..9ec2f50 100644 --- a/src/diagram.typ +++ b/src/diagram.typ @@ -1,71 +1,6 @@ #import "utils.typ": get-group-span #import "renderer.typ": render - -#let _seq( - p1, - p2, - comment: none, - dashed: false, - tip: "default", - color: black, - flip: false, - enable-dst: false, - disable-dst: false, - destroy-dst: false, - disable-src: false, - destroy-src: false, -) = { - return (( - type: "seq", - p1: p1, - p2: p2, - comment: comment, - dashed: dashed, - tip: tip, - color: color, - flip: flip, - enable-dst: enable-dst, - disable-dst: disable-dst, - destroy-dst: destroy-dst, - disable-src: disable-src, - destroy-src: destroy-src, - ),) -} - -#let _par(name, display-name: auto, start-at: 0) = { - return (( - type: "par", - name: name, - display-name: if display-name == auto {name} else {display-name}, - start-at: start-at - ),) -} - -#let _par-exists(participants, name) = { - for p in participants { - if name == p.name { - return true - } - } - return false -} - -#let _grp(name, desc: none, type: "default", elmts) = { - return (( - type: "grp", - name: name, - desc: desc, - grp-type: type, - elmts: elmts - ),) -} - -#let _sep(name) = { - return (( - type: "sep", - name: name - ),) -} +#import "participant.typ" as participant: _par #let _gap(size: 20) = { return (( @@ -78,6 +13,8 @@ let participants = () let elmts = elements let i = 0 + + // Flatten groups while i < elmts.len() { let elmt = elmts.at(i) if elmt.type == "grp" { @@ -93,19 +30,21 @@ i += 1 } + // List participants for elmt in elmts { if elmt.type == "par" { participants.push(elmt) } else if elmt.type == "seq" { - if not _par-exists(participants, elmt.p1) { + if not participant._exists(participants, elmt.p1) { participants.push(_par(elmt.p1).first()) } - if not _par-exists(participants, elmt.p2) { + if not participant._exists(participants, elmt.p2) { participants.push(_par(elmt.p2).first()) } } } + // Compute groups spans (horizontal) for (i, elmt) in elmts.enumerate() { if elmt.type == "grp" { let (min-i, max-i) = get-group-span(participants, elmt) diff --git a/src/group.typ b/src/group.typ new file mode 100644 index 0000000..4c20f89 --- /dev/null +++ b/src/group.typ @@ -0,0 +1,52 @@ +#import "@preview/cetz:0.2.2": draw + +#let _grp(name, desc: none, type: "default", elmts) = { + return (( + type: "grp", + name: name, + desc: desc, + grp-type: type, + elmts: elmts + ),) +} + +#let render(x0, x1, y0, y1, group) = { + let shapes = () + let m = measure(box(group.name)) + let w = m.width / 1pt + 15 + let h = m.height / 1pt + 6 + shapes += draw.rect( + (x0, y0), + (x1, y1) + ) + shapes += draw.merge-path( + fill: gray.lighten(20%), + close: true, + { + draw.line( + (x0, y0), + (x0 + w, y0), + (x0 + w, y0 - h / 2), + (x0 + w - 5, y0 - h), + (x0, y0 - h) + ) + } + ) + shapes += draw.content( + (x0, y0), + group.name, + anchor: "north-west", + padding: (left: 5pt, right: 10pt, top: 3pt, bottom: 3pt) + ) + + if group.desc != none { + shapes += draw.content( + (x0 + w, y0), + text([\[#group.desc\]], weight: "bold"), + anchor: "north-west", + padding: 3pt + ) + } + + return shapes +} \ No newline at end of file diff --git a/src/lib.typ b/src/lib.typ index 2f20eb6..eb85b65 100644 --- a/src/lib.typ +++ b/src/lib.typ @@ -1 +1,6 @@ -#import "diagram.typ": diagram, from-plantuml \ No newline at end of file +#import "diagram.typ": diagram, from-plantuml, _gap + +#import "sequence.typ": _seq +#import "group.typ": _grp +#import "participant.typ": _par +#import "separator.typ": _sep \ No newline at end of file diff --git a/src/participant.typ b/src/participant.typ new file mode 100644 index 0000000..5a1c6f1 --- /dev/null +++ b/src/participant.typ @@ -0,0 +1,17 @@ +#let _par(name, display-name: auto, start-at: 0) = { + return (( + type: "par", + name: name, + display-name: if display-name == auto {name} else {display-name}, + start-at: start-at + ),) +} + +#let _exists(participants, name) = { + for p in participants { + if name == p.name { + return true + } + } + return false +} \ No newline at end of file diff --git a/src/renderer.typ b/src/renderer.typ index 7bf0f10..07e1341 100644 --- a/src/renderer.typ +++ b/src/renderer.typ @@ -1,11 +1,9 @@ #import "@preview/cetz:0.2.2": canvas, draw #import "utils.typ": get-participants-i - -#let Y-SPACE = 10 -#let PAR-PAD = (5pt, 3pt) -#let PAR-SPACE = 10 -#let COMMENT-PAD = 8 -#let LIFELINE-W = 10 +#import "group.typ" +#import "sequence.typ" +#import "separator.typ" +#import "consts.typ": * #let get-columns-width(participants, elements) = { @@ -16,6 +14,8 @@ }) let pars-i = get-participants-i(participants) let cells = () + + // Compute max lifeline levels for elmt in elements { if elmt.type == "seq" { let com = if elmt.comment == none {""} else {elmt.comment} @@ -49,6 +49,8 @@ } } + // Compute column widths + // Compute minimum widths for participant names let widths = () for i in range(participants.len() - 1) { let p1 = participants.at(i) @@ -58,6 +60,7 @@ widths.push(w1 / 2pt + w2 / 2pt + PAR-SPACE) } + // Compute minimum width for simple sequences (spanning 1 column) for cell in cells.filter(c => c.i2 - c.i1 == 1) { let m = measure(cell.cell) widths.at(cell.i1) = calc.max( @@ -66,6 +69,7 @@ ) } + // Compute remaining widths for longer sequences (spanning multiple columns) let multicol-cells = cells.filter(c => c.i2 - c.i1 > 1) multicol-cells = multicol-cells.sorted(key: c => { c.i1 * 1000 + c.i2 @@ -77,6 +81,8 @@ m.width / 1pt - widths.slice(0, cell.i2 - 1).sum() ) } + + // Add lifeline widths for (i, w) in widths.enumerate() { let p1 = participants.at(i) let p2 = participants.at(i + 1) @@ -89,57 +95,21 @@ return widths } -#let draw-group(x0, x1, y0, y1, group) = { - let m = measure(box(group.name)) - let w = m.width / 1pt + 15 - let h = m.height / 1pt + 6 - draw.rect( - (x0, y0), - (x1, y1) - ) - draw.merge-path( - fill: gray.lighten(20%), - close: true, - { - draw.line( - (x0, y0), - (x0 + w, y0), - (x0 + w, y0 - h / 2), - (x0 + w - 5, y0 - h), - (x0, y0 - h) - ) - } - ) - draw.content( - (x0, y0), - group.name, - anchor: "north-west", - padding: (left: 5pt, right: 10pt, top: 3pt, bottom: 3pt) - ) - - if group.desc != none { - draw.content( - (x0 + w, y0), - text([\[#group.desc\]], weight: "bold"), - anchor: "north-west", - padding: 3pt - ) - } -} - #let render(participants, elements) = context canvas(length: 1pt, { + let shapes = () let pars-i = get-participants-i(participants) let widths = get-columns-width(participants, elements) + // Compute each column's X position let x-pos = (0,) for width in widths { x-pos.push(x-pos.last() + width) } - // Draw participants + // Draw participants (start) for (i, p) in participants.enumerate() { - draw.content( + shapes += draw.content( (x-pos.at(i), 0), p.display-name, name: p.name, @@ -156,124 +126,23 @@ lines: () )) - // Draw sequences + let draw-seq = sequence.render.with(pars-i, x-pos) + let draw-group = group.render.with() + let draw-sep = separator.render.with(x-pos) + + // Draw elemnts for elmt in elements { + // Sequences if elmt.type == "seq" { - let i1 = pars-i.at(elmt.p1) - let i2 = pars-i.at(elmt.p2) - - if elmt.comment != none { - y -= measure(box(elmt.comment)).height / 1pt + 6 - } - - if elmt.disable-src { - let src-line = lifelines.at(i1) - src-line.level -= 1 - src-line.lines.push(("disable", y, auto)) - lifelines.at(i1) = src-line - } - if elmt.destroy-src { - let src-line = lifelines.at(i1) - src-line.level -= 1 - src-line.lines.push(("destroy", y, auto)) - lifelines.at(i1) = src-line - } - - let ll-lvl1 = lifelines.at(i1).level * LIFELINE-W / 2 - - if elmt.disable-dst { - let dst-line = lifelines.at(i2) - dst-line.level -= 1 - dst-line.lines.push(("disable", y, auto)) - lifelines.at(i2) = dst-line - } - if elmt.destroy-dst { - let dst-line = lifelines.at(i2) - dst-line.level -= 1 - dst-line.lines.push(("destroy", y, auto)) - lifelines.at(i2) = dst-line - } - if elmt.enable-dst { - let dst-line = lifelines.at(i2) - dst-line.level += 1 - lifelines.at(i2) = dst-line - } - - let x1 = x-pos.at(i1) - let x2 = x-pos.at(i2) - - let ll-lvl2 = lifelines.at(i2).level * LIFELINE-W / 2 - - let f = if elmt.flip {-1} else {1} - if i1 <= i2 { - x1 += ll-lvl1 * f - x2 -= ll-lvl2 * f - } else { - x1 -= ll-lvl1 * f - x2 += ll-lvl2 * f - } - - let style = ( - mark: (end: "straight"), - stroke: ( - dash: if elmt.dashed {"dashed"} else {"solid"}, - paint: elmt.color - ) - ) - - if elmt.p1 == elmt.p2 { - let x3 = x1 - ll-lvl1 + ll-lvl2 - - x2 = if elmt.flip {x1 - 20} else {x1 + 20} - - if elmt.comment != none { - draw.content( - (x1, y), - elmt.comment, - anchor: if elmt.flip {"south-east"} else {"south-west"}, - padding: 3pt - ) - } - - draw.line( - (x1, y), - (x2, y), - (x2, y - 10), - (x3, y - 10), - ..style - ) - y -= 10 - - } else { - if elmt.comment != none { - let x = calc.min(x1, x2) - if x2 < x1 { - x += COMMENT-PAD - } - draw.content( - (x, y), - elmt.comment, - anchor: "south-west", - padding: 3pt - ) - } - - draw.line( - (x1, y), - (x2, y), - ..style - ) - } - if elmt.enable-dst { - let dst-line = lifelines.at(i2) - dst-line.lines.push(("enable", y, auto)) - lifelines.at(i2) = dst-line - } - y -= Y-SPACE + let shps + (y, lifelines, shps) = draw-seq(elmt, y, lifelines) + shapes += shps + // Groups (start) -> reserve space for labels + store position } else if elmt.type == "grp" { let m = measure( box( + elmt.name, inset: (left: 5pt, right: 5pt, top: 3pt, bottom: 3pt), ) ) @@ -285,53 +154,33 @@ groups.push((y, elmt, 0, 0)) y -= m.height / 1pt + Y-SPACE + // Groups (end) -> actual drawing } else if elmt.type == "grp-end" { let (start-y, group, start-lvl, end-lvl) = groups.pop() let x0 = x-pos.at(group.min-i) - start-lvl * 10 - 20 let x1 = x-pos.at(group.max-i) + end-lvl * 10 + 20 - draw-group(x0, x1, start-y, y, group) + shapes += draw-group(x0, x1, start-y, y, group) y -= Y-SPACE + // Separator } else if elmt.type == "sep" { - let x0 = x-pos.first() - 20 - let x1 = x-pos.last() + 20 - let m = measure( - box( - elmt.name, - inset: (left: 3pt, right: 3pt, top: 5pt, bottom: 5pt) - ) - ) - let w = m.width / 1pt - let h = m.height / 1pt - let cx = (x0 + x1) / 2 - let xl = cx - w / 2 - let xr = cx + w / 2 - - y -= h / 2 - draw.line((x0, y), (xl, y)) - draw.line((xr, y), (x1, y)) - y -= 3 - draw.line((x0, y), (xl, y)) - draw.line((xr, y), (x1, y)) - draw.content( - ((x0 + x1) / 2, y + 1.5), - elmt.name, - anchor: "center", - padding: (5pt, 3pt), - frame: "rect" - ) - y -= h / 2 - y -= Y-SPACE + let shps + (y, shps) = draw-sep(elmt, y) + shapes += shps + + // Gap } else if elmt.type == "gap" { y -= elmt.size } } - // Draw vertical lines + end participants - draw.on-layer(-1, { + // Draw vertical lines + lifelines + end participants + shapes += draw.on-layer(-1, { for (i, p) in participants.enumerate() { let x = x-pos.at(i) + + // Draw vertical line draw.line( (x, 0), (x, y), @@ -341,10 +190,13 @@ let rects = () let destructions = () let lines = () + + // Compute lifeline rectangles + destruction positions for line in lifelines.at(i).lines { let event = line.first() if event == "enable" { lines.push(line) + } else if event == "disable" or event == "destroy" { let l = lines.pop() let lvl = lines.len() @@ -355,19 +207,23 @@ } } + // Draw lifeline rectangles (reverse for bottom to top) for rect in rects.rev() { let (cx, y0, y1) = rect draw.rect( (cx - LIFELINE-W / 2, y0), - (cx + LIFELINE-W / 2, y1), + (cx + LIFELINE-W / 2, y1) ) } + + // Draw lifeline destructions for dest in destructions { let (cx, cy) = dest draw.line((cx - 8, cy - 8), (cx + 8, cy + 8), stroke: red + 2pt) draw.line((cx - 8, cy + 8), (cx + 8, cy - 8), stroke: red + 2pt) } + // Draw participants (end) draw.content( (x, y), p.display-name, @@ -378,4 +234,6 @@ ) } }) + + shapes }) \ No newline at end of file diff --git a/src/separator.typ b/src/separator.typ new file mode 100644 index 0000000..eed197a --- /dev/null +++ b/src/separator.typ @@ -0,0 +1,46 @@ +#import "@preview/cetz:0.2.2": draw +#import "consts.typ": * + +#let _sep(name) = { + return (( + type: "sep", + name: name + ),) +} + +#let render(x-pos, elmt, y) = { + let shapes = () + + let x0 = x-pos.first() - 20 + let x1 = x-pos.last() + 20 + let m = measure( + box( + elmt.name, + inset: (left: 3pt, right: 3pt, top: 5pt, bottom: 5pt) + ) + ) + let w = m.width / 1pt + let h = m.height / 1pt + let cx = (x0 + x1) / 2 + let xl = cx - w / 2 + let xr = cx + w / 2 + + y -= h / 2 + shapes += draw.line((x0, y), (xl, y)) + shapes += draw.line((xr, y), (x1, y)) + y -= 3 + shapes += draw.line((x0, y), (xl, y)) + shapes += draw.line((xr, y), (x1, y)) + shapes += draw.content( + ((x0 + x1) / 2, y + 1.5), + elmt.name, + anchor: "center", + padding: (5pt, 3pt), + frame: "rect" + ) + y -= h / 2 + y -= Y-SPACE + + let r = (y, shapes) + return r +} \ No newline at end of file diff --git a/src/sequence.typ b/src/sequence.typ new file mode 100644 index 0000000..beb2256 --- /dev/null +++ b/src/sequence.typ @@ -0,0 +1,152 @@ +#import "consts.typ": * +#import "@preview/cetz:0.2.2": draw + +#let _seq( + p1, + p2, + comment: none, + dashed: false, + tip: "default", + color: black, + flip: false, + enable-dst: false, + disable-dst: false, + destroy-dst: false, + disable-src: false, + destroy-src: false, +) = { + return (( + type: "seq", + p1: p1, + p2: p2, + comment: comment, + dashed: dashed, + tip: tip, + color: color, + flip: flip, + enable-dst: enable-dst, + disable-dst: disable-dst, + destroy-dst: destroy-dst, + disable-src: disable-src, + destroy-src: destroy-src, + ),) +} + +#let render(pars-i, x-pos, elmt, y, lifelines) = { + let shapes = () + + let i1 = pars-i.at(elmt.p1) + let i2 = pars-i.at(elmt.p2) + + if elmt.comment != none { + y -= measure(box(elmt.comment)).height / 1pt + 6 + } + + if elmt.disable-src { + let src-line = lifelines.at(i1) + src-line.level -= 1 + src-line.lines.push(("disable", y, auto)) + lifelines.at(i1) = src-line + } + if elmt.destroy-src { + let src-line = lifelines.at(i1) + src-line.level -= 1 + src-line.lines.push(("destroy", y, auto)) + lifelines.at(i1) = src-line + } + + let ll-lvl1 = lifelines.at(i1).level * LIFELINE-W / 2 + + if elmt.disable-dst { + let dst-line = lifelines.at(i2) + dst-line.level -= 1 + dst-line.lines.push(("disable", y, auto)) + lifelines.at(i2) = dst-line + } + if elmt.destroy-dst { + let dst-line = lifelines.at(i2) + dst-line.level -= 1 + dst-line.lines.push(("destroy", y, auto)) + lifelines.at(i2) = dst-line + } + if elmt.enable-dst { + let dst-line = lifelines.at(i2) + dst-line.level += 1 + lifelines.at(i2) = dst-line + } + + let x1 = x-pos.at(i1) + let x2 = x-pos.at(i2) + + let ll-lvl2 = lifelines.at(i2).level * LIFELINE-W / 2 + + let f = if elmt.flip {-1} else {1} + if i1 <= i2 { + x1 += ll-lvl1 * f + x2 -= ll-lvl2 * f + } else { + x1 -= ll-lvl1 * f + x2 += ll-lvl2 * f + } + + let style = ( + mark: (end: "straight"), + stroke: ( + dash: if elmt.dashed {"dashed"} else {"solid"}, + paint: elmt.color + ) + ) + + if elmt.p1 == elmt.p2 { + let x3 = x1 - ll-lvl1 + ll-lvl2 + + x2 = if elmt.flip {x1 - 20} else {x1 + 20} + + if elmt.comment != none { + shapes += draw.content( + (x1, y), + elmt.comment, + anchor: if elmt.flip {"south-east"} else {"south-west"}, + padding: 3pt + ) + } + + shapes += draw.line( + (x1, y), + (x2, y), + (x2, y - 10), + (x3, y - 10), + ..style + ) + y -= 10 + + } else { + if elmt.comment != none { + let x = calc.min(x1, x2) + if x2 < x1 { + x += COMMENT-PAD + } + shapes += draw.content( + (x, y), + elmt.comment, + anchor: "south-west", + padding: 3pt + ) + } + + shapes += draw.line( + (x1, y), + (x2, y), + ..style + ) + } + if elmt.enable-dst { + let dst-line = lifelines.at(i2) + dst-line.lines.push(("enable", y, auto)) + lifelines.at(i2) = dst-line + } + y -= Y-SPACE + + let r = (y, lifelines, shapes) + return r +} \ No newline at end of file