From a40e068590895562f0d1a4aa17fe97b9bf5630a3 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 26 Sep 2024 14:30:47 +0000 Subject: [PATCH] Add alignment parameter to matrices and vectors (#4998) --- crates/typst/src/math/matrix.rs | 40 +++++++++++++++--- crates/typst/src/math/underover.rs | 6 ++- ...> math-mat-align-explicit-alternating.png} | Bin tests/ref/math-mat-align-explicit-mixed.png | Bin 0 -> 2523 bytes tests/ref/math-mat-align.png | Bin 0 -> 1564 bytes tests/ref/math-vec-align.png | Bin 0 -> 1098 bytes tests/suite/math/mat.typ | 18 +++++++- tests/suite/math/vec.typ | 4 ++ 8 files changed, 61 insertions(+), 7 deletions(-) rename tests/ref/{math-mat-align-explicit--alternating.png => math-mat-align-explicit-alternating.png} (100%) create mode 100644 tests/ref/math-mat-align-explicit-mixed.png create mode 100644 tests/ref/math-mat-align.png create mode 100644 tests/ref/math-vec-align.png diff --git a/crates/typst/src/math/matrix.rs b/crates/typst/src/math/matrix.rs index 9f9b82bca..5b9b17a7d 100644 --- a/crates/typst/src/math/matrix.rs +++ b/crates/typst/src/math/matrix.rs @@ -7,7 +7,8 @@ use crate::foundations::{ Smart, StyleChain, Value, }; use crate::layout::{ - Abs, Axes, Em, FixedAlignment, Frame, FrameItem, Length, Point, Ratio, Rel, Size, + Abs, Axes, Em, FixedAlignment, Frame, FrameItem, HAlignment, Length, Point, Ratio, + Rel, Size, }; use crate::math::{ alignments, scaled_font_size, stack, style_for_denominator, AlignmentResult, @@ -29,7 +30,8 @@ const DEFAULT_STROKE_THICKNESS: Em = Em::new(0.05); /// A column vector. /// -/// Content in the vector's elements can be aligned with the `&` symbol. +/// Content in the vector's elements can be aligned with the +/// [`align`]($math.vec.align) parameter, or the `&` symbol. /// /// # Example /// ```example @@ -47,6 +49,16 @@ pub struct VecElem { #[default(DelimiterPair::PAREN)] pub delim: DelimiterPair, + /// The horizontal alignment that each element should have. + /// + /// ```example + /// #set math.vec(align: right) + /// $ vec(-1, 1, -1) $ + /// ``` + #[resolve] + #[default(HAlignment::Center)] + pub align: HAlignment, + /// The gap between elements. /// /// ```example @@ -70,7 +82,7 @@ impl LayoutMath for Packed { ctx, styles, self.children(), - FixedAlignment::Center, + self.align(styles), self.gap(styles), LeftRightAlternator::Right, )?; @@ -87,7 +99,9 @@ impl LayoutMath for Packed { /// special syntax of math function calls to define custom functions that take /// 2D data. /// -/// Content in cells that are in the same row can be aligned with the `&` symbol. +/// Content in cells can be aligned with the [`align`]($math.mat.align) +/// parameter, or content in cells that are in the same row can be aligned with +/// the `&` symbol. /// /// # Example /// ```example @@ -109,6 +123,16 @@ pub struct MatElem { #[default(DelimiterPair::PAREN)] pub delim: DelimiterPair, + /// The horizontal alignment that each cell should have. + /// + /// ```example + /// #set math.mat(align: right) + /// $ mat(-1, 1, 1; 1, -1, 1; 1, 1, -1) $ + /// ``` + #[resolve] + #[default(HAlignment::Center)] + pub align: HAlignment, + /// Draws augmentation lines in a matrix. /// /// - `{none}`: No lines are drawn. @@ -249,6 +273,7 @@ impl LayoutMath for Packed { ctx, styles, rows, + self.align(styles), augment, Axes::new(self.column_gap(styles), self.row_gap(styles)), self.span(), @@ -454,6 +479,7 @@ fn layout_mat_body( ctx: &mut MathContext, styles: StyleChain, rows: &[Vec], + align: FixedAlignment, augment: Option>, gap: Axes>, span: Span, @@ -538,7 +564,11 @@ fn layout_mat_body( for (cell, &(ascent, descent)) in col.into_iter().zip(&heights) { let cell = cell.into_line_frame(&points, LeftRightAlternator::Right); let pos = Point::new( - if points.is_empty() { x + (rcol - cell.width()) / 2.0 } else { x }, + if points.is_empty() { + x + align.position(rcol - cell.width()) + } else { + x + }, y + ascent - cell.ascent(), ); diff --git a/crates/typst/src/math/underover.rs b/crates/typst/src/math/underover.rs index f1789b7ad..5ad7f4579 100644 --- a/crates/typst/src/math/underover.rs +++ b/crates/typst/src/math/underover.rs @@ -476,7 +476,11 @@ pub(super) fn stack( let mut y = Abs::zero(); for (i, row) in rows.into_iter().enumerate() { - let x = align.position(width - row.width()); + let x = if points.is_empty() { + align.position(width - row.width()) + } else { + Abs::zero() + }; let ascent_padded_part = minimum_ascent_descent .map_or(Abs::zero(), |(a, _)| (a - row.ascent())) .max(Abs::zero()); diff --git a/tests/ref/math-mat-align-explicit--alternating.png b/tests/ref/math-mat-align-explicit-alternating.png similarity index 100% rename from tests/ref/math-mat-align-explicit--alternating.png rename to tests/ref/math-mat-align-explicit-alternating.png diff --git a/tests/ref/math-mat-align-explicit-mixed.png b/tests/ref/math-mat-align-explicit-mixed.png new file mode 100644 index 0000000000000000000000000000000000000000..88ccd6de7f06bdf1ad92c7c9ce83a73ce5c8ad39 GIT binary patch literal 2523 zcmV<12_*K3P)ZylHX;L)+<x3`=5vXw#RKd20!d{eC6uou{ZfqP)Cfdes*dm!Y&DKwn(cgp%Lq!tkUMXefMwFgzk24c+*?F#K#G z8Y+BN7#>=LhKhy?!-JBMiw!ELn=$589U?i!*r4-{11=i?N%EipK5@uCi;EeMtjN)g zMj-m1L0G%%-7)hlr#{dNo>&~7a)TuafF!*x-5HU+w!)dq2wVa)><=%FVR8`N(#DtD z7Y06!8esV0Xn+cTNmJz)hsJ(Gk8j7I5GCCgr3@dJwzJ{#f02iy9~Op(9Y}P`Syp=HV+8W%E-k{!4{uDGu$QD!2deFT$Cjj9 zfW!dVF=hnnDk|!+VkjvoX`!;BysiK6W65YJ`LPG@%k_-5;b(;5S2NI1%B#ZgUm_9d zjQXYtO0FR-66s3CoeN3Gg^m-3=O0C6o8GvToIS2ci$uB-Gq`c&Ix@Q#2*cam5%~Zn z7)f;nX^}`*Lec-Ga1k=gw+X}P;}CfbrWV>2KD>*v@yJY07lzF*m=PEbQ%dX#AKit1 z3cWgiWf6v1n25k1ER5~6D|~GO87t&}&qo~2n~N>FC0q>GeL<=FhY3iFM7k1NSGBH$ zUamToFjMbgODELig0Z2o5owV~S1K|a=~dx^TKL;_XkZBhRKxp&=}~cPG!zD={|6xUM655NksH?1*NtV{L9PrSWK0PP=e^a!9ZnX- z^w#$xJ6ZuJm}Gvh!u-p2`Z}|HfYaOeJ5<5}2T_EpUs@gn@FqClt>)e<+;4c-QI+tR z@08(+U9(1;ftO)wKDec-hOb0M&X0^NvKg)#B@Wk*Kp9@wg=ZA-7JS@H@XczITKHR? z&P}JQwHf9&_k`iF1<09Rht+op%(;7~g z!dlC&#Bo`KysHN!c~1`TsAnefk6VD`Q2y=F_RXu~=5xsHz6Rwk$;=I-Tkf*!_JwI? zv(Es-k0t@sL`qsyHYL9sLO0{lRVbuM_oXSrSETJC(9bi=!%2?{!-H-DbXH0%)D_Ms z=y|9X%P(r^t5LeIQ5mk0w%cgf)>R@8-yG<#FoCBl7a%DMz^S?P1rq{a`(VxFvd8NH zmcM1&{f--t`52Hi-=yNa=XVIiA3K(cm2!W7{{q5}1?u|`KaquovYrrzhv?9dZpef8 z$$a4!8p?iA82)24LY21xOC{1G(@}$fC8G_w^Y016E2+i2b_7;d zZ7xUVz)WFy;|@eVf(c!U>8<39D4gQ17^|({APirbhR6tTF0(5fw3E`Q$jpioh7H3I zc?Fyc>InA485 z^yu*0pgANVojQxcTb1Tt6 zjjI9m@TzPy@%;+*@YS#BQFUxSG=+qP0F)dKP9)7|Q8?DFt%;vLULG0P_cJER@!>8)|BS~%uQ<=9yF+C@)*(J;C8g0bWI zF7fb1eNetUP9;1!QyDfde`zhi%kW7q>^q2BxWi=HYBE`DhVv$i!={%}hCOQArey(d zz{DC-{W~pc;e%dY6TG}qY=(KUT^NpY0l9bJa|0Kn40*V;;Kc2{r3X#6!Y8ec`VLl{ z za|IwtALtO(EsA;NfMj`M>F?~DS0`*__on+Aluu6K^Ye7G?DMrR+?dSXF9#SNoC$zQ z!jXX1caGC7ebqS>x}^KMl;I9(+X8Ll>*V3g!NTw(i2#+yCCywtH7f~_WLbN~=)a~LA|{vnsh*|`yE=_`8aS#0pT=-H0U;iQlUI&elYy;c8Y7Of$9 zCo)@>3Bw@^5g7$js_hD&-A>6YWEKVq!_~tP`87<#<^JX0aZ^DE+ zyTYNK6waep<=cqEKKrqy={lb_FcDIgAT0`ePGIfsQX>55b$Fjj*zh5?d>=?%XmC0O zo@*kI7UbeLZ%M^kT;xGBy(St}!g$7_k%%3rg}df-qKUTI9jamS+|k5m`KX7U9>edonn$k1)8R5${eQRB@1SAkot$9 zC~`1C6QL6#7m=HYB1b!iBZtco0)fLt;9NiL*%{}#oXdEA-;?dQ=l$33zt?`hXZ!Bi z?>uK1{-=g1umUTv0{@5bu6oFeVUW>t9HPbN4_{NtATO7cYeIgPI7x*T4qfUqFM~rUSsuqg=^yV9abL!Kb%^ zZO4d8A;GJYAgJY+_iu|=e7f*W1_(-BLxN{qfuIhp&LCd#8FjiA2)Z?$1V5((V=d28 z;73pOgE8BPUIMJ20Ols>39xn|n4732z(v6TSKlRgsY)ka@dX#rcWhM6gy+<2Q}~s1 z$8;FrVXtE&{)vFq!@a=SN4c_n!Nb)AxH^=5$D!5qqwYTcLJmB3XJw?8U-|tRo12V{ z@YqA6v4dav4=x%7=0+6-;^fj%U>yN&4+3kpk?jN+A?@JmJO@el^WZt_xn6KJj0FXL z^)3JyFU*9vs<|qkDnLSiE`6$;- z7d-oK0?dhBY&-6r1{!?)2mn*c*-VOxfED|621EAS_?>#8Y-bQdABGOWaI?-_C*{O=3bOT$LvzaxI9NCg4DGmhVU{9?GLUXcR6%=!6n zHSUg|j=E{FuiIOGfbd5hh;U($7r68iSE(;JsE`1c1$%)_54cRe;NUVp9rfLL4!E~F z0fHhf`u`alP9&wmt*c2%C2*CMbfVfnaOGGqH@1=h-wq{=%-exDGL0c%Zp;G$Y!B*z zt0qXYw8BFV9N=mVau8s=sP{griz5%fy)(1);JPsd6nJ)lkCB;vz{h=c#=9hV*CAH; za1049`edkZMjBU*3tm!8f~~LHhY0s{aajgo+e>y5jGY;9=BS+02T``f?=5hvZ^3m3 zfGaydgZ~-aXJeWw1EOxKRE2QsqAJY*;L|oDj2)F=sCXNw@IaUYvU{|AfDA*k19Ee$ zs{tLxJtJghysO-EDX;=7umV3`xV{-OYi{rdTo-1A%&Z#?blCb?J7i~BX(PkC)o@iE z7YlKDTP{Id+R|nKg|SrF^db1{ZXaRT<5M8c?tIJqpo0kSx$OJv4>uT2_-rfh@1ke2)f;2xyWJ1){P1{N!>TLLa zJpkm*CBak6!C37S54U~WuvAl~}aj0bb$YY1?8FofOuIR@hX+L#G( zsf&yNS8zG;s2L^~f7Ag7-w5#n+ss@xUvS6`0$ecC3!IzGmFo*0S>VT`z8(olai<_@ zX|4aCv1adj>8#!wEuGb0AJo%f4duTu8UkE8hA=Wq194;;#(}v|0|7RVFoUsf((lK3 zz7c||e%cOk>d12tcV4?#Ix@3ok>Cx-S>a<*B>2MTLxsCKxy&y3tBWMK zYuZrZ%yce|3x1`S1S70MG^@XXq_~riv_uC0HOoPRZ>%NwC_wq{toI-Q O00009o62%%;wC z12;H@{%Dm$YS|6mfxZD^62=7x7bn?O5dHuaBgf9e6iz`q%?T=?_TSKS%C$ zI4yR0zz=tEffw`wGe*f8Vl&Ht86$NDu@yTJFk{mk9&pP{#?~ynCCXZuMf|*aIu%m) zx60Z&pZLAZi8mEReIkF<#sMymW~?AISY&-gh6fbC=~OQW2K+FwAItLlbSgFo&G;ei zJ_ooU5kaLR3n5HKV9l(@GW$K4?#kx?A3ThpGLLu3Qnd&MFR8>b!2qU5vpK*?c?c|p zN$%=cIE_`~O;x~Yei8>bu>gS;Y?Qld6`Zb8$=w)?4X}M2;BY0;x9vXQCb{Q?dPkZ* zd!Ivd@Q4rC^n$1|oPWlT5;Y^txccDP5oWBtltjO4|J1V(p90wdjj0iG2PgM+kq zw?bfWh!U@D5Dbo=#Q|QO3xgG%a=#o6gQG9Yy;A{$b64{(^_RFq&Hk6VF)QJl|D`tM zN51*%JvQp~qJOYiukZE`wshz_`N3gI1lBlmj_1PZTyl=b!KpHA%znB4WpG9=f=W~j z%2E-9z>2JpyIo-c(_@(&;B)MU?4@?BwbQVC^|{4V>xfRT#WIyW3GGjark>{jmuzBe z=~5HcM@zBvOk&ci-D<3Z!6Md(8amY$xzBM-DF^sV1cEMmMajfY`2}6}+Cqp;DFJ^# zLO*eU(Nrr0KBOWxQ3HXWZy{Ed0f8MeTs+|TSJ1sWtc#?@Incd2=O#(@VbHz$`}8t%fI Ql>h($07*qoM6N<$g18(7SO5S3 literal 0 HcmV?d00001 diff --git a/tests/suite/math/mat.typ b/tests/suite/math/mat.typ index 1f6716d7c..baec53eee 100644 --- a/tests/suite/math/mat.typ +++ b/tests/suite/math/mat.typ @@ -92,7 +92,12 @@ $ mat(1, 0, 0, 0; 0, 1, 0, 0; 0, 0, 1, 1) $ // Error: 3-37 cannot draw a vertical line after column 3 of a matrix with 3 columns $ mat(1, 0, 0; 0, 1, 1; augment: #3) $, ---- math-mat-align-explicit--alternating --- +--- math-mat-align --- +$ mat(-1, 1, 1; 1, -1, 1; 1, 1, -1; align: #left) $ +$ mat(-1, 1, 1; 1, -1, 1; 1, 1, -1; align: #center) $ +$ mat(-1, 1, 1; 1, -1, 1; 1, 1, -1; align: #right) $ + +--- math-mat-align-explicit-alternating --- // Test alternating explicit alignment in a matrix. $ mat( "a" & "a a a" & "a a"; @@ -124,6 +129,17 @@ $ mat( "a a a"&, "a"&, "a a a"&; ) $ +--- math-mat-align-explicit-mixed --- +// Test explicit alignment in some columns with align parameter in a matrix. +#let data = ( + ($&18&&.02$, $1$, $+1$), + ($-&9&&.3$, $-1$, $-&21$), + ($&&&.011$, $1$, $&0$) +) +$ #math.mat(align: left, ..data) $ +$ #math.mat(align: center, ..data) $ +$ #math.mat(align: right, ..data) $ + --- math-mat-align-complex --- // Test #460 equations. #let stop = { diff --git a/tests/suite/math/vec.typ b/tests/suite/math/vec.typ index d7bc0b6c0..cf7057f33 100644 --- a/tests/suite/math/vec.typ +++ b/tests/suite/math/vec.typ @@ -4,6 +4,10 @@ #set math.vec(gap: 1em) $ vec(1, 2) $ +--- math-vec-align --- +$ vec(-1, 1, -1, align: #left) + vec(-1, 1, -1, align: #center) + vec(-1, 1, -1, align: #right) $ --- math-vec-align-explicit-alternating --- // Test alternating alignment in a vector.