From d14797af3ee5b7d6f2a2f99a4d43bb11f65bc45c Mon Sep 17 00:00:00 2001 From: Johannes Wolf Date: Sun, 13 Oct 2024 00:51:19 +0200 Subject: [PATCH] canvas: Support custom coordinate systems --- src/canvas.typ | 4 +++- src/coordinate.typ | 16 ++++++++++++--- src/draw/grouping.typ | 1 - src/draw/shapes.typ | 33 +----------------------------- src/draw/transformations.typ | 4 ---- src/lib/decorations/brace.typ | 6 ------ tests/coordinate/custom/ref/1.png | Bin 0 -> 6146 bytes tests/coordinate/custom/test.typ | 29 ++++++++++++++++++++++++++ 8 files changed, 46 insertions(+), 47 deletions(-) create mode 100644 tests/coordinate/custom/ref/1.png create mode 100644 tests/coordinate/custom/test.typ diff --git a/src/canvas.typ b/src/canvas.typ index 4e29820b8..92d80ac21 100644 --- a/src/canvas.typ +++ b/src/canvas.typ @@ -57,7 +57,9 @@ marks: ( mnemonics: (:), marks: (:), - ) + ), + // coordinate resolver + resolve-coordinate: none, ) let (ctx, bounds, drawables) = process.many(ctx, body) diff --git a/src/coordinate.typ b/src/coordinate.typ index 7bd23113e..078191e26 100644 --- a/src/coordinate.typ +++ b/src/coordinate.typ @@ -251,7 +251,7 @@ /// Figures out what system a coordinate belongs to and returns the corresponding string. /// - c (coordinate): The coordinate to find the system of. /// -> str -#let resolve-system(c) = { +#let resolve-system(ctx, c) = { let t = if type(c) == dictionary { let keys = c.keys() let len = c.len() @@ -294,6 +294,9 @@ } else { "element" } + } else if ctx.at("resolve-system", default: none) != none { + ctx.resolve-system = none + resolve-system(ctx, c) } if t == none { @@ -319,9 +322,16 @@ /// - update (bool): Update the context's last position /// -> array #let resolve(ctx, ..coordinates, update: true) = { + let ctx-resolver = ctx.at("resolve-coordinate", default: none) + let coordinates = if ctx-resolver != none { + coordinates.pos().map(ctx-resolver.with(ctx)) + } else { + coordinates.pos() + } + let result = () - for c in coordinates.pos() { - let t = resolve-system(c) + for c in coordinates { + let t = resolve-system(ctx, c) let out = if t == "xyz" { resolve-xyz(c) } else if t == "previous" { diff --git a/src/draw/grouping.typ b/src/draw/grouping.typ index d3dc791fa..c2038201d 100644 --- a/src/draw/grouping.typ +++ b/src/draw/grouping.typ @@ -368,7 +368,6 @@ assert(name != none and name != "" and not name.starts-with("."), message: "Anchors must not be none, \"\" or start with \".\"!") - coordinate.resolve-system(position) return (ctx => { let (ctx, position) = coordinate.resolve(ctx, position) position = util.apply-transform(ctx.transform, position) diff --git a/src/draw/shapes.typ b/src/draw/shapes.typ index bbf2f6749..8e8778db9 100644 --- a/src/draw/shapes.typ +++ b/src/draw/shapes.typ @@ -116,8 +116,6 @@ assert.eq(style.pos(), (), message: "Unexpected positional arguments: " + repr(style.pos())) style = style.named() - (a, b, c).map(coordinate.resolve-system) - return (ctx => { let (ctx, a, b, c) = coordinate.resolve(ctx, a, b, c) @@ -220,9 +218,6 @@ ) let style = style.named() - // Coordinate check - let t = coordinate.resolve-system(position) - let start-angle = if start == auto { stop - delta } else { start } let stop-angle = if stop == auto { start + delta } else { stop } // Border angles can break if the angle is 0. @@ -445,8 +440,6 @@ to = ((rel: (to, 1), to: from)) } - (from, to).map(coordinate.resolve-system) - return (ctx => { let (ctx, ..pts) = coordinate.resolve(ctx, from, to) let style = styles.resolve(ctx.style, merge: style, root: "mark") @@ -506,9 +499,6 @@ assert(pts.len() >= 2, message: "Line must have a minimum of two points") - // Coordinate check - let pts-system = pts.map(coordinate.resolve-system) - // Find the intersection between line a-b next to b // if no intersection could be found, return a. let element-line-intersection(ctx, elem, a, b) = { @@ -534,6 +524,7 @@ return (ctx => { let first-elem = pts.first() let last-elem = pts.last() + let pts-system = pts.map(coordinate.resolve-system.with(ctx)) let (ctx, ..pts) = coordinate.resolve(ctx, ..pts) // If the first/last element, test for intersection @@ -610,8 +601,6 @@ /// ## Anchors /// Supports border anchors. #let grid(from, to, name: none, ..style) = { - (from, to).map(coordinate.resolve-system) - assert.eq(style.pos(), (), message: "Unexpected positional arguments: " + repr(style.pos())) style = style.named() @@ -770,16 +759,6 @@ panic("Expected 2 or 3 positional arguments, got " + str(args.len())) } - coordinate.resolve-system(a) - - if b != auto { - coordinate.resolve-system(b) - } - - if type(angle) != typst-angle { - coordinate.resolve-system(angle) - } - return (ctx => { let style = styles.resolve(ctx.style, merge: style, root: "content") let padding = util.as-padding-dict(style.padding) @@ -1016,9 +995,6 @@ /// Supports border and path anchors. It's default is the `"center"` anchor. /// #let rect(a, b, name: none, anchor: none, ..style) = { - // Coordinate check - let t = (a, b).map(coordinate.resolve-system) - // No extra positional arguments from the style sink assert.eq( style.pos(), @@ -1208,9 +1184,6 @@ ) let coordinates = (start, ..ctrl, end) - // Coordinates check - let t = coordinates.map(coordinate.resolve-system) - return ( ctx => { let (ctx, start, ..ctrl, end) = coordinate.resolve(ctx, ..coordinates) @@ -1310,8 +1283,6 @@ assert(pts.len() >= 2, message: "Catmull-rom curve requires at least two points. Got " + repr(pts.len()) + "instead.") - pts.map(coordinate.resolve-system) - return (ctx => { let (ctx, ..pts) = coordinate.resolve(ctx, ..pts) let style = styles.resolve(ctx.style, merge: style, root: "catmull") @@ -1385,8 +1356,6 @@ assert(pts.len() >= 2, message: "Hobby curve requires at least two points. Got " + repr(pts.len()) + "instead.") - pts.map(coordinate.resolve-system) - return (ctx => { let (ctx, ..pts) = coordinate.resolve(ctx, ..pts) let style = styles.resolve(ctx.style, merge: style, root: "hobby") diff --git a/src/draw/transformations.typ b/src/draw/transformations.typ index b8c07f020..7c9c748d7 100644 --- a/src/draw/transformations.typ +++ b/src/draw/transformations.typ @@ -223,8 +223,6 @@ /// /// - pt (coordinate): The coordinate to move to. #let move-to(pt) = { - let t = coordinate.resolve-system(pt) - return (ctx => { let (ctx, pt) = coordinate.resolve(ctx, pt) return (ctx: ctx) @@ -244,8 +242,6 @@ /// - bounds (vector): Viewport bounds vector that describes the inner width, /// height and depth of the viewport #let set-viewport(from, to, bounds: (1, 1, 1)) = { - (from, to).map(coordinate.resolve-system) - return (ctx => { let bounds = vector.as-vec(bounds, init: (1, 1, 1)) diff --git a/src/lib/decorations/brace.typ b/src/lib/decorations/brace.typ index 5571d38f4..4b9765299 100644 --- a/src/lib/decorations/brace.typ +++ b/src/lib/decorations/brace.typ @@ -62,9 +62,6 @@ assert.eq(style.pos().len(), 0, message: "Brace takes no additional positional arugments.") - // Validate coordinates - let _ = (start, end).map(coordinate.resolve-system) - group(name: name, ctx => { // Resolve all coordinates let (ctx, start, end) = coordinate.resolve(ctx, start, end) @@ -195,9 +192,6 @@ name: none, ..style, ) = { - // Validate coordinates - let _ = (start, end).map(coordinate.resolve-system) - group(name: name, ctx => { // Get styles and validate their types and values let style = styles.resolve(ctx.style, merge: style.named(), diff --git a/tests/coordinate/custom/ref/1.png b/tests/coordinate/custom/ref/1.png new file mode 100644 index 0000000000000000000000000000000000000000..c179a466e152aa41cb70f9b06a8b8de91980d948 GIT binary patch literal 6146 zcmdT|cQl-9w;v@WN+L=G(Gs1g(HV&lMj3+%qKp!~_h_ScQKE$?qm2;`BN1Wr1Q8{M zXi=gZks(a9=!S3HbIv{G-gVbG>$~@l`>yr8&w77*@8910sqfke5U>U<6)P100H6hF zsu=(PWUK%HN%DD8BEmA8HUR)I_kz^!Km(@MCM9SA0GiHP=VN&fuAsFS`f6HN0O4=R z0pXGPq<}O-03h`k37McbPWTtoGu^)s{(p^y824Y2`#%{MIsa7%%32`&t@uJQA1GFJo^Oxz zu0)dRjcgq{au(!qPAE{P$-x@d-SP zb{%bR?_aykpJ}*;$J^zALkGb?oRpN5Vg>C8s6eGS-zGZ0^5%|L;AOx?otr}Pyr`@E zsKW!1-bH`2@v3)wKWae+XuOKE|5Vr@uGtKwa`^2?$6&7d(%4Ev*rB%;Si&Bhwc{+O z!k(R!JyGqRKI+oy5i;p?Du1+E#bf7Vxn6K$QnfAxf;Rx6sJZXu3`7_EdHJTIv9J9s zo|eJlzS=oB!shlsT7o;XE2+rL^s@C}&dL#w?EFK^lvDuVQ`GOP`?uu(;W}$y8!t{K z)n|+yE(VZChLdZHo6KZ}tCdJt4M9lRA*8&Jw}#74c&@6RJL1oCrA=MBCP6a#0WLN` z`A_!4|GZ>~+wKor=}h;xWaPUi0^8^Ij#>}MvADX`rmqaPgFTCzf+&Q8Ve8s<6jB7Co!-#~yiNM22n17D0Jz~#N=WSxl)g$rEo6p z=yujL!(%;UW=qBv2fnFhZXIi1H^=#?*<$+j{niuogv-4TQiil~oxQeOdk?*9pZA8g zV|POp#e7nF76(Ew<_1D6jg_~Zra5;9KFO$Z#h38T_<8cqWQ7X@dW*@lipafrsQJ~h z$*%LzUz6kKd7-Atpq3eq_^vG+Eoc?}m9lEgRq|M6>Oirj$Pa?ZYSrDbzI3sd3Wx;K zWbnJ11?N{lkidGa!QvpopqJbXSRQ=7@aRX`5doO8C{D9q>GZ6rrrBJy86;Y`(t z=Le~hmFmM)th;t!*gv=joLKccx{#wHbI+AZ-Vu7Qa3DZ6KR(($SGzcrQJs6R zF#cNBsv^Q zb~k;qZT>u)HwP;o&}Zd2>79m?jMRgdn##mXPmL|_TSpi)rsEYwpaHRNH@;;=xpGPe z?gu>y+#$*8w7yu?Dfe#f0AKGmjX+~p2&n~9qq zUILl}gt7WW&TB$kHkA5y`U$;d4^nKV)_&wzn!lW8@{!8RLuateVheqhI54U|`aTUV zcy>Sw=PuDuMEN+m$*_rVYfpm5Di?1%eXy63e&iCUP#{#&K5a+&dK!;;B~E6cp`rpY z-KUb`%Sgb_>*;him{xjm^b~3*vBmSwyjv^rzvfx0beNK*F^7Q}Cx&hxGv*GplcG*8 zfH9d(Da*hb_aw3kq3VN>6c(lRc5Nr?s6+ej?b`aoLs2Z93$;x*Px@HE_kM(_92QeT z=ArY@irapD*6@-VmlFln&jtzHAr-m=Lo ze^sGVQJ}3|j?F~AYB3jehJH-S9P?Gk8c`?}i(k+&G~L%4?g^gK|K8JJ`mY*XB zQY$isCvP)pP|01gj%ZCF_(&G=?IFS{3`_p^2o3Bhapl=QD#!Q^CVnc!XhuVR6TE2-;ICK}%jb z!=!HvrGhNWf0n?gF0*5_%W!P>5sPd}Jn_b?Jq8}lmZa_}lzJe!RFvTC@F%ZIIOUk+ zn24jjLyHn2ft3HO!7v(p(Q5O<)`hTF@2x6_6YSs~CBu}mp)Ve-3ni<00phSni&XBwF!L#V1#{MVmOju;Oovh_PC|5ziNG-b;`S7Ek73%dYK}?A~MI7Vo|` zcp!Da#NBHvxnO4QV;*>@r_%OrFG@+b7=hy4llksBgYzv6%^#3MsOW`>R}k+xZd48! z99VYJY&n&}5aF#*mR9MoCT{9yX!PdRK?r(+T54o{FN5bo7`;QixoDh3I#2JCw{;5|jh=Kf zZP{tG)2KJC{_Gwn@iWrq#npHH_H`UaTqw=b{U39mKFKL6n!#YRb8{Z`rVAc07%V$G zdv&y3?8^KQx{kQ!3JQY~l)>i_94ugPL93}+TN=Q<>eo4#}h}b&_8BL-P zub*@^c?Ls2?O!5Z!{4_9lu}wd%DHYvy$KI(bQ~`6N*t%Yz-wL%nU5H>uUp&~Pn@@;Kx@ytUZK+2?|SY5sh<_ihF>yX+QRc-^S8*z!}gt8K`xEtH0 z!{>o%y|&j+w38woDs=FfZ@`YT@cbZ(+g*)GP7 zIghRHF|?!X>ja-@-sb3EN^dH*uRHKnU`pzCZ=mKO)qm4Afwm=Zna~g&C=3mrJx&fyfxc2 z--7o~O3)OIi;B0jubW)9iICG5Us%SissuZrpPAD|FYxj3sB?jy(47vcnr%{&x87i&K3+tN%EkXKGXX!{Ddioeu5}V?! z0D^P9klr+2rYE#LjNQG_=k0UiJSN`bPIb-Q7_N%k67ALY!hg}MC{+dmE3-yfAJnLD z?{01m&r{p6HQ|K64ZWI6JPe3OM)Wn1Er@F#)3A?O&~m6_5oM@fqDi2XR9 zA|yC?+P#4fT*);VuJ|JprOFt!MIUlrmiL`k-K3{^wGn@{vDNqd(&qTvT@19mNNKlL zn0BaeM4%&qJ)J@i=ID5Gc&HWbckCUhVmdQ;$z8kmEMGcxIb?&=IVOR825I&Ye&wML zaDza}YnjP5jY2#E!`H<$w2Wt^7tIVQ@Y8Xf+>E-X_G|R9oJGA22(j);+iP(^K&Vq^-D#oL!@`WbWgc##UPT zl|mUWgLB=_Ac2J?Aw0%0-&ZPAkuE3O+anJ}Q%CG;lHm(WRJ;mUo{H;}caue)U`$V= z1K-YrsPP8}xv%95gN*vZerOi3%#1%jiQt?r%L_O}9_0)IKdqWKTh;jlS6G zA{lkJfwaNUtD(!Lb~lm)XIPUdQd4ua!>~*_)Rb_SrE-rCUvmfL56J52Grw#HS`SLl zeocs~8`4xLeBkJqGQZF=NCTEm8}~EQ`j1^BkO+6pmg7iunjGfrRm9nz=5>XM-Ea!m z1h6Q-iw(1s1K!n5WUt}RxB!Me;!qt!!xP9)Cz~HK#uRfsUYx)0+C}{-nBr!2OGI*7 zkT8h(y@f%UraRsHh}|CGRn^xJ&${veLy=+ z2muha&PSyjqsJ+Wn!@4SHy5Tw+;Q6vEOy0dQgSVVzahzQD)D3XWpyIu#!jT5D-T;65giOvy;`xuY?wQtFCX|_i9?^}UU zBZ6N8YDM+$Wd-oJ0hI0p)a+^w41Z)O>|i1pk~|C@x-N2cQ+I?%nLHu16;=&y+fwf= znsaZsT2`2Gb@^LIWV(mG-gJIbjbSJ7hjGB8Lfo|RYu5*0JF_JGUhoPfyP24{lFk7@ z-*d_}I(uvWRR$|*S$^iH4~711b-d1h&uO`H!4qRX^?J+zBFdj*1H~#590UL> z2pe&3#*z)a$LT)a0{T5^t9B2-NDiZb3BMAdpwu4O;74gwP`VDUM%2f8*c+sBxGz)F zw@OMna@iPzGHT-M<=`Nh8e{5$;5*I>*~TAZR7);x!D)Ci({=2(Bu@vG>}Tgbf7V@B zULP@oPgT`fi0bL-O+P>O!VQ?4o8Pj4rU;@L-zw}NOD(`BCWKEQvFUPwA>Uq|20%6D zTVD`{YESm_?x>(_Gqb7m8ZB7tl`m%yljw21elT0;_z7}sr4=_ZF;V?pTBGr^QtR?K zRS{ZDuISAG{Pb`epCQg1tsk*?vDZ$4oH8m033QL_cb#O|kT>QwY z$?8H)qTBTNv=WqA=^Ge0$@QxGBVXHqD(zS=kJ~#7pDf)xF{jAl*>%~6r6waA5BLN~ zQ^G1zYKom}gp7DyIeeQ+Clg}2BmII?Qd@;MCmhF|rya+98n$#r zvPkm;SlQ|ws!gYN8$(VvHXtVgY00=BUx@#ORb!QQ&6~1Qoh5xxq|mtVxFexXqR_c+ zsIbLz?z27?=2+mbm{ZPY!hxv+!hsPs?jD=(W(o4EY?Z=%i$}jlvxEzerI(DXt zd5=w5oPH1?#|vg(G5$t)m(%K$|M4i869R|qw8?eDOxDN%p6kRKnH;eIN~~nY6U&!p z?dRX2UtpetP5hUg{s&L72=GTN2eyT08;v`dd^oX6Xw90E!Z`pdg2G;R$K%f1Ui7r* z1Zcd=g{R!MM|f4bfJ#W^21R(m35IJo!Geh;0vaY=f?mW@>{&I3*yxBA(Jo@iln9-* zKSIC4e}?9boBeK|6@$5!PgniPn^Xb5L@Oc*u>%y40)~iOzjnV1&g6d({0;f9jDLst zpE>?B@qZ2M&+^|=`EMiscO3tPn8^PR9RDv3&k(!D= literal 0 HcmV?d00001 diff --git a/tests/coordinate/custom/test.typ b/tests/coordinate/custom/test.typ new file mode 100644 index 000000000..930f6c00b --- /dev/null +++ b/tests/coordinate/custom/test.typ @@ -0,0 +1,29 @@ +#set page(width: auto, height: auto) +#import "/src/lib.typ": * +#import "/tests/helper.typ": * + +#test-case({ + import draw: * + grid((-2,-1), (7,1), stroke: gray) + + let log-resolver(ctx, coordinate) = { + if type(coordinate) == dictionary and "log" in coordinate { + coordinate = coordinate.log + coordinate = coordinate.map(n => calc.log(calc.max(n, util.float-epsilon), base: 10)) + } + + return coordinate + } + + set-ctx(ctx => { + ctx.resolve-coordinate = log-resolver + return ctx + }) + + set-style(circle: (radius: .1)) + for i in (.1, 1, 10, 100, 1000, 10000) { + let pt = (log: (i * 1, 1)) + circle(pt) + content(pt, repr(i), anchor: "north", padding: (top: .5)) + } +})