% \iffalse meta-comment % dtx/mahjong-tiles-05-rendering.dtx -- rendering for mahjong-tiles % % Copyright (c) 2021 Daniel Schmitz % Copyright (c) 2026 Lilia Chen May Wither % % This file is distributed under the MIT Licence; see LICENCE. % \fi % % \subsection{Rendering} % % \begin{macrocode} %<*package> % ----------------------------------------------------------------------------- % Rendering % ----------------------------------------------------------------------------- \cs_new:Npn \__mahjongtiles_file:n #1 { \g__mahjongtiles_tile_dir_tl / mahjong-tiles-#1.pdf } \cs_new_protected:Npn \__mahjongtiles_render_hand:n #1 { \dim_set:Nn \l__mahjongtiles_symbol_height_dim { \fp_to_decimal:N \l__mahjongtiles_tile_scale_fp \l__mahjongtiles_tile_height_dim } \dim_set:Nn \l__mahjongtiles_baseline_offset_dim { ( \l__mahjongtiles_tile_height_dim - \l__mahjongtiles_symbol_height_dim ) / 2 } \dim_set:Nn \l__mahjongtiles_tile_width_dim { 0.75 \l__mahjongtiles_tile_height_dim } \__mahjongtiles_parse:n {#1} \leavevmode \raisebox { -\l__mahjongtiles_baseline_offset_dim } { \seq_map_inline:Nn \l__mahjongtiles_tiles_seq { \__mahjongtiles_render_item:nnnnn ##1 } } } \cs_new_protected:Npn \__mahjongtiles_render_river:n #1 { \dim_set:Nn \l__mahjongtiles_symbol_height_dim { \fp_to_decimal:N \l__mahjongtiles_tile_scale_fp \l__mahjongtiles_tile_height_dim } \dim_set:Nn \l__mahjongtiles_baseline_offset_dim { ( \l__mahjongtiles_tile_height_dim - \l__mahjongtiles_symbol_height_dim ) / 2 } \dim_set:Nn \l__mahjongtiles_tile_width_dim { 0.75 \l__mahjongtiles_tile_height_dim } \__mahjongtiles_parse:n {#1} \int_zero:N \l__mahjongtiles_river_total_tiles_int \seq_map_inline:Nn \l__mahjongtiles_tiles_seq { \__mahjongtiles_count_river_tile:nnnnn ##1 } \leavevmode \vtop \bgroup \offinterlineskip \int_zero:N \l__mahjongtiles_river_seen_tiles_int \int_zero:N \l__mahjongtiles_river_col_int \hbox \bgroup \seq_map_inline:Nn \l__mahjongtiles_tiles_seq { \__mahjongtiles_render_river_item:nnnnn ##1 } \egroup \egroup } \cs_new_protected:Npn \__mahjongtiles_count_river_tile:nnnnn #1#2#3#4#5 { \str_if_eq:nnF {#1} { gap } { \int_incr:N \l__mahjongtiles_river_total_tiles_int } } \cs_new_protected:Npn \__mahjongtiles_render_river_item:nnnnn #1#2#3#4#5 { \str_if_eq:nnTF {#1} { gap } { \__mahjongtiles_render_gap:n {#2} } { \str_case:nnF {#1} { { draw } { \__mahjongtiles_render_draw_tile:nn {#2} {#3} } { akastack } { \__mahjongtiles_render_akastack_with_overlay:nnn {#2} {#3} {#5} } } { \__mahjongtiles_render_tile_with_overlay:nnnn {#2} {#3} {#4} {#5} } \int_incr:N \l__mahjongtiles_river_seen_tiles_int \int_incr:N \l__mahjongtiles_river_col_int \int_compare:nT { \l__mahjongtiles_river_cols_int > 0 } { \int_compare:nT { \l__mahjongtiles_river_col_int >= \l__mahjongtiles_river_cols_int } { \int_compare:nT { \l__mahjongtiles_river_seen_tiles_int < \l__mahjongtiles_river_total_tiles_int } { \egroup \vskip \l__mahjongtiles_river_row_gap_dim \hbox \bgroup \int_zero:N \l__mahjongtiles_river_col_int } } } } } \cs_new_protected:Npn \__mahjongtiles_render_item:nnnnn #1#2#3#4#5 { \str_case:nnF {#1} { { gap } { \__mahjongtiles_render_gap:n {#2} } { draw } { \__mahjongtiles_render_draw_tile:nn {#2} {#3} } { akastack } { \__mahjongtiles_render_akastack_with_overlay:nnn {#2} {#3} {#5} } } { \__mahjongtiles_render_tile_with_overlay:nnnn {#2} {#3} {#4} {#5} } } \cs_new_protected:Npn \__mahjongtiles_render_gap:n #1 { \hspace { \dim_eval:n { #1 \l__mahjongtiles_tile_width_dim / 7 } } } \cs_new_protected:Npn \__mahjongtiles_render_akastack_with_overlay:nnn #1#2#3 { \tl_if_blank:nTF {#3} { \__mahjongtiles_render_akastack:nn {#1} {#2} } { \__mahjongtiles_make_akastack_overlay_tile:nnn {#1} {#2} {#3} } } \cs_new_protected:Npn \__mahjongtiles_render_akastack:nn #1#2 { \int_compare:nNnTF {#2} > {2} { \__mahjongtiles_render_sideways_stack_raw:nnn {0} {#1} {0} } { \int_compare:nNnTF { \g__mahjongtiles_aka_int } = {0} { \__mahjongtiles_render_sideways_stack_raw:nnn {5} {#1} {5} } { \int_compare:nNnTF {#2} = {1} { \__mahjongtiles_render_sideways_stack_raw:nnn {0} {#1} {5} } { \__mahjongtiles_render_sideways_stack_raw:nnn {5} {#1} {0} } } } } \cs_new_protected:Npn \__mahjongtiles_render_sideways_stack_raw:nnn #1#2#3 { \stackon [0pt] { \__mahjongtiles_render_tile_raw:nnn {#1} {#2} {1} } { \__mahjongtiles_render_tile_raw:nnn {#3} {#2} {1} } } \cs_new_protected:Npn \__mahjongtiles_make_akastack_overlay_tile:nnn #1#2#3 { \hbox_set:Nn \l__mahjongtiles_tile_box { \__mahjongtiles_render_akastack:nn {#1} {#2} } \tikz [ baseline = (base.base), inner~sep = 0pt, outer~sep = 0pt ] { \node [ inner~sep = 0pt, outer~sep = 0pt ] (base) { \box_use:N \l__mahjongtiles_tile_box }; \exp_args:NV \__mahjongtiles_make_overlay_node:nn \l__mahjongtiles_overlay_style_tl {#3} \path [ use~as~bounding~box ] (base.south~west) rectangle (base.north~east |- mahjongtilesoverlay.north); } } \cs_new_protected:Npn \__mahjongtiles_render_tile_with_overlay:nnnn #1#2#3#4 { \tl_if_blank:nTF {#4} { \__mahjongtiles_render_tile:nnn {#1} {#2} {#3} } { \__mahjongtiles_make_overlay_tile:nnnn {#1} {#2} {#3} {#4} } } \cs_new_protected:Npn \__mahjongtiles_make_overlay_tile:nnnn #1#2#3#4 { \hbox_set:Nn \l__mahjongtiles_tile_box { \__mahjongtiles_render_tile:nnn {#1} {#2} {#3} } \tikz [ baseline = (base.base), inner~sep = 0pt, outer~sep = 0pt ] { \node [ inner~sep = 0pt, outer~sep = 0pt ] (base) { \box_use:N \l__mahjongtiles_tile_box }; \exp_args:NV \__mahjongtiles_make_overlay_node:nn \l__mahjongtiles_overlay_style_tl {#4} \path [ use~as~bounding~box ] (base.south~west) rectangle (base.north~east |- mahjongtilesoverlay.north); } } \cs_new_protected:Npn \__mahjongtiles_make_overlay_node:nn #1#2 { \node [ #1, overlay ] (mahjongtilesoverlay) at (base.north) {#2}; } \cs_new_protected:Npn \__mahjongtiles_render_draw_tile:nn #1#2 { \hbox_to_wd:nn { 0pt } { \tikz [ baseline = (base.base), inner~sep = 0pt, outer~sep = 0pt ] { % This phantom only provides the hand-height reference. \node [ inner~sep = 0pt, outer~sep = 0pt ] (base) { \phantom { \__mahjongtiles_render_tile:nnn {#1} {#2} {0} } }; \node [ inner~sep = 0pt, outer~sep = 0pt, anchor = south~west ] at (base.north~west) { \__mahjongtiles_render_tile:nnn {#1} {#2} {1} }; } \hss } } \cs_new_protected:Npn \__mahjongtiles_render_tile:nnn #1#2#3 { \str_if_eq:nnTF {#1} {0} { \int_compare:nNnTF { \g__mahjongtiles_aka_int } = {0} { \tl_if_in:nnTF { mps } {#2} { \__mahjongtiles_render_tile_raw:nnn {5} {#2} {#3} } { \__mahjongtiles_render_tile_raw:nnn {#1} {#2} {#3} } } { \__mahjongtiles_render_tile_raw:nnn {#1} {#2} {#3} } } { \__mahjongtiles_render_tile_raw:nnn {#1} {#2} {#3} } } \cs_new_protected:Npn \__mahjongtiles_render_tile_raw:nnn #1#2#3 { \str_case:nnF {#1} { { x } { \__mahjongtiles_make_back_tile:n {#3} } { ? } { \__mahjongtiles_make_symbol_tile:nn { \__mahjongtiles_file:n { Blank } } {#3} } } { \__mahjongtiles_make_symbol_tile:nn { \__mahjongtiles_file:n { #1#2 } } {#3} } } \cs_new_protected:Npn \__mahjongtiles_make_symbol_tile:nn #1#2 { \file_if_exist:nTF {#1} { \int_case:nnF {#2} { {0} { \stackinset { c } { 0pt } { c } { 0pt } { \includegraphics [ height = \l__mahjongtiles_symbol_height_dim ] {#1} } { \includegraphics [ height = \l__mahjongtiles_tile_height_dim ] { \__mahjongtiles_file:n { Front } } } } {1} { \stackinset { c } { 0pt } { c } { 0pt } { \includegraphics [ angle = 90, width = \l__mahjongtiles_symbol_height_dim ] {#1} } { \includegraphics [ angle = 90, width = \l__mahjongtiles_tile_height_dim ] { \__mahjongtiles_file:n { Front } } } } {2} { \stackon [0pt] { \__mahjongtiles_make_symbol_tile:nn {#1} {1} } { \__mahjongtiles_make_symbol_tile:nn {#1} {1} } } } { \msg_fatal:nnn { mahjong-tiles } { unknown-orientation } {#2} } } { \msg_error:nnn { mahjong-tiles } { unknown-tile } {#1} } } \cs_new_protected:Npn \__mahjongtiles_make_back_tile:n #1 { \int_case:nnF {#1} { {0} { \__mahjongtiles_make_back_tile_upright: } {1} { \rotatebox { 90 } { \__mahjongtiles_make_back_tile_upright: } } {2} { \stackon [0pt] { \__mahjongtiles_make_back_tile:n {1} } { \__mahjongtiles_make_back_tile:n {1} } } } { \msg_fatal:nnn { mahjong-tiles } { unknown-orientation } {#1} } } \cs_new_protected:Npn \__mahjongtiles_make_back_tile_upright: { \bool_if:NTF \g__mahjongtiles_back_recolor_bool { \__mahjongtiles_make_back_tile_upright_recolored: } { \includegraphics [ height = \l__mahjongtiles_tile_height_dim ] { \__mahjongtiles_file:n { Back } } } } % The back tile PDF is a single pure-red vector shape on a transparent % background. Recoloring by blending a rectangle over the whole PDF box leaks % into the transparent rounded corners, so we redraw the exact vector outline as % a clipping mask and fill only the visible shape. \cs_new_protected:Npn \__mahjongtiles_back_clip_path: { \path [ clip ] (30,300.059) -- (195.043,300.059) .. controls (211.664,300.059) and (225.043,286.68) .. (225.043,270.059) -- (225.043,29.477) .. controls (225.043,12.8551) and (211.664,-0.523047) .. (195.043,-0.523047) -- (30,-0.523047) .. controls (13.3789,-0.523047) and (0,12.8551) .. (0,29.477) -- (0,270.059) .. controls (0,286.68) and (13.3789,300.059) .. (30,300.059) -- cycle; } % Faster than computing a blend-mode recolouring. \cs_new_protected:Npn \__mahjongtiles_make_back_tile_upright_recolored: { \tikz [ baseline = 0pt, x = { \dim_eval:n { \l__mahjongtiles_tile_width_dim / 225 } }, y = { \dim_eval:n { \l__mahjongtiles_tile_height_dim / 300 } }, inner~sep = 0pt, outer~sep = 0pt ] { \path [ use~as~bounding~box ] (0,0) rectangle (225,300); \begin{scope} \__mahjongtiles_back_clip_path: \fill [ \tl_use:N \g__mahjongtiles_back_color_tl, opacity = 1 ] (0,-1) rectangle (226,301); \end{scope} } } % % \end{macrocode} \endinput