diff options
Diffstat (limited to 'Master/texmf-dist/tex')
18 files changed, 3379 insertions, 703 deletions
diff --git a/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.chit.code.tex b/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.chit.code.tex index 3e5b240b7a8..21a42dddecd 100644 --- a/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.chit.code.tex +++ b/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.chit.code.tex @@ -78,10 +78,12 @@ bevel/SW/.style = {/chit/bev=3}, bevel/SE/.style = {/chit/bev=4}, bevel/.default = north west, - clip/.is if=chit@clip% + clip/.is if=chit@clip,% + nato shape/.store in=\chit@n@to@shape, } +\def\chit@sym@sc@le{.4} \tikzset{ - chit/symbol/.style={scale=.4,transform shape}, + chit/symbol/.style={scale=\chit@sym@sc@le,transform shape}, chit/parts/.style={shape=rectangle,transform shape}, chit/factors/.style={chit/parts,anchor=south}, chit/left/.style={chit/parts,anchor=base,rotate=90},%Anchor was south @@ -118,7 +120,7 @@ chit/.code={% \chit@dbg{2}{chit arguments are `#1'}% \pgfkeys{/tikz/transform shape,/tikz/shape=chit}% - \pgfkeys{/chit/.cd,#1}}} + \pgfkeys{/chit/.cd,nato shape=natoapp6c,#1}}} \newcounter{chit@id}\setcounter{chit@id}{0} \def\chit@n@to#1#2{% %% Without a following start square bracket '[' by-pass to final @@ -129,12 +131,16 @@ %\message{^^JNo start square bracket}% \@chit@n@to@{#1}{#2}}%]] } +\def\chit@n@to@shape{natoapp6c} \def\@chit@n@to@#1#2#3\@end@chit@n@to{% \chit@dbg{1}{Chit NATO App6(c) w/o offset: - ^^J Options: #3 - ^^J ID: #1 - ^^J Position: #2} - \node[chit/symbol,natoapp6c={#3,id=#1}] (#1) at (#2) {}; + ^^J Options: `#3' + ^^J ID: `#1' + ^^J Position: `#2' + ^^J Style: `\chit@n@to@shape' + } + \chit@dbg{2}{NATO shape: `\chit@n@to@shape'} + \node[chit/symbol,\chit@n@to@shape={#3,id=#1}] (#1) at (#2) {}; \chit@dbg{4}{Chit NATO App6(c) ended}% } \def\@chit@n@to#1#2[#3]{% @@ -147,30 +153,73 @@ ^^J ID: #1 ^^J Position: #2 ^^J Offset: #4} - \node[chit/symbol,natoapp6c={#3,id=#1}] (#1) at ($(#2)+(#4)$) {};} -\def\chit@tr@ns@nchor#1{% - \pgf@x=0.4\pgf@x% - \pgf@y=0.4\pgf@y\advance\pgf@y#1} -\def\chit@nchor#1#2#3{% - \wg@sub@nchor{#1}{#2} - \chit@tr@ns@nchor{#3}} + \chit@dbg{10}{==== NATO shape: `\chit@n@to@shape' ====} + \node[chit/symbol,\chit@n@to@shape={#3,id=#1}] (#1) at ($(#2)+(#4)$) {};} +\def\chit@tr@ns@nchor{% + \chit@dbg{10}{Translating anchor `\the\pgf@x',`\the\pgf@y'} + \wg@tmpa=\pgf@x% + \wg@tmpb=\pgf@y% + \symbol% + \chit@dbg{10}{Symbol origin `\the\pgf@x',`\the\pgf@y'} + \wg@tmpc=\pgf@x% + \wg@tmpd=\pgf@y% + \pgf@x=\chit@sym@sc@le\wg@tmpa% + \pgf@y=\chit@sym@sc@le\wg@tmpb% + \chit@dbg{10}{Scaled anchor `\the\pgf@x',`\the\pgf@y'} + \advance\pgf@x\wg@tmpc% + \advance\pgf@y\wg@tmpd% + \chit@dbg{10}{Offset anchor `\the\pgf@x',`\the\pgf@y'} +} +\def\chit@nchor#1#2{% + \chit@dbg{10}{Get chit sub anchor of `#1' `#2'}% + \wg@sub@nchor{#1}{#2}% + \chit@tr@ns@nchor% + \chit@dbg{10}{Got chit sub anchor of `\the\pgf@x `\the\pgf@y}% +} +\def\chit@sym@nchor#1{% + \chit@dbg{10}{Get chit symbol `#1'}% + \edef\tmpid{\id symbol}% + \chit@nchor{\tmpid}{#1}} \def\chit@report{} \tikzset{ zone turn/.style={}, zone mult/.style={} } +\def\chit@bkg@p@th{% + \northeast% + \wg@tmpa=\pgf@x\wg@tmpb=\pgf@y% + \pgfpathmoveto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% + \wg@tmpa=-\wg@tmpa \pgfpathlineto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% + \wg@tmpb=-\wg@tmpb \pgfpathlineto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% + \wg@tmpa=-\wg@tmpa \pgfpathlineto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% + \pgfclosepath% +} \def\chit@bevel@path#1{ \scope[#1] - \wg@tmpc=\wg@tmpa\multiply\wg@tmpc by \chit@bevel@frac - \wg@tmpd=\wg@tmpb\multiply\wg@tmpd by \chit@bevel@frac - \divide\wg@tmpc100 - \divide\wg@tmpd100 + \wg@tmpc=\wg@tmpa% + \wg@tmpd=\wg@tmpb% + %% Absolute values + \ifdim\wg@tmpa<0pt\multiply\wg@tmpc by -1\fi% + \ifdim\wg@tmpb<0pt\multiply\wg@tmpd by -1\fi% + % Why the hell do I need this? + \chit@dbg{10}{`\the\wg@tmpa' `\the\wg@tmpb'}% + %% Smallest dimension + \ifdim\wg@tmpc>\wg@tmpd \wg@tmpc=\wg@tmpd\fi% + \ifdim\wg@tmpc<\wg@tmpd \wg@tmpd=\wg@tmpc\fi% + %% Restore sign + \ifdim\wg@tmpa<0pt\multiply\wg@tmpc by -1\fi% + \ifdim\wg@tmpb<0pt\multiply\wg@tmpd by -1\fi% + %% Take the fraction + \multiply\wg@tmpc by \chit@bevel@frac% + \multiply\wg@tmpd by \chit@bevel@frac% + \divide\wg@tmpc100% + \divide\wg@tmpd100% \pgfpathmoveto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% % Move down along edge - \wg@tmpb=-\wg@tmpb + \wg@tmpb=-\wg@tmpb% \pgfpathlineto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% % Move left along edge - \wg@tmpa=-\wg@tmpa + \wg@tmpa=-\wg@tmpa% \pgfpathlineto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% % Move in and up \advance\wg@tmpa\wg@tmpc% @@ -185,14 +234,13 @@ \advance\wg@tmpb-\wg@tmpd% \pgfpathlineto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% \pgfclosepath% - \pgfusepath{fill} - \endscope + \pgfusepath{fill}% + \endscope% } - \pgfdeclareshape{chit}{ \savedanchor\center{\pgf@x=0cm\pgf@y=0cm} - \savedanchor\northeast{\pgf@x=0.6cm\pgf@y=0.6cm} - \savedanchor\symbol{\pgf@x=0cm\pgf@y=0.2cm} + \savedanchor\northeast{\pgf@x=0.6cm\pgf@y=\pgf@x} + \savedanchor\symbol{\pgf@x=0cm\pgf@y=0.1cm} \savedanchor\factors{\pgf@x=0cm\pgf@y=-0.5cm} \saveddimen\margin{\pgf@x=0.04cm} \savedmacro\id{% @@ -212,6 +260,7 @@ \edef\chitframeopt{\chit@frame}} \chit@dbg{3}{Chit Frame options: \meaning\chitframeopt}% } + \savedmacro\thisname{\def\thisname{chit}} \anchor{center}{\center} \anchor{north east}{\northeast} \anchor{north west}{\northeast\pgf@x=-\pgf@x} @@ -221,24 +270,24 @@ \anchor{south} {\northeast\pgf@x=0cm\pgf@y=-\pgf@y} \anchor{east} {\northeast\pgf@y=0cm} \anchor{west} {\northeast\pgf@x=-\pgf@x\pgf@y=0cm} - \anchor{symbol north east}{\chit@nchor{M\id symbol}{north east}{0.2cm}} - \anchor{symbol north west}{\chit@nchor{M\id symbol}{north west}{0.2cm}} - \anchor{symbol south east}{\chit@nchor{M\id symbol}{south east}{0.2cm}} - \anchor{symbol south west}{\chit@nchor{M\id symbol}{south west}{0.2cm}} - \anchor{symbol north} {\chit@nchor{M\id symbol}{north}{0.2cm}} - \anchor{symbol west} {\chit@nchor{M\id symbol}{west}{0.2cm}} - \anchor{symbol south} {\chit@nchor{M\id symbol}{south}{0.2cm}} - \anchor{symbol east} {\chit@nchor{M\id symbol}{east}{0.2cm}} - \anchor{symbol upper} {\chit@nchor{M\id symbol}{upper}{0.2cm}} - \anchor{symbol lower} {\chit@nchor{M\id symbol}{lower}{0.2cm}} - \anchor{symbol left} {\chit@nchor{M\id symbol}{left}{0.2cm}} - \anchor{symbol right} {\chit@nchor{M\id symbol}{right}{0.2cm}} - \anchor{symbol echelon} {\chit@nchor{M\id symbol}{north}{0.2cm}} - \anchor{symbol below} {\chit@nchor{M\id symbol}{south}{0.1cm}} + \anchor{symbol north east}{\chit@sym@nchor{north east}} + \anchor{symbol north west}{\chit@sym@nchor{north west}} + \anchor{symbol south east}{\chit@sym@nchor{south east}} + \anchor{symbol south west}{\chit@sym@nchor{south west}} + \anchor{symbol north} {\chit@sym@nchor{north}} + \anchor{symbol west} {\chit@sym@nchor{west}} + \anchor{symbol south} {\chit@sym@nchor{south}} + \anchor{symbol east} {\chit@sym@nchor{east}} + \anchor{symbol upper} {\chit@sym@nchor{upper}} + \anchor{symbol lower} {\chit@sym@nchor{lower}} + \anchor{symbol left} {\chit@sym@nchor{left}} + \anchor{symbol right} {\chit@sym@nchor{right}} + \anchor{symbol echelon} {\chit@sym@nchor{echelon}} + \anchor{symbol below} {\chit@sym@nchor{below}} \anchor{symbol} {\symbol} \anchor{factors} {\factors} - \anchor{left} {\chit@nchor{M\id symbol}{west}{.2cm}\advance\pgf@x-\margin} - \anchor{right}{\chit@nchor{M\id symbol}{east}{.2cm}\advance\pgf@x+\margin} + \anchor{left} {\chit@sym@nchor{west}\advance\pgf@x-\margin} + \anchor{right}{\chit@sym@nchor{east}\advance\pgf@x+\margin} \anchor{upper right} {% \northeast \advance\pgf@x-\margin \advance\pgf@y-\margin% } @@ -256,13 +305,14 @@ %% This is the outline of the chit only. The rest of the chit is %% made on the foreground "path". \chit@dbg{1}{Chit drawing background path} - \northeast% - \wg@tmpa=\pgf@x\wg@tmpb=\pgf@y% - \pgfpathmoveto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% - \wg@tmpa=-\wg@tmpa \pgfpathlineto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% - \wg@tmpb=-\wg@tmpb \pgfpathlineto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% - \wg@tmpa=-\wg@tmpa \pgfpathlineto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% - \pgfclosepath + \chit@bkg@p@th% + % \northeast% + % \wg@tmpa=\pgf@x\wg@tmpb=\pgf@y% + % \pgfpathmoveto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% + % \wg@tmpa=-\wg@tmpa \pgfpathlineto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% + % \wg@tmpb=-\wg@tmpb \pgfpathlineto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% + % \wg@tmpa=-\wg@tmpa \pgfpathlineto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% + % \pgfclosepath } \behindforegroundpath{% \chit@dbg{1}{Chit drawing foreground path} @@ -288,13 +338,14 @@ % \ifchit@clip% \chit@dbg{1}{Chit clip path} - \northeast% - \wg@tmpa=\pgf@x\wg@tmpb=\pgf@y% - \pgfpathmoveto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% - \wg@tmpa=-\wg@tmpa \pgfpathlineto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% - \wg@tmpb=-\wg@tmpb \pgfpathlineto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% - \wg@tmpa=-\wg@tmpa \pgfpathlineto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% - \pgfclosepath% + \chit@bkg@p@th% + % \northeast% + % \wg@tmpa=\pgf@x\wg@tmpb=\pgf@y% + % \pgfpathmoveto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% + % \wg@tmpa=-\wg@tmpa \pgfpathlineto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% + % \wg@tmpb=-\wg@tmpb \pgfpathlineto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% + % \wg@tmpa=-\wg@tmpa \pgfpathlineto{\pgfqpoint{\wg@tmpa}{\wg@tmpb}}% + % \pgfclosepath% \pgfusepath{clip}% \fi% \@ifundefined{chit@symbol}{% @@ -309,26 +360,22 @@ \symbol% \edef\args{{\symid}{\the\pgf@x,\the\pgf@y}\chit@symbol}% \chit@dbg{6}{Arguments to chit NATO symbol: \meaning\args}% - \chit@dbg{1}{Chit draw nato image} + \chit@dbg{1}{Chit draw nato image `\symid'} \expandafter\chit@n@to\args\@end@chit@n@to% \chit@dbg{6}{After making NATO symbol in chit}% % Put in left of symbol \@ifundefined{chit@left}{}{% - \chit@dbg{1}{Chit draw left: `\meaning\chit@left'} + \chit@dbg{2}{Chit draw left: `\meaning\chit@left'} + \wg@nchor{\thisname}{left} \begin{scope}[] - \pgfpointanchor{\symid}{west}% - \wg@tmpa=\pgf@x\advance\wg@tmpa-\margin% - \wg@tmpb=\pgf@y% - \wg@pic@all{\chit@left}{}{\the\wg@tmpa,\the\wg@tmpb}{chit/left}% + \wg@pic@all{\chit@left}{}{\pgf@x,\pgf@y}{chit/left}% \end{scope}}% % Put in right of symbol \@ifundefined{chit@right}{}{% - \chit@dbg{1}{Chit draw left: `\meaning\chit@right'} + \chit@dbg{2}{Chit draw right: `\meaning\chit@right'} + \wg@nchor{\thisname}{right} \begin{scope}[] - \pgfpointanchor{\symid}{east}% - \wg@tmpa=\pgf@x\advance\wg@tmpa+\margin% - \wg@tmpb=\pgf@y% - \wg@pic@all{\chit@right}{}{\the\wg@tmpa,\the\wg@tmpb}{chit/right}% + \wg@pic@all{\chit@right}{}{\pgf@x,\pgf@y}{chit/right}% \end{scope}}% % Get coordinates \northeast% @@ -338,31 +385,31 @@ \advance\wg@tmpb-\margin% % Put in upper left corner \@ifundefined{chit@upper@left}{}{% - \chit@dbg{1}{Chit draw upper left: `\meaning\chit@upper@left'} + \chit@dbg{1}{Chit draw upper left: `\meaning\chit@upper@left'}% + \wg@nchor{\thisname}{upper left} \begin{scope}[] - \wg@pic@all{\chit@upper@left}{}{-\the\wg@tmpa,\the\wg@tmpb}{% - chit/upper left}% + \wg@pic@all{\chit@upper@left}{}{\pgf@x,\pgf@y}{chit/upper left}% \end{scope}} % Put in upper right corner \@ifundefined{chit@upper@right}{}{% \chit@dbg{1}{Chit draw upper right: `\meaning\chit@upper@right'} + \wg@nchor{\thisname}{upper right}% \begin{scope}[] - \wg@pic@all{\chit@upper@right}{}{\the\wg@tmpa,\the\wg@tmpb}{% - chit/upper right}% + \wg@pic@all{\chit@upper@right}{}{\pgf@x,\pgf@y}{chit/upper right}% \end{scope}} % Put in lower left corner \@ifundefined{chit@lower@left}{}{% \chit@dbg{1}{Chit draw lower left: `\meaning\chit@lower@left'} + \wg@nchor{\thisname}{lower left}% \begin{scope}[] - \wg@pic@all{\chit@lower@left}{}{-\the\wg@tmpa,-\the\wg@tmpb}{% - chit/lower left}% + \wg@pic@all{\chit@lower@left}{}{\pgf@x,\pgf@y}{chit/lower left}% \end{scope}} % Put in lower right corner \@ifundefined{chit@lower@right}{}{% \chit@dbg{1}{Chit draw lower right: `\meaning\chit@lower@right'} + \wg@nchor{\thisname}{lower right}% \begin{scope}[] - \wg@pic@all{\chit@lower@right}{}{\the\wg@tmpa,-\the\wg@tmpb}{% - chit/lower right}% + \wg@pic@all{\chit@lower@right}{}{\pgf@x,\pgf@y}{chit/lower right}% \end{scope}} % Put in factors \@ifundefined{chit@factors}{}{% @@ -451,10 +498,83 @@ \fi% \@ifnextchar;{\@gobble}{}% } +\tikzset{ + /chit/kriegspiel/.code={ + \pgfkeys{% + /tikz/shape=kriegspiel,% + /chit/nato shape=kriegspiel symbol} + } +} +\pgfdeclareshape{kriegspiel}{ + \inheritsavedanchors[from=chit] + \savedanchor\northeast{\pgf@x=0.8cm\pgf@y=0.4cm} + \savedanchor\symbol{\pgf@x=0cm\pgf@y=0.15cm} + \savedanchor\factors{\pgf@x=.2cm\pgf@y=-.4cm} + \savedmacro\thisname{\def\thisname{kriegspiel}} + \saveddimen\margin{\pgf@x=0.03cm} + \inheritanchor[from=chit]{center} + \inheritanchor[from=chit]{north east} + \inheritanchor[from=chit]{north west} + \inheritanchor[from=chit]{south west} + \inheritanchor[from=chit]{south east} + \inheritanchor[from=chit]{north} + \inheritanchor[from=chit]{south} + \inheritanchor[from=chit]{east} + \inheritanchor[from=chit]{west} + \inheritanchor[from=chit]{symbol north east} + \inheritanchor[from=chit]{symbol north west} + \inheritanchor[from=chit]{symbol south east} + \inheritanchor[from=chit]{symbol south west} + \inheritanchor[from=chit]{symbol north} + \inheritanchor[from=chit]{symbol west} + \inheritanchor[from=chit]{symbol south} + \inheritanchor[from=chit]{symbol east} + \inheritanchor[from=chit]{symbol upper} + \inheritanchor[from=chit]{symbol lower} + \inheritanchor[from=chit]{symbol left} + \inheritanchor[from=chit]{symbol right} + \inheritanchor[from=chit]{symbol echelon} + \inheritanchor[from=chit]{symbol below} + \inheritanchor[from=chit]{symbol} + \inheritanchor[from=chit]{factors} + \inheritanchor[from=chit]{left} + \inheritanchor[from=chit]{right} + \inheritanchor[from=chit]{upper right} + \inheritanchor[from=chit]{upper left} + \inheritanchor[from=chit]{lower right} + \inheritanchor[from=chit]{lower left} + % \anchor{upper right} {% + % \northeast% + % \advance\pgf@x-\margin% + % \advance\pgf@y-\margin% + % \pgf@y=-\pgf@y% + % \advance\pgf@y5pt% + % } + % \anchor{upper left}{ + % \northeast% + % \advance\pgf@x-\margin% + % \advance\pgf@y-\margin% + % \pgf@x=-\pgf@x% + % \pgf@y=-\pgf@y% + % \advance\pgf@y5pt% + % } + %% + \inheritbackgroundpath[from=chit] + \inheritbehindforegroundpath[from=chit] +} \DeclareRobustCommand\chit@sep[2][/]{% \foreach[count=\is] \s in {#2}{% \ifnum\is>1\relax#1\fi% \s}} +\def\chit@oset#1#2{% + %$\stackrel{{}_{\text{\scriptsize #1}}}{\text{#2}}$% + %$\stackrel{\lower2ex\hbox{\text{\scriptsize #1}}}{\text{#2}}$% + %${}\overset{\text{\scriptsize #1}}{\text{#2}}{}$% + \tikz[]{% + \node[inner sep=0pt](chit@dd){#2};% + \node[above=-.1ex of chit@dd,inner sep=0pt]{{\scriptsize #1}};% + }% +} \tikzset{% chit/1 factor/.pic={ \chit@dbg{4}{ Chit 1 factor: #1}% @@ -467,7 +587,7 @@ code={ \chit@dbg{4}{ Chit 2 factors w/artillery: `#1' `#2' `#3'}% \node[chit/factor,chit/2 factors]{% - {#1}$\overset{\text{\scriptsize #3}}{\text{--}}${#2}};}}, + {#1}\chit@oset{#3}{--}{#2}};}}, pics/chit/3 factors/.style args={#1,#2,#3}{% code={ \chit@dbg{4}{ Chit 3 factors: `#1' `#2' `#3'}% @@ -626,28 +746,28 @@ \@ifstar{\wg@star@oob% }{\wg@nostar@oob% }% -} -\def\wg@oob#1#2#3#4{ - \def\r{0} +}% +\def\wg@oob#1#2#3#4{% + \def\r{0}% \pgfmathparse{#3*(#2-1)}% - \edef\a{\pgfmathresult} - \chit@dbg{2}{OOB: `#1'} - \foreach[count=\ti from 0] \t/\y in #1{ - \xdef\o{\r} - \def\c{0} - \ifx\t\y\def\y{0}\fi - \chit@dbg{2}{Turn \ti\space(\r,\t,y=\y):'} + \edef\a{\pgfmathresult}% + \chit@dbg{2}{OOB: `#1'}% + \foreach[count=\ti from 0] \t/\y in #1{% + \xdef\o{\r}% + \def\c{0}% + \ifx\t\y\def\y{0}\fi% + \chit@dbg{2}{Turn \ti\space(\r,\t,y=\y):'}% \ifwg@oob@inv% \pic[transform shape] at ( .5*#3,\r) {chit/oob turn=\ti};% was dx=0.5 - \else + \else% \pic[transform shape] at (-.5*#3,\r) {chit/oob turn=\ti};% was dx=-0.5 \fi% \ifx\t\empty\else% - \foreach \u/\m in \t{ + \foreach \u/\m in \t{% %% \chit@dbg{2}{ `\u'=`\m'} - \ifx\u\empty\else - \ifx\m\@empty\def\m{1}\fi - \ifx\u\m\def\m{1}\fi + \ifx\u\empty\else% + \ifx\m\@empty\def\m{1}\fi% + \ifx\u\m\def\m{1}\fi% \foreach \n in {1,...,\m}{% \chit@dbg{2}{OOB Chit is `\u' `\chit@oob@spacer'}% \ifx\u\chit@oob@spacer% @@ -656,33 +776,33 @@ \xdef\c{\pgfmathresult}% \else% \ifx\u\chit@oob@vspacer% - \chit@dbg{3}{Chit `\u' is vspacer `\chit@oob@vspacer'} - \pgfmathparse{ifthenelse(abs(\c)<0.0001,0,#3)} - \xdef\ll{\pgfmathresult} - \chit@dbg{2}{\string\ll=`\ll'} - \chit@oob@rowupdate(\c,\r){\ll}{#4} + \chit@dbg{3}{Chit `\u' is vspacer `\chit@oob@vspacer'}% + \pgfmathparse{ifthenelse(abs(\c)<0.0001,0,#3)}% + \xdef\ll{\pgfmathresult}% + \chit@dbg{2}{\string\ll=`\ll'}% + \chit@oob@rowupdate(\c,\r){\ll}{#4}% \else \ifnum\chitdbglvl>2% \node[minimum width=#3cm,minimum height=#3cm, - draw,transform shape] at (\c,\r) {}; + draw,transform shape] at (\c,\r) {};% \fi \ifx\u\chit@blank\else% \chit[\u=\ti,zone oob point={\u}{\c}{\r}](\c,\r);% \fi% - \chit@oob@cellupdate(\c,\r){#2}{#3}{\y} - \fi - \fi - } - \fi - } - \fi + \chit@oob@cellupdate(\c,\r){#2}{#3}{\y}% + \fi% + \fi% + }% + \fi% + }% + \fi% \chit@dbg{1}{ End of chits in turn - \ti\space(c=`\c',r=`\r',o='\o',y='\y')} + \ti\space(c=`\c',r=`\r',o='\o',y='\y')}% % IF no units where given, then we force \c to be non-zero so that % \chit@oob@turnupdate increments the row - \ifx\t\@empty - \def\c{#3} - \chit@dbg{2}{ Turn is empty, set c=`\c'} + \ifx\t\@empty% + \def\c{#3}% + \chit@dbg{2}{ Turn is empty, set c=`\c'}% \fi %\ifnum\y<0% No explicit number of rows given % \def\c{#3} @@ -696,22 +816,22 @@ % rows. \ifnum\y>0% \chit@dbg{2}{ Looping rows from 2 to \y, break when row > \y}% - \foreach \rr in {2,...,\y}{ + \foreach \rr in {2,...,\y}{% %\ifnum\rr>\y% A little funny, but \y can be negative! % \chit@dbg{2}{ \space Breaking loop \rr\space > \y}% % \breakforeach% %\else% \chit@oob@rowupdate(\c,\r){#3}{0}% Extra spacing? %\fi - } - \fi + }% + \fi% % This will zero \c. However, if on entry |\c|>0, then we also % increment the row - \chit@oob@turnupdate(\c,\r){#3}{#4} - \chit@dbg{2}{End of turn \ti\space(c=`\c',r=`\r',o='\o',y='\y')} + \chit@oob@turnupdate(\c,\r){#3}{#4}% + \chit@dbg{2}{End of turn \ti\space(c=`\c',r=`\r',o='\o',y='\y')}% } - \chit@dbg{3}{End of OOB (c=`\c',r=`\r',y=`\y')} - \@ifnextchar;{\@gobble}{}} + \chit@dbg{3}{End of OOB (c=`\c',r=`\r',y=`\y')}% + \@ifnextchar;{\@gobble}{}}% \def\wg@star@hoob{\wg@oob@invtrue\wg@hoob} \def\wg@nostar@hoob{\wg@oob@invfalse\wg@hoob} \def\hoob{% @@ -719,49 +839,49 @@ }{\wg@nostar@hoob% }% } -\def\wg@hoob#1#2#3#4{ - \def\r{0} - \def\c{0} +\def\wg@hoob#1#2#3#4{% + \def\r{0}% + \def\c{0}% \pgfmathparse{#3*(#2-1)}% - \edef\a{\pgfmathresult} - \chit@dbg{2}{OOB: `#1'} - \foreach[count=\ti from 0] \t/\y in #1{ - \xdef\o{\r} + \edef\a{\pgfmathresult}% + \chit@dbg{2}{OOB: `#1'}% + \foreach[count=\ti from 0] \t/\y in #1{% + \xdef\o{\r}% % \def\c{0} - \ifx\t\y\def\y{0}\fi + \ifx\t\y\def\y{0}\fi% \chit@dbg{2}{Turn \ti\space(\r,\t,y=\y):'} - \ifx\t\empty\else + \ifx\t\empty\else% % Count how many are left for this turn - \chit@dbg{2}{At start of turn \t\space\string\c=\c} + \chit@dbg{2}{At start of turn \t\space\string\c=\c}% \def\l{\c}% - \let\ig\empty - \foreach \u/\m in \t{ - \ifx\ig\empty - \ifx\u\empty\else - \ifx\u\m\def\m{1}\fi + \let\ig\empty% + \foreach \u/\m in \t{% + \ifx\ig\empty% + \ifx\u\empty\else% + \ifx\u\m\def\m{1}\fi% \ifx\u\chit@oob@spacer% - \pgfmathparse{\l+\m*#4}\xdef\l{\pgfmathresult} - \chit@dbg{2}{Got \m\space hspace (#4) -> \l} - \else + \pgfmathparse{\l+\m*#4}\xdef\l{\pgfmathresult}% + \chit@dbg{2}{Got \m\space hspace (#4) -> \l}% + \else% \ifx\u\chit@oob@vspace% - \xdef\ig{1} - \chit@dbg{2}{Got vspace -> \l (\ig)} - \else - \pgfmathparse{\l+\m*#3} - \xdef\l{\pgfmathresult} - \chit@dbg{2}{Got \m\space units -> \l} - \fi - \fi - \fi - \fi} + \xdef\ig{1}% + \chit@dbg{2}{Got vspace -> \l (\ig)}% + \else% + \pgfmathparse{\l+\m*#3}% + \xdef\l{\pgfmathresult}% + \chit@dbg{2}{Got \m\space units -> \l}% + \fi% + \fi% + \fi% + \fi}% % Check if there's enough room \chit@dbg{2}{To fill the rest of turn needs `\l' compared to - `\a' (#3*(#2-1))} + `\a' (#3*(#2-1))}% \pgfmathparse{ifthenelse(abs(\l)>=#3*(#2-1),0,1}% \xdef\l{\pgfmathresult}% - \chit@dbg{2}{Break or not `\l'} - \ifnum\l=0\chit@oob@turnupdate(\c,\r){#3}{#4}\fi - \fi + \chit@dbg{2}{Break or not `\l'}% + \ifnum\l=0\chit@oob@turnupdate(\c,\r){#3}{#4}\fi% + \fi% \ifwg@oob@inv% \pic[transform shape] at (\c+.5*#3,\r) {chit/oob turn=\ti};% was dx=0.5 \else @@ -769,43 +889,43 @@ \fi% %\chit@oob@cellupdate(\c,\r){#2}{#3}{\y} \ifx\t\empty\else% - \def\lv{0} - \foreach \u/\m in \t{ + \def\lv{0}% + \foreach \u/\m in \t{% %% \chit@dbg{2}{ `\u'=`\m'} - \ifx\u\empty\else - \ifx\m\@empty\def\m{1}\fi - \ifx\u\m\def\m{1}\fi + \ifx\u\empty\else% + \ifx\m\@empty\def\m{1}\fi% + \ifx\u\m\def\m{1}\fi% \foreach \n in {1,...,\m}{% \chit@dbg{2}{OOB Chit is `\u' `\chit@oob@spacer'}% \ifx\u\chit@oob@spacer% - \chit@dbg{3}{Chit `\u' is spacer `\chit@oob@spacer'} + \chit@dbg{3}{Chit `\u' is spacer `\chit@oob@spacer'}% \pgfmathparse{\c+#4}% \xdef\c{\pgfmathresult}% \else% \ifx\u\chit@oob@vspacer% - \chit@dbg{3}{Chit `\u' is vspacer `\chit@oob@vspacer'} - \pgfmathparse{ifthenelse(abs(\c)<0.0001,0,#3)} - \xdef\ll{\pgfmathresult} - \chit@dbg{2}{\string\ll=`\ll'} - \chit@oob@rowupdate(\c,\r){\ll}{#4} - \xdef\lv{1} + \chit@dbg{3}{Chit `\u' is vspacer `\chit@oob@vspacer'}% + \pgfmathparse{ifthenelse(abs(\c)<0.0001,0,#3)}% + \xdef\ll{\pgfmathresult}% + \chit@dbg{2}{\string\ll=`\ll'}% + \chit@oob@rowupdate(\c,\r){\ll}{#4}% + \xdef\lv{1}% \else \ifnum\chitdbglvl>2% \node[minimum width=#3cm,minimum height=#3cm, - draw,transform shape] at (\c,\r) {}; - \fi + draw,transform shape] at (\c,\r) {};% + \fi% \ifx\u\chit@blank\else% \chit[\u=\ti,zone oob point={\u}{\c}{\r}](\c,\r);% \fi% \chit@oob@cellupdate(\c,\r){#2}{#3}{\y} - \fi - \fi - } - \fi - } - \fi + \fi% + \fi% + }% + \fi% + }% + \fi% \chit@dbg{2}{ End of chits in turn - \ti\space(c=`\c',r=`\r',o='\o',y='\y')} + \ti\space(c=`\c',r=`\r',o='\o',y='\y')}% % --- Not relevant, I think % IF no units where given, then we force \c to be non-zero so that % \chit@oob@turnupdate increments the row @@ -826,7 +946,7 @@ % rows. \ifnum\y>0% \chit@dbg{2}{ Looping rows from 2 to \y, break when row > \y}% - \foreach \rr in {2,...,\y}{ + \foreach \rr in {2,...,\y}{% %\ifnum\rr>\y% A little funny, but \y can be negative! % \chit@dbg{2}{ \space Breaking loop \rr\space > \y}% % \breakforeach% @@ -834,7 +954,7 @@ \chit@oob@rowupdate(\c,\r){#3}{0}% Extra spacing? %\fi } - \fi + \fi% % --- Not relevant I think % This will zero \c. However, if on entry |\c|>0, then we also % increment the row @@ -856,12 +976,17 @@ \fi \fi % \xdef\y{0} - \chit@dbg{2}{End of turn \ti\space(c=`\c',r=`\r',o='\o',y='\y')} - } - \chit@dbg{3}{End of OOB (c=`\c',r=`\r',y=`\y')} - \@ifnextchar;{\@gobble}{}} + \chit@dbg{2}{End of turn \ti\space(c=`\c',r=`\r',o='\o',y='\y')}% + }% + \chit@dbg{3}{End of OOB (c=`\c',r=`\r',y=`\y')}% + \@ifnextchar;{\@gobble}{}}% \tikzset{ - chit/cell background/.style={fill=black}, + chit/cell background/.style={draw=black}, + %% Define to not dp anyting to disable wings + chit/cell wings/.style={ + dashed, + dash pattern=on 0pt off 3pt on 3pt off 0pt, + draw=black}, %chit/cell background flipped/.style={fill=black}, blank chit/.style={/chit/frame={draw=none,fill=none}}, chit/grid lines/.style={dashed}, @@ -882,12 +1007,35 @@ \pgfmathparse{ifthenelse(#1>=#4*(#3-1),0,#1+#4)}% \xdef#1{\pgfmathresult}% } +\def\chit@cell@frame#1#2{% + \coordinate(cell@frame@north east) at ($(#1.north east)+( #2/2, #2/2)$); + \coordinate(cell@frame@north west) at ($(#1.north west)+(-#2/2, #2/2)$); + \coordinate(cell@frame@south east) at ($(#1.south east)+( #2/2,-#2/2)$); + \coordinate(cell@frame@south west) at ($(#1.south west)+(-#2/2,-#2/2)$); + \draw[chit/cell background,fill=none,scale line widths,line width=#2cm] + (cell@frame@south west) rectangle (cell@frame@north east); + \draw[scale line widths,line width=#2cm,chit/cell wings=10pt,fill=none,scale line widths,line width=#2cm] + (cell@frame@north east)--++( 0,10*#2) + (cell@frame@north east)--++( 10*#2,0) + (cell@frame@north west)--++( 0,10*#2) + (cell@frame@north west)--++(-10*#2,0) + (cell@frame@south east)--++( 0,-10*#2) + (cell@frame@south east)--++( 10*#2,0) + (cell@frame@south west)--++( 0,-10*#2) + (cell@frame@south west)--++(-10*#2,0) + ; +} \def\chits{% \@ifstar{\chits@resetfalse\@chits}{\chits@resettrue\@chits}} +\newcount\wg@col +\newcount\wg@row \def\@chits#1#2#3{ \ifchits@reset - \def\r{0}% - \def\c{0}% + %\def\r{0}% + %\def\c{0}% + \coordinate(wg@next) at (0,0); + \wg@col=1 + \wg@row=1 \fi \chit@dbg{1}{Chits to make: #1}% \foreach[count=\ti from 0] \t/\x in #1{% @@ -903,15 +1051,48 @@ \ifx\u\chit@blank% \chit@dbg{3}{Ignoring blank chit:\u}% \else% - \chit@cellbg(\c,\r){#3}% - \chit[\u=\ti](\c,\r)% - \chit@sng@cellupdate(\c,\r){#2}{#3}% + \ifx\u\chit@oob@vspacer% + \chit@dbg{3}{Ignoring vertical spacer:\u}% + \else + \ifx\u\chit@oob@spacer% + \chit@dbg{3}{Ignoring horizontal spacer:\u}% + \else + % \chit@cellbg(\c,\r){#3}% + %\chit[\u=\ti](\c,\r)% + \chit[\u=\ti](wg@next)(wg@chit)% + \chit@cell@frame{wg@chit}{#3} + \global\advance\wg@col1 + \ifnum\wg@col>#2% + \path + let + \p1=(wg@chit.north), + \p2=(wg@chit.south), + \p3=(wg@chit), + \n1={\y3-\y1+\y2-#3cm} in + coordinate(wg@next) at (0,\n1); + \global\wg@col=1 + \global\advance\wg@row1 + \chit@dbg{10}{Row row, next column `\the\wg@col'} + \else + \path + let + \p1=(wg@chit.east), + \p2=(wg@chit.west), + \p3=(wg@chit), + \n1={\x3+\x1-\x2+#3cm} in + coordinate(wg@next) at (\n1,\y3); + \chit@dbg{10}{Next column `\the\wg@col'} + \fi + %\chit@sng@cellupdate(\c,\r){#2}{#3}% + \fi% + \fi% \fi% }% \fi% }% \fi% }% + \chit@dbg{10}{Table is `\the\wg@col'x`\the\wg@row'} \@ifnextchar;{\@gobble}{}} \def\chitgrid#1#2#3{% \pgfmathparse{#3/2}\edef\rmin{\pgfmathresult}% @@ -936,14 +1117,22 @@ } \def\doublechits{% \@ifstar{\chits@resetfalse\@doublechits}{\chits@resettrue\@doublechits}} +\newdimen\chit@w +\newdimen\chit@h \def\@doublechits#1#2#3{% \chit@dbg{1}{Setting double-sided chits: #1} - \ifchits@reset - \pgfmathparse{-(#2-.5)*#3} - \xdef\c{\pgfmathresult} - \def\r{0} - \fi - + \ifchits@reset% + %\pgfmathparse{-(#2-.5)*#3} + %\xdef\c{\pgfmathresult} + %\def\r{0} + \coordinate(wg@next) at (0,0);% + \coordinate(wg@fnxt) at (0,0);% + \wg@col=1% + \wg@row=1% + \chit@w=0pt% + \chit@h=0pt% + \fi% + % \foreach[count=\ti from 0] \t/\x in #1{ \ifx\t\empty\else% \foreach \u/\m in \t{ @@ -955,21 +1144,105 @@ \ifx\u\chit@blank \chit@dbg{3}{Ignoring blank chit:\u} \else - \chit@cellbg(\c,\r){#3} - \chit[\u=\ti](\c,\r) - \chit@dbl@flip(\c,\r){#3} - \chit@celldblbg(\mc,\r){#3} - \chit[\u\space flipped=\ti,zone turn=\t,zone mult=\n](\mc,\r) - \chit@dbl@cellupdate(\c,\r){#2}{#3} - \fi - } - \fi - } - \fi - } - \draw[dashed](0,-3*#3/4)--(0,\r-#3/4);% - \draw[dashed,<-] (#3/5,-2*#3/3)--(#3/2,-2*#3/3) node[transform shape,anchor=west]{Back};% - \draw[dashed,<-] (-#3/5,-2*#3/3)--(-#3/2,-2*#3/3) node[transform shape,anchor=east]{Front};% + \ifx\u\chit@oob@vspacer% + \chit@dbg{3}{Ignoring vertical spacer:\u}% + \else + \ifx\u\chit@oob@spacer% + \chit@dbg{3}{Ignoring horizontal spacer:\u}% + \else + \chit@dbg{10}{Drawing `\u' at wg@next} + \chit[\u=\ti](wg@next)(wg@chit)% + \chit@cell@frame{wg@chit}{#3} + \ifdim\chit@w=0pt + \pgfextractx\chit@w{\pgfpointanchor{wg@chit}{east}}% + \pgfextractx\wg@tmpa{\pgfpointanchor{wg@chit}{west}}% + \advance\chit@w-\wg@tmpa% + \global\advance\chit@w#3cm% + \pgfextracty\chit@h{\pgfpointanchor{wg@chit}{north}}% + \pgfextracty\wg@tmpa{\pgfpointanchor{wg@chit}{south}}% + \advance\chit@h-\wg@tmpa% + \global\advance\chit@h#3cm% + \fi + \ifnum\wg@col=1 + \pgfextractx\wg@tmpa{\pgfpointanchor{wg@chit}{center}}% + \advance\wg@tmpa+\chit@w% + \coordinate(wg@fnxt) at ($(wg@chit)+(\the\chit@w,0pt)$); + %\coordinate(wg@fnxt + %\chit@dbg{10}{Calculating wg@fnxt since `\the\wg@col'==1} + %\path + %let + %\p1=(wg@chit.east), + %\p2=(wg@chit.west), + %\p3=(wg@chit), + %\n1={\x3+\x1-\x2+#3cm} in + %coordinate(wg@fnxt) at (\n1,\y3); + \fi + %\chit@dbg{10}{Drawing `\u\space flipped' at wg@fnxt} + \chit[\u\space flipped=\ti, + zone turn=\t, + zone mult=\n](wg@fnxt)(wg@flip)% + \chit@cell@frame{wg@flip}{#3} + \chit@dbg{10}{Next column: `\the\wg@col'} + \ifnum\wg@col=1 + \ifnum\wg@row=1 + \coordinate(wg@table@top) at (wg@chit.north east); + \fi + \coordinate(wg@table@bot) at (wg@chit.south east); + \fi + \global\advance\wg@col1 + \ifnum\wg@col>#2% + % \path + % let + % \p1=(wg@chit.north), + % \p2=(wg@chit.south), + % \p3=(wg@chit), + % \n1={\y3-\y1+\y2-#3cm} in %1.21991 + % coordinate(wg@next) at (0,\n1); + \coordinate(wg@next) at (0,-\wg@row\chit@h); + \global\wg@col=1 + \global\advance\wg@row1 + \chit@dbg{10}{Row row, next column `\the\wg@col'} + \else + % \path + % let + % \p1=(wg@chit.east), + % \p2=(wg@chit.west), + % \p3=(wg@chit), + % \p4=(wg@flip.east), + % \p5=(wg@flip.west), + % \p6=(wg@flip), + % \n1={\x3-\x1+\x2-#3cm}, + % \n2={\x6+\x4-\x5+#3cm} in + % coordinate(wg@next) at (\n1,\y3) + % coordinate(wg@fnxt) at (\n2,\y6); + \coordinate(wg@next) at ($(wg@chit)+(-\the\chit@w,0)$); + \coordinate(wg@fnxt) at ($(wg@flip)+(+\the\chit@w,0)$); + \chit@dbg{10}{Next column `\the\wg@col'} + \fi + \fi% + \fi% + \fi% + }% + \fi% + }% + \fi% + }% + \draw[dashed,scale line widths](wg@table@top)--++(0,.4); + \draw[dashed,scale line widths](wg@table@bot)--++(0,-.4); + \draw[dashed,scale line widths,{Stealth[]}-] + ($(wg@table@top)+(0,.2)$) -- ++(.5,0) + node[transform shape,anchor=west]{Back}; + \draw[dashed,scale line widths,{Stealth[]}-] + ($(wg@table@top)+(0,.2)$) -- ++(-.5,0) + node[transform shape,anchor=east]{Front}; + \draw[dashed,scale line widths,{Stealth[]}-] + ($(wg@table@bot)+(0,-.2)$) -- ++(.5,0) + node[transform shape,anchor=west]{Back}; + \draw[dashed,scale line widths,{Stealth[]}-] + ($(wg@table@bot)+(0,-.2)$) -- ++(-.5,0) + node[transform shape,anchor=east]{Front}; + % \draw[dashed,<-] (#3/5,-2*#3/3)--(#3/2,-2*#3/3) node[transform shape,anchor=west]{Back};% + % \draw[dashed,<-] (-#3/5,-2*#3/3)--(-#3/2,-2*#3/3) node[transform shape,anchor=east]{Front};% % \foreach \cc in {0,...,#2}{ % \draw[dashed] (\cc*#3,-3*#3/4)--(\cc*#3,\r-#3/4); % \draw[dashed] (-\cc*#3,-3*#3/4)--(-\cc*#3,\r-#3/4);} @@ -980,15 +1253,18 @@ % \draw[dashed] (-#2*#3-#3/4,\rr)--(#2*#3+#3/4,\rr);} \@ifnextchar;{\@gobble}{}} \def\doublechitgrid#1#2#3{% + \message{^^JWARNING - the grid table might be messed up!} + %\iffalse \pgfmathparse{#3/2}\edef\rmin{\pgfmathresult}% \pgfmathparse{#2*#3-#3/2}\edef\rmax{\pgfmathresult}% \foreach \cc in {0,...,#1}{ - \draw[chit/grid lines] (\cc*#3,-3*#3/4)--(\cc*#3,\rmax+#3/4); - \draw[chit/grid lines] (-\cc*#3,-3*#3/4)--(-\cc*#3,\rmax+#3/4);} + \draw[chit/grid lines] (\cc*#3+\rmin,3*#3/4)--(\cc*#3+\rmin,-\rmax-#3/4); + \draw[chit/grid lines] (-\cc*#3+\rmin,3*#3/4)--(-\cc*#3+\rmin,-\rmax-#3/4);} %\chit@dbg{0}{Drawing horizontal lines from `-\rmin, `\rmin', ..., `\rmax'} - \foreach \rr in {-\rmin,\rmin,...,\rmax}{ - %\chit@dbg{0}{Horizontal line at `\rr'} - \draw[chit/grid lines] (-#1*#3-#3/4,\rr)--(#1*#3+#3/4,\rr);} + \foreach \rr in {\rmin,-\rmin,...,-\rmax}{ + \chit@dbg{0}{Horizontal line at `\rr'} + \draw[chit/grid lines] (-#1*#3-#3/4+\rmin,\rr)--(#1*#3+#3/4+\rmin,\rr);} + %\fi } \tikzset{% battle marker/.pic={ @@ -1023,7 +1299,7 @@ \tikzset{ pics/result marker/.style args={#1,#2}{ code={ - \message{^^JResults marker #1 (#2)} + \chit@dbg{3}{Results marker #1 (#2)} \node[shape=circle, font=\sffamily\bfseries\large, inner sep=0pt, @@ -1097,6 +1373,12 @@ % \pgfusepath{draw} %draw border % \pgfusepath{draw} %draw rectangle }} +\newif\ifwg@dsixdot\wg@dsixdotfalse +\tikzset{ + d6 dots/.is if=wg@dsixdot, + d6 dots/.initial=false, + d6 dots/.default=true +} \pgfdeclareshape{d6}{ \anchor{center}{\pgfpointorigin} % within the node, (0,0) is the center \anchor{text}{ @@ -1105,7 +1387,48 @@ \backgroundpath{ % draw border \pgfpathrectanglecorners{\pgfpoint{.4cm}{.4cm}}{\pgfpoint{-.4cm}{-.4cm}} % \pgfusepath{draw} %draw rectangle - }} + } + \behindforegroundpath{ % draw border + \ifwg@dsixdot% + \edef\numdots{\tikz@node@content}% + % Draw over text + \pgfpathrectanglecorners{\pgfpoint{.4cm}{.4cm}}{\pgfpoint{-.4cm}{-.4cm}}% + \pgfusepath{fill} %draw rectangle + \ifx\tikz@textcolor\pgfutilempty% + \pgfutil@colorlet{.}{pgfstrokecolor}% + \else + % \pgfsetfillcolor{\tikz@textcolor}% + \pgfutil@colorlet{.}{\tikz@textcolor}% + \fi + \pgfsetfillcolor{.}% + \ifodd\numdots + %\message{^^JCenter dot} + \pgfpathcircle{\pgfpoint{0cm}{0cm}}{.09cm} + \fi + \ifnum\numdots=2% + %\message{^^JSpecial 2} + \pgfpathcircle{\pgfpointpolar{-45}{.32cm}}{.09cm} + \pgfpathcircle{\pgfpointpolar{135}{.32cm}}{.09cm} + \fi% + \ifnum\numdots>2% + %\message{^^JAt least 2} + \pgfpathcircle{\pgfpointpolar{-45}{.35cm}}{.09cm} + \pgfpathcircle{\pgfpointpolar{135}{.35cm}}{.09cm} + \fi% + \ifnum\numdots>3% + %\message{^^JAt least 4} + \pgfpathcircle{\pgfpointpolar{ 45}{.35cm}}{.09cm} + \pgfpathcircle{\pgfpointpolar{-135}{.35cm}}{.09cm} + \fi% + \ifnum\numdots=6% + %\message{^^JLucky 6} + \pgfpathcircle{\pgfpoint{-.247cm}{0cm}}{.09cm} + \pgfpathcircle{\pgfpoint{ .247cm}{0cm}}{.09cm} + \fi + \pgfusepath{fill} %draw rectangle + \fi% + } +} \pgfdeclareshape{d8}{ \anchor{center}{\pgfpointorigin} % within the node, (0,0) is the center \anchor{text}{ @@ -1226,7 +1549,7 @@ \pgfkeysalso{% chit has drop=true, /tikz/blur shadow={shadow blur steps=5, - shadow opacity=25, + shadow opacity=30, shadow xshift=.05cm, shadow yshift=-.05cm, shadow blur radius=.05cm, diff --git a/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.hex.code.tex b/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.hex.code.tex index 2dde041e313..f08d0574e02 100644 --- a/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.hex.code.tex +++ b/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.hex.code.tex @@ -9,6 +9,8 @@ %% hex/shape.dtx (with options: `hex') %% hex/terrain.dtx (with options: `hex') %% hex/terrain/beach.dtx (with options: `hex') +%% hex/terrain/fields.dtx (with options: `hex') +%% hex/terrain/speckle.dtx (with options: `hex') %% hex/terrain/light_woods.dtx (with options: `hex') %% hex/terrain/woods.dtx (with options: `hex') %% hex/terrain/swamp.dtx (with options: `hex') @@ -223,7 +225,7 @@ \xdef\hex@eff@row{\pgfmathresult}% \hex@dbg{2}{Effective row: \hex@coords@row@fac * (\hex@row + \hex@coords@row@off) -> \hex@eff@row}% - \pgfmathparse{(2*\hex@eff@row-mod(round((\hex@col+\hex@coords@col@off)),2))*\hex@yy}% + %\pgfmathparse{(2*\hex@eff@row-mod(round((\hex@col+\hex@coords@col@off)),2))*\hex@yy}% \pgfmathparse{(2*\hex@eff@row-mod(abs(round(\hex@col+\hex@coords@col@off)),2))*\hex@yy}% \xdef\hex@y{\pgfmathresult}% \ifx\hex@vtx\@empty\else% @@ -533,15 +535,19 @@ code/.store in=\hex@t@code,% clip/.store in=\hex@t@clip,% random rotation/.is if=@hex@t@rot, + rotate/.store in=\hex@t@s@angle, pic/.default=, image/.default=, code/.default=, clip/.default=, + rotate/.default=, } \iffalse \tikzset{ /hex/terrain/.cd,% beach/.style={pic=hex/terrain/beach}, + fields/.style={pic=hex/terrain/fields}, + speckle/.style={pic=hex/terrain/speckle}, light woods/.style={pic=hex/terrain/light woods}, woods/.style={pic=hex/terrain/woods}, swamp/.style={pic=hex/terrain/swamp}, @@ -555,6 +561,8 @@ \tikzset{ /hex/terrain/.cd,% beach/.style={image=wargame.beach}, + fields/.style={image=wargame.fields}, + speckle/.style={image=wargame.speckle}, light woods/.style={image=wargame.light_woods}, woods/.style={image=wargame.woods}, swamp/.style={image=wargame.swamp}, @@ -597,6 +605,12 @@ \if@hex@t@rot% \pgfmathrandominteger{\hex@t@angle}{0}{5} \pgfmathparse{int(60*\hex@t@angle)}\edef\hex@t@angle{\pgfmathresult}% + \else% + \@ifundefined{hex@t@s@angle}{}{ + \ifx\hex@t@s@angle\@empty% + \else% + \edef\hex@t@angle{\hex@t@s@angle}% + \fi}% \fi% \hex@dbg{5}{Will rotate terrain by `\hex@t@angle'}% \scope[rotate=\hex@t@angle]% @@ -671,7 +685,7 @@ pics/hex/#2sextant/S/.style=hex/#2sextant/south, pics/hex/#2sextant/SE/.style=hex/#2sextant/south east, pics/hex/#2sextant/C/.style=hex/#2sextant/center, - } + }% } \hex@make@sextants{.7}{} \hex@make@sextants{.3}{large } @@ -1347,6 +1361,83 @@ } \fi \tikzset{ + hex/terrain/fields/.style={% + draw=none% + }% +} +\ifhex@terrain@pic +\tikzset{ + hex/terrain/fields/.pic={ + \path[draw=none,fill=none](1,0) + --(60:1) + --(120:1) + --(180:1) + --(240:1) + --(300:1) + --cycle; + \fill[black,opacity=.05] ( .1, .3)rectangle++(.5,.4); + \fill[white,opacity=.15] (-.65, -.5)rectangle++(.3,.5); + \fill[white,opacity=.25] (-.60, .2)rectangle++(.6,.5); + \fill[black,opacity=.02] (-.30, -.1)rectangle++(.4,.2); + \fill[black,opacity=.02] (-.50, -.8)rectangle++(.4,.25); + \fill[black,opacity=.05] (-.30, -.5)rectangle++(.4,.35); + \fill[white,opacity=.15] ( .15, -.6)rectangle++(.4,.8); + \fill[black,opacity=.02] (-.05, -.8)rectangle++(.5,.15); + \fill[black,opacity=.02] (-.8, -.3)rectangle++(.1,.6); + \fill[black,opacity=.02] ( .6, -.35)rectangle++(.2,.6); + } +} +\fi +\tikzset{ + hex/terrain/speckle/.style={% + }% +} +\ifhex@terrain@pic +\pgfmathdeclarerandomlist{black white speckles}{{black}{white}} +\pgfmathdeclarerandomlist{gray white speckles}{{gray}{white}} +\tikzset{ + hex/terrain/speckle/minimum radius/.initial=3, + hex/terrain/speckle/maximum radius/.initial=10, + hex/terrain/speckle/minimum opacity/.initial=3, + hex/terrain/speckle/maximum opacity/.initial=7, + %% + pics/hex/terrain/speckle/.style args={#1,#2}{ + code={ + \tikzset{ + hex/terrain/speckle/minimum radius/.get=\minR, + hex/terrain/speckle/maximum radius/.get=\maxR, + hex/terrain/speckle/minimum opacity/.get=\minO, + hex/terrain/speckle/maximum opacity/.get=\maxO, + } + \foreach \i in {1,...,#1}{ + + \pgfmathrandominteger{\R}{\minR}{\maxR} + \pgfmathrandominteger{\n}{1}{3}% Sub trapezoid + \pgfmathrandominteger{\x}{0}{100}% X coordinate + \pgfmathrandominteger{\y}{0}{100}% Y coordinate + \pgfmathrandominteger{\o}{\minO}{\maxO}% Opacity + \pgfmathrandomitem{\c}{#2} + \pgfmathparse{\x/100}\edef\x{\pgfmathresult} + \pgfmathparse{\y/100}\edef\y{\pgfmathresult} + \pgfmathparse{\R/100}\edef\R{\pgfmathresult} + \pgfmathparse{\o/100}\edef\o{\pgfmathresult} + \pgfmathparse{1-\R}\edef\O{\pgfmathresult} + \ifnum\n=1 + \fill[\c,opacity=\o]($\y*(120:\O)+(\x-\R,0)$) circle(\R); + \else + \ifnum\n=2 + \fill[\c,opacity=\o]($\y*(240:\O)+(\x-\R,0)$) circle(\R); + \else + \fill[\c,opacity=\o]($\x*(120:\O)+\y*(240:\O)$) circle(\R); + \fi + \fi + } + } + }, + pics/hex/terrain/speckle/.default={200,gray white speckles} +} +\fi +\tikzset{ hex/terrain/light woods/.style={ draw=none, fill={rgb,100:red,69;green,98;blue,69} @@ -12832,170 +12923,170 @@ \@ifnextchar[{\bo@rdfr@me@}{\bo@rdfr@me@[]}%] } \def\bo@rdfr@me@u(#1)#2#3#4#5{ - \hex@coords@conv{#1} + \hex@coords@conv{#1}% % \hex@dbg{0}{#1 -> `\hex@x',`\hex@y'} - \pgfmathparse{min(#2,\hex@x)}\xdef#2{\pgfmathresult} - \pgfmathparse{min(#3,\hex@y)}\xdef#3{\pgfmathresult} - \pgfmathparse{max(#4,\hex@x)}\xdef#4{\pgfmathresult} - \pgfmathparse{max(#5,\hex@y)}\xdef#5{\pgfmathresult} - \hex@dbg{2}{#1 -> ll=`#2',`#3', ur=`#4',`#5'} + \pgfmathparse{min(#2,\hex@x)}\xdef#2{\pgfmathresult}% + \pgfmathparse{min(#3,\hex@y)}\xdef#3{\pgfmathresult}% + \pgfmathparse{max(#4,\hex@x)}\xdef#4{\pgfmathresult}% + \pgfmathparse{max(#5,\hex@y)}\xdef#5{\pgfmathresult}% + \hex@dbg{2}{#1 -> ll=`#2',`#3', ur=`#4',`#5'}% } -\def\bo@rdfr@me@[#1]#2#3#4#5{ +\def\bo@rdfr@me@[#1]#2#3#4#5{% % Define rtmp and a ctmp to by directions - \pgfmathparse{int(\hex@coords@row@fac)}\edef\rtmp{\pgfmathresult} - \pgfmathparse{int(\hex@coords@col@fac)}\edef\ctmp{\pgfmathresult} + \pgfmathparse{int(\hex@coords@row@fac)}\edef\rtmp{\pgfmathresult}% + \pgfmathparse{int(\hex@coords@col@fac)}\edef\ctmp{\pgfmathresult}% % Define vertices for path - \def\ctfv{SW} - \def\ctsv{SE} - \def\cbfv{NE} - \def\cbsv{NW} - \def\rrfv{E} - \def\rrsv{NE} - \def\rlfv{W} - \def\rlsv{SW} + \def\ctfv{SW}% + \def\ctsv{SE}% + \def\cbfv{NE}% + \def\cbsv{NW}% + \def\rrfv{E}% + \def\rrsv{NE}% + \def\rlfv{W}% + \def\rlsv{SW}% % Swap around some definitions based on the row direction - \ifnum\rtmp<0 - \let\max@short\hex@bot@short@col - \let\min@short\hex@top@short@col - \let\swp\ctfv\let\ctfv\cbsv\let\cbsv\swp - \let\swp\ctsv\let\ctsv\cbfv\let\cbfv\swp - \def\rrsv{SE} - \def\rlsv{NW} - \else - \let\max@short\hex@top@short@col - \let\min@short\hex@bot@short@col - \fi + \ifnum\rtmp<0% + \let\max@short\hex@bot@short@col% + \let\min@short\hex@top@short@col% + \let\swp\ctfv\let\ctfv\cbsv\let\cbsv\swp% + \let\swp\ctsv\let\ctsv\cbfv\let\cbfv\swp% + \def\rrsv{SE}% + \def\rlsv{NW}% + \else% + \let\max@short\hex@top@short@col% + \let\min@short\hex@bot@short@col% + \fi% % Swap around some definitions based on the column direction - \ifnum\ctmp<0 - \let\swp\ctfv\let\ctfv\ctsv\let\ctsv\swp - \let\swp\cbfv\let\cbfv\cbsv\let\cbsv\swp - \let\swp\rrfv\let\rrfv\rlsv\let\rlsv\swp - \let\swp\rrsv\let\rrsv\rlfv\let\rlfv\swp - \fi + \ifnum\ctmp<0% + \let\swp\ctfv\let\ctfv\ctsv\let\ctsv\swp% + \let\swp\cbfv\let\cbfv\cbsv\let\cbsv\swp% + \let\swp\rrfv\let\rrfv\rlsv\let\rlsv\swp% + \let\swp\rrsv\let\rrsv\rlfv\let\rlfv\swp% + \fi% % Define tmp = 0 if no shorts, 1 if top short, 2 if both \pgfmathparse{ifthenelse(\hex@got@top@short, - ifthenelse(\hex@got@bot@short,2,1),0)}\edef\tmp{\pgfmathresult} + ifthenelse(\hex@got@bot@short,2,1),0)}\edef\tmp{\pgfmathresult}% % If top-short, set factors - \ifnum\tmp=1 - \def\mnf{-1} - \def\mxf{-1} - \def\mnn{} - \def\mxn{} + \ifnum\tmp=1% + \def\mnf{-1}% + \def\mxf{-1}% + \def\mnn{}% + \def\mxn{}% % If both short, set factors - \else\ifnum\tmp=2 - \def\mnf{\rtmp} - \def\mxf{(-\rtmp)} + \else\ifnum\tmp=2% + \def\mnf{\rtmp}% + \def\mxf{(-\rtmp)}% % If inverse rows, set factors - \ifnum\rtmp<0 - \def\mnn{} - \def\mxn{not} - \else - \def\mnn{not} - \def\mxn{} - \fi + \ifnum\rtmp<0% + \def\mnn{}% + \def\mxn{not}% + \else% + \def\mnn{not}% + \def\mxn{}% + \fi% % If none is short - \else - \def\mnf{1} - \def\mxf{1} - \def\mnn{not} - \def\mxn{not} - \fi\fi + \else% + \def\mnf{1}% + \def\mxf{1}% + \def\mnn{not}% + \def\mxn{not}% + \fi\fi% % Define row@mn to give least row of column \def\row@mn##1{% \pgfmathparse{int(#3+\mnf* \hex@coords@row@fac*\min@short(##1)* - \mnn(\min@short(\hex@coords@col@off)))} - \edef\lr{\pgfmathresult}} + \mnn(\min@short(\hex@coords@col@off)))}% + \edef\lr{\pgfmathresult}}% % Define row@mx to give largest row of column \def\row@mx##1{% \pgfmathparse{int(#5+\mxf* \hex@coords@row@fac*\max@short(##1)* - \mxn(\max@short(\hex@coords@col@off)))} - \edef\ur{\pgfmathresult}} + \mxn(\max@short(\hex@coords@col@off)))}% + \edef\ur{\pgfmathresult}}% % % % Below defines a path around the perimeter of the hexes. % - \def\@llx{10000} - \def\@lly{10000} - \def\@urx{-10000} - \def\@ury{-10000} + \def\@llx{10000}% + \def\@lly{10000}% + \def\@urx{-10000}% + \def\@ury{-10000}% % Start with an empty path \def\p{} % Loop across least row (can be top if \rtmp<0) \foreach \c in {#2,...,#4}{% - \row@mn{\c} - \row@mx{\c} + \row@mn{\c}% + \row@mx{\c}% % \message{^^JColumn: `\c' -> `\lr',`\ur' (#3,#5)} } \foreach \c in {#2,...,#4}{% - \row@mn{\c} + \row@mn{\c}% \xdef\p{\p (hex cs:c=\c,r=\lr,v=\ctfv)-- - (hex cs:c=\c,r=\lr,v=\ctsv)--} - \bo@rdfr@me@u(c=\c,r=\lr,v=\ctfv)\@llx\@lly\@urx\@ury - \bo@rdfr@me@u(c=\c,r=\lr,v=\ctsv)\@llx\@lly\@urx\@ury - } + (hex cs:c=\c,r=\lr,v=\ctsv)--}% + \bo@rdfr@me@u(c=\c,r=\lr,v=\ctfv)\@llx\@lly\@urx\@ury% + \bo@rdfr@me@u(c=\c,r=\lr,v=\ctsv)\@llx\@lly\@urx\@ury% + }% % Go up (down if \rtmp<0) right side - \row@mn{#4} - \row@mx{#4} + \row@mn{#4}% + \row@mx{#4}% \foreach \r in {\lr,...,\ur}{% \xdef\p{\p (hex cs:c=#4,r=\r,v=\rrfv)-- - (hex cs:c=#4,r=\r,v=\rrsv)--} - \bo@rdfr@me@u(c=#4,r=\r,v=\rrfv)\@llx\@lly\@urx\@ury - \bo@rdfr@me@u(c=#4,r=\r,v=\rrsv)\@llx\@lly\@urx\@ury - } + (hex cs:c=#4,r=\r,v=\rrsv)--}% + \bo@rdfr@me@u(c=#4,r=\r,v=\rrfv)\@llx\@lly\@urx\@ury% + \bo@rdfr@me@u(c=#4,r=\r,v=\rrsv)\@llx\@lly\@urx\@ury% + }% % Go across largest row (can be bottom if \rtmp<0) \foreach \c in {#4,...,#2}{% - \row@mx{\c} + \row@mx{\c}% % \message{^^JColumn: `\c', max:`\ur'} \xdef\p{\p (hex cs:c=\c,r=\ur,v=\cbfv)-- - (hex cs:c=\c,r=\ur,v=\cbsv)--} - \bo@rdfr@me@u(c=\c,r=\ur,v=\cbfv)\@llx\@lly\@urx\@ury - \bo@rdfr@me@u(c=\c,r=\ur,v=\cbsv)\@llx\@lly\@urx\@ury + (hex cs:c=\c,r=\ur,v=\cbsv)--}% + \bo@rdfr@me@u(c=\c,r=\ur,v=\cbfv)\@llx\@lly\@urx\@ury% + \bo@rdfr@me@u(c=\c,r=\ur,v=\cbsv)\@llx\@lly\@urx\@ury% } % Go up (down if \rtmp<0) left side. - \row@mn{#2} - \row@mx{#2} + \row@mn{#2}% + \row@mx{#2}% \foreach \r in {\ur,...,\lr}{% \xdef\p{\p (hex cs:c=#2,r=\r,v=\rlfv)-- - (hex cs:c=#2,r=\r,v=\rlsv)--} - \bo@rdfr@me@u(c=#2,r=\r,v=\rlfv)\@llx\@lly\@urx\@ury - \bo@rdfr@me@u(c=#2,r=\r,v=\rlsv)\@llx\@lly\@urx\@ury - } + (hex cs:c=#2,r=\r,v=\rlsv)--}% + \bo@rdfr@me@u(c=#2,r=\r,v=\rlfv)\@llx\@lly\@urx\@ury% + \bo@rdfr@me@u(c=#2,r=\r,v=\rlsv)\@llx\@lly\@urx\@ury% + }% % End path with cycle - \edef\p{\p cycle} + \edef\p{\p cycle}% % Define global path - \global\let\hex@board@path\p - \hex@dbg{3}{Hex board path: `\meaning\hex@board@path'} + \global\let\hex@board@path\p% + \hex@dbg{3}{Hex board path: `\meaning\hex@board@path'}% % If an optional argument was given, then use that to actually make % hexes. - \ifx|#1|\else + \ifx|#1|\else% \foreach[count=\nc] \c in {#2,...,#4}{% - \row@mn{\c} - \row@mx{\c} + \row@mn{\c}% + \row@mx{\c}% \foreach \r in {\lr,...,\ur}{% - \hex[#1={\c,\r}](c=\c,r=\r) - } - } - \fi -} + \hex[#1={\c,\r}](c=\c,r=\r)% + }% + }% + \fi% +}% \tikzset{% - /hex/board/no op/.style args={#1,#2}{}} + /hex/board/no op/.style args={#1,#2}{}}% \def\boardhexes{% \@ifnextchar[{\bo@rdhexes}{\bo@rdhexes[board/no op]}%] -} +}% \def\bo@rdhexes[#1](#2)(#3){% - \hex@coords@conv{#2} - \edef\llc{\hex@col} - \edef\llr{\hex@row} - \hex@coords@conv{#3} - \edef\urc{\hex@col} - \edef\urr{\hex@row} - \bo@rdfr@me[#1]{\llc}{\llr}{\urc}{\urr}} + \hex@coords@conv{#2}% + \edef\llc{\hex@col}% + \edef\llr{\hex@row}% + \hex@coords@conv{#3}% + \edef\urc{\hex@col}% + \edef\urr{\hex@row}% + \bo@rdfr@me[#1]{\llc}{\llr}{\urc}{\urr}}% \tikzset{board frame bb/.code={ \pgfkeys{ %/tikz/local bounding box=tmp board frame, @@ -13011,65 +13102,66 @@ }}}} \def\bo@rdframe[#1](#2)(#3){% - \hex@coords@conv{#2} - \edef\llc{\hex@col} - \edef\llr{\hex@row} + \hex@coords@conv{#2}% + \edef\llc{\hex@col}% + \edef\llr{\hex@row}% % \hex@coords@conv{#3} - \edef\urc{\hex@col} - \edef\urr{\hex@row} + \edef\urc{\hex@col}% + \edef\urr{\hex@row}% % - \def\margin{#1} + \def\margin{#1}% % % This will store the bounding box in tmp node `board frame' \bo@rdfr@me{\llc}{\llr}{\urc}{\urr}% - \begin{scope}[board frame bb] - \expandafter\path\hex@board@path; - \end{scope} - \hex@dbg{1}{Board frame LL: -> `\llx',`\lly'} - \pgfmathparse{\llx+ifthenelse(\llx<0,-1,1)*\margin}\edef\llx{\pgfmathresult} - \pgfmathparse{\lly+ifthenelse(\lly<0,-1,1)*\margin}\edef\lly{\pgfmathresult} + \begin{scope}[board frame bb]% + \expandafter\path\hex@board@path;% + \end{scope}% + \hex@dbg{1}{Board frame LL: -> `\llx',`\lly'}% + \pgfmathparse{\llx+ifthenelse(\llx<0,-1,1)*\margin}\edef\llx{\pgfmathresult}% + \pgfmathparse{\lly+ifthenelse(\lly<0,-1,1)*\margin}\edef\lly{\pgfmathresult}% % - \hex@dbg{1}{Board frame UR: -> `\urx',`\ury'} - \pgfmathparse{\urx+ifthenelse(\urx<0,-1,1)*\margin}\edef\urx{\pgfmathresult} - \pgfmathparse{\ury+ifthenelse(\ury<0,-1,1)*\margin}\edef\ury{\pgfmathresult} + \hex@dbg{1}{Board frame UR: -> `\urx',`\ury'}% + \pgfmathparse{\urx+ifthenelse(\urx<0,-1,1)*\margin}\edef\urx{\pgfmathresult}% + \pgfmathparse{\ury+ifthenelse(\ury<0,-1,1)*\margin}\edef\ury{\pgfmathresult}% % - \pgfmathparse{\urx-\llx}\edef\w{\pgfmathresult} - \pgfmathparse{\ury-\lly}\edef\h{\pgfmathresult} + \pgfmathparse{\urx-\llx}\edef\w{\pgfmathresult}% + \pgfmathparse{\ury-\lly}\edef\h{\pgfmathresult}% %% Print to the log - \hex@dbg{0}{Board Frame: (\llx,\lly)x(\urx,\ury) (\w x\h) (\llc,\llr)x(\urc,\urr)} + \hex@dbg{0}{Board Frame: (\llx,\lly)x(\urx,\ury) (\w x\h) (\llc,\llr)x(\urc,\urr)}% %% Possibly draw - \draw[hex/board frame/.try](\llx,\lly) rectangle(\urx,\ury); + \draw[hex/board frame/.try](\llx,\lly) rectangle(\urx,\ury);% %% Store macros \xdef\boardXmin{\llx}% \xdef\boardYmin{\lly}% \xdef\boardXmax{\urx}% \xdef\boardYmax{\ury}% + \@ifnextchar;{\@gobble}{}% } -\def\boardpath(#1)(#2){ +\def\boardpath(#1)(#2){% \hex@coords@reset% - \tikzset{/hex/coords/.cd, #1} - \edef\llc{\hex@col} - \edef\llr{\hex@row} + \tikzset{/hex/coords/.cd, #1}% + \edef\llc{\hex@col}% + \edef\llr{\hex@row}% %% \hex@coords@reset% - \tikzset{/hex/coords/.cd, #2} - \edef\urc{\hex@col} - \edef\urr{\hex@row} - + \tikzset{/hex/coords/.cd, #2}% + \edef\urc{\hex@col}% + \edef\urr{\hex@row}% % This will store the bounding box in tmp node `board frame' \bo@rdfr@me{\llc}{\llr}{\urc}{\urr}% %% Use the path to extract the bounding box %\begin{scope}[local bounding box=board frame] % \expandafter\path\hex@board@path; %\end{scope} - \global\let\hexboardpath\hex@board@path + \global\let\hexboardpath\hex@board@path% + \@ifnextchar;{\@gobble}{}% } \def\boardclip(#1)(#2)#3{% - \boardpath(#1)(#2) + \boardpath(#1)(#2)% \draw \ifx|#3|\else[preaction={#3}]\fi% - [clip] \hexboardpath; + [clip] \hexboardpath;% } \def\debuggrid{% diff --git a/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.natoapp6c.code.tex b/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.natoapp6c.code.tex index fe65f5779e2..531065c91b9 100644 --- a/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.natoapp6c.code.tex +++ b/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.natoapp6c.code.tex @@ -297,8 +297,8 @@ \pgfdeclareshape{natoapp6c friendly equipment}{% \inheritsavedanchors[from=natoapp6c base] \savedanchor\northeast{% - \pgf@x=\n@to@pp@r% - \pgf@y=\n@to@pp@r} + \pgf@x=1.2\n@to@pp@r% + \pgf@y=1.2\n@to@pp@r} \anchor{north east}{\northeast} \anchor{north west}{\northeast\pgf@x=-\pgf@x} \anchor{south east}{\northeast\pgf@y=-\pgf@y} @@ -484,6 +484,49 @@ \backgroundpath{} \behindforegroundpath{} } +\def\n@to@friendly@l@nd{% + \northeast \wg@tmpa=\pgf@x\wg@tmpb=\pgf@y% + \pgfpathmoveto{\pgfqpoint{ \wg@tmpa}{ \wg@tmpb}}% + \pgfpathlineto{\pgfqpoint{-\wg@tmpa}{ \wg@tmpb}}% + \pgfpathlineto{\pgfqpoint{-\wg@tmpa}{-\wg@tmpb}}% + \pgfpathlineto{\pgfqpoint{ \wg@tmpa}{-\wg@tmpb}}% + \pgfclosepath} +\def\n@to@pp@friendl@dismounted{% + \northeast\wg@tmpa=\pgf@x\wg@tmpb\pgf@y% + \pgfpathmoveto{\pgfpoint{-\wg@tmpa}{-.5\wg@tmpb}}% + \pgfpathlineto{\pgfpoint{-\wg@tmpa}{ .5\wg@tmpb}}% + \pgfpathlineto{\pgfpoint{0cm} { \wg@tmpb}}% + \pgfpathlineto{\pgfpoint{ \wg@tmpa}{ .5\wg@tmpb}}% + \pgfpathlineto{\pgfpoint{ \wg@tmpa}{-.5\wg@tmpb}}% + \pgfpathlineto{\pgfpoint{0cm} { -\wg@tmpb}}% + \pgfclosepath% +}% +\pgfdeclareshape{natoapp6c friendly dismounted}{% + \inheritsavedanchors[from=natoapp6c base] + \savedanchor\northeast{% + \pgf@x=1.1\n@to@pp@r% + \pgf@y=1.1\n@to@pp@r} + \anchor{north east}{\northeast} + \anchor{north west}{\northeast\pgf@x=-\pgf@x} + \anchor{south east}{\northeast\pgf@y=-\pgf@y} + \anchor{south west}{\northeast\pgf@x=-\pgf@x\pgf@y=-\pgf@y} + \anchor{north}{\northeast\pgf@x=0cm} + \anchor{south}{\northeast\pgf@x=0cm\pgf@y=-\pgf@y} + \anchor{east}{\northeast\pgf@y=0cm} + \anchor{west}{\northeast\pgf@x=-\pgf@x\pgf@y=0cm} + \inheritanchor[from=natoapp6c base]{upper} + \inheritanchor[from=natoapp6c base]{lower} + \inheritanchor[from=natoapp6c base]{left} + \inheritanchor[from=natoapp6c base]{right} + \inheritanchor[from=natoapp6c base]{center} + \backgroundpath{% + \n@to@pp@friendl@dismounted% + } + \behindforegroundpath{% + \n@to@pp@friendl@dismounted% + \pgfusepath{stroke}% + } +} \def\n@to@hostile@@ir{% \southeast \wg@tmpa=\pgf@x\wg@tmpb=\pgf@y% \cntrl \wg@tmpc=\pgf@y% @@ -813,6 +856,28 @@ \pgfusepath{stroke}% } } +\pgfdeclareshape{natoapp6c hostile dismounted}{% + \inheritsavedanchors[from=natoapp6c hostile land] + \inheritanchor[from=natoapp6c hostile land]{inner north east} + \inheritanchor[from=natoapp6c hostile land]{inner north west} + \inheritanchor[from=natoapp6c hostile land]{inner south west} + \inheritanchor[from=natoapp6c hostile land]{inner south east} + \inheritanchor[from=natoapp6c hostile land]{north east} + \inheritanchor[from=natoapp6c hostile land]{north west} + \inheritanchor[from=natoapp6c hostile land]{south east} + \inheritanchor[from=natoapp6c hostile land]{south west} + \inheritanchor[from=natoapp6c hostile land]{north} + \inheritanchor[from=natoapp6c hostile land]{west} + \inheritanchor[from=natoapp6c hostile land]{east} + \inheritanchor[from=natoapp6c hostile land]{south} + \inheritanchor[from=natoapp6c hostile land]{upper} + \inheritanchor[from=natoapp6c hostile land]{lower} + \inheritanchor[from=natoapp6c hostile land]{left} + \inheritanchor[from=natoapp6c hostile land]{right} + \inheritanchor[from=natoapp6c hostile land]{center} + \inheritbackgroundpath[from=natoapp6c hostile land] + \inheritbehindforegroundpath[from=natoapp6c hostile land] +} \def\n@to@pp@neutr@l@init{% \northeast\wg@tmpa=\pgf@x\wg@tmpb=\pgf@y% \def\n@to@pp@neutr@l@left {\pgflineto{\pgfqpoint{-\wg@tmpa}{-\wg@tmpb}}}% @@ -1111,6 +1176,29 @@ \pgfusepath{stroke}% } } +\pgfdeclareshape{natoapp6c neutral dismounted}{% + \inheritsavedanchors[from=natoapp6c neutral land] + \inheritanchor[from=natoapp6c neutral land]{center} + \inheritanchor[from=natoapp6c neutral land]{inner north east} + \inheritanchor[from=natoapp6c neutral land]{inner north west} + \inheritanchor[from=natoapp6c neutral land]{inner south west} + \inheritanchor[from=natoapp6c neutral land]{inner south east} + \inheritanchor[from=natoapp6c neutral land]{north east} + \inheritanchor[from=natoapp6c neutral land]{north west} + \inheritanchor[from=natoapp6c neutral land]{south east} + \inheritanchor[from=natoapp6c neutral land]{south west} + \inheritanchor[from=natoapp6c neutral land]{north} + \inheritanchor[from=natoapp6c neutral land]{west} + \inheritanchor[from=natoapp6c neutral land]{east} + \inheritanchor[from=natoapp6c neutral land]{south} + \inheritanchor[from=natoapp6c neutral land]{upper} + \inheritanchor[from=natoapp6c neutral land]{lower} + \inheritanchor[from=natoapp6c neutral land]{left} + \inheritanchor[from=natoapp6c neutral land]{right} + \inheritanchor[from=natoapp6c neutral land]{center} + \inheritbackgroundpath[from=natoapp6c neutral land] + \inheritbehindbackgroundpath[from=natoapp6c neutral land] +} \def\n@to@pp@unknown@init{% \def\n@to@pp@unknown@top{% \innernortheast \wg@tmpa=\pgf@x% @@ -1485,6 +1573,28 @@ \n@to@pp@unknown@right % \pgfusepath{stroke}} } +\pgfdeclareshape{natoapp6c unknown dismounted}{% + \inheritsavedanchors[from=natoapp6c unknown land] + \inheritanchor[from=natoapp6c unknown land]{inner north east} + \inheritanchor[from=natoapp6c unknown land]{inner north west} + \inheritanchor[from=natoapp6c unknown land]{inner south west} + \inheritanchor[from=natoapp6c unknown land]{inner south east} + \inheritanchor[from=natoapp6c unknown land]{north east} + \inheritanchor[from=natoapp6c unknown land]{north west} + \inheritanchor[from=natoapp6c unknown land]{south east} + \inheritanchor[from=natoapp6c unknown land]{south west} + \inheritanchor[from=natoapp6c unknown land]{north} + \inheritanchor[from=natoapp6c unknown land]{west} + \inheritanchor[from=natoapp6c unknown land]{east} + \inheritanchor[from=natoapp6c unknown land]{south} + \inheritanchor[from=natoapp6c unknown land]{upper} + \inheritanchor[from=natoapp6c unknown land]{lower} + \inheritanchor[from=natoapp6c unknown land]{left} + \inheritanchor[from=natoapp6c unknown land]{right} + \inheritanchor[from=natoapp6c unknown land]{center} + \inheritbackgroundpath[from=natoapp6c unknown land] + \inheritbehindforegroundpath[from=natoapp6c unknown land] +} \def\n@to@pp@e@y{.12} \def\n@to@pp@e@yy{.24} \def\n@to@pp@e@d#1{($(#1*\n@to@pp@e@y,0)$) circle(0.09)} @@ -1574,6 +1684,7 @@ fac/.store in=\natoapp@fac, cmd/.store in=\natoapp@cmd, ech/.store in=\natoapp@ech, + path/.store in=\n@to@ppp@th } \tikzset{ /natoapp6c/.cd, @@ -1602,6 +1713,7 @@ command/space/.style={cmd=space}, command/sub surface/.style={cmd=sub surface}, command/sea mine/.style={cmd=sub surface}, + command/dismounted/.style={cmd=dismounted}, command/none/.style={cmd=none}, } \tikzset{ @@ -1656,7 +1768,7 @@ \tikzset{% natoapp6c/.code={% \pgfkeys{/tikz/transform shape,/tikz/shape=natoapp6c} - \pgfkeys{/natoapp6c/.cd,#1}}} + \pgfkeys{/natoapp6c/.cd,path=natoapp6c/s/,#1}}} \newcounter{natoappid}\setcounter{natoappid}{0} \newif\ifn@to@pp@below\n@to@pp@belowfalse% \newif\ifn@to@pp@mod\n@to@pp@modfalse% @@ -1685,6 +1797,7 @@ \edef\frameopt{\natoapp@frame}} \n@to@pp@dbg{3}{NATO App6(c) Frame options: \meaning\frameopt}% } + \savedmacro\thisname{\def\thisname{natoapp6c}} \anchor{center} {\center} \anchor{north east}{\wg@sub@nchor{M\id}{north east}} \anchor{north west}{\wg@sub@nchor{M\id}{north west}} @@ -1699,17 +1812,26 @@ \anchor{left} {\wg@sub@nchor{M\id}{left}} \anchor{right} {\wg@sub@nchor{M\id}{right}} \anchor{echelon} {% - \n@to@pp@dbg{3}{NATO App6(c) get echelon anchor}% - \wg@sub@nchor{M\id}{north}% - \wg@tmpa=\n@to@pp@e@y cm% + \wg@sub@nchor{M}{north}% + \wg@tmpa\pgflinewidth% by 1.5% + %\wg@getscale% + %\pgfmathparse{ifthenelse(\wg@scale>0.00001,\wg@tmpa/\wg@scale,\wg@tmpa)} + %\edef\wg@a{\pgfmathresult}\pgfmathsetlength{\wg@tmpa}{\wg@a pt} + %\n@to@pp@dbg{0}{Scale is `\wg@scale' -> `\the\wg@tmpa'}% + %\divide\wg@tmpa by \wg@scale% + \advance\wg@tmpa by\n@to@pp@e@y cm% \advance\pgf@y\wg@tmpa% - }% + } \anchor{below} {% \n@to@pp@dbg{3}{NATO App6(c) get below anchor}% - \wg@sub@nchor{M\id}{south} + \wg@sub@nchor{M}{south}% \wg@tmpa=\n@to@pp@e@yy cm% - \advance\pgf@y-\wg@tmpa} + \advance\wg@tmpa-\pgflinewidth% by 1.5% + \advance\pgf@y-\wg@tmpa% + } \behindbackgroundpath{% + \wg@ignore@sub@nchortrue% + \n@to@pp@dbg{3}{NATO App6(c) Before print} \n@to@pp@dbg{3}{NATO App6(c) background path: \meaning\id ^^J ID: \meaning\natoapp@id ^^J Faction: \meaning\natoapp@fac @@ -1734,7 +1856,7 @@ %% Clip to shape in scope %% \message{^^JClipping to NATO App6(c) shape} \n@to@pp@iscliptrue% - \n@to@pp@dbg{2}{NATO App6(c) frame node M (clip)} + \n@to@pp@dbg{1}{NATO App6(c) frame node M (clip)} \pgfnode{natoapp6c \frameshape}{center}{}{M}{\pgfusepath{clip}} \n@to@pp@isclipfalse% %% Start new scope including frame key options @@ -1752,9 +1874,10 @@ \endscope% % Render mains \@ifundefined{natoapp@main}{}{ - \n@to@pp@dbg{2}{NATO App6(c) mains: \meaning\natoapp@main} + \n@to@pp@dbg{1}{NATO App6(c) mains: \meaning\natoapp@main} \begin{scope}[natoapp6c/main] - \wg@pic@all{\natoapp@main}{natoapp6c/s/}{M.center}{natoapp6c/main}% + \n@to@pp@dbg{1}{NATO symbol path `\n@to@ppp@th'} + \wg@pic@all{\natoapp@main}{\n@to@ppp@th}{M.center}{natoapp6c/main}% \end{scope}}% % Modififiers flagged \n@to@pp@modtrue @@ -1788,41 +1911,51 @@ \endpgfinterruptboundingbox \end{scope}% \fi% + \wg@ignore@sub@nchorfalse% } \behindforegroundpath{% - \n@to@pp@dbg{2}{NATO App6(c) foreground path: + \wg@ignore@sub@nchortrue% + \n@to@pp@dbg{2}{NATO App6(c) foreground path: `\meaning\id' ^^J Echelon: \meaning\natoapp@ech ^^J Symbol: \meaning\frameshape ^^J Below: \meaning\natoapp@below ^^J Frame: \meaning\frameopt} % + % \ifx\frameshape\pgfutil@empty% \n@to@pp@dbg{2}{NATO App6(c) has no frame shape!}% \else% - - \edef\tmp@opt{[\frameopt]} - \expandafter\scope\tmp@opt - \n@to@pp@dbg{2}{NATO App6(c) inner node `M\id' ===} - \pgfnode{natoapp6c \frameshape}{center}{}{M\id}{\pgfusepath{stroke}} + \edef\tmp@opt{[\frameopt]} + \expandafter\scope\tmp@opt + \edef\mid{M\id{}} + \n@to@pp@dbg{1}{NATO App6(c) inner node `M\id' ===} + \pgfnode{natoapp6c \frameshape}{center}{}{M\id}{\pgfusepath{stroke}} + \wg@ignore@sub@nchorfalse% % Put in the echelon - \@ifundefined{natoapp@ech}{}{% - \ifx\natoapp@ech\pgfutil@empty\else% - \def\args{echelon=\natoapp@ech} - \expandafter\wg@pic\args\@endwg@pic% - {natoapp6c/s/}{$(M.north)+(0,1.2*\n@to@pp@e@y)$}{natoapp6c/echelon} - \fi% - } - % Put in stuff below main - \@ifundefined{natoapp@below}{}{% - \n@to@pp@belowtrue - \begin{scope} - \wg@pic@all{\natoapp@below}{natoapp6c/s/}{% - $(M.south)+(0,-\n@to@pp@e@yy)$}{% - natoapp6c/below}% - \end{scope}% - \n@to@pp@belowfalse} - \ifnatoapp@decoy% - \scope[dash pattern=on 3\pgflinewidth off 2\pgflinewidth]% + \n@to@pp@dbg{2}{Echelon} + \@ifundefined{natoapp@ech}{}{% + \ifx\natoapp@ech\pgfutil@empty\else% + \def\args{echelon=\natoapp@ech} + \n@to@pp@dbg{3}{Make the echelon}% + \wg@nchor{\thisname}{echelon}% + \expandafter\wg@pic\args\@endwg@pic% + {natoapp6c/s/}{\pgf@x,\pgf@y}{natoapp6c/echelon} + \fi% + } + % Put in stuff below main + \n@to@pp@dbg{2}{Below} + \@ifundefined{natoapp@below}{}{% + \n@to@pp@belowtrue + \begin{scope} + \n@to@pp@dbg{3}{Make below}% + \wg@nchor{\thisname}{below}% + \wg@pic@all{\natoapp@below}{natoapp6c/s/}{\pgf@x,\pgf@y}{% + natoapp6c/below}% + \end{scope}% + \n@to@pp@belowfalse} + \n@to@pp@dbg{2}{Decoy} + \ifnatoapp@decoy% + \scope[dash pattern=on 3\pgflinewidth off 2\pgflinewidth]% \n@to@pp@dbg{1}{Drawing decoy modifier}% \wg@sub@nchor{M\id}{north east} \wg@tmpa=\pgf@x% @@ -1834,10 +1967,11 @@ \pgfpathlineto{\pgfqpoint{0cm}{\wg@tmpc}}% \pgfpathlineto{\pgfqpoint{-\wg@tmpa}{\wg@tmpb}}% \pgfusepath{stroke}% + \endscope% + \fi% \endscope% \fi% - \endscope% - \fi% + \wg@ignore@sub@nchorfalse% } } \newcommand\natoapp[1][]{% @@ -1850,13 +1984,129 @@ \@ifnextchar({\n@to@pp{#1}{#2}}{\n@to@pp{#1}{#2}()}%) } \def\n@to@pp#1#2(#3){% - %\let\name\pgfutil@empty% - %\ifx|#3|\else\edef\name{(#3)}\fi% - %\n@to@pp@dbg{3}{Arguments: #1}% - %\edef\args{[natoapp6c={#1},transform shape] \name at (#2) {}} - %\expandafter\node\args;% \node[draw,transform shape,natoapp6c={#1}] (#3) at (#2) {};% \@ifnextchar;{\@gobble}{}} +\tikzset{ + kriegspiel symbol/.code={ + \pgfkeys{% + /tikz/transform shape,% + /tikz/shape=kriegspiel symbol} + \n@to@pp@dbg{10}{Args for symbol `#1'} + \pgfkeys{/natoapp6c/.cd,% + path=kriegspiel/s/,% + #1} + } +} +\pgfdeclareshape{kriegspiel symbol}{ + \inheritsavedanchors[from=natoapp6c] + \savedmacro\frameshape{% + \def\frameshape{kriegspiel}} + \inheritanchor[from=natoapp6c]{center} + \inheritanchor[from=natoapp6c]{north east} + \inheritanchor[from=natoapp6c]{north west} + \inheritanchor[from=natoapp6c]{south east} + \inheritanchor[from=natoapp6c]{south west} + \inheritanchor[from=natoapp6c]{north} + \inheritanchor[from=natoapp6c]{west} + \inheritanchor[from=natoapp6c]{south} + \inheritanchor[from=natoapp6c]{east} + \inheritanchor[from=natoapp6c]{upper} + \inheritanchor[from=natoapp6c]{lower} + \inheritanchor[from=natoapp6c]{left} + \inheritanchor[from=natoapp6c]{right} + \anchor{echelon} {% + \wg@sub@nchor{M\id}{north west}% + \wg@tmpa=\n@to@pp@e@y cm% + \divide\wg@tmpa by 2% + \advance\pgf@y\wg@tmpa% + }% + \inheritbackgroundpath[from=natoapp6c] + \inheritbehindforegroundpath[from=natoapp6c] + \inheritbehindbackgroundpath[from=natoapp6c] + \savedmacro\thisname{\def\thisname{kriegspiel symbol}} +} +\pgfdeclareshape{natoapp6c kriegspiel}{ + \inheritsavedanchors[from=rectangle] + \inheritbackgroundpath[from=rectangle] + \savedanchor\northeast{\pgf@x=1.4cm\pgf@y=.3125cm} + \savedanchor\southwest{\pgf@x=-1.4cm\pgf@y=-.3125cm} + \inheritanchor[from=rectangle]{center} + \inheritanchor[from=rectangle]{north east} + \inheritanchor[from=rectangle]{north west} + \inheritanchor[from=rectangle]{south east} + \inheritanchor[from=rectangle]{south west} + \inheritanchor[from=rectangle]{north} + \inheritanchor[from=rectangle]{west} + \inheritanchor[from=rectangle]{south} + \inheritanchor[from=rectangle]{east} + \backgroundpath{} +} +\tikzset{ + ks debug frame/.style={ + draw=none, + %draw=magenta, + %dashed, + }, + kriegspiel/s/infantry/.pic={ + \coordinate(a) at (M.south west); + \coordinate(b) at ($(M.north east)+(0,-.2)$); + \@ifundefinedcolor{pgffillcolor}{}{ + \path[fill=pgffillcolor](a)rectangle(b);} + \path[pic actions,fill=none](a)rectangle(b); + \path[pic actions,fill=none](a)--(b); + \path[pic actions,fill=none] let + \p1=(a),\p2=(b) in (\x2,\y1)--(\x1,\y2); + \path[fill=pgfstrokecolor] let + \p2=(b) in (-.05,\y2)rectangle++(.1,.2); + \draw[ks debug frame](M.south west)rectangle(M.north east); + }, + kriegspiel/s/reconnaissance/.pic={ + \coordinate(a) at (M.south west); + \coordinate(b) at ($(M.north east)+(0,-.2)$); + \@ifundefinedcolor{pgffillcolor}{}{ + \path[fill=pgffillcolor](a)rectangle(b);} + \path[pic actions,fill=none](a)rectangle(b); + \path[fill=pgfstrokecolor] (a)--(b)--(a|-b)--cycle; + \path[fill=pgfstrokecolor] let + \p2=(b) in + (-.05,\y2)--++(0,.2)--++(-10:.4)--++(-170:.3)--(.05,\y2)--cycle; + \draw[ks debug frame](M.south west)rectangle(M.north east); + }, + kriegspiel/s/artillery/.pic={ + \coordinate(a) at (M.south west); + \coordinate(b) at ($(M.north east)+(0,-.5)$); + \@ifundefinedcolor{pgffillcolor}{}{ + \path[fill=pgffillcolor](a)rectangle(b);} + \path[pic actions,fill=none](a)rectangle(b); + \foreach \s in {-.8,0,.8}{ + \path[fill=pgfstrokecolor] let + \p2=(b),% + \n1={\y2+\pgflinewidth} in + (\s-.04,\y2)rectangle++( .08,.5) + ($(\s-.18,\n1)+(0,.05)$)rectangle++( .08,.2) + ($(\s+.18,\n1)+(0,.05)$)rectangle++(-.08,.2); + } + \draw[ks debug frame](M.south west)rectangle(M.north east); + }, + kriegspiel/s/reconnaissance artillery/.pic={ + \coordinate(a) at (M.south west); + \coordinate(b) at ($(M.north east)+(0,-.5)$); + \@ifundefinedcolor{pgffillcolor}{}{ + \path[fill=pgffillcolor](a)rectangle(b);} + \path[pic actions,fill=none](a)rectangle(b); + \path[fill=pgfstrokecolor] (a)--(b)--(a|-b)--cycle; + \foreach \s in {-.8,0,.8}{ + \path[fill=pgfstrokecolor] let + \p2=(b),% + \n1={\y2+\pgflinewidth} in + (\s-.04,\y2)rectangle++( .08,.5) + ($(\s-.18,\n1)+(0,.05)$)rectangle++( .08,.2) + ($(\s+.18,\n1)+(0,.05)$)rectangle++(-.08,.2); + } + \draw[ks debug frame](M.south west)rectangle(M.north east); + } +} + \providecommand\natoappmark[2][]{% \tikz[transform shape, scale=.25, @@ -3110,6 +3360,11 @@ natoapp6c/s/reconnaissance/.pic={% \path[draw] (M.north east)--(M.south west);}, } +\tikzset{ + natoapp6c/s/reconnaissance artillery/.pic={ + \path[draw] (M.north east)--(M.south west);, + \path[pic actions,fill=pgfstrokecolor] circle(0.2);}, +} \tikzset{% natoapp6c/s/recovery unmanned systems/.pic={% \path[draw] (-.5,.15) to [out=-80,in=180] (0,-.15) to @@ -3650,6 +3905,79 @@ (0,.15) circle(.07 and .1); }, } +\tikzset{ + pics/kriegspiel/s/.unknown/.code={ + \message{^^JTry regular `natoapp6c/s/\pgfkeyscurrentname'} + % \pic[pic actions,draw=black]{natoapp6c/s/\pgfkeyscurrentname}; + \pgfkeysalso{/tikz/pics/natoapp6c/s/\pgfkeyscurrentname/.try} + }, + ks debug frame/.style={ + draw=none, + %draw=magenta, + %dashed, + }, + kriegspiel/s/infantry/.pic={ + \coordinate(a) at (M.south west); + \coordinate(b) at ($(M.north east)+(0,-.2)$); + \@ifundefinedcolor{pgffillcolor}{}{ + \path[fill=pgffillcolor](a)rectangle(b);} + \path[pic actions,fill=none](a)rectangle(b); + \path[pic actions,fill=none](a)--(b); + \path[pic actions,fill=none] let + \p1=(a),\p2=(b) in (\x2,\y1)--(\x1,\y2); + \path[fill=pgfstrokecolor] let + \p2=(b) in (-.05,\y2)rectangle++(.1,.2); + \draw[ks debug frame](M.south west)rectangle(M.north east); + }, + kriegspiel/s/reconnaissance/.pic={ + \coordinate(a) at (M.south west); + \coordinate(b) at ($(M.north east)+(0,-.2)$); + \@ifundefinedcolor{pgffillcolor}{}{ + \path[fill=pgffillcolor](a)rectangle(b);} + \path[pic actions,fill=none](a)rectangle(b); + \path[fill=pgfstrokecolor] (a)--(b)--(a|-b)--cycle; + \path[fill=pgfstrokecolor] let + \p2=(b) in + (-.05,\y2)--++(0,.2)--++(-10:.4)--++(-170:.3)--(.05,\y2)--cycle; + \draw[ks debug frame](M.south west)rectangle(M.north east); + }, + kriegspiel/s/artillery/.pic={ + \coordinate(a) at (M.south west); + \coordinate(b) at ($(M.north east)+(0,-.5)$); + \@ifundefinedcolor{pgffillcolor}{}{ + \path[fill=pgffillcolor](a)rectangle(b);} + \path[pic actions,fill=none](a)rectangle(b); + %\path[pic actions](a)rectangle(b); + \foreach \s in {-.8,0,.8}{ + \path[fill=pgfstrokecolor] let + \p2=(b),% + \n1={\y2+\pgflinewidth} in + (\s-.04,\y2)rectangle++( .08,.5) + ($(\s-.18,\n1)+(0,.05)$)rectangle++( .08,.2) + ($(\s+.18,\n1)+(0,.05)$)rectangle++(-.08,.2); + } + \draw[ks debug frame](M.south west)rectangle(M.north east); + }, + kriegspiel/s/reconnaissance artillery/.pic={ + \coordinate(a) at (M.south west); + \coordinate(b) at ($(M.north east)+(0,-.5)$); + \@ifundefinedcolor{pgffillcolor}{}{ + \path[fill=pgffillcolor](a)rectangle(b);} + \path[pic actions,fill=none](a)rectangle(b); + %\path[pic actions](a)rectangle(b); + \path[fill=pgfstrokecolor] (a)--(b)--(a|-b)--cycle; + \foreach \s in {-.8,0,.8}{ + \path[fill=pgfstrokecolor] let + \p2=(b),% + \n1={\y2+\pgflinewidth} in + (\s-.04,\y2)rectangle++( .08,.5) + ($(\s-.18,\n1)+(0,.05)$)rectangle++( .08,.2) + ($(\s+.18,\n1)+(0,.05)$)rectangle++(-.08,.2); + } + \draw[ks debug frame](M.south west)rectangle(M.north east); + } +} + \def\n@to@pp@s@ll{ weapon=base, weapon=top, diff --git a/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.util.code.tex b/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.util.code.tex index b9a5b211723..64c3185eed9 100644 --- a/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.util.code.tex +++ b/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.util.code.tex @@ -92,9 +92,18 @@ \toks@\expandafter\expandafter\expandafter{\expandafter#1#2}% \xdef#1{\the\toks@}% \endgroup} +\def\wg@nchor#1#2{% shape name, anchor + \wg@dbg{10}{Get `#2' in `#1'}% + \expandafter\csname pgf@anchor@#1@#2\endcsname% + \wg@dbg{10}{Got `\pgf@x',`\pgf@y'} +} +\newif\ifwg@ignore@sub@nchor\wg@ignore@sub@nchorfalse% \def\wg@sub@nchor#1#2{% - \wg@dbg{3}{^^JGet `#2' in `#1'}% + \wg@dbg{10}{^^JGet `#2' in `#1'}% \@ifundefined{pgf@sh@ns@#1}{% + \ifwg@ignore@sub@nchor% + \wg@dbg{0}{WARNING: Shape `#1' not defined, anchor `#2' unknown} + \fi \pgf@x=0cm\pgf@y=0cm}{% \pgf@process{% \csname pgf@sh@ma@#1\endcsname% MW diff --git a/Master/texmf-dist/tex/latex/wargame/wargame.beach.pdf b/Master/texmf-dist/tex/latex/wargame/wargame.beach.pdf Binary files differindex bfb53528757..acaa753c50a 100644 --- a/Master/texmf-dist/tex/latex/wargame/wargame.beach.pdf +++ b/Master/texmf-dist/tex/latex/wargame/wargame.beach.pdf diff --git a/Master/texmf-dist/tex/latex/wargame/wargame.city.pdf b/Master/texmf-dist/tex/latex/wargame/wargame.city.pdf Binary files differindex 5cd11b2618d..3b0ead11ade 100644 --- a/Master/texmf-dist/tex/latex/wargame/wargame.city.pdf +++ b/Master/texmf-dist/tex/latex/wargame/wargame.city.pdf diff --git a/Master/texmf-dist/tex/latex/wargame/wargame.fields.pdf b/Master/texmf-dist/tex/latex/wargame/wargame.fields.pdf Binary files differnew file mode 100644 index 00000000000..72e3646ee29 --- /dev/null +++ b/Master/texmf-dist/tex/latex/wargame/wargame.fields.pdf diff --git a/Master/texmf-dist/tex/latex/wargame/wargame.light_woods.pdf b/Master/texmf-dist/tex/latex/wargame/wargame.light_woods.pdf Binary files differindex 1986d919468..e400e6e3302 100644 --- a/Master/texmf-dist/tex/latex/wargame/wargame.light_woods.pdf +++ b/Master/texmf-dist/tex/latex/wargame/wargame.light_woods.pdf diff --git a/Master/texmf-dist/tex/latex/wargame/wargame.mountains.pdf b/Master/texmf-dist/tex/latex/wargame/wargame.mountains.pdf Binary files differindex 4ccadc4e536..2dc669dbd88 100644 --- a/Master/texmf-dist/tex/latex/wargame/wargame.mountains.pdf +++ b/Master/texmf-dist/tex/latex/wargame/wargame.mountains.pdf diff --git a/Master/texmf-dist/tex/latex/wargame/wargame.rough.pdf b/Master/texmf-dist/tex/latex/wargame/wargame.rough.pdf Binary files differindex 9dc4d625b59..1b374a7a8f6 100644 --- a/Master/texmf-dist/tex/latex/wargame/wargame.rough.pdf +++ b/Master/texmf-dist/tex/latex/wargame/wargame.rough.pdf diff --git a/Master/texmf-dist/tex/latex/wargame/wargame.speckle.pdf b/Master/texmf-dist/tex/latex/wargame/wargame.speckle.pdf Binary files differnew file mode 100644 index 00000000000..33392c9dd89 --- /dev/null +++ b/Master/texmf-dist/tex/latex/wargame/wargame.speckle.pdf diff --git a/Master/texmf-dist/tex/latex/wargame/wargame.swamp.pdf b/Master/texmf-dist/tex/latex/wargame/wargame.swamp.pdf Binary files differindex bcaea332625..5c1f7d6b3c0 100644 --- a/Master/texmf-dist/tex/latex/wargame/wargame.swamp.pdf +++ b/Master/texmf-dist/tex/latex/wargame/wargame.swamp.pdf diff --git a/Master/texmf-dist/tex/latex/wargame/wargame.town.pdf b/Master/texmf-dist/tex/latex/wargame/wargame.town.pdf Binary files differindex 78b59768887..f07074c804a 100644 --- a/Master/texmf-dist/tex/latex/wargame/wargame.town.pdf +++ b/Master/texmf-dist/tex/latex/wargame/wargame.town.pdf diff --git a/Master/texmf-dist/tex/latex/wargame/wargame.village.pdf b/Master/texmf-dist/tex/latex/wargame/wargame.village.pdf Binary files differindex b9beeafce35..15982cd3f8c 100644 --- a/Master/texmf-dist/tex/latex/wargame/wargame.village.pdf +++ b/Master/texmf-dist/tex/latex/wargame/wargame.village.pdf diff --git a/Master/texmf-dist/tex/latex/wargame/wargame.woods.pdf b/Master/texmf-dist/tex/latex/wargame/wargame.woods.pdf Binary files differindex 9823bd52855..4c208af026d 100644 --- a/Master/texmf-dist/tex/latex/wargame/wargame.woods.pdf +++ b/Master/texmf-dist/tex/latex/wargame/wargame.woods.pdf diff --git a/Master/texmf-dist/tex/latex/wargame/wgexport.cls b/Master/texmf-dist/tex/latex/wargame/wgexport.cls index 52f2073aff5..9fcdcb22898 100644 --- a/Master/texmf-dist/tex/latex/wargame/wgexport.cls +++ b/Master/texmf-dist/tex/latex/wargame/wgexport.cls @@ -73,7 +73,7 @@ \mk@w{ \space "number": \thepage #1}% \let\oldmk@i\mk@i% \ifx#1,\relax\edef\mk@i{\mk@i\space\space}\fi} -\def\end@info{% +\def\endinfo{% \let\mk@i\oldmk@i% \mk@w{ \space \@rbchar,}} \def\wg@add@drop@margin{% @@ -104,30 +104,37 @@ \foreach \u/\m in \t{% \ifx\u\empty\else% Ignore empty cells \ifx\u\chit@blank\else% - \chit@dbg{2}{Next chit `\u' with possible multiplicity `\m'}% - \ifx\m\@empty\def\m{1}\fi% If no multiplicity defined - \ifx\u\m\def\m{1}\fi% If the same as unit - \chit@dbg{2}{Next chit `\u' multiplicity `\m'}% - %% We only make one copy of the chit, since we can duplicate - %% it in VASSAL - \info*{\u}{counter}{\x} - \nopagecolor% - \gdef\wg@drop@margin{0pt}% - \begin{tikzpicture}[chit has drop=false,#2] - \chit[\u=\ti]% - \wg@add@drop@margin% - \end{tikzpicture} - \end@info% - %% \foreach \n in {1,...,\m}{% Make a number of copies - %% \ifx\u\chit@blank% - %% \chit@dbg{3}{Ignoring blank chit:\u}% - %% \else% - %% \info{\u}{counter}{#2} - %% \begin{tikzpicture} - %% \chit[\u=\ti](\c,\r)% - %% \end{tikzpicture} - %% \fi% - %% }% + \ifx\u\chit@oob@vspacer\else% + \ifx\u\chit@oob@spacer\else% + \chit@dbg{2}{Next chit `\u' with possible multiplicity + `\m'}% + \chit@dbg{1}{Next chit `\u' not `\chit@blank', + `\chit@oob@vspacer', `\chit@oob@spacer'} + \ifx\m\@empty\def\m{1}\fi% If no multiplicity defined + \ifx\u\m\def\m{1}\fi% If the same as unit + \chit@dbg{2}{Next chit `\u' multiplicity `\m'}% + %% We only make one copy of the chit, since we can duplicate + %% it in VASSAL + \begin{info}*{\u}{counter}{\x} + \nopagecolor% + \gdef\wg@drop@margin{0pt}% + \begin{tikzpicture}[chit has drop=false,#2] + \chit[\u=\ti]% + \wg@add@drop@margin% + \end{tikzpicture} + \end{info}% + %% \foreach \n in {1,...,\m}{% Make a number of copies + %% \ifx\u\chit@blank% + %% \chit@dbg{3}{Ignoring blank chit:\u}% + %% \else% + %% \info{\u}{counter}{#2} + %% \begin{tikzpicture} + %% \chit[\u=\ti](\c,\r)% + %% \end{tikzpicture} + %% \fi% + %% }% + \fi% + \fi% \fi% \fi% }% @@ -155,39 +162,43 @@ \foreach \u/\m in \t{% \ifx\u\empty\else% Ignore empty cells \ifx\u\chit@blank\else% - \chit@dbg{2}{Next chit `\u' with possible multiplicity `\m'}% - \ifx\m\@empty\def\m{1}\fi% If not multiplicity defined - \ifx\u\m\def\m{1}\fi% If the same as unit - \chit@dbg{2}{Next chit `\u' multiplicity `\m'}% - %% Flipped chit - \edef\s{\u\space flipped}% - %% We only make one copy of the chit, since we can duplicate - %% it in VASSAL - \info*{\u}{counter}{\x}% - \nopagecolor% - \gdef\wg@drop@margin{0pt}% - \begin{tikzpicture}[chit has drop=false,#2]% - \chit[\u=\ti]% - \wg@add@drop@margin% - \end{tikzpicture}% - \end@info% - \info*{\s}{counter}{\x}% - \nopagecolor% - \begin{tikzpicture}[chit has drop=false,#2]% - \chit[\s=\ti]% - \wg@add@drop@margin% - \end{tikzpicture}% - \end@info% - %% \foreach \n in {1,...,\m}{% Make a number of copies - %% \ifx\u\chit@blank% - %% \chit@dbg{3}{Ignoring blank chit:\u}% - %% \else% - %% \info{\u}{counter}{#2} - %% \begin{tikzpicture} - %% \chit[\u=\ti](\c,\r)% - %% \end{tikzpicture} - %% \fi% - %% }% + \ifx\u\chit@oob@vspacer\else% + \ifx\u\chit@oob@spacer\else% + \chit@dbg{2}{Next chit `\u' with possible multiplicity `\m'}% + \ifx\m\@empty\def\m{1}\fi% If not multiplicity defined + \ifx\u\m\def\m{1}\fi% If the same as unit + \chit@dbg{2}{Next chit `\u' multiplicity `\m'}% + %% Flipped chit + \edef\s{\u\space flipped}% + %% We only make one copy of the chit, since we can duplicate + %% it in VASSAL + \begin{info}*{\u}{counter}{\x}% + \nopagecolor% + \gdef\wg@drop@margin{0pt}% + \begin{tikzpicture}[chit has drop=false,#2]% + \chit[\u=\ti]% + \wg@add@drop@margin% + \end{tikzpicture}% + \end{info}% + \begin{info}*{\s}{counter}{\x}% + \nopagecolor% + \begin{tikzpicture}[chit has drop=false,#2]% + \chit[\s=\ti]% + \wg@add@drop@margin% + \end{tikzpicture}% + \end{info}% + %% \foreach \n in {1,...,\m}{% Make a number of copies + %% \ifx\u\chit@blank% + %% \chit@dbg{3}{Ignoring blank chit:\u}% + %% \else% + %% \info{\u}{counter}{#2} + %% \begin{tikzpicture} + %% \chit[\u=\ti](\c,\r)% + %% \end{tikzpicture} + %% \fi% + %% }% + \fi% + \fi% \fi% \fi% }% @@ -371,10 +382,10 @@ \mk@w{ \mk@i "name": "\bd@n" }% \let\mk@i\oomk@i% \mk@w{ \mk@i\@rbchar}% - \end@info% + \endinfo% } \def\wg@gennumberm@rkers#1#2#3#4{ - \message{^^JNumbered markers: Type=`#1' Max=`#2' Category=`#3'} + \chit@dbg{2}{Numbered markers: Type=`#1' Max=`#2' Category=`#3'} \def\markers{} \def\keys{} \foreach \i in {1,...,#2}{% @@ -397,7 +408,7 @@ }% \def\@@battlemarkers[#1][#2]#3{% \wg@gennumberm@rkers{battle marker}{#3}{#2}{#1}% - \message{^^JMake a hidden unit and add to Markers category} + \chit@dbg{1}{Make a hidden unit and add to Markers category} {% \nopagecolor% \chitimages[Markers]{{wg hidden unit}}% @@ -417,7 +428,7 @@ \foreach \o/\f/\n [count=\i] in {#2}{% \ifx\n\f\def\n{\o}\fi% \ifx\o\f\def\f{white}\fi% - \message{^^JColour no \i marker `#1 \n' w/fill `\f' text `\o'}% + \chit@dbg{3}{Colour no `\i' marker `#1 \n' w/fill `\f' text `\o'}% \protected@xdef\keys{/tikz/#1 \n/.style={/tikz/#1={\o,\f}},\keys} \xdef\markers{\markers,#1 \n} }% @@ -477,22 +488,22 @@ \info{unit-icon}{icon}{}% \tikz[auto icon,scale=.7,auto icon more/.try]{% \chit[fill=#2, - symbol={[ - scale line widths, - line width=1pt, - faction=friend, - command=land, - main=infantry, - scale=1.3](0,-.15)}]}% - % - \info{layer-icon}{icon}{}% - \begin{tikzpicture}[scale=.25] - \foreach \i in {-1,0,1}{ - \scoped[shift={(0,\i*.15)}]{ - \draw[black,fill=white] (-.5,0) - --(0,.3)--(.5,0)--(0,-.3)--cycle; - } + symbol={[ + scale line widths, + line width=1pt, + faction=friend, + command=land, + main=infantry, + scale=1.3](0,-.15)}]}% + % + \info{layer-icon}{icon}{}% + \begin{tikzpicture}[scale=.25] + \foreach \i in {-1,0,1}{ + \scoped[shift={(0,\i*.15)}]{ + \draw[black,fill=white] (-.5,0) + --(0,.3)--(.5,0)--(0,-.3)--cycle; } + } \end{tikzpicture}% % \info{los-icon}{icon}{} @@ -521,8 +532,9 @@ %\node[shape=#4,transform shape,draw=none,fill=black,opacity=.5] %at (.05,-.03){}; \node[shape=#4,#2,transform shape, - chit drop - ]{\p};\wg@add@drop@margin{}}}} + chit drop, + node contents={\p} + ]{};\wg@add@drop@margin{}}}} \tikzset{ zone turn/.store in=\zone@turn, zone mult/.store in=\zone@mult diff --git a/Master/texmf-dist/tex/latex/wargame/wgexport.py b/Master/texmf-dist/tex/latex/wargame/wgexport.py index d573b63d36d..2e5dfd243de 100755 --- a/Master/texmf-dist/tex/latex/wargame/wgexport.py +++ b/Master/texmf-dist/tex/latex/wargame/wgexport.py @@ -31,6 +31,7 @@ # command.py # trait.py # withtraits.py +# traits/area.py # traits/dynamicproperty.py # traits/globalproperty.py # traits/prototype.py @@ -65,6 +66,7 @@ # save.py # vsav.py # vmod.py +# upgrade.py # exporter.py # # ==================================================================== @@ -766,6 +768,7 @@ class PredefinedSetup(GameElement): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : PredefinedSetup @@ -779,6 +782,7 @@ class PredefinedSetup(GameElement): ---------- asdict : bool If `True`, return a dictonary that maps key to `PredefinedSetup` elements. If `False`, return a list of all PredefinedSetup` children. + Returns ------- children : dict or list @@ -948,6 +952,7 @@ class GlobalOptions(GameElement): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Option @@ -1266,6 +1271,7 @@ class Prototypes(GameElement): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Prototype @@ -1279,6 +1285,7 @@ class Prototypes(GameElement): ---------- asdict : bool If `True`, return a dictonary that maps key to `Prototype` elements. If `False`, return a list of all Prototype` children. + Returns ------- children : dict or list @@ -1491,7 +1498,25 @@ class SymbolicDice(GameElement): backgroundColor = rgb(0xdd,0xdd,0xdd), # Window background windowTitleResultFormat = "$name$", # Window title windowX = '67', # Window size - windowY = '65'): + windowY = '65', + doHotkey = False, + doLoop = False, + doReport = False, + doSound = False, + hideWhenDisabled = False, + hotkeys = '', + index = False, + indexProperty = '', + indexStart = 1, + indexStep = 1, + loopCount = 1, + loopType = 'counted', + postLoopKey = '', + reportFormat = '', + soundClip = '', + untilExpression = '', + whileExpression = '' + ): super(SymbolicDice,self).\ __init__(game, self.TAG, @@ -1511,7 +1536,25 @@ class SymbolicDice(GameElement): backgroundColor = backgroundColor, windowTitleResultFormat = windowTitleResultFormat, windowX = windowX, - windowY = windowY) + windowY = windowY, + doHotkey = doHotkey, + doLoop = doLoop, + doReport = doReport, + doSound = doSound, + hideWhenDisabled = hideWhenDisabled, + hotkeys = hotkeys, + index = index, + indexProperty = indexProperty, + indexStart = indexStart, + indexStep = indexStep, + loopCount = loopCount, + loopType = loopType, + postLoopKey = postLoopKey, + reportFormat = reportFormat, + soundClip = soundClip, + untilExpression = untilExpression, + whileExpression = whileExpression) + def addDie(self,**kwargs): return self.add(SpecialDie,**kwargs) @@ -1549,6 +1592,9 @@ class SpecialDie(GameElement): def getSymbolicDice(self): return self.getParent(SymbolicDice) + + def getFaces(self): + return self.getAllElements(DieFace,single=False) registerElement(SpecialDie) @@ -1622,6 +1668,7 @@ class PieceLayers(MapElement): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : LayerControl @@ -1977,10 +2024,11 @@ class CounterDetailViewer(MapElement): emptyHexReportForma = '$LocationName$', enableHTML = True, extraTextPadding = 0, + bgColor = None, fgColor = rgb(0,0,0), - fontSize = 9, + fontSize = 11, graphicsZoom = 1.0,# Zoom on counters - hotkey = key('\n',0), + hotkey = key('\n'), layerList = '', minDisplayPieces = 2, propertyFilter = '', @@ -1993,15 +2041,19 @@ class CounterDetailViewer(MapElement): showOverlap = False, showgraph = True, showgraphsingle = False, - showtext = False, + showtext = True, showtextsingle = False, stretchWidthSummary = False, summaryReportFormat = '$LocationName$', unrotatePieces = False, version = 3, - verticalOffset = 0, - verticalTopText = 5, - zoomlevel = 1.0): # showTerrain attributes + verticalOffset = 2, + verticalTopText = 0, + zoomlevel = 1.0, + stopAfterShowing = False): # showTerrain attributes + + bg = '' if bgColor is None else bgColor + fg = '' if fgColor is None else fgColor super(CounterDetailViewer,self)\ .__init__(map,self.TAG,node=node, borderWidth = borderWidth, @@ -2015,7 +2067,8 @@ class CounterDetailViewer(MapElement): emptyHexReportForma = emptyHexReportForma, enableHTML = enableHTML, extraTextPadding = extraTextPadding, - fgColor = fgColor, + bgColor = bg, + fgColor = fg, fontSize = fontSize, graphicsZoom = graphicsZoom, hotkey = hotkey, @@ -2039,7 +2092,8 @@ class CounterDetailViewer(MapElement): version = version, verticalOffset = verticalOffset, verticalTopText = verticalTopText, - zoomlevel = zoomlevel) + zoomlevel = zoomlevel, + stopAfterShowing = stopAfterShowing) registerElement(CounterDetailViewer) @@ -2198,7 +2252,26 @@ class AtStart(MapElement): owningBoard = '', x = 0, y = 0): - '''Pieces are existing PieceSlot elements''' + '''Pieces are existing PieceSlot elements + + + Parameters + ---------- + node : xml.minidom.Node + Existing node or None + name : str + Name of node + location : str + Where the at-start element is put if `useGridLocation` + useGridLocation : bool + If true, use maps grid + owningBoard : str + Board that owns the at-start (can be empty) + x : float + Coordinate (ignored if `useGridLocation`) + y : float + Coordinate (ignored if `useGridLocation`) + ''' super(AtStart,self).\ __init__(map,self.TAG,node=node, name = name, @@ -2215,6 +2288,7 @@ class AtStart(MapElement): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Pieces @@ -2235,6 +2309,7 @@ class AtStart(MapElement): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Piece @@ -2264,7 +2339,7 @@ class AtStart(MapElement): Dictionary or list of `Piece` children ''' - return self.getElementsWithKey(PieceSlot,'entryName',asdict) + return self.getElementsByKey(PieceSlot,'entryName',asdict) registerElement(AtStart) @@ -2292,6 +2367,7 @@ class GlobalProperties(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Property @@ -2347,6 +2423,7 @@ class TurnLevel(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Level @@ -2374,6 +2451,7 @@ class TurnLevel(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Counter @@ -2387,6 +2465,7 @@ class TurnLevel(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : List @@ -2495,6 +2574,7 @@ class TurnTrack(TurnLevel): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Hotkey @@ -2626,6 +2706,7 @@ class Documentation(GameElement): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : AboutScreen @@ -2639,6 +2720,7 @@ class Documentation(GameElement): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : HelpFile @@ -2652,6 +2734,7 @@ class Documentation(GameElement): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : BrowserHelpFile @@ -2665,6 +2748,7 @@ class Documentation(GameElement): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : BrowserPDFFile @@ -2678,6 +2762,7 @@ class Documentation(GameElement): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Tutorial @@ -2909,6 +2994,7 @@ class PlayerRoster(GameElement): buttonText='Retire', buttonToolTip='Switch sides, become observer, or release faction'): '''Add a player roster to the module + Parameters ---------- doc : Element @@ -2931,6 +3017,7 @@ class PlayerRoster(GameElement): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Side @@ -3150,6 +3237,7 @@ class ChessClockControl(GameElement): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : AboutScreen @@ -3180,6 +3268,7 @@ class WidgetElement: ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Tabs @@ -3192,6 +3281,7 @@ class WidgetElement: Parameters ---------- Dictionary of attribute key-value pairs + Returns ------- element : Combo @@ -3205,6 +3295,7 @@ class WidgetElement: ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Panel @@ -3218,6 +3309,7 @@ class WidgetElement: ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : List @@ -3231,6 +3323,7 @@ class WidgetElement: ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : MapWidget @@ -3244,6 +3337,7 @@ class WidgetElement: ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Chart @@ -3257,6 +3351,7 @@ class WidgetElement: ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : PieceSlot @@ -3270,6 +3365,7 @@ class WidgetElement: ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Piece @@ -3288,6 +3384,7 @@ class WidgetElement: ---------- asdict : bool If `True`, return a dictonary that maps key to `Tab` elements. If `False`, return a list of all Tab` children. + Returns ------- children : dict or list @@ -3301,6 +3398,7 @@ class WidgetElement: ---------- asdict : bool If `True`, return a dictonary that maps key to `Tab` elements. If `False`, return a list of all Tab` children. + Returns ------- children : dict or list @@ -3314,6 +3412,7 @@ class WidgetElement: ---------- asdict : bool If `True`, return a dictonary that maps key to `List` elements. If `False`, return a list of all List` children. + Returns ------- children : dict or list @@ -3327,6 +3426,7 @@ class WidgetElement: ---------- asdict : bool If `True`, return a dictonary that maps key to `Panel` elements. If `False`, return a list of all Panel` children. + Returns ------- children : dict or list @@ -3340,6 +3440,7 @@ class WidgetElement: ---------- asdict : bool If `True`, return a dictonary that maps key to `MapWidget` elements. If `False`, return a list of all MapWidget` children. + Returns ------- children : dict or list @@ -3353,6 +3454,7 @@ class WidgetElement: ---------- asdict : bool If `True`, return a dictonary that maps key to `Chart` elements. If `False`, return a list of all Chart` children. + Returns ------- children : dict or list @@ -3366,6 +3468,7 @@ class WidgetElement: ---------- asdict : bool If `True`, return a dictonary that maps key to `PieceSlot` elements. If `False`, return a list of all PieceSlot` children. + Returns ------- children : dict or list @@ -3470,6 +3573,7 @@ class MapWidget(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : WidgetMap @@ -3483,6 +3587,7 @@ class MapWidget(Element): ---------- asdict : bool If `True`, return a dictonary that maps key to `WidgetMap` elements. If `False`, return a list of all WidgetMap` children. + Returns ------- children : dict or list @@ -3679,6 +3784,7 @@ class HexGrid(BaseGrid): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Numbering @@ -3720,8 +3826,8 @@ class SquareGrid(BaseGrid): dx = dx, dy = dy, edgesLegal = edgesLegal, - x0 = 0, - y0 = int(0.4*RECT_HEIGHT), + x0 = x0, + y0 = y0, **kwargs) def addNumbering(self,**kwargs): '''Add a `Numbering` element to this @@ -3730,6 +3836,7 @@ class SquareGrid(BaseGrid): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Numbering @@ -3892,6 +3999,7 @@ class RegionGrid(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Region @@ -4017,6 +4125,7 @@ class ZonedGrid(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Highlighter @@ -4031,6 +4140,7 @@ class ZonedGrid(Element): single : bool If `True`, there can be only one `Highligter` child, otherwise fail. If `False` return all `Highligter` children in this element + Returns ------- children : list @@ -4044,6 +4154,7 @@ class ZonedGrid(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Zone @@ -4057,6 +4168,7 @@ class ZonedGrid(Element): ---------- asdict : bool If `True`, return a dictonary that maps key to `Zone` elements. If `False`, return a list of all Zone` children. + Returns ------- children : dict or list @@ -4080,6 +4192,7 @@ class ZonedGridHighlighter(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : ZoneHighlight @@ -4093,6 +4206,7 @@ class ZonedGridHighlighter(Element): ---------- asdict : bool If `True`, return a dictonary that maps key to `Zone` elements. If `False`, return a list of all Zone` children. + Returns ------- children : dict or list @@ -4210,6 +4324,7 @@ class Zone(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : HexGrid @@ -4223,6 +4338,7 @@ class Zone(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : SquareGrid @@ -4236,6 +4352,7 @@ class Zone(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : RegionGrid @@ -4249,6 +4366,7 @@ class Zone(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Property @@ -4263,6 +4381,7 @@ class Zone(Element): single : bool If `True`, there can be only one `HexGrid` child, otherwise fail. If `False` return all `HexGrid` children in this element + Returns ------- children : list @@ -4277,6 +4396,7 @@ class Zone(Element): single : bool If `True`, there can be only one `SquareGrid` child, otherwise fail. If `False` return all `SquareGrid` children in this element + Returns ------- children : list @@ -4291,6 +4411,7 @@ class Zone(Element): single : bool If `True`, there can be only one `RegionGrid` child, otherwise fail. If `False` return all `RegionGrid` children in this element + Returns ------- children : list @@ -4305,6 +4426,7 @@ class Zone(Element): single : bool If `True`, there can be only one `Grid` child, otherwise fail. If `False` return all `Grid` children in this element + Returns ------- children : list @@ -4323,8 +4445,6 @@ class Zone(Element): def getProperties(self): '''Get all `Property` element from this - Parameters - ---------- Returns ------- children : dict @@ -4339,6 +4459,7 @@ class Zone(Element): c = pp.split(',') r.append([int(c[0]),int(c[1])]) return r + def getBB(self): from functools import reduce path = self.getPath() @@ -4393,6 +4514,7 @@ class BoardPicker(MapElement): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Setup @@ -4411,6 +4533,7 @@ class BoardPicker(MapElement): single : bool If `True`, there can be only one `Setup` child, otherwise fail. If `False` return all `Setup` children in this element + Returns ------- children : list @@ -4424,6 +4547,7 @@ class BoardPicker(MapElement): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Board @@ -4437,6 +4561,7 @@ class BoardPicker(MapElement): ---------- asdict : bool If `True`, return a dictonary that maps key to `Board` elements. If `False`, return a list of all Board` children. + Returns ------- children : dict or list @@ -4512,6 +4637,7 @@ class Board(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : ZonedGrid @@ -4526,6 +4652,7 @@ class Board(Element): single : bool If `True`, there can be only one `ZonedGrid` child, otherwise fail. If `False` return all `ZonedGrid` children in this element + Returns ------- children : list @@ -4539,6 +4666,7 @@ class Board(Element): ---------- asdict : bool If `True`, return a dictonary that maps key to `Zone` elements. If `False`, return a list of all Zone` children. + Returns ------- children : dict or list @@ -4596,6 +4724,65 @@ class BaseMap(Element): moveWithinFormat = '$pieceName$ moves $previousLocation$ → $location$ *', showKey = '', thickness = '3'): + '''Create a map + + Parameters + ---------- + doc : xml.minidom.Document + Parent document + tag : str + XML tag + node : xml.minidom.Node or None + Existing node or None + mapName : str + Name of map + allowMultiple : bool + Allow multiple boards + backgroundcolor : color + Bckground color + buttonName : str + Name on button to show map = '', + changeFormat : + Message format to show on changes + color : color + Color of selected pieces + createFormat : str + Message format when creating a piece + edgeHeight : int + Height of edge (margin) + edgeWidth : int + Width of edge (margin) + hideKey : Key + Hot-key or key-command to hide map + hotkey : Key + Hot-key or key-command to show map + icon : path + Icon image + launch : bool + Show on launch + markMoved : str + Show moved + markUnmovedHotkey : key + Remove moved markers + markUnmovedIcon : path + Icon for unmoved + markUnmovedReport : str + Message when marking as unmoved + markUnmovedText : str + Text on button + markUnmovedTooltip : str + Tooltip on button + moveKey : key + Key to set moved marker + moveToFormat : str + Message format when moving + moveWithinFormat : str + Message when moving within map + showKey : str, + Key to show map + thickness : int + Thickness of line around selected pieces + ''' super(BaseMap,self).__init__(doc,tag,node=node, allowMultiple = allowMultiple, backgroundcolor = backgroundcolor, @@ -4632,6 +4819,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Picker @@ -4646,6 +4834,7 @@ class BaseMap(Element): single : bool If `True`, there can be only one `BoardPicker` child, otherwise fail. If `False` return all `BoardPicker` children in this element + Returns ------- children : list @@ -4660,6 +4849,7 @@ class BaseMap(Element): single : bool If `True`, there can be only one `BoardPicker` child, otherwise fail. If `False` return all `BoardPicker` children in this element + Returns ------- children : list @@ -4674,6 +4864,7 @@ class BaseMap(Element): single : bool If `True`, there can be only one `StackMetric` child, otherwise fail. If `False` return all `StackMetric` children in this element + Returns ------- children : list @@ -4688,6 +4879,7 @@ class BaseMap(Element): single : bool If `True`, there can be only one `ImageSaver` child, otherwise fail. If `False` return all `ImageSaver` children in this element + Returns ------- children : list @@ -4702,6 +4894,7 @@ class BaseMap(Element): single : bool If `True`, there can be only one `TextSaver` child, otherwise fail. If `False` return all `TextSaver` children in this element + Returns ------- children : list @@ -4716,6 +4909,7 @@ class BaseMap(Element): single : bool If `True`, there can be only one `ForwardToChatter` child, otherwise fail. If `False` return all `ForwardToChatter` children in this element + Returns ------- children : list @@ -4730,6 +4924,7 @@ class BaseMap(Element): single : bool If `True`, there can be only one `MenuDi` child, otherwise fail. If `False` return all `MenuDi` children in this element + Returns ------- children : list @@ -4744,6 +4939,7 @@ class BaseMap(Element): single : bool If `True`, there can be only one `MapCenterer` child, otherwise fail. If `False` return all `MapCenterer` children in this element + Returns ------- children : list @@ -4758,6 +4954,7 @@ class BaseMap(Element): single : bool If `True`, there can be only one `StackExpander` child, otherwise fail. If `False` return all `StackExpander` children in this element + Returns ------- children : list @@ -4772,6 +4969,7 @@ class BaseMap(Element): single : bool If `True`, there can be only one `PieceMover` child, otherwise fail. If `False` return all `PieceMover` children in this element + Returns ------- children : list @@ -4786,6 +4984,7 @@ class BaseMap(Element): single : bool If `True`, there can be only one `SelectionHighlighter` child, otherwise fail. If `False` return all `SelectionHighlighter` children in this element + Returns ------- children : list @@ -4802,6 +5001,7 @@ class BaseMap(Element): single : bool If `True`, there can be only one `HighlightLa` child, otherwise fail. If `False` return all `HighlightLa` children in this element + Returns ------- children : list @@ -4816,6 +5016,7 @@ class BaseMap(Element): single : bool If `True`, there can be only one `CounterDetailViewer` child, otherwise fail. If `False` return all `CounterDetailViewer` children in this element + Returns ------- children : list @@ -4830,6 +5031,7 @@ class BaseMap(Element): single : bool If `True`, there can be only one `GlobalMap` child, otherwise fail. If `False` return all `GlobalMap` children in this element + Returns ------- children : list @@ -4844,6 +5046,7 @@ class BaseMap(Element): single : bool If `True`, there can be only one `Zoomer` child, otherwise fail. If `False` return all `Zoomer` children in this element + Returns ------- children : list @@ -4858,6 +5061,7 @@ class BaseMap(Element): single : bool If `True`, there can be only one `HidePiece` child, otherwise fail. If `False` return all `HidePiece` children in this element + Returns ------- children : list @@ -4871,6 +5075,7 @@ class BaseMap(Element): ---------- asdict : bool If `True`, return a dictonary that maps key to `MassKey` elements. If `False`, return a list of all MassKey` children. + Returns ------- children : dict or list @@ -4885,6 +5090,7 @@ class BaseMap(Element): single : bool If `True`, there can be only one `Flare` child, otherwise fail. If `False` return all `Flare` children in this element + Returns ------- children : list @@ -4899,6 +5105,7 @@ class BaseMap(Element): single : bool If `True`, there can be only one `AtStart` child, otherwise fail. If `False` return all `AtStart` children in this element + Returns ------- children : list @@ -4912,6 +5119,7 @@ class BaseMap(Element): ---------- asdict : bool If `True`, return a dictonary that maps key to `Board` elements. If `False`, return a list of all Board` children. + Returns ------- children : dict or list @@ -4947,6 +5155,7 @@ class BaseMap(Element): elements. If `False`, return a list of all Board` children. + Returns ------- children : dict or list @@ -4964,6 +5173,7 @@ class BaseMap(Element): elements. If `False`, return a list of all Board` children. + Returns ------- children : dict or list @@ -4978,6 +5188,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : BoardPicker @@ -4991,6 +5202,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : StackMetrics @@ -5004,6 +5216,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : ImageSaver @@ -5017,6 +5230,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : TextSaver @@ -5030,6 +5244,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : ForwardToChatter @@ -5043,6 +5258,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : MenuDisplayer @@ -5056,6 +5272,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : MapCenterer @@ -5069,6 +5286,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : StackExpander @@ -5082,6 +5300,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : PieceMover @@ -5095,6 +5314,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : SelectionHighlighters @@ -5108,6 +5328,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : KeyBufferer @@ -5121,6 +5342,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : HighlightLastMoved @@ -5134,6 +5356,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : CounterDetailViewer @@ -5147,6 +5370,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : GlobalMap @@ -5160,6 +5384,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Zoomer @@ -5173,6 +5398,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : HidePiecesButton @@ -5186,6 +5412,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : MassKey @@ -5199,6 +5426,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : StartupMassKey @@ -5212,6 +5440,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Flare @@ -5225,6 +5454,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : AtStart @@ -5240,6 +5470,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : PieceLayers @@ -5253,6 +5484,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Menu @@ -5266,6 +5498,7 @@ class BaseMap(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Menu @@ -5377,6 +5610,7 @@ class ChartWindow(GameElement,WidgetElement): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Chart @@ -5388,12 +5622,16 @@ class ChartWindow(GameElement,WidgetElement): Parameters ---------- - asdict : bool - If `True`, return a dictonary that maps key to `Chart` elements. If `False`, return a list of all Chart` children. + asdict : bool + If `True`, return a dictonary that maps key to `Chart` + elements. If `False`, return a list of all Chart` + children. + Returns ------- children : dict or list Dictionary or list of `Chart` children + ''' return self.getElementsById(Chart,'chartName',asdict=asdict) @@ -5440,11 +5678,12 @@ class AddCommand(Command): class Trait: known_traits = [] def __init__(self): - '''Base class for trait capture. Unlike the Element classes, - this actually holds state that isn't reflected elsewhere in - the DOM. This means that the data here is local to the - object. So when we do - + '''Base class for trait capture. + + Unlike the Element classes, this actually holds state that + isn't reflected elsewhere in the DOM. This means that the + data here is local to the object. So when we do + piece = foo.getPieceSlots()[0] traits = p.getTraits() for trait in traits: @@ -5467,6 +5706,9 @@ class Trait: traits.append(MarkTrait('Hello','World;) piece.setTraits(traits) + .. include:: ../../vassal/traits/README.md + :parser: myst_parser.sphinx_ + ''' self._type = None self._state = None @@ -5479,6 +5721,7 @@ class Trait: ''' self._type = list(kwargs.values()) self._tnames = list(kwargs.keys()) + def setState(self,**kwargs): '''Set states. Dictionary of names and values. Dictonary keys defines how we access the fields, which is internal here. @@ -5487,7 +5730,6 @@ class Trait: self._state = list(kwargs.values()) self._snames = list(kwargs.keys()) - def __getitem__(self,key): '''Look up item in either type or state''' try: @@ -5574,39 +5816,78 @@ class Trait: return [k.replace('\\'+f'{sep}',f'{sep}') for k in ks] @classmethod - def flatten(cls,traits,game=None,prototypes=None): + def flatten(cls,traits,game=None,prototypes=None,verbose=False): if prototypes is None: if game is None: print(f'Warning: Game or prototypes not passed') return None prototypes = game.getPrototypes()[0].getPrototypes() - ret = [] - return cls._flatten(traits,ret,prototypes,False) + if len(traits) < 1: return None + + basic = None + if traits[-1].ID == BasicTrait.ID: + basic = traits.pop() + + if verbose: + print(f'Piece {basic["name"]}') + + ret = cls._flatten(traits,prototypes,' ',verbose) + ret.append(basic) + + return ret @classmethod - def _flatten(cls,traits,ret,prototypes,nobasic=True): + def _flatten(cls,traits,prototypes,ind,verbose): '''Expand all prototype traits in traits''' + ret = [] for trait in traits: # Ignore recursive basic traits - if nobasic and trait.ID == BasicTrait.ID: + if trait.ID == BasicTrait.ID: continue # Add normal traits if trait.ID != PrototypeTrait.ID: + if verbose: + print(f'{ind}Adding trait "{trait.ID}"') + ret.append(trait) continue # Find prototype - proto = prototypes.get(trait['name'],None) + name = trait['name'] + proto = prototypes.get(name,None) if proto is None: - print(f'Warning, prototype {trait["name"]} not found') + if name != ' prototype': + print(f'{ind}Warning, prototype {name} not found') continue + if verbose: + print(f'{ind}Expanding prototype "{name}"') + # Recursive call to add prototype traits (and possibly # more recursive calls - cls._flatten(proto.getTraits(), ret, prototypes,not nobasic) + ret.extend(cls._flatten(proto.getTraits(), prototypes, + ind+' ',verbose)) return ret + + def print(self,file=None): + if file is None: + from sys import stdout + file = stdout + + nt = max([len(i) for i in self._tnames]) + ns = max([len(i) for i in self._snames]) + nw = max(nt,ns) + + print(f'Trait ID={self.ID}',file=file) + print(f' Type:', file=file) + for n,v in zip(self._tnames,self._type): + print(f' {n:<{nw}s}: {v}',file=file) + print(f' State:', file=file) + for n,v in zip(self._snames,self._state): + print(f' {n:<{nw}s}: {v}',file=file) + # # EOF # @@ -5660,6 +5941,7 @@ class WithTraits(Element): traits.append(trait) self.setTraits(*traits) + def getTraits(self): '''Get all element traits as objects. This decodes the trait definitions. This is useful if we have read the element from @@ -5680,9 +5962,109 @@ class WithTraits(Element): The decoded traits ''' + code = self._node.childNodes[0].nodeValue + return self.decodeAdd(code) + + def encodedStates(self): + from re import split + + code = self._node.childNodes[0].nodeValue + cmd, iden, typ, sta = split(fr'(?<!\\)/',code) #code.split('/') + + return sta + + def decodeStates(self,code,verbose=False): + from re import split + + newstates, oldstates = split(fr'(?<!\\)/',code)#code.split('/') + + splitit = lambda l : \ + [s.strip('\\').split(';') for s in l.split(r' ')] + + newstates = splitit(newstates) + oldstates = splitit(oldstates) + + traits = self.getTraits() + + if len(traits) != len(newstates): + print(f'Piece has {len(traits)} traits but got ' + f'{len(newstates)} states') + + for trait, state in zip(traits,newstates): + trait._state = state; + # print(trait.ID) + # for n,s in zip(trait._snames,trait._state): + # print(f' {n:30s}: {s}') + + self.setTraits(*traits) + + def copyStates(self,other,verbose=False): + straits = other.getTraits() + dtraits = self.getTraits() + + matches = 0 + for strait in straits: + if len(strait._state) < 1: + continue + + cand = [] + ttrait = None + for dtrait in dtraits: + if dtrait.ID == strait.ID: + cand.append(dtrait) + + if verbose and len(cand) < 1: + print(f'Could not find candidate for {strait.ID}') + continue + + if len(cand) == 1: + ttrait = cand[0] + + else: + # print(f'Got {len(cand)} candidiate targets {strait.ID}') + + best = None + count = 0 + types = strait._type + for c in cand: + cnt = sum([ct == t for ct,t in zip(c._type, types)]) + if cnt > count: + best = c + count = cnt + + if verbose and best is None: + print(f'No candidate for {strait.ID} {len(again)}') + + if verbose and count+2 < len(types): + print(f'Ambigious candidate for {strait.ID} ' + f'({count} match out of {len(types)})') + #print(best._type) + #print(types) + + ttrait = best + + if ttrait is None: + continue + + ttrait._state = strait._state + matches += 1 + # print(ttrait.ID) + # for n,s in zip(ttrait._snames,ttrait._state): + # print(f' {n:30s}: {s}') + + if verbose: + print(f'Got {matches} matches out of {len(dtraits)}') + + self.setTraits(*dtraits) + + + def decodeAdd(self,code,verbose=False): + '''Try to decode make a piece from a piece of state code''' from re import split - code = self._node.childNodes[0].nodeValue + cmd, iden, typ, sta = split(fr'(?<!\\)/',code) #code.split('/') + # print(cmd,iden,typ,sta) + types = typ.split(r' ') states = sta.split(r' ') types = [t.strip('\\').split(';') for t in types] @@ -5705,19 +6087,11 @@ class WithTraits(Element): print(f'Warning: Unknown trait {tid}') return traits - - def setTraits(self,*traits,iden='null'): - '''Set traits on this element. This encodes the traits into - this object. - - Parameters - ---------- - traits : tuple of Trait objects - The traits to set on this object. - iden : str - Identifier - ''' + def encodeAdd(self,*traits,iden='null',verbose=False): + '''Encodes type and states''' + if len(traits) < 1: return '' + last = traits[-1] # A little hackish to use the name of the class, but needed # because of imports into patch scripts. @@ -5740,9 +6114,30 @@ class WithTraits(Element): tpe = WithTraits.encodeParts(*types) state = WithTraits.encodeParts(*states) add = AddCommand(str(iden),tpe,state) + return add.cmd + + + def setTraits(self,*traits,iden='null'): + '''Set traits on this element. This encodes the traits into + this object. + + Parameters + ---------- + traits : tuple of Trait objects + The traits to set on this object. + iden : str + Identifier + + ''' + add = self.encodeAdd(*traits,iden=iden) + if self._node is None: + from xml.dom.minidom import Element, Text + self._node = Element(self.TAG) + self._node.appendChild(Text()) + if len(self._node.childNodes) < 1: self.addText('') - self._node.childNodes[0].nodeValue = add.cmd + self._node.childNodes[0].nodeValue = add def removeTrait(self,ID,key=None,value=None,verbose=False): '''Remove a trait from this object. @@ -5896,6 +6291,7 @@ class PieceSlot(WithTraits): width = width, icon = icon) + def clone(self,parent): '''Adds copy of self to parent, possibly with new GPID''' game = self.getParentOfClass([Game]) @@ -5951,6 +6347,53 @@ registerElement(Prototype) # EOF # # ==================================================================== +# From traits/area.py + +class AreaTrait(Trait): + ID = 'AreaOfEffect' + def __init__(self, + transparancyColor = rgb(0x77,0x77,0x77), + transparancyLevel = 30, + radius = 1, + alwaysActive = False, + activateCommand = 'Toggle area of effect', + activateKey = key('A'), # Ctrl-A + mapShaderName = '', + fixedRadius = True, + radiusMarker = '', # Property + description = 'Show area of effect', + name = 'EffectArea', + onMenuText = '', # Show area of effect + onKey = '', # key('A') + offMenuText = '', # Hide area of effect + offKey = '', # key(A,SHIFT) + globallyVisible = True): + super(AreaTrait,self).__init__() + self.setType( + transparancyColor = transparancyColor, + transparancyLevel = int(transparancyLevel), + radius = radius, + alwaysActive = alwaysActive, + activateCommand = activateCommand, + activateKey = activateKey, + mapShaderName = mapShaderName, + fixedRadius = fixedRadius, + radiusMarker = radiusMarker, + description = description, + name = name, + onMenuText = onMenuText, + onKey = onKey, + offMenuText = offMenuText, + offKey = offKey, + globallyVisible = globallyVisible + ) + self.setState(active = alwaysActive or not globallyVisible) + +Trait.known_traits.append(AreaTrait) +# +# EOF +# +# ==================================================================== # From traits/dynamicproperty.py # -------------------------------------------------------------------- @@ -5990,9 +6433,9 @@ class ChangePropertyTrait(Trait): # print(cmd) com = cmd[0] + ':' + cmd[1].replace(',',r'\,') + ':' + cmd[2] if cmd[2] == self.DIRECT: - com += f'\,'+cmd[3].replace(',',r'\\,').replace(':',r'\:') + com += r'\,'+cmd[3].replace(',',r'\\,').replace(':',r'\:') elif cmd[2] == self.INCREMENT: - com += f'\,'+cmd[3].replace(',',r'\\,').replace(':',r'\:') + com += r'\,'+cmd[3].replace(',',r'\\,').replace(':',r'\:') cmds.append(com) # print(cmds) return ','.join(cmds) @@ -6758,7 +7201,7 @@ class MaskTrait(Trait): autoPeek = True, dealKey = '', dealExpr = ''): - '''Create a mark trait (static property)''' + '''Create a masking trait''' super(MaskTrait,self).__init__() disp = displayStyle if displayStyle == self.PEEK: @@ -6822,25 +7265,32 @@ class TrailTrait(Trait): inactiveOpacity = 50, edgesBuffer = 20, displayBuffer = 30, - lineWidth = 1, + lineWidth = 5, turnOn = '', turnOff = '', reset = '', - description = ''): + description = 'Enable or disable movement trail'): ''' Create a movement trail trait ( VASSAL.counters.Footprint)''' super(TrailTrait,self).__init__() + lw = (lineWidth + if isinstance(lineWidth,str) and lineWidth.startswith('{') else + int(lineWidth)) + ra = (radius + if isinstance(radius,str) and radius.startswith('{') else + int(radius)) + self.setType(key = key,# ENABLE KEY name = name,# MENU localVisible = localVisible,# LOCAL VISABLE globalVisible = globalVisible,# GLOBAL VISABLE - radius = radius,# RADIUS + radius = ra,# RADIUS fillColor = fillColor,# FILL COLOR lineColor = lineColor,# LINE COLOR activeOpacity = activeOpacity,# ACTIVE OPACITY inactiveOpacity = inactiveOpacity,# INACTIVE OPACITY edgesBuffer = edgesBuffer,# EDGES BUFFER displayBuffer = displayBuffer,# DISPLAY BUFFER - lineWidth = int(lineWidth),# LINE WIDTH + lineWidth = lw,# LINE WIDTH turnOn = turnOn,# TURN ON KEY turnOff = turnOff,# TURN OFF KEY reset = reset,# RESET KEY @@ -7127,13 +7577,19 @@ class NonRectangleTrait(Trait): self.setState() @classmethod - def getShape(cls,image): - if image is None: + def getShape(cls,buffer): + if buffer is None: return [] - from PIL import Image from io import BytesIO + image = buffer + if image[:5] == b'<?xml': + from cairosvg import svg2png + image = svg2png(image) + + from PIL import Image + code = [] with Image.open(BytesIO(image)) as img: alp = img.getchannel('A') # Alpha channel @@ -7277,6 +7733,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : BasicCommandEncoder @@ -7290,6 +7747,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : GlobalTranslatableMessages @@ -7303,6 +7761,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : PlayerRoster @@ -7316,6 +7775,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : PlayerRoster @@ -7329,6 +7789,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Language @@ -7342,6 +7803,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Chatter @@ -7355,6 +7817,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : KeyNamer @@ -7368,6 +7831,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Notes @@ -7381,6 +7845,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Language @@ -7394,6 +7859,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Chatter @@ -7407,6 +7873,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : KeyNamer @@ -7420,6 +7887,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : GlobalProperties @@ -7433,6 +7901,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : GlobalOptions @@ -7446,6 +7915,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : TurnTrack @@ -7459,6 +7929,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Documentation @@ -7472,6 +7943,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Prototypes @@ -7485,6 +7957,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : PieceWindow @@ -7498,6 +7971,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : ChartWindow @@ -7511,6 +7985,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Inventory @@ -7524,6 +7999,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Map @@ -7537,6 +8013,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : DiceButton @@ -7550,6 +8027,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : PredefinedSetup @@ -7563,6 +8041,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : GameMassKey @@ -7576,6 +8055,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : StartupMassKey @@ -7589,6 +8069,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Menu @@ -7602,6 +8083,7 @@ class Game(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : SymbolicDice @@ -7619,6 +8101,7 @@ class Game(Element): single : bool If `True`, there can be only one `GlobalPropertie` child, otherwise fail. If `False` return all `GlobalPropertie` children in this element + Returns ------- children : list @@ -7633,6 +8116,7 @@ class Game(Element): single : bool If `True`, there can be only one `Ba` child, otherwise fail. If `False` return all `Ba` children in this element + Returns ------- children : list @@ -7647,6 +8131,7 @@ class Game(Element): single : bool If `True`, there can be only one `GlobalTranslatableMessage` child, otherwise fail. If `False` return all `GlobalTranslatableMessage` children in this element + Returns ------- children : list @@ -7661,6 +8146,7 @@ class Game(Element): single : bool If `True`, there can be only one `Language` child, otherwise fail. If `False` return all `Language` children in this element + Returns ------- children : list @@ -7675,6 +8161,7 @@ class Game(Element): single : bool If `True`, there can be only one `Language` child, otherwise fail. If `False` return all `Language` children in this element + Returns ------- children : list @@ -7689,6 +8176,7 @@ class Game(Element): single : bool If `True`, there can be only one `Chatter` child, otherwise fail. If `False` return all `Chatter` children in this element + Returns ------- children : list @@ -7703,6 +8191,7 @@ class Game(Element): single : bool If `True`, there can be only one `KeyNamer` child, otherwise fail. If `False` return all `KeyNamer` children in this element + Returns ------- children : list @@ -7717,6 +8206,7 @@ class Game(Element): single : bool If `True`, there can be only one `Documentation` child, otherwise fail. If `False` return all `Documentation` children in this element + Returns ------- children : list @@ -7733,6 +8223,7 @@ class Game(Element): single : bool If `True`, there can be only one `Prototypes` child, otherwise fail. If `False` return all `Prototypes` children in this element + Returns ------- children : list @@ -7748,6 +8239,7 @@ class Game(Element): single : bool If `True`, there can be only one `PlayerRo` child, otherwise fail. If `False` return all `PlayerRo` children in this element + Returns ------- children : list @@ -7762,6 +8254,7 @@ class Game(Element): single : bool If `True`, there can be only one `GlobalOption` child, otherwise fail. If `False` return all `GlobalOption` children in this element + Returns ------- children : list @@ -7775,6 +8268,7 @@ class Game(Element): ---------- asdict : bool If `True`, return a dictonary that maps key to `Inventorie` elements. If `False`, return a list of all Inventorie` children. + Returns ------- children : dict or list @@ -7788,6 +8282,7 @@ class Game(Element): ---------- asdict : bool If `True`, return a dictonary that maps key to `PieceWindow` elements. If `False`, return a list of all PieceWindow` children. + Returns ------- children : dict or list @@ -7801,6 +8296,7 @@ class Game(Element): ---------- asdict : bool If `True`, return a dictonary that maps key to `ChartWindow` elements. If `False`, return a list of all ChartWindow` children. + Returns ------- children : dict or list @@ -7814,6 +8310,7 @@ class Game(Element): ---------- asdict : bool If `True`, return a dictonary that maps key to `DiceButton` elements. If `False`, return a list of all DiceButton` children. + Returns ------- children : dict or list @@ -7827,6 +8324,7 @@ class Game(Element): ---------- asdict : bool If `True`, return a dictonary that maps key to `PredefinedSetup` elements. If `False`, return a list of all PredefinedSetup` children. + Returns ------- children : dict or list @@ -7841,6 +8339,7 @@ class Game(Element): single : bool If `True`, there can be only one `Note` child, otherwise fail. If `False` return all `Note` children in this element + Returns ------- children : list @@ -7854,6 +8353,7 @@ class Game(Element): ---------- asdict : bool If `True`, return a dictonary that maps key to `TurnTrack` elements. If `False`, return a list of all TurnTrack` children. + Returns ------- children : dict or list @@ -7867,6 +8367,7 @@ class Game(Element): ---------- asdict : bool If `True`, return a dictonary that maps key to `Piece` elements. If `False`, return a list of all Piece` children. + Returns ------- children : dict or list @@ -7880,6 +8381,7 @@ class Game(Element): ---------- asdict : bool If `True`, return a dictonary that maps key to `SpecificPiece` elements. If `False`, return a list of all SpecificPiece` children. + Returns ------- children : dict or list @@ -7896,6 +8398,7 @@ class Game(Element): ---------- asdict : bool If `True`, return a dictonary that maps key to `WidgetMap` elements. If `False`, return a list of all WidgetMap` children. + Returns ------- children : dict or list @@ -7909,6 +8412,7 @@ class Game(Element): ---------- asdict : bool If `True`, return a dictonary that maps key to `Map` elements. If `False`, return a list of all Map` children. + Returns ------- children : dict or list @@ -7931,6 +8435,7 @@ class Game(Element): elements. If `False`, return a list of all Board` children. + Returns ------- children : dict or list @@ -7948,6 +8453,7 @@ class Game(Element): elements. If `False`, return a list of all Board` children. + Returns ------- children : dict or list @@ -7965,6 +8471,7 @@ class Game(Element): elements. If `False`, return a list of all Board` children. + Returns ------- children : dict or list @@ -7982,6 +8489,7 @@ class Game(Element): elements. If `False`, return a list of all Board` children. + Returns ------- children : dict or list @@ -8014,6 +8522,7 @@ class Game(Element): single : bool If `True`, there can be only one `AtStart` child, otherwise fail. If `False` return all `AtStart` children in this element + Returns ------- children : list @@ -8057,6 +8566,7 @@ class BuildFile(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Game @@ -8096,6 +8606,7 @@ class Data(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Version @@ -8109,6 +8620,7 @@ class Data(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : VASSALVersion @@ -8122,6 +8634,7 @@ class Data(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Name @@ -8135,6 +8648,7 @@ class Data(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Description @@ -8148,6 +8662,7 @@ class Data(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : DateSaved @@ -8162,6 +8677,7 @@ class Data(Element): single : bool If `True`, there can be only one `Version` child, otherwise fail. If `False` return all `Version` children in this element + Returns ------- children : list @@ -8177,6 +8693,7 @@ class Data(Element): If `True`, there can be only one `VASSALVersion` child, otherwise fail. If `False` return all `VASSALVersion` children in this element + Returns ------- children : list @@ -8195,6 +8712,7 @@ class Data(Element): If `True`, there can be only one `Description` child, otherwise fail. If `False` return all `De` children in this element + Returns ------- children : list @@ -8210,6 +8728,7 @@ class Data(Element): single : bool If `True`, there can be only one `DateSaved` child, otherwise fail. If `False` return all `DateSaved` children in this element + Returns ------- children : list @@ -8289,6 +8808,7 @@ class ModuleData(Element): ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Data @@ -8928,6 +9448,18 @@ class VMod: def fileName(self): '''Get name of VMod file''' return self._vmod.filename + + def replaceFiles(self,**files): + '''Replace existing files with new files + + Parameters + ---------- + files : dict + Dictionary that maps file name to content + ''' + self.removeFiles(*list(files.keys())) + + self.addFiles(**files); def addFiles(self,**files): '''Add a set of files to this @@ -8949,6 +9481,7 @@ class VMod: File name in module content : str File content + Returns ------- element : File @@ -8963,6 +9496,7 @@ class VMod: ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : ExternalFile @@ -8974,6 +9508,14 @@ class VMod: def getFileNames(self): '''Get all filenames in module''' return self._vmod.namelist() + + def getFileMapping(self): + '''Get mapping from short name to full archive name''' + from pathlib import Path + + names = self.getFileNames() + + return {Path(p).stem: str(p) for p in names} def getFiles(self,*filenames): '''Return named files as a dictionary. @@ -8982,6 +9524,7 @@ class VMod: ---------- filenames : tuple The files to get + Returns ------- files : dict @@ -9009,7 +9552,7 @@ class VMod: r = self.getFiles(filename) if filename not in r: - raise RuntimeException(f'No {filename} found!') + raise RuntimeError(f'No {filename} found!') return parseString(r[filename]) @@ -9031,6 +9574,7 @@ class VMod: ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : VSav @@ -9043,6 +9587,187 @@ class VMod: # EOF # # ==================================================================== +# From upgrade.py + + +class VLogUpgrader: + def __init__(self, + vmodFileName, + vlogFileName, + verbose=False): + self._readVModFile(vmodFileName,verbose) + self._readVLogFile(vlogFileName,verbose) + + def _readVModFile(self,vmodFileName,verbose=False): + with VMod(vmodFileName, 'r') as vmod: + self._build = BuildFile(vmod.getBuildFile()) + self._game = self._build.getGame() + + self._vmod_pieces = {} + for piece in self._game.getPieces(): + name, piece = self._expandPiece(piece,verbose) + self._vmod_pieces[name] = piece + + def _expandPiece(self,piece,verbose=False): + traits = piece.getTraits(); + newTraits = Trait.flatten(traits, game=self._game,verbose=verbose) + + piece.setTraits(*newTraits) + + name = newTraits[-1]['name'] + + return name, piece + + def _readVLogFile(self,vlogFileName,verbose=False): + key, lines, sdata, mdata = SaveIO.readSave(vlogFileName, + alsometa=True) + + self._key = key + self._lines = lines + self._save_data = sdata + self._meta_data = mdata + self._vlog_pieces = {} + + for line in self._lines: + iden, name, piece = self._vlogPiece(line,verbose) + if piece is None: + continue + + vmod_piece = self._vmod_pieces.get(name,None) + if vmod_piece is None: + print(f'Did not find piece "{name}" in vmod') + vmod_piece = piece + + vmod_piece.copyStates(piece) + self._vlog_pieces[iden] = {'name': name, + 'vlog': piece, + 'vmod': vmod_piece} + + + def _vlogPiece(self,line,verbose=False): + from re import match + + m = match(r'^\+/([0-9]+)/.*;([a-z0-9_]+)\.png.*',line) + if m is None: + return None,None,None + + iden = int(m.group(1)) + piece = PieceSlot(None) + piece.setTraits(*piece.decodeAdd(line,verbose),iden=iden) + basic = piece.getTraits()[-1] + + return iden,basic['name'],piece + + + def _newLine(self,line,verbose): + self._new_lines.append(line) + if verbose: + print(line) + + def upgrade(self,shownew=False,verbose=False): + self._new_lines = [] + for line in self._lines: + add_line = self.newDefine(line,verbose) + if add_line: + self._newLine(add_line,shownew) + continue + + cmd_line = self.newCommand(line,verbose) + if cmd_line: + self._newLine(cmd_line,shownew) + continue + + oth_line = self.other(line,verbose) + if oth_line: + self._newLine(oth_line,shownew) + continue + + self._newLine(line,shownew) + + def newCommand(self,line,verbose=False): + from re import match + + m = match(r'LOG\s+([+MD])/([0-9]+)/([^/]+)(.*)',line) + if not m: + return None + + cmd = m.group(1) + iden = int(m.group(2)) + more = m.group(3) + + if more == 'stack': + return None + + vp = self._vlog_pieces.get(iden,None) + if vp is None: + print(f'Piece {iden} not found: "{line}"') + return None + + if cmd == '+' or cmd == 'M': + return None + + # Get the code + code = more + m.group(4) + + # Decode the states from the code into the old piece + vp['vlog'].decodeStates(code,verbose) + + # Get the previsous state from the new piece + old = vp['vmod'].encodedStates() + + # Copy states from the old piece to the new piece + vp['vmod'].copyStates(vp['vlog'],verbose) + + # Get the new state code from the new piece + new = vp['vmod'].encodedStates() + + newline = 'LOG\t'+cmd+'/'+str(iden)+'/'+new+'/'+old+'\\\\' + # print('WAS',line) + # print('NOW',newline) + return newline + + def newDefine(self,line,verbose): + from re import match + + m = match(r'\+/([0-9]+)/([^/]+).*',line) + + if not m: + return False + + iden = int(m.group(1)) + more = m.group(2) + if more == 'stack': + return False + + vp = self._vlog_pieces.get(iden,None) + if vp is None: + print(f'Piece {iden} not known') + + old = vp['vlog'] + new = vp['vmod'] + + old_add = old._node.childNodes[0].nodeValue; + new_add = new.encodeAdd(*new.getTraits(),iden=iden,verbose=verbose); + + return new_add + + def other(self,line,verbose=False): + return None + + + def write(self,outFileName,verbose=False): + SaveIO.writeSave(outFileName, + key = 0xAA, + lines = self._new_lines, + savedata = self._save_data, + moduledata = self._meta_data) + + + +# +# EOF +# +# ==================================================================== # From exporter.py class Exporter: @@ -9137,12 +9862,87 @@ class Exporter: # Exporter class # class LaTeXExporter(Exporter): + class Specials: + BATTLE_MARK = 'wgBattleMarker' + BATTLE_CTRL = 'wgBattleCtrl' + BATTLE_CALC = 'wgBattleCalc' + BATTLE_UNIT = 'wgBattleUnit' + ODDS_MARK = 'wgOddsMarker' + HIDDEN_NAME = 'wg hidden unit' + + class Keys: + MARK_BATTLE = key(NONE,0)+',wgMarkBattle' + CLEAR_BATTLE = key(NONE,0)+',wgClearBattle' + CLEAR_ALL_BATTLE = key(NONE,0)+',wgClearAllBattle' + ZERO_BATTLE = key(NONE,0)+',wgZeroBattle' + INCR_BATTLE = key(NONE,0)+',wgIncrBattle' + SET_BATTLE = key(NONE,0)+',wgSetBattle' + GET_BATTLE = key(NONE,0)+',wgGetBattle' + MARK_ODDS = key(NONE,0)+',wgMarkOdds' + MARK_RESULT = key(NONE,0)+',wgMarkResult' + CLEAR_MOVED = key(NONE,0)+',wgClearMoved' + ZERO_BATTLE_AF = key(NONE,0)+',wgZeroBattleAF' + ZERO_BATTLE_DF = key(NONE,0)+',wgZeroBattleDF' + ZERO_BATTLE_FRAC = key(NONE,0)+',wgZeroBattleFrac' + ZERO_BATTLE_ODDS = key(NONE,0)+',wgZeroBattleOdds' + ZERO_BATTLE_SHFT = key(NONE,0)+',wgZeroBattleShift' + ZERO_BATTLE_IDX = key(NONE,0)+',wgZeroBattleIdx' + CALC_BATTLE_AF = key(NONE,0)+',wgCalcBattleAF' + CALC_BATTLE_DF = key(NONE,0)+',wgCalcBattleDF' + CALC_BATTLE_FRAC = key(NONE,0)+',wgCalcBattleFrac' + CALC_BATTLE_ODDS = key(NONE,0)+',wgCalcBattleOdds' + CALC_BATTLE_SHFT = key(NONE,0)+',wgCalcBattleShift' + CALC_BATTLE_IDX = key(NONE,0)+',wgCalcBattleIdx' + CALC_BATTLE_RES = key(NONE,0)+',wgCalcBattleResult' + CLEAR_BATTLE_PHS = key(NONE,0)+',wgClearBattlePhs' + RESOLVE_BATTLE = key(NONE,0)+',wgResolveBattle' + ROLL_DICE = key(NONE,0)+',wgRollDice' + DICE_INIT_KEY = key(NONE,0)+',wgInitDice' + CLEAR_KEY = key('C') + CLEAR_ALL_KEY = key('C',CTRL_SHIFT) + DELETE_KEY = key('D') + ELIMINATE_KEY = key('E') + FLIP_KEY = key('F') + TRAIL_KEY = key('T') + RESTORE_KEY = key('R') + MARK_KEY = key('X') + RESOLVE_KEY = key('Y') + ROTATE_CCWKey = key('[') + ROTATE_CWKey = key(']') + CHARTS_KEY = key('A',ALT) + OOB_KEY = key('B',ALT) + COUNTERS_KEY = key('C',ALT) + DEAD_KEY = key('E',ALT) + DICE_KEY = key('6',ALT) + RECALC_ODDS = key('X',CTRL_SHIFT) + + class Globals: + BATTLE_COUNTER = 'wgBattleCounter' + CURRENT_BATTLE = 'wgCurrentBattle' + CURRENT_ATTACKER = 'wgCurrentAttacker' + BATTLE_NO = 'wgBattleNo' + BATTLE_AF = 'wgBattleAF' + BATTLE_DF = 'wgBattleDF' + BATTLE_FRAC = 'wgBattleFrac' + BATTLE_IDX = 'wgBattleIdx' + BATTLE_ODDS = 'wgBattleOdds' + BATTLE_ODDSM = 'wgBattleOddsMarker' + BATTLE_SHIFT = 'wgBattleShift' + BATTLE_RESULT = 'wgBattleResult' + AUTO_ODDS = 'wgAutoOdds' + AUTO_RESULTS = 'wgAutoResults' + NO_CLEAR_MOVES = 'wgNoClearMoves' + NO_CLEAR_BATTLES = 'wgNoClearBattles' + DEBUG = 'wgDebug' + VERBOSE = 'wgVerbose' + def __init__(self, vmodname = 'Draft.vmod', pdfname = 'export.pdf', infoname = 'export.json', title = 'Draft', version = 'draft', + imageFormat = 'png', description = '', rules = None, tutorial = None, @@ -9197,76 +9997,80 @@ class LaTeXExporter(Exporter): self._nochit = nochit self._resolution = resolution self._counterScale = counterScale - self._battleMark = 'wgBattleMarker' + self._img_format = imageFormat.lower() + + self._battleMark = LaTeXExporter.Specials.BATTLE_MARK + self._oddsMark = LaTeXExporter.Specials.ODDS_MARK + self._battleCtrl = LaTeXExporter.Specials.BATTLE_CTRL + self._battleCalc = LaTeXExporter.Specials.BATTLE_CALC + self._battleUnit = LaTeXExporter.Specials.BATTLE_UNIT + self._hiddenName = LaTeXExporter.Specials.HIDDEN_NAME + self._markBattle = LaTeXExporter.Keys.MARK_BATTLE + self._clearBattle = LaTeXExporter.Keys.CLEAR_BATTLE + self._clearAllBattle = LaTeXExporter.Keys.CLEAR_ALL_BATTLE + self._zeroBattle = LaTeXExporter.Keys.ZERO_BATTLE + self._incrBattle = LaTeXExporter.Keys.INCR_BATTLE + self._setBattle = LaTeXExporter.Keys.SET_BATTLE + self._getBattle = LaTeXExporter.Keys.GET_BATTLE + self._markOdds = LaTeXExporter.Keys.MARK_ODDS + self._markResult = LaTeXExporter.Keys.MARK_RESULT + self._clearMoved = LaTeXExporter.Keys.CLEAR_MOVED + self._zeroBattleAF = LaTeXExporter.Keys.ZERO_BATTLE_AF + self._zeroBattleDF = LaTeXExporter.Keys.ZERO_BATTLE_DF + self._zeroBattleFrac = LaTeXExporter.Keys.ZERO_BATTLE_FRAC + self._zeroBattleOdds = LaTeXExporter.Keys.ZERO_BATTLE_ODDS + self._zeroBattleShft = LaTeXExporter.Keys.ZERO_BATTLE_SHFT + self._zeroBattleIdx = LaTeXExporter.Keys.ZERO_BATTLE_IDX + self._calcBattleAF = LaTeXExporter.Keys.CALC_BATTLE_AF + self._calcBattleDF = LaTeXExporter.Keys.CALC_BATTLE_DF + self._calcBattleFrac = LaTeXExporter.Keys.CALC_BATTLE_FRAC + self._calcBattleOdds = LaTeXExporter.Keys.CALC_BATTLE_ODDS + self._calcBattleShft = LaTeXExporter.Keys.CALC_BATTLE_SHFT + self._calcBattleIdx = LaTeXExporter.Keys.CALC_BATTLE_IDX + self._calcBattleRes = LaTeXExporter.Keys.CALC_BATTLE_RES + self._clearBattlePhs = LaTeXExporter.Keys.CLEAR_BATTLE_PHS + self._resolveBattle = LaTeXExporter.Keys.RESOLVE_BATTLE + self._rollDice = LaTeXExporter.Keys.ROLL_DICE + self._diceInitKey = LaTeXExporter.Keys.DICE_INIT_KEY + self._clearKey = LaTeXExporter.Keys.CLEAR_KEY + self._clearAllKey = LaTeXExporter.Keys.CLEAR_ALL_KEY + self._deleteKey = LaTeXExporter.Keys.DELETE_KEY + self._eliminateKey = LaTeXExporter.Keys.ELIMINATE_KEY + self._flipKey = LaTeXExporter.Keys.FLIP_KEY + self._trailKey = LaTeXExporter.Keys.TRAIL_KEY + self._restoreKey = LaTeXExporter.Keys.RESTORE_KEY + self._markKey = LaTeXExporter.Keys.MARK_KEY + self._resolveKey = LaTeXExporter.Keys.RESOLVE_KEY + self._rotateCCWKey = LaTeXExporter.Keys.ROTATE_CCWKey + self._rotateCWKey = LaTeXExporter.Keys.ROTATE_CWKey + self._chartsKey = LaTeXExporter.Keys.CHARTS_KEY + self._oobKey = LaTeXExporter.Keys.OOB_KEY + self._countersKey = LaTeXExporter.Keys.COUNTERS_KEY + self._deadKey = LaTeXExporter.Keys.DEAD_KEY + self._diceKey = LaTeXExporter.Keys.DICE_KEY + self._recalcOdds = LaTeXExporter.Keys.RECALC_ODDS + self._battleCounter = LaTeXExporter.Globals.BATTLE_COUNTER + self._currentBattle = LaTeXExporter.Globals.CURRENT_BATTLE + self._currentAttacker = LaTeXExporter.Globals.CURRENT_ATTACKER + self._battleNo = LaTeXExporter.Globals.BATTLE_NO + self._battleAF = LaTeXExporter.Globals.BATTLE_AF + self._battleDF = LaTeXExporter.Globals.BATTLE_DF + self._battleFrac = LaTeXExporter.Globals.BATTLE_FRAC + self._battleIdx = LaTeXExporter.Globals.BATTLE_IDX + self._battleOdds = LaTeXExporter.Globals.BATTLE_ODDS + self._battleOddsM = LaTeXExporter.Globals.BATTLE_ODDSM + self._battleShift = LaTeXExporter.Globals.BATTLE_SHIFT + self._battleResult = LaTeXExporter.Globals.BATTLE_RESULT + self._autoOdds = LaTeXExporter.Globals.AUTO_ODDS + self._autoResults = LaTeXExporter.Globals.AUTO_RESULTS + self._noClearMoves = LaTeXExporter.Globals.NO_CLEAR_MOVES + self._noClearBattles = LaTeXExporter.Globals.NO_CLEAR_BATTLES + self._debug = LaTeXExporter.Globals.DEBUG + self._verbose = LaTeXExporter.Globals.VERBOSE self._battleMarks = [] self._oddsMarks = [] self._resultMarks = [] - self._markBattle = key(NONE,0)+',wgMarkBattle' - self._clearBattle = key(NONE,0)+',wgClearBattle' - self._clearAllBattle = key(NONE,0)+',wgClearAllBattle' - self._zeroBattle = key(NONE,0)+',wgZeroBattle' - self._incrBattle = key(NONE,0)+',wgIncrBattle' - self._setBattle = key(NONE,0)+',wgSetBattle' - self._getBattle = key(NONE,0)+',wgGetBattle' - self._markOdds = key(NONE,0)+',wgMarkOdds' - self._markResult = key(NONE,0)+',wgMarkResult' - self._clearMoved = key(NONE,0)+',wgClearMoved' - self._zeroBattleAF = key(NONE,0)+',wgZeroBattleAF' - self._zeroBattleDF = key(NONE,0)+',wgZeroBattleDF' - self._zeroBattleFrac = key(NONE,0)+',wgZeroBattleFrac' - self._zeroBattleOdds = key(NONE,0)+',wgZeroBattleOdds' - self._zeroBattleShft = key(NONE,0)+',wgZeroBattleShift' - self._zeroBattleIdx = key(NONE,0)+',wgZeroBattleIdx' - self._calcBattleAF = key(NONE,0)+',wgCalcBattleAF' - self._calcBattleDF = key(NONE,0)+',wgCalcBattleDF' - self._calcBattleFrac = key(NONE,0)+',wgCalcBattleFrac' - self._calcBattleOdds = key(NONE,0)+',wgCalcBattleOdds' - self._calcBattleShft = key(NONE,0)+',wgCalcBattleShift' - self._calcBattleIdx = key(NONE,0)+',wgCalcBattleIdx' - self._calcBattleRes = key(NONE,0)+',wgCalcBattleResult' - self._clearBattlePhs = key(NONE,0)+',wgClearBattlePhs' - self._resolveBattle = key(NONE,0)+',wgResolveBattle' - self._rollDice = key(NONE,0)+',wgRollDice' - self._diceInitKey = key(NONE,0)+',wgInitDice' - self._clearKey = key('C') - self._clearAllKey = key('C',CTRL_SHIFT) - self._deleteKey = key('D') - self._eliminateKey = key('E') - self._flipKey = key('F') - self._trailKey = key('M') - self._restoreKey = key('R') - self._markKey = key('X') - self._resolveKey = key('Y') - self._rotateCCWKey = key('[') - self._rotateCWKey = key(']') - self._chartsKey = key('A',ALT) - self._oobKey = key('B',ALT) - self._countersKey = key('C',ALT) - self._deadKey = key('E',ALT) - self._diceKey = key('6',ALT) - self._battleCounter = 'wgBattleCounter' - self._currentBattle = 'wgCurrentBattle' - self._currentAttacker = 'wgCurrentAttacker' - self._battleNo = 'wgBattleNo' - self._battleAF = 'wgBattleAF' - self._battleDF = 'wgBattleDF' - self._battleFrac = 'wgBattleFrac' - self._battleIdx = 'wgBattleIdx' - self._battleOdds = 'wgBattleOdds' - self._battleOddsM = 'wgBattleOddsMarker' - self._battleShift = 'wgBattleShift' - self._battleCtrl = 'wgBattleCtrl' - self._battleCalc = 'wgBattleCalc' - self._battleUnit = 'wgBattleUnit' - self._battleResult = 'wgBattleResult' - self._autoOdds = 'wgAutoOdds' - self._autoResults = 'wgAutoResults' - self._noClearMoves = 'wgNoClearMoves' - self._noClearBattles = 'wgNoClearBattles' - self._debug = 'wgDebug' - self._verbose = 'wgVerbose' self._hidden = None - self._hiddenName = 'wg hidden unit' self._dice = {} self._diceInit = None @@ -9283,6 +10087,8 @@ class LaTeXExporter(Exporter): v(f'Visible grids: {self._visible}') v(f'Resolution: {self._resolution}') v(f'Scale of counters: {self._counterScale}') + v(f'Image format: {self._img_format}') + def setup(self): # Start the processing @@ -9306,6 +10112,7 @@ class LaTeXExporter(Exporter): ---------- args : list List of process command line elements + Returns ------- pipe : subprocess.Pipe @@ -9320,16 +10127,18 @@ class LaTeXExporter(Exporter): def addPws(self,opw=None,upw=None): '''Add a `Pws` element to arguments + Add password options + Parameters ---------- kwargs : dict Dictionary of attribute key-value pairs + Returns ------- element : Pws The added element ''' - '''Add password options''' args = [] if upw is not None: args.extend(['-upw',upw]) if opw is not None: args.extend(['-opw',opw]) @@ -9392,6 +10201,92 @@ class LaTeXExporter(Exporter): return info # ================================================================ + @classmethod + def parseLength(cls,value,def_unit='px'): + from re import match + + scales = { + 'px': 1, + 'pt': 1.25, + 'pc': 15, + 'in': 90, + 'mm': 3.543307, + 'cm': 35.43307, + '%': -1/100 + } + + if not value: + return 0 + + parts = match(r'^\s*(-?\d+(?:\.\d+)?)\s*(px|in|cm|mm|pt|pc|%)?', value) + if not parts: + raise RuntimeError(f'Unknown length format: "{value}"') + + number = float(parts.group(1)) + unit = parts.group(2) or def_unit + factor = scales.get(unit,None) + + if not factor: + raise RuntimeError(f'Unknown unit: "{unit}"') + + return factor * number + + # ---------------------------------------------------------------- + @classmethod + def scaleSVG(cls,buffer,factor): + '''Buffer is bytes''' + from xml.dom.minidom import parse + from re import split + from io import StringIO, BytesIO + + if not LaTeXExporter.isSVG(buffer): + return buffer + + with BytesIO(buffer) as stream: + doc = parse(stream) + + if not doc: + raise RuntimeError('Failed to parse buffer as XML') + + root = doc.childNodes[0] + getA = lambda e,n,d=None : \ + e.getAttribute(n) if e.hasAttribute(n) else d + setA = lambda e,n,v : e.setAttribute(n,v) + leng = LaTeXExporter.parseLength + + width = leng(getA(root,'width', '0')) + height = leng(getA(root,'height','0')) + vport = getA(root,'viewBox','0 0 0 0').strip() + vp = [leng(v) for v in split('[ \t,]',vport)] + # print(f'Input WxH: {width}x{height} ({vp})') + + width *= factor + height *= factor + vp = [factor * v for v in vp] + + # print(f'Scaled WxH: {width}x{height} ({vp})') + + if width <= 0 and vp: + width = vp[2] - vp[0] + + if height <= 0 and vp: + height = vp[3] - vp[1] + + if not vp: + vp = [0, 0, width, height] + + setA(root,'transform',f'scale({factor})') + setA(root,'width', f'{width}') + setA(root,'height',f'{height}') + setA(root,'viewBox',' '.join([f'{v}' for v in vp])) + + + with StringIO() as out: + doc.writexml(out) + return out.getvalue().encode() + + + # ================================================================ def convertPage(self,page,opw=None,upw=None,timeout=None): '''Convert a page from PDF into an image (bytes) @@ -9411,17 +10306,22 @@ class LaTeXExporter(Exporter): info : dict Image information ''' - args = ['pdftocairo', + args = ['pdftocairo'] + if self._img_format != 'svg': + args.extend([ '-transp', - '-singlefile', + '-singlefile']) + + args.extend([ '-r', str(self._resolution), '-f', str(page), '-l', str(page), - '-png' ] + f'-{self._img_format}' ]) args.extend(self.addPws(opw=opw,upw=upw)) args.append(self._pdfname) args.append('-') - + + # print(f'Conversion command',' '.join(args)) proc = self.createProcess(args) try: @@ -9432,7 +10332,16 @@ class LaTeXExporter(Exporter): raise RuntimeError(f'Failed to convert page {page} of ' f'{self._pdfname}: {e}') - # Just return the bytes + if len(out) <= 0: + raise RuntimeError(f'Failed to convert page {page} of ' + f'{self._pdfname}: {err}') + + # This does not seem to work - VASSAL (and Inkscape) does not + # apply the 'scale' transformation to the image! + # + # if self._img_format == 'svg': + # out = LaTeXExporter.scaleSVG(out,2) + return out @@ -9501,6 +10410,11 @@ class LaTeXExporter(Exporter): return imgsinfo # ---------------------------------------------------------------- + @classmethod + def isSVG(cls,buffer): + return buffer[:5] == b'<?xml' + + # ---------------------------------------------------------------- def getBB(self,buffer): '''Get bounding box of image @@ -9514,11 +10428,26 @@ class LaTeXExporter(Exporter): ulx, uly, lrx, lry : tuple The coordinates of the bounding box ''' - from PIL import Image from io import BytesIO + + with BytesIO(buffer) as inp: + if LaTeXExporter.isSVG(buffer): + from svgelements import SVG + + svg = SVG.parse(inp) + # bb = svg.bbox() + # if bb is None: + # print(f'No bounding box!') + # bb = [0, 0, 1, 1] + # else: + # bb = [int(b) for b in bb] + x, y, w, h = svg.x, svg.y, svg.width, svg.height + bb = (x,y,x+w,y+h) + else: + from PIL import Image - with Image.open(BytesIO(buffer)) as img: - bb = img.getbbox() + with Image.open(inp) as img: + bb = img.getbbox() return bb @@ -9536,11 +10465,21 @@ class LaTeXExporter(Exporter): ulx, uly, lrx, lry : tuple The coordinates of the bounding box ''' - from PIL import Image from io import BytesIO - - with Image.open(BytesIO(buffer)) as img: - w, h = img.width, img.height + + with BytesIO(buffer) as inp: + if LaTeXExporter.isSVG(buffer): + from svgelements import SVG + + svg = SVG.parse(inp) + w, h = svg.x, svg.y, svg.width, svg.height + # bb = svg.bbox() + # w, h = int(bb[2]-bb[0]),int(bb[3]-bb[1]) + else: + from PIL import Image + + with Image.open(inp) as img: + w, h = img.width, img.height return w,h @@ -9591,11 +10530,13 @@ class LaTeXExporter(Exporter): typ = info.get('category','counter') sub = info.get('subcategory','all') - - info['filename'] = info['name'].replace(' ','_') + '.png' + nam = info['name'] + num = info['number'] + + info['filename'] = f'{nam.replace(" ","_")}.{self._img_format}' imgfn = 'images/'+info['filename'] if imgfn not in self._vmod.getFileNames(): - if typ == 'counter': + if typ == 'counter' and self._img_format != 'svg': # print(f'Possibly scale file {imgfn}') info['img'] = self.scaleImage(info['img'], counterScale) @@ -9621,9 +10562,19 @@ class LaTeXExporter(Exporter): cat[sub] = {} tgt = cat[sub] - v(f'[{info["name"]}]',end=' ',flush=True,noindent=True) + v(f'[{nam}]',end=' ',flush=True,noindent=True) #self.message(f'Adding "{info["name"]}" to catalogue') - tgt[info['name']] = info + # + # Here we could handle multiple info's with the same + # name by adding a unique postfix - e.g., for dices + # what have non-uniform PMFs. + # + # if info['name'] in tgt: + # n = len([i for k,i in tgt.items() if k.startswith(info['name'])]) + # info['name'] += '_' + str(n) + # info['filename'] = info['name'].replace(' ','_') + '.png' + unam = f'{nam}' + tgt[unam] = info if self._nonato: continue @@ -9854,7 +10805,7 @@ class LaTeXExporter(Exporter): v(f'We have symbolic dice') self._diceInit = [] # from pprint import pprint - # pprint(self._dice,depth=3) + # pprint(self._dice,depth=2) for die, faces in self._dice.items(): ico = self.getIcon(die+'-die-icon','') # print(f'Die {die} icon="{ico}"') @@ -9866,7 +10817,9 @@ class LaTeXExporter(Exporter): text = die if ico == '' else '', icon = ico, tooltip = f'{die} die roll', - format = (f'{{"{die} die roll: "+result1' + format = (f'{{"<b>"+PlayerSide+"</b> "+' + f'"(<i>"+PlayerName+"</i>): "+'+ + f'"{die} die roll: "+result1' # f'+" <img src=\'{die}-"+result1' # f'+".png\' width=24 height=24>"' f'}}'), @@ -9876,7 +10829,8 @@ class LaTeXExporter(Exporter): sdie = symb.addDie(name = die); for face, fdata in faces.items(): fn = fdata['filename'] - val = int(fn.replace('.png','').replace(die+'-','')) + val = int(fn.replace(f'.{self._img_format}','') + .replace(die+'-','')) dmin = min(dmin,val) dmax = min(dmax,val) sdie.addFace(icon = fn, @@ -9988,6 +10942,7 @@ class LaTeXExporter(Exporter): [['Ctrl-C', 'Counter', 'Clear battle'], ['Ctrl-Shift-C','Board', 'Clear all battle'], ['Ctrl-X', 'Board,Counter','Mark battle'], + ['Ctrl-Shift-X','Board,Counter','Recalculate Odds'], ['Ctrl-Y', 'Board,Counter','Resolve battle'], ]): ks = [k[0] for k in keys] @@ -10092,6 +11047,7 @@ class LaTeXExporter(Exporter): wrap = True, description = 'Zero battle counter', ), + # Set global property combat # from this GlobalPropertyTrait( ['',self._setBattle,GlobalPropertyTrait.DIRECT, f'{{{self._battleCounter}}}'], @@ -10411,6 +11367,8 @@ class LaTeXExporter(Exporter): # for the current battle # traits = [ + # getBattle retrieves the battle number from the global property. + # clearBattle sets piece battle to -1 DynamicPropertyTrait(['',self._getBattle, DynamicPropertyTrait.DIRECT, f'{{{self._currentBattle}}}'], @@ -10421,7 +11379,9 @@ class LaTeXExporter(Exporter): numeric = True, value = f'{{-1}}', description = 'Set battle number'), - GlobalPropertyTrait(['',self._setBattle, GlobalPropertyTrait.DIRECT, + # This setBattle sets current attacker in global property + GlobalPropertyTrait(['',self._setBattle, + GlobalPropertyTrait.DIRECT, '{IsAttacker}'], name = self._currentAttacker, numeric = True, @@ -10457,6 +11417,9 @@ class LaTeXExporter(Exporter): description = f'Add battle marker {i+1}', placement = PlaceTrait.ABOVE, above = False)) + # Get current global battle number + # Set current battle + # Filtered on current global battle # is equal to trig.append( TriggerTrait(command = '',#Mark battle', key = self._markBattle, @@ -10758,7 +11721,9 @@ class LaTeXExporter(Exporter): def oddsMarkerTraits(self,c=None): '''Derives from the CurrentBattle prototype and adds a submenu to replace odds counter with result marker''' + gpid = self._game.nextPieceSlotId() traits = [PrototypeTrait(name=self._currentBattle), + MarkTrait(self._oddsMark,'true'), NonRectangleTrait(filename = c['filename'], image = c['img']), DynamicPropertyTrait( @@ -10773,13 +11738,53 @@ class LaTeXExporter(Exporter): TriggerTrait(command = '', key = self._getBattle+'Details', actionKeys = [self._getBattle, - self._getBattle+'More'])] + self._getBattle+'More']), + # DeleteTrait('',self._recalcOdds+'Delete'), + # ReplaceTrait(command = '', + # key = self._recalcOdds+'Replace', + # markerSpec = '', + # markerText = 'null', + # xOffset = 0, + # yOffset = 0, + # matchRotation = False, + # afterKey = '', + # gpid = gpid, + # description = f'Replace with nothing'), + GlobalHotkeyTrait(name = '', + key = self._calcBattleOdds+'Start', + globalHotkey = self._calcBattleOdds+'ReAuto', + description = 'Trampoline to global'), + # Recalculate odds + # First setBatle to make battle No global + # Then delete this + # Then send global key command + TriggerTrait(command = 'Recalculate', + key = self._recalcOdds, + actionKeys = [self._setBattle, + self._calcBattleOdds+'Start', + self._clearBattle, + ]), + ReportTrait(self._recalcOdds+'Delete', + report=(f'{{{self._debug}?' + f'("~ "+BasicName+' + f'": Deleting self"):""}}')), + ReportTrait(self._clearBattle, + report=(f'{{{self._debug}?' + f'("~ "+BasicName+' + f'": Remove"):""}}')), + ReportTrait(self._recalcOdds, + report=(f'{{{self._debug}?' + f'("! Recalculate Odds"):""}}')), + ReportTrait(self._calcBattleOdds+'Start', + report=(f'{{{self._debug}?' + f'("~ Start auto recalculate Odds"):""}}')), + ] subs = [] place = [] trig = [] rept = [] - ukeys = [] + ukeys = [self._recalcOdds] first = '' for i, result in enumerate(self._resultMarks): r = result.replace('result marker','').strip() @@ -10910,7 +11915,9 @@ class LaTeXExporter(Exporter): traits = [#ReportTrait(self._eliminateKey, # self._restoreKey, # self._trailKey), - TrailTrait(), + TrailTrait(lineWidth = 5, + radius = 10, + key = self._trailKey), RotateTrait(), MovedTrait(xoff = int(offX),yoff = int(offY)), DeleteTrait(), @@ -10921,6 +11928,8 @@ class LaTeXExporter(Exporter): restoreName = 'Restore', restoreKey = self._restoreKey, description = 'Eliminate unit'), + # ReportTrait(self._trailKey, + # f'{{"Enabling trail on "+BasicName}}'), PrototypeTrait(name=self._battleUnit), MarkTrait(name='Faction',value=faction)] @@ -10966,6 +11975,7 @@ class LaTeXExporter(Exporter): bb = self.getBB(c['img']) height = bb[3]-bb[1] if bb is not None else 1 width = bb[2]-bb[0] if bb is not None else 1 + cf = subc.get(cn + ' flipped', None) traits = [PrototypeTrait(name=f'{subn} prototype')] @@ -10974,10 +11984,14 @@ class LaTeXExporter(Exporter): if not self._nonato: mains = c.get('mains','') - m = set([clean(mains)] + - [clean(m) for m in mains.split(',')]) - traits.extend([PrototypeTrait(name=f'{m.strip()} prototype') - for m in set(m)]) + mm = clean(mains).strip() + traits.append(PrototypeTrait(name=f'{mm} prototype')) + # Commented code adds all 'main' types as prototypes, which + # doesn't make so much sense + # + # m = set([clean(m) for m in mains.split(',')]) + # traits.extend([PrototypeTrait(name=f'{m.strip()} prototype') + # for m in set(m)]) for p in ['echelon','command']: val = c.get(p,None) if val is not None: @@ -11187,10 +12201,17 @@ class LaTeXExporter(Exporter): True if any piece can be flipped ''' with VerboseGuard(f'Adding board {name}') as v: + # from pprint import pprint + # pprint(info) map = self._game.addMap(mapName=name, markUnmovedHotkey=self._clearMoved) map.addCounterDetailViewer( - propertyFilter=f'{{{self._battleMark}!=true}}') + propertyFilter=f'{{{self._battleMark}!=true}}', + fontSize = 14, + summaryReportFormat = '<b>$LocationName$</b>', + hotkey = key('\n'), + stopAfterShowing = True + ) map.addHidePiecesButton() map.addGlobalMap() # Basics @@ -11207,18 +12228,23 @@ class LaTeXExporter(Exporter): map.addHighlightLastMoved() map.addZoomer() - map.addMassKey(name='Eliminate', + map.addMassKey(name = 'Eliminate', buttonHotkey = self._eliminateKey, hotkey = self._eliminateKey, icon = self.getIcon('eliminate-icon', '/icons/16x16/edit-undo.png'), tooltip = 'Eliminate selected units') - map.addMassKey(name='Delete', + map.addMassKey(name = 'Delete', buttonHotkey = self._deleteKey, hotkey = self._deleteKey, icon = self.getIcon('delete-icon', '/icons/16x16/no.png'), tooltip = 'Delete selected units') + map.addMassKey(name = 'Trail', + buttonHotkey = self._trailKey, + hotkey = self._trailKey, + icon = '', + tooltip = '') map.addMassKey(name='Rotate CW', buttonHotkey = self._rotateCWKey, hotkey = self._rotateCWKey, @@ -11244,7 +12270,7 @@ class LaTeXExporter(Exporter): f'{self._noClearMoves})' f':""}}')) if hasFlipped: - map.addMassKey(name='Flip', + map.addMassKey(name = 'Flip', buttonHotkey = self._flipKey, hotkey = self._flipKey, icon = self.getIcon('flip-icon', @@ -11266,14 +12292,15 @@ class LaTeXExporter(Exporter): curUnt = (f'{{{self._battleNo}=={self._currentBattle}&&' f'{self._battleUnit}==true}}') markSel = (f'{{{self._battleNo}=={self._currentBattle}&&' - f'{self._battleMark}==true}}') + f'{self._battleMark}==true&&' + f'{self._oddsMark}!=true}}') # ctrlSel = '{BasicName=="wg hidden unit"}' map.addMassKey(name = 'User mark battle', buttonHotkey = self._markKey, buttonText = '', hotkey = self._markBattle, - icon = 'battle-marker-icon.png', + icon = f'battle-marker-icon.{self._img_format}', tooltip = 'Mark battle', target = '', singleMap = False, @@ -11336,7 +12363,7 @@ class LaTeXExporter(Exporter): buttonText = '', buttonHotkey = self._clearAllKey, hotkey = self._clearAllBattle, - icon = 'clear-battles-icon.png', + icon = f'clear-battles-icon.{self._img_format}', tooltip = 'Clear all battles', target = '', singleMap = False, @@ -11364,7 +12391,7 @@ class LaTeXExporter(Exporter): map.addMassKey(name = 'Selected resolve battle', buttonHotkey = self._resolveKey, hotkey = self._resolveKey, - icon = 'resolve-battles-icon.png', + icon = f'resolve-battles-icon.{self._img_format}', tooltip = 'Resolve battle', singleMap = False, filter = oddsSel, @@ -11433,15 +12460,45 @@ class LaTeXExporter(Exporter): reportFormat = (f'{{{self._debug}?' f'("~ {name}: ' f'Auto calculate odds"):""}}')) + map.addMassKey(name = 'User recalc', + buttonHotkey = self._recalcOdds, + buttonText = '', + hotkey = self._recalcOdds, + icon = '', + tooltip = 'Recalculate odds', + singleMap = False, + filter = '', + reportFormat = (f'{{{self._debug}?' + f'("~ {name}: ' + f'Recalculate odds"):""}}')) + map.addMassKey(name = 'Auto recalc battle odds', + buttonText = '', + buttonHotkey = self._calcBattleOdds+'ReAuto', + hotkey = self._calcBattleOdds+'Start', + icon = '', + tooltip = '', + target = '', + singleMap = False, + filter = markSel, + reportFormat = (f'{{{self._debug}?' + f'("~ {name}: ' + f'Auto re-calculate odds"):""}}')) + v(f'Getting the board dimensions') ulx,uly,lrx,lry = self.getBB(info['img']) - width = abs(ulx - lrx) - height = abs(uly - lry) - width, height = self.getWH(info['img']) + width = int(abs(ulx - lrx)) + height = int(abs(uly - lry)) + # Why is it we take the width and height like this? + # Do they every differ from the above? + # This is the only place that we actually use this + # + # width, height = self.getWH(info['img']) height += 20 width += 5 - + # v(f'{ulx},{uly},{lrx},{lry}') + + v(f'Board BB=({lrx},{lry})x({ulx},{uly}) {width}x{height}') picker = map.addBoardPicker() board = picker.addBoard(name = name, image = info['filename'], @@ -11451,13 +12508,14 @@ class LaTeXExporter(Exporter): zoned.addHighlighter() if not 'zones' in info: - full = zoned.addZone(name = full, + color = rgb(255,0,0) + full = zoned.addZone(name = 'full', useParentGrid = False, path=(f'{ulx},{uly};' + f'{lrx},{uly};' + f'{lrx},{lry};' + f'{ulx},{lry}')) - grid = zone.addHexGrid(color = color, + grid = full.addHexGrid(color = color, dx = HEX_WIDTH, dy = HEX_HEIGHT, visible = self._visible) @@ -11489,7 +12547,7 @@ class LaTeXExporter(Exporter): '''Add a "Dead Map" element to the module ''' name = 'DeadMap' - with VerboseGuard(f'Adding board {name}') as v: + with VerboseGuard(f'Adding deadmap {name}') as v: map = self._game.addMap(mapName = name, buttonName = '', markMoved = 'Never', @@ -12009,7 +13067,7 @@ class LaTeXExporter(Exporter): # Do not add grids to pools if ispool: - v('Board {n} is pool with no points') + v(f'Board {n} is pool with no points') continue targs = {'color':rgb(255,0,0),'visible':self._visible} @@ -12227,118 +13285,216 @@ class LaTeXExporter(Exporter): self._game.addDiceButton(name = '1d6', hotkey = self._diceKey) +# ==================================================================== +def patchVmod(vmod_filename,patch_name,verbose): + + with VMod(vmod_filename,'r') as vmod: + buildFile = BuildFile(vmod.getBuildFile()) + moduleData = ModuleData(vmod.getModuleData()) + + from importlib.util import spec_from_file_location, module_from_spec + from pathlib import Path + from sys import modules + + p = Path(patch_name) + + spec = spec_from_file_location(p.stem, p.absolute()) + module = module_from_spec(spec) + spec.loader.exec_module(module) + + modules[p.stem] = module + + with VMod(vmod_filename,'a') as vmod: + module.patch(buildFile, + moduleData, + vmod, + verbose) + + vmod.replaceFiles(**{VMod.BUILD_FILE : + buildFile.encode(), + VMod.MODULE_DATA : + moduleData.encode()}) + + # # EOF # # ==================================================================== # From main.py +from argparse import ArgumentParser + +class DefaultSubcommandArgParse(ArgumentParser): + _default_subparser = None + + def set_default_subparser(self, name): + self._default_subparser = name + + def _parse_known_args(self, arg_strings, *args, **kwargs): + from argparse import _SubParsersAction + in_args = set(arg_strings) + d_sp = self._default_subparser + if d_sp is not None and not {'-h', '--help'}.intersection(in_args): + for x in self._subparsers._actions: + subparser_found = ( + isinstance(x, _SubParsersAction) and + in_args.intersection(x._name_parser_map.keys()) + ) + if subparser_found: + break + else: + # insert default in first position, this implies no + # global options without a sub_parsers specified + arg_strings = [d_sp] + arg_strings + return super(DefaultSubcommandArgParse, self)._parse_known_args( + arg_strings, *args, **kwargs + ) +# ==================================================================== +def patchIt(args): + vmodname = args.output.name + patchname = args.patch.name + args.output.close() + args.patch .close() + + patchVmod(vmodname, patchname, args.verbose) + +# ==================================================================== +def exportIt(args): + + vmodname = args.output.name + patchname = args.patch.name if args.patch is not None else None + + args.output.close() + if args.patch is not None: + args.patch.close() + + Verbose().setVerbose(args.verbose) + + try: + if args.version.lower() == 'draft': + args.visible_grids = True + + rulesname = args.rules.name if args.rules is not None else None + tutname = args.tutorial.name if args.tutorial is not None else None + + exporter = LaTeXExporter(vmodname = vmodname, + pdfname = args.pdffile.name, + infoname = args.infofile.name, + title = args.title, + version = args.version, + description = args.description, + rules = rulesname, + tutorial = tutname, + patch = patchname, + visible = args.visible_grids, + vassalVersion = args.vassal_version, + nonato = args.no_nato_prototypes, + nochit = args.no_chit_information, + resolution = args.resolution, + counterScale = args.counter_scale, + imageFormat = args.image_format) + exporter.run() + except Exception as e: + from sys import stderr + print(f'Failed to build {vmodname}: {e}',file=stderr) + from os import unlink + try: + unlink(vmodname) + except: + pass + + raise e + + # ==================================================================== if __name__ == '__main__': from argparse import ArgumentParser, FileType - ap = ArgumentParser(description='Create draft VASSAL module') - ap.add_argument('pdffile', + ap = DefaultSubcommandArgParse(description='Create draft VASSAL module') + ap.set_default_subparser('export') + sp = ap.add_subparsers(dest='mode') + + pp = sp.add_parser('patch',help='Patch VMod') + pp.add_argument('output', + help='Module to patch', + type=FileType('r'), + default='Draft.vmod') + pp.add_argument('patch', + help='A python script to patch generated module', + type=FileType('r'), + default='patch.py') + pp.add_argument('-V','--verbose', + help='Be verbose', + action='store_true') + + + ep = sp.add_parser('export',help='Export from PDF and JSON to VMod') + ep.add_argument('pdffile', help='The PDF file to read images from', type=FileType('r'), default='export.pdf', nargs='?') - ap.add_argument('infofile', + ep.add_argument('infofile', help='The JSON file to read image information from', type=FileType('r'), default='export.json', nargs='?') - ap.add_argument('-p','--patch', - help='A python script to patch generated module', - type=FileType('r')) - ap.add_argument('-o','--output', + ep.add_argument('-o','--output', help='Output file to write module to', type=FileType('w'), default='Draft.vmod') - ap.add_argument('-t','--title', + ep.add_argument('-p','--patch', + help='A python script to patch generated module', + type=FileType('r')) + ep.add_argument('-V','--verbose', + help='Be verbose', + action='store_true') + ep.add_argument('-t','--title', help='Module title', default='Draft', type=str) - ap.add_argument('-v','--version', + ep.add_argument('-v','--version', help='Module version', type=str, default='draft') - ap.add_argument('-r','--rules', + ep.add_argument('-r','--rules', help='Rules PDF file', type=FileType('r')) - ap.add_argument('-T','--tutorial', + ep.add_argument('-T','--tutorial', help='Tutorial (v)log file', type=FileType('r')) - ap.add_argument('-d','--description', + ep.add_argument('-d','--description', help='Short description of module', type=str, default='draft of module') - ap.add_argument('-W','--vassal-version', + ep.add_argument('-W','--vassal-version', help='Vassal version number', type=str, default='3.6.7') - ap.add_argument('-V','--verbose', - help='Be verbose', - action='store_true') - ap.add_argument('-G','--visible-grids', + ep.add_argument('-G','--visible-grids', action='store_true', help='Make grids visible in the module') - ap.add_argument('-N','--no-nato-prototypes', + ep.add_argument('-N','--no-nato-prototypes', action='store_true', help='Do not make prototypes for types,echelons,commands') - ap.add_argument('-C','--no-chit-information', + ep.add_argument('-C','--no-chit-information', action='store_true', help='Do not make properties from chit information') - ap.add_argument('-S','--counter-scale', + ep.add_argument('-S','--counter-scale', type=float, default=1, help='Scale counters by factor') - ap.add_argument('-R','--resolution', + ep.add_argument('-R','--resolution', type=int, default=150, help='Resolution of images') - - + ep.add_argument('-I','--image-format', + choices = ['png','svg'], default='png', + help='Image format to use') + args = ap.parse_args() - vmodname = args.output.name - rulesname = args.rules.name if args.rules is not None else None - tutname = args.tutorial.name if args.tutorial is not None else None - args.output.close() - - patchname = args.patch.name if args.patch is not None else None - if args.patch is not None: - args.patch.close() - - if args.version.lower() == 'draft': - args.visible_grids = True - - Verbose().setVerbose(args.verbose) - - try: - exporter = LaTeXExporter(vmodname = vmodname, - pdfname = args.pdffile.name, - infoname = args.infofile.name, - title = args.title, - version = args.version, - description = args.description, - rules = rulesname, - tutorial = tutname, - patch = patchname, - visible = args.visible_grids, - vassalVersion = args.vassal_version, - nonato = args.no_nato_prototypes, - nochit = args.no_chit_information, - resolution = args.resolution, - counterScale = args.counter_scale) - exporter.run() - except Exception as e: - from sys import stderr - print(f'Failed to build {vmodname}: {e}',file=stderr) - from os import unlink - try: - unlink(vmodname) - except: - pass - - raise e + if args.mode == 'patch': + patchIt(args) + else: + exportIt(args) # # EOF diff --git a/Master/texmf-dist/tex/latex/wargame/wgmakenato.py b/Master/texmf-dist/tex/latex/wargame/wgmakenato.py new file mode 100755 index 00000000000..65dcceb41f6 --- /dev/null +++ b/Master/texmf-dist/tex/latex/wargame/wgmakenato.py @@ -0,0 +1,756 @@ +#!/usr/bin/env python + +# ==================================================================== +def ensure_list(what): + if not what: + return None + + if isinstance(what,str): + return [what] + + return what + +# -------------------------------------------------------------------- +def clean(base, suffixes): + from pathlib import Path + + if not isinstance(base,Path): + base = Path(base) + + for suffix in suffixes: + tgt = base.with_suffix(suffix) + tgt.unlink(missing_ok=True) + +# ==================================================================== +possible_commands = ['air', + 'land', + 'equipment', + 'installation', + 'sea surface', + 'sub surface', + 'space', + 'activity', + 'dismounted'] +possible_factions = ['friendly', + 'hostile', + 'neutral', + 'unknown'] +echelon_mapping = {'': 'team', + '*': 'squad', + '**': 'section', + '***': 'platoon', + '|': 'company', + '||': 'battalion', + '|||': 'regiment', + 'x': 'brigade', + 'xx': 'division', + 'xxx': 'corps', + 'xxxx': 'army', + 'xxxxx': 'army group', + 'xxxxxx': 'theater'} +possible_echelons = list(echelon_mapping.values())+list(echelon_mapping.keys()) + +# ==================================================================== +def make_picture(command, + faction, + echelon, + main, + top=None, + bottom=None, + left=None, + right=None, + below=None, + decoy=False, + line_width='1pt', + scale=.45, + more=None, + **kwargs): + '''Create latex code for the NATO App6 symbol - a single tikzpicture + + Parameters + ---------- + command : str + Command (base symbol) + faction : str + Faction (base symbol) + echelon : str or None + Echelon (top of base symbol + main : str or list of str + Main symbols + top : str or list of str + Top modifiers + bottom : str or list of str + Bottom modifiers + left : str or list of str + Left modifiers + right : str or list of str + Right modifiers + below : str or list of str + Below base frame + decoy : bool + True if it should be a decoy + line_width : str + Line width, including unit + scale : float + Scaling of image + + Return + ------ + out : str + LaTeX code to write to file + ''' + ind = ' ' + def add_line(out,key,what,ind=ind): + '''Adds a line with a key''' + lwhat = ensure_list(what) + if not lwhat: + return out + + swhat = ",".join(lwhat) + + return out+f'{ind} {key}={{{swhat}}},%'+'\n' + + + out = fr'''\begin{{tikzpicture}}% + \natoapp[% + scale={scale},% + scale line widths,% + line width={line_width},% + command={command},% + faction={faction},%'''+'\n' + + out = add_line(out,'echelon',echelon) + out = add_line(out,'main', main) + out = add_line(out,'top', top) + out = add_line(out,'bottom', bottom) + out = add_line(out,'left', left) + out = add_line(out,'right', right) + out = add_line(out,'below', below) + if more: + out += f'{ind} {more}%'+'\n' + + out += fr''' ];% + \end{{tikzpicture}}% + ''' + lout = out.split('\n') + lout = [l[len(ind):] if l.startswith(ind) else l for l in lout] + return '\n'.join(lout) + +# -------------------------------------------------------------------- +def make_latex(db): + '''Create latex code for the all NATO App6 symbols - + a complete document + + Parameters + ---------- + db : list of dict + Configurations of each symbol + + Return + ------ + out : str + LaTeX code to write to file + ''' + + ind = ' ' + out = r'''\documentclass[tikz,11pt]{standalone} + \usepackage{wargame} + \begin{document}%'''+'\n' + + for entry in db: + out += make_picture(**entry) + + out += r'\end{document}' + + lout = out.split('\n') + lout = [l[len(ind):] if l.startswith(ind) else l for l in lout] + return '\n'.join(lout) + + +# -------------------------------------------------------------------- +def make_name(command, + faction, + echelon, + main, + top=None, + bottom=None, + left=None, + right=None, + below=None, + decoy=False, + output=None, + **kwargs): + '''Create the file name + + Parameters + ---------- + command : str + Command (base symbol) + faction : str + Faction (base symbol) + echelon : str or None + Echelon (top of base symbol + main : str or list of str + Main symbols + top : str or list of str + Top modifiers + bottom : str or list of str + Bottom modifiers + left : str or list of str + Left modifiers + right : str or list of str + Right modifiers + decoy : bool + True if it should be a decoy + + Return + ------ + out : str + Output file name + ''' + if output: + return output.format(command=command, + faction=faction, + echelon=echelon)\ + .replace(' ','-')\ + .replace('=','-') + + def clean_sub(what): + from re import sub + + res = what + while True: + old = res + res = sub(r'\[[^]]+\]','',res) + res = sub(r'\{[^}]+\}','',res) + if res == old: + break + + return res + + def add_component(out, what,sep='-'): + + lwhat = ensure_list(what) + + if not lwhat: + return out + + return out+'_'+sep.join([clean_sub(l) for l in lwhat]) + + def add_front(out,what,sep='_'): + if not what: + return out + + if len(out) > 0: out += sep + return out + what + + out = '' + out = add_front(out,faction) + out = add_front(out,command.replace(' ','-')) + out = add_front(out,echelon.replace(' ','-')) + out = add_component(out,main) + out = add_component(out,top) + out = add_component(out,bottom) + out = add_component(out,left) + out = add_component(out,right) + out = add_component(out,below) + + return out.replace(' ','-').replace('=','-') + + +# ==================================================================== +default_latex_flags = ['-interaction=nonstopmode', + '-file-line-error', + '-synctex','15', + '-shell-escape'] +default_latex_command = 'pdflatex' + +# -------------------------------------------------------------------- +def create_process(args,verbose=False): + '''Create a subprocess with arguments + + Returns + ------- + proc : Popen + Process with stdout and stderr as pipes (fifos) + ''' + from os import environ + from subprocess import Popen, PIPE + + if verbose: + print(f'Will run: "{" ".join(args)}"') + + return Popen(args,env=environ.copy(),stdout=PIPE,stderr=PIPE) + +# -------------------------------------------------------------------- +default_timeout = None +default_keep = False +# -------------------------------------------------------------------- +def run_latex(file, + content, + latex_cmd = default_latex_command, + latex_flags = default_latex_flags, + timeout = default_timeout, + keep = default_keep, + verbose = False): + '''Run latex on generated content + + Parameters + ---------- + filename : str + The file to write + content : str + Content to write to source file + latex_cmd : str + LaTeX command to run e.g., 'pdflatex' + latex_flags : list of str + Flags to pass to the LaTeX command line + timeout : int + Process timeout + keep : bool + If true, keep intermediate files + + Returns + ------- + None + ''' + from pathlib import Path + + fpath = Path(file.name).with_suffix('') + csuf = ['.aux','.log','.out','.synctex','.synctex.gz','.tex'] + + file.write(content) + file.close() + + lflags = ensure_list(latex_flags) + + args = [latex_cmd] + (lflags if lflags else + []) + \ + [fpath.with_suffix('.tex').name] + + proc = create_process(args,verbose=verbose) + + try: + out, err = proc.communicate(timeout=timeout) + + sout = out.decode() + if 'Error' in sout or \ + 'Emergency stop' in sout or \ + not Path(fpath).with_suffix('.pdf').is_file(): + raise RuntimeError(sout) + + except Exception as e: + proc.kill() + proc.communicate() + if not keep: + clean(base=fpath,suffixes=csuf+['.pdf']) + + raise RuntimeError(f'Failed on "{" ".join(args)}" with:\n'+ + str(e) + '\n--- Start LaTeX code -------\n'+ + content+ '\n--- End LaTeX code ---------') + + if not keep: + clean(base=fpath,suffixes=csuf) + +# -------------------------------------------------------------------- +possible_formats = ['png','svg','jpg','tiff'] +default_format = possible_formats[0] +default_resolution = 150 + +# -------------------------------------------------------------------- +def run_cairo(filename, + npages, + format = default_format, + resolution = default_resolution, + timeout = default_timeout, + keep = default_keep, + verbose = False): + '''Run 'pdftocairo' on generated PDF + + Parameters + ---------- + filename : str + The file to write + format : str + Image format to write + resolution : int + Pixels Per Inch (PPI) + timeout : int + Process timeout + keep : bool + If true, keep intermediate files + + Returns + ------- + None + ''' + from pathlib import Path + + fpath = Path(filename) + args = ['pdftocairo'] + if format != 'svg': + args.extend(['-transp']) + + args.extend(['-r',str(resolution),f'-{format}']) + + inpdf = fpath.with_suffix('.pdf').name + + def runit(args,pdf=inpdf,outimg=None): + args.append(inpdf) + if outimg: args.append(outimg) + + proc = create_process(args,verbose) + + try: + out, err = proc.communicate(timeout=timeout) + + except Exception as e: + proc.kill() + proc.communicate() + if not keep: + clean(base=fpath,suffixes=[f'.{format}','.pdf']) + + raise RuntimeError(f'Failed on "{" ".join(args)}" with:\n'+e) + + if format != 'svg': + runit(args) + else: + from math import ceil, log10 + + base = fpath.stem + wn = ceil(log10(npages)) + for no in range(1,npages+1): + no_args = args.copy() + no_args.extend(['-f',str(no),'-l',str(no)]) + + runit(no_args,outimg=base+f'-{no:0{wn}d}.{format}') + + if not keep: + clean(base=fpath,suffixes=['.pdf']) + +# -------------------------------------------------------------------- +def make_images(db, + base_name, + format = default_format, + verbose = False): + from math import ceil, log10 + from pathlib import Path + + results = [] + ndb = len(db) + wn = ceil(log10(ndb)) + for no,entry in enumerate(db): + output = entry['output']+'.'+format + inpath = Path(f'{base_name}-{no+1:0{wn}d}.{format}') + + inpath.rename(output) + results.append(output) + if verbose: + print(f'{no+1:0{wn}d}/{ndb}: {output}') + + return results + +# -------------------------------------------------------------------- +default_xml = False + +# -------------------------------------------------------------------- +def write_xmls(db, + format = default_format, + verbose = False): + '''Build an XML snippet of a prototype''' + + ndb = len(db) + wn = ceil(log10(ndb)) + for no,entry in enumerate(db): + if verbose: + print(f'{no+1:0{wn}d}/{ndb} ...{entry["output"]}.xml') + + write_xml(**entry,format=format) + +# -------------------------------------------------------------------- +def write_xml(command = None, + faction = None, + echelon = None, + main = None, + top = None, + bottom = None, + left = None, + right = None, + below = None, + decoy = False, + output = None, + format = default_format, + **kwargs): + '''Build an XML snippet of a prototype''' + from xml.dom.minidom import Document + from wgexport import LayerTrait, Prototype, MarkTrait, \ + BasicTrait, BuildFile + + doc = BuildFile() + hasEchelon = echelon and len(echelon) > 0 + + yoff = -8 if main else -10 if echelon else -8 + traits = [ + LayerTrait( + images = [f'{output}.{format}'], + newNames = [''], + activateName = '', + activateMask = '', + activateChar = '', + increaseName = '', + increaseMask = '', + increaseChar = '', + decreaseName = '', + decreaseMask = '', + decreaseChar = '', + resetName = '', + resetKey = '', + resetLevel = 1, + under = False, + underXoff = 0, + underYoff = yoff, + loop = False, + name = ('NATOAPP6_command_faction' + +('_main' if main else '') + +('_echelon' if hasEchelon else '')), + description = 'NATO App6 symbology', + randomKey = '', + randomName = '', + follow = False, + expression = '', + first = 1, + version = 1, + always = True, + activateKey = '', + increaseKey = '', + decreaseKey = '', + scale = 1.)] + + if command: + traits.append(MarkTrait(name = 'NATOAPP6_command', + value = command)) + if main: + traits.append(MarkTrait(name = 'NATOAPP6_type', + value = make_name(command = '', + faction = '', + echelon = '', + main = main, + top = top, + bottom = bottom, + left = left, + right = right, + below = below, + decoy = decoy))) + if echelon: + traits.append(MarkTrait(name = 'NATOAPP6_echelon', + value = echelon)) + + traits.append(BasicTrait()) + + + proto = Prototype(doc, + name = output, + traits = traits, + description = 'NATO App6 symbol') + + doc._node.insertBefore( + doc._root.createComment('Created via LaTeX wargame package, ' + 'licensed under the CreativeCommons ' + 'Share-Alike, ' + '© 2024 Christian Holm Christensen'), + proto._node) + xml_out = doc.encode() + + with open(output+'.xml','wb') as out: + out.write(xml_out) + + +# ==================================================================== +def create_images(db, + latex_command = default_latex_command, + latex_flags = default_latex_flags, + timeout = default_timeout, + resolution = default_resolution, + format = default_format, + keep = default_keep, + verbose = False, + xml = default_xml): + from tempfile import NamedTemporaryFile + from pathlib import Path + + result = [] + + + with NamedTemporaryFile(mode='w',suffix='.tex',dir='.', + delete=not keep, + delete_on_close=False) as texfile: + if verbose: + print(f'Temporary file: {texfile.name}') + + base_name = Path(texfile.name).stem + latex_code = make_latex(db) + try: + run_latex(texfile, + latex_code, + latex_cmd = latex_command, + latex_flags = latex_flags, + timeout = timeout, + keep = keep, + verbose = verbose) + + run_cairo(base_name, + npages = len(db), + format = format, + resolution = resolution, + timeout = timeout, + keep = keep, + verbose = verbose) + + result = make_images(db, + base_name, + format = format, + verbose = verbose) + + if xml: + write_xmls(db) + + except Exception as e: + import traceback + print(traceback.format_exc()) + print(e) + return None + + return result + + +# ==================================================================== +# +# Main program +# +if __name__ == '__main__': + from argparse import ArgumentParser, FileType + from pprint import pprint + + ap = ArgumentParser(description='Create an image of NATO App6 symbol') + ap.add_argument('-c','--command',type=str,choices=possible_commands, + help='Select command',default=possible_commands[1]) + ap.add_argument('-f','--faction',type=str,choices=possible_factions, + help='Select faction',default=possible_factions[0]) + ap.add_argument('-e','--echelon',type=str,choices=possible_echelons, + help='Select echelon') + ap.add_argument('-m','--main',type=str,nargs='*', + help='Select main symbol(s)') + ap.add_argument('-t','--top',type=str,nargs='*', + help='Select top modifier(s)') + ap.add_argument('-b','--bottom',type=str,nargs='*', + help='Select bottom modifier(s)') + ap.add_argument('-l','--left',type=str,nargs='*', + help='Select left modifier(s)') + ap.add_argument('-r','--right',type=str,nargs='*', + help='Select left modifier(s)') + ap.add_argument('-B','--below',type=str,nargs='*', + help='Select below (mobility) modifier(s)') + ap.add_argument('-d','--decoy',action='store_true', + help='Mark as decoy') + ap.add_argument('-w','--line-width',default='1pt',type=str, + help='Line width, including unit') + ap.add_argument('-s','--scale',default=0.45,type=float, + help='Scaling of symbol (width in centimetre)') + ap.add_argument('-I','--format',default=default_format, + choices=possible_formats,type=str, + help='Output image format') + ap.add_argument('-L','--latex-command',default=default_latex_command, + help='Set the LaTeX command to use',type=str) + ap.add_argument('-F','--latex-flags',default=default_latex_flags, + help='Set the LaTeX command to use',type=str,nargs='*') + ap.add_argument('-T','--timeout',default=default_timeout,type=int, + help='Timeout of sub-processes') + ap.add_argument('-R','--resolution',default=default_resolution,type=int, + help='Resolution out output image (ppi)') + ap.add_argument('-k','--keep',action='store_true', + help='Leave intermediate files') + ap.add_argument('-o','--output',type=str, + help='Set output file name, default is autogenerated') + ap.add_argument('-j','--json',type=str,nargs='*', + help='Read symbols to create from JSON file') + ap.add_argument('-v','--verbose',action='store_true') + ap.add_argument('-X','--xml',action='store_true', + help='Also output XML snippets') + + args = ap.parse_args() + vargs = vars(args) + oargs = {} + to_remove = ['latex_command', + 'latex_flags', + 'resolution', + 'format', + 'timeout', + 'keep', + 'json', + 'verbose', + 'xml'] + for key in to_remove: + if key not in vargs: + continue + + oargs[key] = vargs[key] + del vargs[key] + + commons = ['infantry', + 'armoured', + 'armoured+infantry', + 'reconnaissance', + 'armoured+reconnaissance', + '[fill=pgfstrokecolor]artillery', + 'armoured+[fill=pgfstrokecolor]artillery', + 'text=SF'] + + + db = None + main = vargs['main'] + injson = oargs.pop('json',None) + + #pprint(vargs) + #pprint(oargs) + + if injson: + from json import load + + db = [] + for j in injson: + print(f'=== JSON input: {j}') + with open(j,'r') as jin: + db.extend(load(jin)) + + elif main and len(main) == 1 and main[0] == 'common': + db = [{'main': w.split('+')} for w in commons] + else: + db = [vargs] + + if not db: + raise RuntimeError('Noting to do') + + from math import log10, ceil + + def ensure(d, key, default): + if key not in d and default: + d[key] = default[key] + + results = [] + ndb = len(db) + wn = ceil(log10(ndb)) + for no,entry in enumerate(db): + print(f'{no:{wn}d} of {ndb} ...',end='') + if not isinstance(entry,dict): + print(' skipped') + continue + + for key in vargs.keys(): + ensure(entry,key,vargs) + entry['output'] = make_name(**entry) + print(entry['output']) + + + results = create_images(db,**oargs) +# +# +# |