diff options
author | Norbert Preining <norbert@preining.info> | 2023-01-16 03:03:27 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2023-01-16 03:03:27 +0000 |
commit | 6f9e1680085e7bb4d258f6f8116369d122e196e1 (patch) | |
tree | 9ac0ecb239240d1d672b188f29c1479de215074b /graphics/pgf/base/tex/generic/modules | |
parent | b8345f39630408bb198e7636381ce4240154ca9b (diff) |
CTAN sync 202301160303
Diffstat (limited to 'graphics/pgf/base/tex/generic/modules')
12 files changed, 11953 insertions, 0 deletions
diff --git a/graphics/pgf/base/tex/generic/modules/pgfmoduleanimations.code.tex b/graphics/pgf/base/tex/generic/modules/pgfmoduleanimations.code.tex new file mode 100644 index 0000000000..c0a8346811 --- /dev/null +++ b/graphics/pgf/base/tex/generic/modules/pgfmoduleanimations.code.tex @@ -0,0 +1,1314 @@ +% Copyright 2019 by Till Tantau +% +% This file may be distributed and/or modified +% +% 1. under the LaTeX Project Public License and/or +% 2. under the GNU Public License. +% +% See the file doc/generic/pgf/licenses/LICENSE for more details. + +\ProvidesFileRCS{pgfmoduleanimations.code.tex} + + +% We need the animation system abstraction: + +\input pgfsysanimations.code.tex + + +% Animate an attribute over time +% +% #1 = an attribute +% #2 = configuration keys +% +% This command adds animation commands for the attribute given in #1 +% of some future object to the current graphic scope. For instance, +% when #1 is "fill opacity" then the fill opacity of the object will +% change over time. The object to which the command refers is +% specified using a special key in #2. +% +% The keys in #2 specify how the changes occur over time. There are a +% number of keys that have an effect on the animation "as a whole" +% (like "repeats") while the most central key is the entry key. This +% key takes two parameters: a time and a value. It specifies that at +% the given time the attribute should have the specified value. The +% times must be given in ascending order. Between the times given in +% this way, the values of the attribute will be interpolated; you can +% influence the details of how this interpolation is done using +% additional keys. +% +% The keys in #2 are given in key-value syntax and can be given in any +% order, except that the entry keys must be given in order of +% ascending times. +% +% The syntax of the second parameter of the entry key depends on +% #1. Currently, these are: +% +% Attribute Value Type +% --------------------------------------- +% fill opacity scalar +% stroke opacity scalar +% opacity scalar +% visible boolean +% line width dimension +% fill color +% stroke color +% motion motion +% translate point +% scale scaling +% rotate scalar +% xskew scalar +% yskew scalar +% path path +% softpath softpath +% view viewbox +% dash phase dimension +% dash pattern dashpattern +% none any +% +% Example: +% +% \pgfanimateattribute{fill opacity}{entry={0s}{1}, entry={2s}{0}} + +\def\pgfanimateattribute#1#2{% + \pgfanimateattributecode{#1}{\pgfkeys{/pgf/animation/.cd,#2}}% +}% + + +% Variant of \pgfanimateattribute +% +% #1 = attribute +% #2 = code +% +% Description: +% +% A version of \pgfanimateattribute where instead of keys #2 you +% provide a code #2 that calls the animation keys itself. + +\def\pgfanimateattributecode#1#2{% + {% + \pgfkeys{/pgf/@animation attributes/.cd,#1}% + #2% + \csname pgf@animation@check@\pgf@animation@attribute\endcsname% + \pgfsysanimate{\pgf@animation@attribute}% + }% +}% + + + +% Create a time snapshot +% +% #1 = a time +% +% Description: +% +% This command should be called in a scope prior to all calls of +% animation commands in that scope. When called, all animations will +% be "intercepted" and, instead of creating the animation code, normal +% code will be generated that shows the state of the animation at the +% specified "moment" #1. Since no animation code is involved, this +% works with all drivers. +% +% The command \pgfsnapshotafter works the same way as \pgfsnapshot, +% only the timing is slightly different: Conceptually, #1 is +% interpreted as "#1+epsilon". For instance, if there are two values +% specified for the time #1, then \pgfsnapshot will use the first +% while \pgfsnapshotafter will use the second. Likewise, when an +% animation ends at time #1, \pgfsnapshot will still us the +% animation's value, while \pgfsnapshotafter will not. +% +% Example: +% +% { +% \pgfsnapshot{1.2s} +% \pgfanimateattribute{fill opacity}{entry={0s}{1}, entry={2s}{0}} +% \fill (0,0) rectangle (1,1); +% } + +\def\pgfsnapshot#1{\pgfparsetime{#1}\expandafter\pgfsysanimsnapshot\expandafter{\pgftimeresult}}% +\def\pgfsnapshotafter#1{\pgfparsetime{#1}\expandafter\pgfsysanimsnapshotafter\expandafter{\pgftimeresult}}% + + +% The animation attributes + +\pgfkeys{/pgf/@animation attributes/.cd, + visible/.code=\pgf@animate@attr{visibility}{mapped}\def\pgf@anim@map@true{visible}\def\pgf@anim@map@false{hidden}, + stage/.code=\pgf@animate@attr{visibility}{mapped}\def\pgf@anim@map@true{visible}\def\pgf@anim@map@false{hidden}\pgfsysanimkeybase\pgfsysanimvaltext{hidden}, + opacity/.code=\pgf@animate@attr{opacity}{scalar}, + fill opacity/.code=\pgf@animate@attr{fillopacity}{scalar}, + draw opacity/.code=\pgf@animate@attr{strokeopacity}{scalar}, + stroke opacity/.style=draw opacity, + line width/.code=\pgf@animate@attr{linewidth}{dimension}, + fill/.code=\pgf@animate@attr{fillcolor}{color}, + draw/.code=\pgf@animate@attr{strokecolor}{color}, + color/.code=\pgf@animate@attr{color}{color}, + stroke/.style=draw, + motion/.code=\pgf@animate@attr{motion}{scalar}, + translate/.code=\pgf@animate@attr{translate}{point}, + scale/.code=\pgf@animate@attr{scale}{scaling}, + rotate/.code=\pgf@animate@attr{rotate}{scalar}, + xskew/.code=\pgf@animate@attr{skewx}{scalar}, + yskew/.code=\pgf@animate@attr{skewy}{scalar}, + skew x/.style=xskew, + skew y/.style=yskew, + path/.code=\pgf@animate@attr{path}{path}, + softpath/.code=\pgf@animate@attr{path}{softpath}, + view/.code=\pgf@animate@attr{viewbox}{viewbox}, + dash/.code=\pgf@animate@attr{dash}{dash}, + none/.code=\pgf@animate@attr{none}{none}, +}% + +\def\pgf@animate@attr#1#2{% + \def\pgf@animation@attribute{#1}% + \expandafter\let\expandafter\pgfanim@type@parser\csname pgfanim@parse@type@#2\endcsname% +}% + + + +% The time parser +% +% #1 = a time +% +% This macro parses the time in #1, but adds some support for times: +% +% 1) The postfix operator "s" is added, which has no effect. +% 2) The postfix operator "ms" is added, which divides a number by +% 1000, so "2ms" equals "0.002". +% 3) The postfix operator "min" is added, which multiplies a number by +% 60. +% 4) The postfix operator "h" is added, which multiplies a number by +% 3600. +% 5) The infix operator ":" is redefined, so that it multiplies its +% first argument by 60 and adds the second. This implies that +% "1:20" equals "80" and "01:00:00" equals "3600". +% 6) The parsing of octal numbers is switched off to allow things like +% "01:08" for 68s. + +\def\pgfparsetime#1{% + \begingroup% + \pgfmathdeclareoperator{s}{@seconds}{1}{postfix}{600}% + \pgfmathdeclareoperator{m}{m@encountered}{1}{postfix}{600}% + \pgfmathdeclareoperator{i}{i@encountered}{1}{postfix}{600}% + \pgfmathdeclareoperator{n}{@minutes}{1}{postfix}{600}% + \pgfmathdeclareoperator{h}{@hours}{1}{postfix}{600}% + \pgfmathdeclareoperator{:}{time@colon}{2}{infix}{50}% + \pgfmath@octalparsingfalse% + \pgfmathparse{#1}% + \expandafter\endgroup% + \expandafter\def\expandafter\pgftimeresult\expandafter{\pgfmathresult}% +}% + +\newif\ifpgfanim@m@encountered +\newif\ifpgfanim@i@encountered +\pgfmathdeclarefunction{@seconds}{1}{% + \begingroup% + \expandafter\pgfmath@x#1pt\relax% + \ifpgfanim@m@encountered% + \divide\pgfmath@x by1000\relax% + \fi% + \global\pgfanim@m@encounteredfalse% + \pgfmath@returnone\pgfmath@x% + \endgroup% +}% +\pgfmathdeclarefunction{m@encountered}{1}{% + \global\pgfanim@m@encounteredtrue% + \begingroup% + \expandafter\pgfmath@x#1pt\relax% + \pgfmath@returnone\pgfmath@x% + \endgroup% +}% +\pgfmathdeclarefunction{i@encountered}{1}{% + \ifpgfanim@m@encountered% + \global\pgfanim@m@encounteredfalse% + \global\pgfanim@i@encounteredtrue% + \else% + \pgfmath@error{Unexpected i}{}% + \fi% + \begingroup% + \expandafter\pgfmath@x#1pt\relax% + \pgfmath@returnone\pgfmath@x% + \endgroup% +}% +\pgfmathdeclarefunction{@minutes}{1}{% + \ifpgfanim@i@encountered% + \global\pgfanim@i@encounteredfalse% + \else% + \pgfmath@error{Unexpected n}{}% + \fi% + \begingroup% + \expandafter\pgfmath@x#1pt\relax% + \pgfmath@x60\pgfmath@x\relax% + \pgfmath@returnone\pgfmath@x% + \endgroup% +}% +\pgfmathdeclarefunction{@hours}{1}{% + \begingroup% + \expandafter\pgfmath@x#1pt\relax% + \pgfmath@x3600\pgfmath@x\relax% + \pgfmath@returnone\pgfmath@x% + \endgroup% +}% +\pgfmathdeclarefunction{time@colon}{2}{% + \begingroup% + \pgfmath@x=#1pt\relax% + \pgfmath@x=60\pgfmath@x\relax% + \pgfmath@y=#2pt\relax% + \advance\pgfmath@x by\pgfmath@y% + \pgfmath@returnone\pgfmath@x% + \endgroup% +}% + + + +% Sets general animation attributes +% +% #1 = key-value pairs that are executed for the path /pgf/animation/ + +\def\pgfanimationset{\pgfqkeys{/pgf/animation}}% + + +% Sets the target animation scope that should be animated. +% +% #1 = A name that will be used with an idscope. +% +% Example: +% +% \pgfanimateattribute{fill opacity}{whom=some scope, entry={0s}{1}, entry={1s}{0}} +% \begin{scope}[name=some scope] +% \draw (0,0) -- (1,1); +% \draw (1,0) -- (2,1); +% \end{scope} + +\pgfanimationset{ + whom/.code={% + \pgf@anim@decomp{#1}% + \pgfidrefnextuse\pgf@anim@id\pgf@anim@id@name% + \expandafter\expandafter\expandafter\pgfsysanimkeywhom\expandafter\expandafter\expandafter{\expandafter\pgf@anim@id\expandafter}\expandafter{\pgf@anim@type}% + \let\pgf@anim@whom@id\pgf@anim@id% + \let\pgf@anim@whom@type\pgf@anim@type% + } +}% + +\def\pgf@anim@decomp#1{% + \edef\pgf@temp{#1}% + \expandafter\pgfutil@in@\expandafter.\expandafter{\pgf@temp}% + \ifpgfutil@in@% + \expandafter\pgf@anim@decomp@\pgf@temp\pgf@stop% + \else% + \expandafter\pgf@anim@decomp@\pgf@temp.\pgf@stop% + \fi% +}% +\def\pgf@anim@decomp@#1.#2\pgf@stop{% + \def\pgf@anim@id@name{#1}% + \def\pgf@anim@type{#2}% +}% + + +% Assigns a name to this animation so that it can be used as an event +% id in another animation +% +% #1 = A name +% +% The name "previous" is special and always refers to the most recent +% animation before the animation currently being constructed. +% +% Example: +% +% \pgfanimateattribute{fill opacity}{name=anim 1, entry={0s}{1}, entry={1s}{0}} +% \pgfanimateattribute{fill opacity}{begin on={end,of=anim 1}, entry={0s}{1}, entry={1s}{0}} + +\pgfanimationset{ + name/.code=\pgfuseid{#1}, +}% + + + + +% Configures whether an animation can be restarted +% +% Some animations can restart when certain events take place. This key +% configures this. Setting it to +% +% "true" allows a restart at any time, +% "false" does not allow a restart, +% "when not active" allows a restart only, when the element is not +% active. +% +% Example: +% +% \pgfanimateattribute{fill opacity}{entry={0s}{1}, entry={1s}{0}, restart=false} + +\pgfanimationset{ + restart/.is choice, + restart/.default=true, + restart/true/.code=\pgfsysanimkeyrestartalways, + restart/false/.code=\pgfsysanimkeyrestartnever, + restart/never/.code=\pgfsysanimkeyrestartnever, + restart/when not active/.code=\pgfsysanimkeyrestartwhennotactive, +}% + + +% Sets the number times an animation should repeat +% +% #1 = A string of the following form: +% +% [<empty> | <number> | for <time>] ["accumulating"] +% +% When empty (the default value) the animation repeats forever. +% +% When a <number> is provided, the animation will repeat <number> +% times, which need not be an integer. +% +% When a <time> is given, the repeating will stop after this much time. +% +% When the optional "accumulating" is specified, the repeat +% accumulates, otherwise each repeat begins with the original start +% value. +% +% Example: +% +% \pgfanimateattribute{fill opacity}{repeats, entry={0s}{1}, entry={6s}{0}} +% \pgfanimateattribute{fill opacity}{repeats=2, entry={0s}{1}, entry={6s}{0}} +% \pgfanimateattribute{fill opacity}{repeats=2 accumulating, entry={0s}{1}, entry={6s}{0}} +% \pgfanimateattribute{fill opacity}{repeats=for 15s, entry={0s}{1}, entry={6s}{0}} + +\pgfanimationset{ + repeats/.code={% + \pgfutil@in@{accumulating\pgf@stop}{#1\pgf@stop}% + \ifpgfutil@in@% + \pgfsysanimkeyaccumulate% + \pgfanim@parse@acc#1\pgf@stop% + \else% + \pgfsysanimkeynoaccumulate% + \pgfanim@parse@noacc{#1}% + \fi% + }, + repeats/.default=, + repeat/.style={repeats={#1}}, + repeat/.default=, +}% +\def\pgfanim@parse@acc#1accumulating\pgf@stop{% + \pgfanim@parse@noacc{#1}% +}% +\def\pgfanim@parse@noacc#1{% + \pgfutil@in@{\pgf@stop for}{\pgf@stop#1}% + \ifpgfutil@in@% + \pgfanim@parse@for#1\pgf@stop% + \else% + \def\pgf@temp{#1}% + \ifx\pgf@temp\pgfutil@empty% + \pgfsysanimkeyrepeatindefinite% + \else% + \pgfmathparse{#1}% + \expandafter\pgfsysanimkeyrepeat\expandafter{\pgfmathresult}% + \fi% + \fi% +}% +\def\pgfanim@parse@for for#1\pgf@stop{\pgfparsetime{#1}\expandafter\pgfsysanimkeyrepeatdur\expandafter{\pgftimeresult}}% + + + + + +% Specifies that an animation should begin or end at a certain time +% (relative to the current context). The "begin..." options may be +% used multiple times and their effects accumulate, likewise for the +% "end..." options. +% +% #1 = A time +% +% Example: +% +% \pgfanimateattribute{fill opacity}{begin=2s, end=2.5s, entry={0s}{1}, entry={1s}{0}} + +\pgfanimationset{ + begin/.code=\pgfparsetime{#1}\expandafter\pgfsysanimkeyoffset\expandafter{\pgftimeresult}{begin}, + end/.code=\pgfparsetime{#1}\expandafter\pgfsysanimkeyoffset\expandafter{\pgftimeresult}{end}, +}% + + + +% Specifies that an animation should begin (or end) when a certain +% event takes place. +% +% #1 = A list of key-value pairs. +% +% The following keys are allowed: +% +% event=some event : The begin / end is triggered by that event. The +% list of allowed events is defined in the SVG +% standard. +% of=some name : The event does not refer to the current group, +% but to the object named "some name", which must +% previously have been named \pgfuseid. If some +% name contains a period, the part before the +% period must be the name of the id, the part +% following it the type of the id. +% key=some key : The trigger is some key being pressed +% repeat=number : The trigger is that an animation has been +% repeated number times. +% delay=time : A delay, may be negative. +% +% These keys are executed with the path prefix /pgf/animation/events. +% +% Some of them are predefined: +% +% "click" is a shorthand for "event=click" +% "focus in" is a shorthand for "event=focusin" +% "focus out" is a shorthand for "event=focusout" +% "activate" is a shorthand for "event=activate" +% "mouse down" is a shorthand for "event=mousedown" +% "mouse up" is a shorthand for "event=mouseup" +% "mouse over" is a shorthand for "event=mouseover" +% "mouse move" is a shorthand for "event=mousemove" +% "mouse out" is a shorthand for "event=mouseout" +% "begin" is a shorthand for "event=begin" +% "end" is a shorthand for "event=end" +% +% Example: +% +% % Begin after 5s or when clicked: +% \pgfanimateattribute{fill opacity}{begin=5s, begin on=click, entry={0s}{1}, entry={1s}{0}} +% +% % Begin, when an object named "button" is clicked +% \pgfanimateattribute{fill opacity}{begin on={click, of=button}, entry={0s}{1}, entry={1s}{0}, name=another animation} +% +% % Begin 1s after some other animation ends +% \pgfanimateattribute{fill opacity}{begin on={end, of=another animation, delay=1s}, entry={0s}{1}, entry={1s}{0}} + +\pgfanimationset{ + begin on/.code={\pgfanim@make@event{#1}{begin}},% + end on/.code={\pgfanim@make@event{#1}{end}}, +}% + +\def\pgfanim@make@event#1#2{% + \let\pgfanim@event@event\pgfutil@empty% + \let\pgf@anim@id\pgfutil@empty% + \let\pgf@anim@type\pgfutil@empty% + \let\pgfanim@event@key\pgfutil@empty% + \let\pgfanim@event@repeat\pgfutil@empty% + \let\pgfanim@event@delay\pgfutil@empty% + \pgfkeys{/pgf/animation/events/.cd,#1}% + \ifx\pgfanim@event@key\pgfutil@empty% + \ifx\pgfanim@event@repeat\pgfutil@empty% + \ifx\pgfanim@event@event\pgfutil@empty% + \pgferror{No event specified}% + \else% + \pgfsysanimkeyevent{\pgf@anim@id}{\pgf@anim@type}{\pgfanim@event@event}{\pgfanim@event@delay}{#2}% + \fi% + \else% + \pgfsysanimkeyrepeatevent{\pgf@anim@id}{\pgf@anim@type}{\pgfanim@event@repeat}{\pgfanim@event@delay}{#2}% + \fi% + \else% + \pgfsysanimkeyaccesskey{\pgfanim@event@key}{\pgfanim@event@delay}{#2}% + \fi% +}% + +\pgfkeys{/pgf/animation/events/.cd, + event/.store in=\pgfanim@event@event, + of/.code={% + \pgf@anim@decomp{#1}% + \pgfidrefprevuse\pgf@anim@id\pgf@anim@id@name% + \ifx\pgf@anim@id\pgfutil@empty% + \pgferror{Unknown object/node name '\pgf@anim@id@name'. Did you + mean 'of next=\pgf@anim@id@name'?}% + \fi% + }, + of next/.code={% + \pgf@anim@decomp{#1}% + \pgfidrefnextuse\pgf@anim@id\pgf@anim@id@name% + }, + of id/.code=\def\pgf@anim@id{#1}, + key/.store in=\pgfanim@event@key, + repeat/.code=\pgfmathparse{#1}\let\pgfanim@event@repeat\pgfmathresult, + delay/.code=\pgfparsetime{#1}\let\pgfanim@event@delay\pgftimeresult, + click/.style={event=click}, + focus in/.style={event=focusin}, + focus out/.style={event=focusout}, + mouse down/.style={event=mousedown}, + mouse up/.style={event=mouseup}, + mouse over/.style={event=mouseover}, + mouse move/.style={event=mousemove}, + mouse out/.style={event=mouseout}, + begin/.style={event=begin}, + end/.style={event=end}, +}% + + +% Specifies the timeline start for a snapshot +% +% #1 = a time +% +% Description: +% +% What a snapshot is taken of an animation (using \pgfsnapshot), all +% animations normally start at time 0s. When this key is used, the +% animation's timeline start is considered to be at moment #1 +% instead. + +\pgfanimationset{ + begin snapshot/.code= + \pgfparsetime{#1}\expandafter\pgfsysanimkeysnapshotstart\expandafter{\pgftimeresult} +}% + + +% Specifies the origin for transforming animations +% +% #1 = a pgf point +% +% Specifies the origin of transformations (for instance, a rotate will +% rotate around this point). +% +% Example: +% +% \pgfanimateattribute{rotate}{entry={0s}{0}, entry={1s}{90}, origin=\pgfpoint{1cm}{1cm}} + +\pgfanimationset{ + origin/.code={% + \pgf@process{#1}% + \edef\pgf@marshal{% + \noexpand\pgfsysanimkeycanvastransform% + {\noexpand\pgftransformshift{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}{\noexpand\pgflowlevelsynccm}}% + {\noexpand\pgftransforminvert\noexpand\pgflowlevelsynccm}% + \noexpand\pgf@anim@save@transform{\noexpand\pgftransformshift{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}}% + }% + \pgf@marshal% + }, +}% + +\pgfsysanimkeycanvastransform{{\pgflowlevelsynccm}}{\pgftransforminvert\pgflowlevelsynccm}% + + + +% Specifies the transforming for the animation coordinate system +% +% #1 = code that changes the pgf coordinate system +% +% Specifies an additional transformation of the coordinate system for +% animations. +% +% Example: +% +% \pgfanimateattribute{shift}{entry={0s}{\pgfpointorigin}, entry={1s}{\pgfpoint{1cm}{0cm}}, transform=\pgftransformrotate{30}} + +\pgfanimationset{ + transform/.code={% + \pgfsysanimkeycanvastransform{#1{\pgflowlevelsynccm}}{\pgftransforminvert\pgflowlevelsynccm}% + \pgf@anim@save@transform{#1}% + }, +}% + + + +% Specifies a motion path. +% +% #1 = pgf commands for constructing a path +% +% Use this key to specify a motion path for the motion attribute. The +% values of the "entry" key specify fractions along this path. +% +% Example: +% +% \pgfanimateattribute{motion}{ +% entry={0s}{0}, entry={1s}{1}, +% along=\pgfpathmoveto{\pgfpointorigin} \pgfpathlineto{\pgfpoint{1cm}{1cm}} +% } + +\pgfanimationset{ + along/.code=% + {% + \pgfsyssoftpath@getcurrentpath\pgfanim@save@path% + \pgfsyssoftpath@setcurrentpath\pgfutil@empty% + #1% + \pgfsyssoftpath@getcurrentpath\pgfanim@the@path% + \global\let\pgfanim@the@path\pgfanim@the@path% + \pgfsyssoftpath@setcurrentpath\pgfanim@save@path% + }% + \expandafter\pgf@anim@protocol@path@size\pgfanim@the@path\pgf@stop% + \expandafter\pgfsysanimkeymovealong\expandafter{\pgfanim@the@path}% +}% + + +% Specifies a motion soft path. +% +% #1 = a pgf soft path (result of calling \pgfsyssoftpath@getcurrentpath) +% +% Use this key to specify a motion path for the motion attribute when +% a softpath has already been constructed. +% +% Example: +% +% \pgfanimateattribute{motion}{entry={0s}{0}, entry={1s}{1}, +% along softpath=\pgfsyssoftpath@movetotoken {0pt}{0pt}\pgfsyssoftpath@linetotoken {10pt}{10pt}} + +\pgfanimationset{ + along softpath/.code=% + \pgf@anim@protocol@path@size#1\pgf@stop% + \pgfsysanimkeymovealong{#1}% +}% + + +\def\pgf@anim@protocol@path@size{% + \pgfutil@ifnextchar\pgf@stop\pgfutil@gobble\pgf@anim@protocol@path@size@% +}% +\def\pgf@anim@protocol@path@size@#1#2#3{% + \pgf@protocolanimsizes@direct{#2}{#3}% + \pgf@anim@protocol@path@size% +}% + + +% Configures whether the motion is rotated along the path. +% +% Example: +% +% \pgfanimateattribute{motion}{rotate along, entry={0s}{0}, entry={1s}{1}, +% along=\pgfpathmoveto{\pgfpointorigin} \pgfpathlineto{\pgfpoint{1cm}{1cm}}} + +\pgfanimationset{ + rotate along/.is choice, + rotate along/.default=true, + rotate along/true/.code=\pgfsysanimkeyrotatealong, + rotate along/false/.code=\pgfsysanimkeynorotatealong, +}% + + + +% Specifies an entry +% +% #1 = A time. This time will be parsed using \pgfparsetime +% #2 = A value. The format of this value depends on the current type +% of the animation attribute. (So for an animation of the "fill +% opacity", #1 must be a scalar value, for an animation of the "fill" +% color, #1 must be a color, and so on.) +% If #2 is the special text "current value", then the current +% value will be used. For SVG, this is allowed only when exactly +% two entries are specified and this special value is used for +% the first one. +% +% Example: +% +% \pgfanimateattribute{fill opacity}{entry={0s}{0}, entry={1s}{1}} + +\pgfanimationset{entry/.code 2 args=\pgf@anim@entry{#1}{#2}}% +\def\pgf@anim@entry#1#2{% + \pgf@anim@entry@giventrue% + \pgfparsetime{#1}% + \edef\pgf@marshal{\noexpand\pgfsysanimkeytime{\pgftimeresult}\pgf@anim@entry@spline\pgf@anim@exit@spline}% + \pgf@marshal% + \def\pgf@temp{#2}% + \ifx\pgf@temp\pgf@special@current@text% + \pgfsysanimvalcurrent% + \else% + \pgfanim@type@parser{#2}% + \fi% +}% +\def\pgf@special@current@text{current value}% +\newif\ifpgf@anim@entry@given + + +% Specifies a base value +% +% #1 = A value. The format is as for the entry key. +% +% Description: +% +% Will set the "base" value of the animation, which is a value that is +% used when the animation is not active. +% +% Example: +% +% \pgfanimateattribute{fill opacity}{base = 0.5, entry={0s}{0.5}, entry={1s}{1}} + +\pgfanimationset{base/.code=\pgf@anim@base{#1}}% +\def\pgf@anim@base#1{% + \pgfsysanimkeybase% + \pgfanim@type@parser{#1}% +}% + + +% Specifies that there is an entry "at infinity". +% +% When an animation ends, its "effect" can either persist or it can be +% removed. Setting "freeze at end" to true will cause it to persist, +% otherwise it will be removed. +% +% Example: +% +% \pgfanimateattribute{fill opacity}{entry={0s}{1}, entry={1s}{0}, freeze at end} + +\pgfanimationset{ + freeze at end/.is choice, + freeze at end/.default=true, + freeze at end/true/.code=\pgfsysanimkeyfreezeatend, + freeze at end/false/.code=\pgfsysanimkeyremoveatend, +}% + + + +% Specifies an entry or exit spline control +% +% #1 = time fraction +% #2 = attribute fraction +% +% Description: +% +% Suppose that an animation is supposed to interpolate a attribute's +% value between the two values "50" and "100" over a time of 10s. The +% simplest way of doing so is to do a linear interpolation, where the +% value as, say, 1s is 55, at 2s it is 60, and so on. +% +% Unfortunately, the linear interpolation does not "look" nice in many +% cases since the acceleration of a linear interpolation is zero +% during the animation, but infinite at the beginning and at the end; +% which looks "jerky". +% +% To avoid this, you can specify that the time--attribute curve should +% not be a straight line, but rather a curve. You specify this curve +% using a spline. +% +% The most logical "coordinate rectangle" used for this spline in our +% example would be (0s,50) and (10s,100) and we would like to specify +% something like "(0s,50) .. controls (5s,50) and (9s,100) +% .. (10s,100)". This would result in a time--attribute curve where +% the attribute at 50 changes slowly at 0s and also arrives slowly at +% 100 at 10s, but speeds up between these values. +% +% We call the first control point (5s,50) the "exit control" and call +% (9s,100) the "entry control": The first control dictates how quickly +% or slowly a time point is left, the second dictates how quickly or +% slowly we enter the next one. +% +% The control points are, however, not specified in the coordinate +% system indicated above. Rather, the rectangle (0s,50) to (10s, 100), +% gets normalized to (0,0) to (1,1). The control point (5s,50) would +% thus become (0.5,0) and (9s,100) becomes (0.9,1). +% +% Example: +% +% \pgfanimateattribute{rotate}{ +% exit control={0.5}{0}, +% entry control={0.9}{1}, +% entry={0s}{50}, +% entry={10s}{100}} + +\pgfanimationset{ + entry control/.code 2 args={% + \pgfmathsetmacro\pgf@temp@t{#1}% + \pgfmathsetmacro\pgf@temp@a{#2}% + \edef\pgf@anim@entry@spline{{\pgf@temp@t}{\pgf@temp@a}}% + }, + exit control/.code 2 args={% + \pgfmathsetmacro\pgf@temp@t{#1}% + \pgfmathsetmacro\pgf@temp@a{#2}% + \edef\pgf@anim@exit@spline{{\pgf@temp@t}{\pgf@temp@a}}% + }, + stay/.code=\let\pgf@anim@exit@spline\pgfsys@stay@text,% + jump/.code=\let\pgf@anim@entry@spline\pgfsys@jump@text,% + linear/.style={entry control={1}{1},exit control={0}{0}}, +}% +\def\pgf@anim@entry@spline@base{{1}{1}}% +\def\pgf@anim@exit@spline@base{{0}{0}}% + +\def\pgf@anim@reset@linear{\let\pgf@anim@entry@spline\pgf@anim@entry@spline@base\let\pgf@anim@exit@spline\pgf@anim@exit@spline@base}% +\pgf@anim@reset@linear + + + +% Specifies how much arrow tips should be shortened (as in +% \pgfsetshortenstart) +% +% #1 = an additional shortening distance the path start. +% +% Description: +% +% Works like \pgfsetshortenstart, only for animated paths. + +\pgfanimationset{ + shorten </.code={% + \ifpgf@anim@entry@given% + \pgferror{You must specify shorten < prior to the path entries}% + \else% + \pgfmathsetmacro\pgf@anim@marker@extra@shorten@start{#1}% + \edef\pgf@anim@marker@extra@shorten@start{\pgf@anim@marker@extra@shorten@start pt}% + \fi% + }, + shorten >/.code={% + \ifpgf@anim@entry@given% + \pgferror{You must specify shorten < prior to the path entries}% + \else% + \pgfmathsetmacro\pgf@anim@marker@extra@shorten@end{#1}% + \edef\pgf@anim@marker@extra@shorten@end{\pgf@anim@marker@extra@shorten@end pt}% + \fi% + }, +}% +\def\pgf@anim@marker@extra@shorten@start{0pt}% +\def\pgf@anim@marker@extra@shorten@end{0pt}% +\def\pgf@anim@marker@shorten@start{0pt}% +\def\pgf@anim@marker@shorten@end{0pt}% + +% Specifies the arrow tips used for path animations +% +% #1 = an arrow specification of the form <start>-<end> as for the +% command \pgfsetarrows +% +% Description: +% +% Specifies arrow tips that are used in a path animation. Note that in +% order to set the arrow tips of a path that is animated, you always +% have to use this command. In particular, you should *not* set arrow +% tips for the to-be-animated path, but use this key and the base key +% to install a base path with arrow tips. + +\pgfanimationset{ + arrows/.code={% + \ifpgf@anim@entry@given% + \pgferror{You must specify animation arrows prior to the path entries}% + \else% + \pgf@anim@parser@arrows#1\pgf@stop% + \fi% + }, +}% + +\def\pgf@anim@parser@arrows#1-#2\pgf@stop{% + \pgf@anim@prepare@arrow@start{#1}% + \pgf@anim@prepare@arrow@end{#2}% + \expandafter\expandafter\expandafter\pgfsysanimkeytipmarkers\expandafter\expandafter\expandafter{\expandafter\pgf@anim@marker@id@start\expandafter}\expandafter{\pgf@anim@marker@id@end}% +}% + +\def\pgf@anim@prepare@arrow@start#1{% + \def\pgf@anim@temp{#1}% + \ifx\pgf@anim@temp\pgfutil@empty% + \let\pgf@anim@marker@id@start\pgfutil@empty% + \def\pgf@anim@marker@shorten@start{0pt}% + \else% + {% + \pgfsetarrowsstart{#1}% + \pgf@anim@prep@marker@arrow\pgf@start@tip@sequence\pgf@anim@marker@id\pgf@anim@marker@shorten% + \global\let\pgf@anim@marker@id@start\pgf@anim@marker@id% + \global\let\pgf@anim@temp\pgf@anim@marker@shorten% + }% + \let\pgf@anim@marker@shorten@start\pgf@anim@temp% + \fi% +}% + +\def\pgf@anim@prepare@arrow@end#1{% + \def\pgf@anim@temp{#1}% + \ifx\pgf@anim@temp\pgfutil@empty% + \let\pgf@anim@marker@id@end\pgfutil@empty% + \def\pgf@anim@marker@shorten@end{0pt}% + \else% + {% + \pgfsetarrowsend{#1}% + \pgf@anim@prep@marker@arrow\pgf@end@tip@sequence\pgf@anim@marker@id\pgf@anim@marker@shorten% + \global\let\pgf@anim@marker@id@end\pgf@anim@marker@id% + }% + \let\pgf@anim@marker@shorten@end\pgf@anim@marker@shorten% + \fi% +}% + + +% Draw an arrow using markers + +\def\pgf@anim@prep@marker@arrow#1#2#3{% + % #1 = tip sequence + % #2 = macro for storing marker code + % #3 = macro for storing shortening length + % + % First, compute id's: + {% + \let\pgf@arrow@handle\pgf@anim@marker@id@test% + \let\pgf@arrow@handle@dot\relax%? + \global\let\pgf@anim@marker@id@list\pgfutil@empty% + #1% + }% + \expandafter\let\expandafter#2\csname pgfanim@marker@cache@\pgf@anim@marker@id@list\endcsname% + \expandafter\let\expandafter#3\csname pgfanim@marker@cachel@\pgf@anim@marker@id@list\endcsname% + \ifx#2\relax% ok, have to create it! + \pgf@relevantforpicturesizefalse% + \pgfinterruptpath% + \pgf@arrow@compute@shortening{#1}% + \xdef#3{\the\pgf@xa}% + {% + \let\pgf@arrow@handle\pgf@anim@drawer@marker + \let\pgf@arrow@handle@dot\relax% + \pgfsys@marker@declare#2{% + \pgftransformxshift{-\pgf@xb}% + \pgftransformxshift{\pgf@xa}% + #1% + }% + }% + \endpgfinterruptpath% + \expandafter\global\expandafter\let\csname pgfanim@marker@cache@\pgf@anim@marker@id@list\endcsname#2% + \expandafter\global\expandafter\let\csname pgfanim@marker@cachel@\pgf@anim@marker@id@list\endcsname#3% + \fi% +}% + +\def\pgf@anim@marker@id@test#1#2{% + \pgfarrows@getid{#1}{#2}% + \expandafter\expandafter\expandafter\pgf@arrow@drawer@shift\csname pgf@ar@ends@\pgf@arrow@id\endcsname% + \xdef\pgf@anim@marker@id@list{\pgf@anim@marker@id@list:\pgf@arrow@id\ifpgfarrowswap s\else n\fi\pgfarrows@slant w\the\pgf@xc}% +}% + +\def\pgf@anim@drawer@marker#1#2{% + % Prepare: + {% + \pgfarrows@getid{#1}{#2}% + % Do shift: + \expandafter\expandafter\expandafter\pgf@arrow@drawer@shift\csname pgf@ar@ends@\pgf@arrow@id\endcsname% + % Do slant: + \ifdim\pgfarrows@slant pt=0pt% + \else% + \pgftransformxslant{\pgfarrows@slant}% + \fi% + % do swap: + \ifpgfarrowswap% + \pgftransformyscale{-1}% + \fi% + {% + \csname pgf@ar@saves@\pgf@arrow@id\endcsname% + \pgfsys@beginscope% + \pgf@arrows@color@setup% + \pgflowlevelsynccm\csname pgf@ar@cache@\pgf@arrow@id\endcsname% + \pgfsys@endscope% + }% + \expandafter}% + % Transform to next tip: + \expandafter\pgftransformxshift\expandafter{\the\pgf@xc}% +}% + + + + + + + + + +% The parsers + +% Parse no value +% +% #1 is ignored +% +% Example: +% +% entry = {0s}{5+6} + +\def\pgfanim@parse@type@none#1{% + \pgfsysanimvalnone% +}% + +% Parse a scalar +% +% #1 is a number parsed using \pgfmathparse +% +% Example: +% +% entry = {0s}{5+6} + +\def\pgfanim@parse@type@scalar#1{% + \pgfmathparse{#1}% + \expandafter\pgfsysanimvalscalar\expandafter{\pgfmathresult}% +}% + + +% Parse a dimension +% +% #1 is a number parsed using \pgfmathparse +% +% Example: +% +% entry = {0s}{5cm + 2pt} + +\def\pgfanim@parse@type@dimension#1{% + \pgfmathparse{#1}% + \expandafter\pgfsysanimvaldimension\expandafter{\pgfmathresult pt}% +}% + + + +% Parse a mapped text +% +% #1 is a text. It is looked up in \pgf@anim@map@#1 and gets replaced +% by what this macro expands to. +% +% Example: +% +% entry = {0s}{true} + +\def\pgfanim@parse@type@mapped#1{% + {% + \expandafter\let\expandafter\pgf@temp\csname pgf@anim@map@#1\endcsname\relax% + \ifx\pgf@temp\relax% + \pgferror{Value '#1' not allowed for an animation for this attribute}% + \let\pgf@temp\pgfutil@empty% + \fi% + \expandafter}% + \expandafter\pgfsysanimvaltext\expandafter{\pgf@temp}% +}% + + +% Parse a color +% +% #1 is a color +% +% entry = {0s}{blue!20} + +\def\pgfanim@parse@type@color#1{% + \pgfsysanimvalcolor{#1}% +}% + + +% Parse a path +% +% #1 is a sequence of pgf path construction commands +% +% entry = {0s}{\pgfpathmoveto{\pgfpointorigin} \pgfpathlineto{\pgfpoint{1cm}{1cm}}} + +\def\pgfanim@parse@type@path#1{% + {% + \pgfsyssoftpath@getcurrentpath\pgfanim@save@path% + \pgfsyssoftpath@setcurrentpath\pgfutil@empty% + #1% + \pgfsyssoftpath@getcurrentpath\pgfanim@the@path% + \pgfprocessround{\pgfanim@the@path}{\pgfanim@the@path}% + \global\let\pgfanim@the@path\pgfanim@the@path% + \pgfsyssoftpath@setcurrentpath\pgfanim@save@path% + }% + \expandafter\pgfanim@parse@type@softpath\expandafter{\pgfanim@the@path}% +}% + + +% Parse a softpath +% +% #1 is a sequence of pgf softpath token commands +% +% entry = {0s}{\pgfsyssoftpath@movetotoken {0pt}{0pt}\pgfsyssoftpath@linetotoken {10pt}{10pt}} + +\def\pgfanim@parse@type@softpath#1{% + {% + \def\pgf@anim@path{#1}% + % Shorten? + \pgf@worryfalse% + \pgf@xb=\pgf@anim@marker@extra@shorten@start% + \advance\pgf@xb by\pgf@anim@marker@shorten@start% + \pgf@xc=\pgf@anim@marker@extra@shorten@end% + \advance\pgf@xc by\pgf@anim@marker@shorten@end% + \ifdim\pgf@xb=0pt\else\pgf@worrytrue\fi% + \ifdim\pgf@xc=0pt\else\pgf@worrytrue\fi% + \ifpgf@worry% + \edef\pgf@path@shortening@distance@end{\the\pgf@xc}% + \edef\pgf@path@shortening@distance@start{\the\pgf@xb}% + % Yes! Need to shorten. + \pgf@anim@shorten@path% + \fi% + \expandafter}% + \expandafter\pgfsysanimvalpath\expandafter{\pgf@anim@path}% +}% + + +\def\pgf@anim@shorten@path{% + \pgf@precise@shorteningfalse% + \let\pgf@path@shortening@distance\pgf@path@shortening@distance@end% + % + % Step 1: Split + % + \pgfprocesssplitpath{\pgf@anim@path}% + \pgfprocesssplitsubpath{\pgfprocessresultpathsuffix}% + % + % Step 2: extract + % + \expandafter\pgf@parse@end\pgfprocessresultsubpathsuffix\pgf@stop\pgf@stop\pgf@stop% + % + % Step 3: prep end + % + \pgf@prep@end% + % + % Step 4: shorten end + % + \ifdim\pgf@path@shortening@distance=0pt\else\pgf@do@shorten@end\fi% + % + \let\pgf@path@shortening@distance\pgf@path@shortening@distance@start% + % + % Step 5: extract + % + \expandafter\pgf@parse@start\pgfprocessresultpathsuffix\pgf@stop\pgf@stop\pgf@stop% + % + % Step 6: prep start + % + \pgf@prep@start% + % + % Step 7: shorten start + % + \ifdim\pgf@path@shortening@distance=0pt\else\pgf@do@shorten@start\fi% + \expandafter\expandafter\expandafter\def% + \expandafter\expandafter\expandafter\pgf@anim@path% + \expandafter\expandafter\expandafter{\expandafter\pgfprocessresultpathprefix\pgfprocessresultpathsuffix}% +}% + + + +% Parse a point +% +% #1 is a point +% +% entry = {0s}{\pgfpoint{1cm}{2cm}} + +\def\pgfanim@parse@type@point#1{% + \pgf@process{#1}% + \pgf@protocolanimsizes\pgf@x\pgf@y% + \expandafter\expandafter\expandafter\pgfsysanimvaltranslate\expandafter\expandafter\expandafter{\expandafter\the\expandafter\pgf@x\expandafter}\expandafter{\the\pgf@y}% +}% + + +% Parse a scaling +% +% #1 = if #1 contains a comma, the two parts are passed to +% pgfmathparse, otherwise the whole thing is passed to pgfmathparse. +% +% entry = {0s}{2} +% entry = {0s}{2,1} + +\def\pgfanim@parse@type@scaling#1{% + \pgfutil@in@{,}{#1}% + \ifpgfutil@in@% + \pgfanim@unpack@comma#1\pgf@stop% + \else% + \pgfmathparse{#1}% + \expandafter\pgfsysanimvalscale\expandafter{\pgfmathresult}{\pgfmathresult}% + \fi% +}% +\def\pgfanim@unpack@comma#1,#2\pgf@stop{% + \pgfmathsetmacro{\pgf@anim@temp@x}{#1}% + \pgfmathsetmacro{\pgf@anim@temp@y}{#2}% + \expandafter\expandafter\expandafter\pgfsysanimvalscale\expandafter\expandafter\expandafter{\expandafter\pgf@anim@temp@x\expandafter}\expandafter{\pgf@anim@temp@y}% +}% + + + +% Parse a viewbox +% +% #1 consists of two points, each in parentheses +% +% entry = {0s}{{\pgfpoint{1cm}{2cm}}{\pgfpoint{3cm}{4cm}}} + +\def\pgfanim@parse@type@viewbox#1{% + {% + \pgfanim@unpack@viewbox#1% + \xdef\pgf@anim@caller{\noexpand\pgfsysanimvalviewbox{\the\pgf@x}{\the\pgf@y}{\the\pgf@xa}{\the\pgf@ya}}% + }% + \pgf@anim@caller% +}% +\def\pgfanim@unpack@viewbox#1#2{% + \pgfpointtransformed{#2}% + \pgf@xa\pgf@x% + \pgf@ya\pgf@y% + \pgfpointtransformed{#1}% + \ifdim\pgf@xa<\pgf@x% swap needed + \pgf@xb=\pgf@x% + \pgf@x=\pgf@xa% + \pgf@xa=\pgf@xb% + \fi% + \ifdim\pgf@ya<\pgf@y% swap needed + \pgf@yb=\pgf@y% + \pgf@y=\pgf@ya% + \pgf@ya=\pgf@yb% + \fi% +}% + + + + +% Parse a dash pattern +% +% #1,#2 is a dash pattern as in \pgfsetdash +% +% entry = {0s}{{{1pt}{2pt}{3pt}{1pt}}{1pt}} + +\def\pgfanim@parse@type@dash#1{\pgfanim@parse@type@dash@#1}% +\def\pgfanim@parse@type@dash@#1#2{% + \def\pgf@temp{}% + \def\pgf@next{\pgf@strip}% + \pgf@strip#1{pgf@stop}% + \expandafter\pgfsysanimvaldash\expandafter{\pgf@temp}{#2}% +}% + + + + + +% +% +% Bounding box adjustments in animations. +% +% + +\def\pgf@protocolanimsizes#1#2{% + \edef\pgf@temp{\noexpand\pgf@anim@handle@point{\the#1}{\the#2}}% + \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter\pgf@protocolanim@list\expandafter\expandafter\expandafter{\expandafter\pgf@protocolanim@list\pgf@temp}% +}% +\def\pgf@protocolanimsizes@direct#1#2{% + \expandafter\def\expandafter\pgf@protocolanim@list\expandafter{\pgf@protocolanim@list\pgf@anim@handle@point{#1}{#2}}% +}% +\let\pgf@protocolanim@list\pgfutil@empty + +\def\pgf@animation@check@translate{% + \ifx\pgf@protocolanim@list\pgfutil@empty% + \else% + \expandafter\expandafter\expandafter\pgf@animation@check@translate@\expandafter\expandafter\expandafter{\expandafter\pgf@protocolanim@list\expandafter}\expandafter{\pgf@anim@saved@transform}% + \fi% +}% +\let\pgf@animation@check@motion\pgf@animation@check@translate + +\def\pgf@animation@check@translate@#1#2{% + \pgfsys@attach@to@id\pgf@anim@whom@id\pgf@anim@whom@type{% + \pgf@size@hookedtrue% + {% + % Compute bounding box in local coordinate system + #2% + \pgf@pt@x0pt% + \pgf@pt@y0pt% + \pgf@picmaxx=-16000pt\relax% + \pgf@picminx=16000pt\relax% + \pgf@picmaxy=-16000pt\relax% + \pgf@picminy=16000pt\relax% + #1% + \edef\pgf@temp{{\the\pgf@picminx}{\the\pgf@picminy}{\the\pgf@picmaxx}{\the\pgf@picmaxy}}% + \expandafter% + }\expandafter% + \def\expandafter\pgf@temp\expandafter{\pgf@temp}% + \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter\pgf@path@size@hook\expandafter\expandafter\expandafter{% + \expandafter\pgf@path@size@hook\expandafter\pgf@anim@bb\pgf@temp}% + }{}% +}% +\def\pgf@anim@handle@point#1#2{% + \pgf@x#1\pgf@y#2% + \pgf@pos@transform\pgf@x\pgf@y% + \ifdim\pgf@x<\pgf@picminx\pgf@picminx\pgf@x\fi% + \ifdim\pgf@x>\pgf@picmaxx\pgf@picmaxx\pgf@x\fi% + \ifdim\pgf@y<\pgf@picminy\pgf@picminy\pgf@y\fi% + \ifdim\pgf@y>\pgf@picmaxy\pgf@picmaxy\pgf@y\fi% +}% + +\newdimen\pgf@anim@dim + +\def\pgf@anim@bb#1#2#3#4{% (#1,#2) lower left, (#3, #4) upper right + \pgf@anim@dim\pgf@size@hook@x% + \advance\pgf@anim@dim by#1\relax% + \ifdim\pgf@anim@dim<\pgf@picminx\global\pgf@picminx\pgf@anim@dim\fi% + \pgf@anim@dim\pgf@size@hook@x% + \advance\pgf@anim@dim by#3\relax% + \ifdim\pgf@anim@dim>\pgf@picmaxx\global\pgf@picmaxx\pgf@anim@dim\fi% + \pgf@anim@dim\pgf@size@hook@y% + \advance\pgf@anim@dim by#2\relax% + \ifdim\pgf@anim@dim<\pgf@picminy\global\pgf@picminy\pgf@anim@dim\fi% + \pgf@anim@dim\pgf@size@hook@y% + \advance\pgf@anim@dim by#4\relax% + \ifdim\pgf@anim@dim>\pgf@picmaxy\global\pgf@picmaxy\pgf@anim@dim\fi% +}% + +\def\pgf@anim@save@transform#1{% + \def\pgf@anim@saved@transform{#1}% +}% +\let\pgf@anim@saved@transform\pgfutil@empty% + +\endinput diff --git a/graphics/pgf/base/tex/generic/modules/pgfmodulebending.code.tex b/graphics/pgf/base/tex/generic/modules/pgfmodulebending.code.tex new file mode 100644 index 0000000000..d2ffa8b3c5 --- /dev/null +++ b/graphics/pgf/base/tex/generic/modules/pgfmodulebending.code.tex @@ -0,0 +1,356 @@ +% Copyright 2019 by Till Tantau +% +% This file may be distributed and/or modified +% +% 1. under the LaTeX Project Public License and/or +% 2. under the GNU Public License. +% +% See the file doc/generic/pgf/licenses/LICENSE for more details. + +\ProvidesFileRCS{pgfmodulebending.code.tex} + + +% +% This file defines commands for drawing bending arrows and lines. +% + +\usepgfmodule{nonlineartransformations}% +\usepgflibrary{curvilinear}% + + +% Configuration options for arrow tips: + +\pgfkeys{ + /pgf/arrow keys/flex/.code=\pgfarrowsaddtooptions{\pgf@arrows@the@rigidity#1pt\let\pgf@arrow@flex@mode\pgf@arrow@mode@is@flex}, + /pgf/arrow keys/flex/.default=1, + /pgf/arrow keys/flex'/.code=\pgfarrowsaddtooptions{\pgf@arrows@the@rigidity#1pt\let\pgf@arrow@flex@mode\pgf@arrow@mode@is@flex@prime}, + /pgf/arrow keys/flex'/.default=1, + /pgf/arrow + keys/bend/.code=\pgfarrowsaddtooptions{\let\pgf@arrow@flex@mode\pgf@arrow@mode@is@bend}, +}% + + +\def\pgf@arrow@mode@is@flex{1}% +\def\pgf@arrow@mode@is@flex@prime{2}% +\def\pgf@arrow@mode@is@bend{3}% + +\newdimen\pgf@arrows@the@rigidity + +% +% +% Handle the end of a curve +% +% + +% First, the preparation. This is, essentially, setting up the +% nonlinear transformation + +\def\pgf@prep@curveend{ + \pgftransformreset% + \pgfsetcurvilinearbeziercurve{\pgfpointlastonpath}{\pgfpointsecondlastonpath}{\pgfpointthirdlastonpath}{\pgfpointfourthlastonpath} +}% + +% Second, the shortening. + +\def\pgf@do@shorten@curvedend{% + \expandafter\expandafter\expandafter\pgf@do@shorten@curvedend@unpack\expandafter\pgfpointfourthlastonpath\pgfpointthirdlastonpath% +}% +\def\pgf@do@shorten@curvedend@unpack\pgfqpoint#1#2\pgfqpoint#3#4{% + \pgf@process{\pgfcurvilineardistancetotime{\pgf@path@shortening@distance}}% + % Compute new curve: + \pgfpointcurveattime{\pgf@x}{\pgfpointlastonpath}{\pgfpointsecondlastonpath}{\pgfpointthirdlastonpath}{\pgfpointfourthlastonpath}% + % \pgfx/y and \pgf xa/xb already correct. Need to setup remaining vector... + \pgf@xb#3% + \pgf@yb#4% + \pgf@xb\pgf@time@t\pgf@xb% + \pgf@yb\pgf@time@t\pgf@yb% + \advance\pgf@xb by\pgf@time@s\pgf@xc% + \advance\pgf@yb by\pgf@time@s\pgf@yc% + \edef\pgfprocessresultsubpathsuffix{% + \expandafter\noexpand\pgfsubpathfourthlasttoken{#1}{#2}% + \noexpand\pgfsyssoftpath@curvetosupportatoken{\the\pgf@xb}{\the\pgf@yb}% + \noexpand\pgfsyssoftpath@curvetosupportbtoken{\the\pgf@xa}{\the\pgf@ya}% + \noexpand\pgfsyssoftpath@curvetotoken{\the\pgf@x}{\the\pgf@y}}% + \expandafter\expandafter\expandafter\def% + \expandafter\expandafter\expandafter\pgfprocessresultpathsuffix% + \expandafter\expandafter\expandafter{\expandafter\pgfprocessresultsubpathprefix\pgfprocessresultsubpathsuffix}% +}% + +% Third, the drawing + +\def\pgf@do@draw@curvedend{% + \pgf@do@draw@curved\pgf@end@tip@sequence% +}% + +\def\pgf@do@draw@curved#1{% + {% + \pgftransformreset% + \pgf@xb\pgf@arrow@tip@total@length% xb will keep track of the total length + \let\pgf@arrow@handle\pgf@draw@curved% + \let\pgf@arrow@handle@dot\relax% + #1% + }% +}% + +\def\pgf@draw@curved#1#2{% + % Prepare: + {% + \pgfarrows@getid{#1}{#2}% + % Do shift: + \expandafter\expandafter\expandafter\pgf@arrow@drawer@rigid@shift\csname pgf@ar@ends@\pgf@arrow@id\endcsname% + \expandafter\let\expandafter\pgf@arrow@bending@mode\csname pgf@ar@bending@mode@#1\endcsname% + \ifx\pgf@arrow@bending@mode\pgfutil@empty\let\pgf@arrow@flex@mode\pgf@arrow@mode@is@flex\fi% + % do swap: + {% + \csname pgf@ar@saves@\pgf@arrow@id\endcsname% + \ifcase\pgf@arrow@flex@mode\relax% + \expandafter\expandafter\expandafter\pgf@arrow@drawer@rigid\csname pgf@ar@visual@\pgf@arrow@id\endcsname% like flex + \or% + \expandafter\expandafter\expandafter\pgf@arrow@drawer@rigid\csname pgf@ar@visual@\pgf@arrow@id\endcsname% + \or% + \expandafter\expandafter\expandafter\pgf@arrow@drawer@rigid\csname pgf@ar@ends@\pgf@arrow@id\endcsname% + \or% + \pgf@arrow@drawer@bend% + \fi% + % hull points inside the above + }% + \expandafter}% + % Transform to next tip: + \expandafter\pgf@xb\the\pgf@xb% +}% +\def\pgf@falsetext{false}% + +\def\pgf@arrow@drawer@rigid@shift#1#2#3{% tip end, back end, line end, sep + % Let xa be the actual back end of the current arrow plus the back end: + \advance\pgf@xb by#2% + \pgf@xa\pgf@xb% + % Update the xb: + \pgf@x#1% + \advance\pgf@x by\pgfarrowsep% + \advance\pgf@xb by-\pgf@x% +}% + + +\def\pgf@arrow@drawer@bend{% + \pgftransformnonlinear{\pgf@arrow@bending@mode{\pgf@x}{\pgf@y}} + \pgftransformscale{-1}% + \pgftransformxshift{-\pgf@xa}% + % Do slant: + \ifdim\pgfarrows@slant pt=0pt% + \else% + \pgftransformxslant{\pgfarrows@slant}% + \fi% + \ifpgfarrowswap% + \pgftransformyscale{-1}% + \fi% + {% + \pgf@relevantforpicturesizefalse% + \pgfscope% + \pgf@arrows@color@setup% + \csname pgf@ar@inst@code@\pgf@arrow@id\endcsname% + \endpgfscope% + }% + \pgf@arrows@bending@hull% +}% + +\def\pgf@arrow@drawer@rigid#1#2#3{% (rigid) start, (rigid) end, dummy + % Compute the rigid positions: + \pgf@x#1% start + \pgf@y#2% flexibility*(end-start) + start + \advance\pgf@y by-\pgf@x% + \pgf@y\pgf@sys@tonumber\pgf@arrows@the@rigidity\pgf@y% + \advance\pgf@y by\pgf@x% + % Are they equal? + \ifdim\pgf@x=\pgf@y% + % Yes, do tangent: + \pgf@bending@do@tangent% + \else% + \pgf@bending@do@flex% + \fi% + \pgf@xc#1% + \pgf@xc-\pgf@xc% + \pgftransformxshift{+\pgf@xc}% + % Do slant: + \ifdim\pgfarrows@slant pt=0pt% + \else% + \pgftransformxslant{\pgfarrows@slant}% + \fi% + % do swap: + \ifpgfarrowswap% + \pgftransformyscale{-1}% + \fi% + \pgfscope% + \pgf@arrows@color@setup% + \pgflowlevelsynccm\csname pgf@ar@cache@\pgf@arrow@id\endcsname% + \endpgfscope% + \pgf@arrows@rigid@hull% +}% + +\def\pgf@bending@do@tangent{% + \pgf@xb\pgf@xa% + \advance\pgf@xb by-\pgf@x% + \pgfcurvilineardistancetotime{\pgf@xb}% + \pgf@xb\pgf@x% + % Compute points: + \ifdim\pgf@xb=0pt% + \pgf@bending@possible@degenerate% + \else% + \pgf@bending@do@tangent@normal% + \fi% + \pgf@ya-\pgf@y% + \pgfsettransformentries% + {\pgf@sys@tonumber\pgf@x}{\pgf@sys@tonumber\pgf@y}% + {\pgf@sys@tonumber\pgf@ya}{\pgf@sys@tonumber\pgf@x}{\pgf@xc}{\pgf@yc}% +}% + +\def\pgf@bending@do@tangent@normal{% + \pgfpointcurveattime{\pgf@xb}{\pgf@curvilinear@line@a}{\pgf@curvilinear@line@b}{\pgf@curvilinear@line@c}{\pgf@curvilinear@line@d}% + \pgf@xc\pgf@x% save + \pgf@yc\pgf@y% save + % compute normal: + \advance\pgf@xb by-\pgf@xa% + \advance\pgf@yb by-\pgf@ya% + \pgf@process{\pgfpointnormalised{\pgf@x=\pgf@xb\pgf@y=\pgf@yb}} +}% + +\def\pgf@bending@possible@degenerate{% + \ifx\pgf@curvilinear@line@a\pgf@curvilinear@line@b% degenerate! + \ifx\pgf@curvilinear@line@a\pgf@curvilinear@line@c% double degenerate! + \let\pgf@ref\pgf@curvilinear@line@d% + \else + \let\pgf@ref\pgf@curvilinear@line@c% + \fi% + \pgf@process{\pgf@curvilinear@line@a}% + \pgf@xc\pgf@x% save + \pgf@yc\pgf@y% save + \pgf@process{\pgf@ref}% + \advance\pgf@x by-\pgf@xc + \advance\pgf@y by-\pgf@yc + \pgf@process{\pgfpointnormalised{}} + \pgf@x-\pgf@x + \pgf@y-\pgf@y + \else% + \pgf@bending@do@tangent@normal% + \fi% +}% + +\def\pgf@bending@do@flex{ + \pgf@xb\pgf@xa% + \pgf@xc\pgf@xa% + \advance\pgf@xc by-\pgf@x% + \advance\pgf@xb by-\pgf@y% + \pgfcurvilineardistancetotime{\pgf@xc}% + \pgf@xc\pgf@x% + \pgfcurvilineardistancetotime{\pgf@xb}% + \pgf@xb\pgf@x% + % Compute points: + \pgf@process{\pgfpointcurveattime{\pgf@xc}{\pgf@curvilinear@line@a}{\pgf@curvilinear@line@b}{\pgf@curvilinear@line@c}{\pgf@curvilinear@line@d}}% + \pgf@xc\pgf@x\pgf@yc\pgf@y% + \pgf@process{\pgfpointcurveattime{\pgf@xb}{\pgf@curvilinear@line@a}{\pgf@curvilinear@line@b}{\pgf@curvilinear@line@c}{\pgf@curvilinear@line@d}}% + \pgf@xb\pgf@x\pgf@yb\pgf@y% + \pgftransformarrow{\pgfqpoint{\pgf@xb}{\pgf@yb}}{\pgfqpoint{\pgf@xc}{\pgf@yc}}% +}% + + + + + + +% Convex hull stuff: + +\def\pgf@arrows@bending@hull{% + \ifpgf@relevantforpicturesize% + % Do bb update: + \expandafter\let\expandafter\pgf@temp\csname pgf@ar@hull@\pgf@arrow@id\endcsname% + \ifx\pgf@temp\pgfutil@empty\else% + \let\pgf@arrow@hull@point\pgf@arrow@bending@hull@point@first% + \pgf@temp% + \pgf@arrow@bending@update@bb% + \fi% + \fi% +}% + +\def\pgf@arrow@bending@hull@point@first#1#2{% + \pgfpointtransformednonlinear{\pgfqpoint{#1}{#2}}% + \pgf@xa\pgf@x\pgf@xb\pgf@x% + \pgf@ya\pgf@y\pgf@yb\pgf@y% + \let\pgf@arrow@hull@point\pgf@arrow@bending@hull@point@other% +}% +\def\pgf@arrow@bending@hull@point@other#1#2{% + \pgfpointtransformednonlinear{\pgfqpoint{#1}{#2}}% + \ifdim\pgf@x<\pgf@xa\pgf@xa\pgf@x\else\ifdim\pgf@x>\pgf@xb\pgf@xb\pgf@x\fi\fi% + \ifdim\pgf@y<\pgf@ya\pgf@ya\pgf@y\else\ifdim\pgf@y>\pgf@yb\pgf@yb\pgf@y\fi\fi% +}% + +\def\pgf@arrow@bending@update@bb{% + \ifdim\pgf@picmaxx=-16000pt\relax% + \global\pgf@picminx\pgf@xa% + \global\pgf@picmaxx\pgf@xb% + \global\pgf@picminy\pgf@ya% + \global\pgf@picmaxy\pgf@yb% + \else% + \ifdim\pgf@xa<\pgf@picminx\global\pgf@picminx\pgf@xa\fi% + \ifdim\pgf@ya<\pgf@picminy\global\pgf@picminy\pgf@ya\fi% + \ifdim\pgf@xb>\pgf@picmaxx\global\pgf@picmaxx\pgf@xb\fi% + \ifdim\pgf@yb>\pgf@picmaxy\global\pgf@picmaxy\pgf@yb\fi% + \fi% +}% + + + + +% +% +% Handle the start of a curve +% +% + + +% First, the preparation. + +\def\pgf@prep@curvedstart{ + \pgftransformreset% + \pgfsetcurvilinearbeziercurve{\pgfpointfirstonpath}{\pgfpointsecondonpath}{\pgfpointthirdonpath}{\pgfpointfourthonpath}% +}% + +% Second, the shortening. + +\def\pgf@do@shorten@curvedstart{% + \expandafter\expandafter\expandafter\pgf@do@shorten@curvedstart@unpack\expandafter\pgfpointthirdonpath\pgfpointfourthonpath% +}% +\def\pgf@do@shorten@curvedstart@unpack\pgfqpoint#1#2\pgfqpoint#3#4{% + \pgf@process{\pgfcurvilineardistancetotime{\pgf@path@shortening@distance}}% + % Compute new curve: + \pgfpointcurveattime{\pgf@x}{\pgfpointfirstonpath}{\pgfpointsecondonpath}{\pgfpointthirdonpath}{\pgfpointfourthonpath}% + % \pgfx/y and \pgf xa/xb already correct. Need to setup remaining vector... + \pgf@xb#1% + \pgf@yb#2% + \pgf@xb\pgf@time@t\pgf@xb% + \pgf@yb\pgf@time@t\pgf@yb% + \advance\pgf@xb by\pgf@time@s\pgf@xc% + \advance\pgf@yb by\pgf@time@s\pgf@yc% + \edef\pgfpointfirstonpath{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}% + \edef\pgfpointsecondonpath{\noexpand\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}% + \edef\pgfprocessresultpathsuffix{% + \noexpand\pgfsyssoftpath@movetotoken{\the\pgf@x}{\the\pgf@y}% + \noexpand\pgfsyssoftpath@curvetosupportatoken{\the\pgf@xa}{\the\pgf@ya}% + \noexpand\pgfsyssoftpath@curvetosupportbtoken{\the\pgf@xb}{\the\pgf@yb}% + \noexpand\pgfsyssoftpath@curvetotoken{#3}{#4}% + } + \expandafter\expandafter\expandafter\def% + \expandafter\expandafter\expandafter\pgfprocessresultpathsuffix% + \expandafter\expandafter\expandafter{\expandafter\pgfprocessresultpathsuffix\pgfsubpathend}% +}% + + +% Third, the drawing + +\def\pgf@do@draw@curvedstart{% + \pgf@do@draw@curved\pgf@start@tip@sequence% +}% + + + + + +\endinput diff --git a/graphics/pgf/base/tex/generic/modules/pgfmoduledatavisualization.code.tex b/graphics/pgf/base/tex/generic/modules/pgfmoduledatavisualization.code.tex new file mode 100644 index 0000000000..96686aa73f --- /dev/null +++ b/graphics/pgf/base/tex/generic/modules/pgfmoduledatavisualization.code.tex @@ -0,0 +1,3216 @@ +% Copyright 2019 by Till Tantau +% +% This file may be distributed and/or modified +% +% 1. under the LaTeX Project Public License and/or +% 2. under the GNU Public License. +% +% See the file doc/generic/pgf/licenses/LICENSE for more details. + +\ProvidesFileRCS{pgfmoduledatavisualization.code.tex} + +\usepgfmodule{oo,shapes}% +\usepgflibrary{fpu}% + + +% This module defines the basic framework for data visualization. + +% In order to visualize data, you first need data. The format for this +% data is not specified, indeed, different formats are possible. A +% data point is created each time the command \pgfdatapoint is +% used. The "parameters" of the data point are just the current values +% of the keys or macros in the current scope. +% +% A set of data points created using the \pgfdata command. +% When a data point is created, a number of signals are emitted, see +% the description of \pgfdatapoint. To actually visualize something, +% objects should be created that listen to these signals and that +% handle them. +% +% The following class manages a data visualization + +\pgfooclass{data visualization} +{% + % Class data visualization + % + % This class is used to "manage" a data visualization. It provides + % methods for hooking into the data visualization process and its + % constructor initializes the signals that are issued during a data + % visualization. + % + % When a data visualization object is created, a whole bunch of + % signal objects. You should then create objects that connect to + % these signals. They will be emitted when datapoints come + % available. + % + % It is permissible to have several data visualization objects + % active at the same time. + % + % To use a data visualization object, you should (possibly + % repeatedly) call the method add data() or the macro \pgfdata. You should also + % create transformation, mapping and visualization objects. Then, + % you should, first, call the method survey, which will "survey" the + % data, allowing the mapping and bounding objects to compute the + % correct ranges. You may then create further objects based on this + % data. Then, you should call the "visualize" method, which will + % invoke the visualization signals for the data points. + + + % These attribute store code that should be executed at certain + % points. The order is the following: + % + % 1. "before survey" code + % 2. "begin survey" phase signal + % 3. "at start survey" code + % 4. data points are processed + % 5. "at end survey" code + % 6. "end survey" phase signal + % 7. "after survey" code + % + % The first seven points will be repeated until the method "do + % another survey" is no longer called during a survey. + % + % 8. "before visualization" code + % 9. "begin visualization" phase signal + % 10. "at start visualization" code + % 11. data points are processed once more + % 12. "at end visualization" code + % 13. "end visualization" phase signal + % 14. "after visualization" code + \attribute before survey;% + \attribute at start survey;% + \attribute at end survey;% + \attribute after survey;% + \attribute before visualization;% + \attribute at start visualization;% + \attribute at end visualization;% + \attribute after visualization;% + + % + % Survey counts and handling + % + + \attribute survey count=1;% + % Each time a survey is done before the visualization is done, this + % count is incremented. Normally, there is only one survey, but + % objects may request another survey or even more surveys to be + % done, giving them a chance to setup further internal values. By + % accessing this count (via the get survey count method), you can + % find out which survey is currently being done. + + \attribute number of surveys=1;% + % By increasing this count, you can request additional surveys to be + % done. + + + % Stores the to-be-visualized data + \attribute data;% + + + % Stores the signal objects + \attribute prepare datapoint signal;% + \attribute map datapoint signal;% + \attribute transform datapoint signal;% + \attribute visualize datapoint signal;% + \attribute finish datapoint signal;% + \attribute survey datapoint signal;% + \attribute phase signal;% + \attribute path signal;% + \attribute direction signal;% + \attribute style signal;% + + + % Constructor + % + % Inits the signals + \method data visualization() { + \pgfoonew{prepare datapoint signal}=new signal()% + \pgfoonew{map datapoint signal}=new signal()% + \pgfoonew{transform datapoint signal}=new signal()% + \pgfoonew{visualize datapoint signal}=new signal()% + \pgfoonew{finish datapoint signal}=new signal()% + \pgfoonew{survey datapoint signal}=new signal()% + \pgfoonew{phase signal}=new signal()% + \pgfoonew{path signal}=new signal()% + \pgfoonew{direction signal}=new signal()% + \pgfoonew{style signal}=new signal()% + % + % Store this object in a key + % + \pgfoothis.get handle(\pgf@dv@me) + \pgfkeyslet{/pgf/data visualization/obj}\pgf@dv@me + }% + + % The phase signal will emit the following constants: + \def\pgfdvbeforesurvey{1}% + \def\pgfdvbeginsurvey{2}% + \def\pgfdvendsurvey{3}% + \def\pgfdvaftersurvey{4}% + \def\pgfdvbeforevisualization{5}% + \def\pgfdvbeginvisualization{6}% + \def\pgfdvendvisualization{7}% + \def\pgfdvaftervisualization{8}% + + % The path signale will meit the following constants: + \def\pgfdvpathmovetotoken{5}% + \def\pgfdvpathlinetotoken{6}% + \def\pgfdvdirectionfromtoken{7}% + \def\pgfdvdirectiontotoken{8}% + \def\pgfdvdirectionattoken{9}% + + % Method + % + % Connect the object #1's slot "#2" to the signal named "#3" + \method connect(#1,#2,#3) { + \pgfoovalueof{#3}.connect(#1,#2) + }% + + % Method + % + % Add data that is to be visualized. The code #1 should call the + % \pgfdatapoint macro for each data point it creates. + \method add data(#1) { + \pgf@dv@stripped@add#1\pgf@stop + }% + \def\pgf@dv@stripped@add#1\pgf@stop{% + \pgfooappend{data}{#1}% + }% + + + % Setters + \method before survey(#1) { + \pgfooappend{before survey}{#1} + }% + \method at start survey(#1) { + \pgfooappend{at start survey}{#1} + }% + \method before visualization(#1) { + \pgfooappend{before visualization}{#1} + }% + \method at start visualization(#1) { + \pgfooappend{at start visualization}{#1} + }% + + % Method + \method at end survey(#1) { + \pgfooprefix{at end survey}{#1} + }% + \method after survey(#1) { + \pgfooprefix{after survey}{#1} + }% + \method at end visualization(#1) { + \pgfooprefix{at end visualization}{#1} + }% + \method after visualization(#1) { + \pgfooprefix{after visualization}{#1} + }% + + + % Method + % + % Copy the signals to macros. This is just for efficiency (ha!) + \method prepare signal macros() { + \pgfooget{prepare datapoint signal}\pgf@signalpreparedatapoint + \pgfooget{map datapoint signal}\pgf@signalmapdatapoint + \pgfooget{transform datapoint signal}\pgf@signaltransformdatapoint + \pgfooget{visualize datapoint signal}\pgf@signalvisualizedatapoint + \pgfooget{finish datapoint signal}\pgf@signalfinishdatapoint + \pgfooget{survey datapoint signal}\pgf@signalsurveydatapoint + \pgfooget{phase signal}\pgf@signalphase + \pgfooget{path signal}\pgf@signalpath + \pgfooget{direction signal}\pgf@signaldirection + \pgfooget{style signal}\pgf@signalstyle + }% + + + % Survey method + % + % Call this method to "survey" the data. This should be done before + % the "visualize" method is called. + \method survey() { + % Survey phase. + \let\pgfdatapoint=\pgfdatapoint@surveyphase% + \pgfoothis.prepare signal macros() + \pgfooset{survey count}{1} + \pgfutil@loop + \pgf@signalphase.emit(\pgfdvbeforesurvey) + \pgfoovalueof{before survey}% + \pgf@signalphase.emit(\pgfdvbeginsurvey) + \pgfoovalueof{at start survey} + \pgfoovalueof{data}% + \pgfoovalueof{at end survey} + \pgf@signalphase.emit(\pgfdvendsurvey)% + \pgfoovalueof{after survey} + \pgf@signalphase.emit(\pgfdvaftersurvey) + \c@pgf@counta=\pgfoovalueof{survey count}\relax + \c@pgf@countb=\pgfoovalueof{number of surveys}\relax + \ifnum\c@pgf@counta<\c@pgf@countb + \advance\c@pgf@counta by1\relax + \pgfooeset{survey count}{\the\c@pgf@counta} + \pgfutil@repeat + }% + + % Getter + \method get survey count(#1) { + \pgfooget{survey count}{#1} + }% + + % Request another survey to be done + \method do an additional survey() { + \c@pgf@counta=\pgfoovalueof{number of surveys}\relax + \advance\c@pgf@counta by1\relax + \pgfooeset{number of surveys}{\the\c@pgf@counta} + }% + + % Visualize method + % + % This method will cause the actual visualization. + \method visualize() { + % Visualization phase. + \let\pgfdatapoint=\pgfdatapoint@visualizationphase% + \pgfoothis.prepare signal macros() + \pgf@signalphase.emit(\pgfdvbeforevisualization)% + \pgfoovalueof{before visualization} + \pgf@signalphase.emit(\pgfdvbeginvisualization)% + \pgfoovalueof{at start visualization} + \pgfoovalueof{data}% + \pgfoovalueof{at end visualization} + \pgf@signalphase.emit(\pgfdvendvisualization)% + \pgfoovalueof{after visualization} + \pgf@signalphase.emit(\pgfdvaftervisualization)% + }% +}% + + +% +% The data point keys +% +% Unlike other keys, the subkeys of /data point/ can simply be set +% directly. If the key has not been initialized, it will automatically +% be. +\pgfkeys{/data point/.unknown/.code={% + \pgfkeyssetvalue{/data point/\pgfkeyscurrentname}{#1} + }, +}% + + + + +% Create and handle a data point +% +% Description: +% +% This command is called by the survey and the visualize methods +% whenever a complete data point has been produced. Depending on the +% current circumstances, different signals will be emitted. +% +% The data that is represented by the data point is not given as a +% parameter. Rather, it is stored in macros and keys, that is, the +% data point is conceptually given by the settings of all the keys and +% macros in the local scope. +% +% There are two phases to data processing: In the survey phase data +% points are produced and handled in order to find out things like +% their number or the minimum and maximum values of attributes, so +% that axes and picture sizes can be prepared correctly. In the +% visualization phase, data point are actually shown. +% +% During the survey phase, for each data point the signal +% "surveydatapoint" is emitted. +% +% During the visualization phase, more signals are emitted. A prepare +% signal is emitted first, giving all objects a +% chance to "prepare" for the data point. Note that it is permissible +% for an object to manipulate the data point here (and also +% in later on). +% +% Next, the command \pgfdvmapdatapointtocanvas is called. Mainly, +% the effect of this command is to setup the keys /data point/canvas x +% and /data point/canvas y, see the description of this command for +% more details. +% +% The next step consists of signaling "visualize data point". Objects +% listening to this will cause some form of visualization of the +% data point to occur. +% +% Before the visualization is started, it is checked whether the key +% /data point/name is set (to a non-empty value). If so, +% a coordinate is created with the given canvas x and y values and +% this key's value as name. +% +% Finally, finish data point allows objects to do any final processing +% of the data point. + +\def\pgfdatapoint@surveyphase{% + \pgf@signalpreparedatapoint.emit()% + \pgf@signalmapdatapoint.emit()% + \pgf@signalsurveydatapoint.emit()% +}% + +\def\pgfdatapoint@visualizationphase{% + \pgf@signalpreparedatapoint.emit()% + \pgfdvmapdatapointtocanvas% + \pgfkeysifdefined{/data point/name} + {% + \pgfcoordinate{\pgfkeysvalueof{/data point/name}}{\pgfpointdvdatapoint}% + }{}% + \pgf@signalvisualizedatapoint.emit()% + \pgf@signalfinishdatapoint.emit()% +}% + + + +% Compute a position of a data point +% +% Description: +% +% This command uses a special signal to compute the position where a +% data point should be visualized on the canvas. In detail, the +% following happens: +% +% A local scope is created and the +% transformation matrix is reset. Then, two signals are emitted: First, +% "map data points" and then "transform data point". The first +% lets listening objects "map" the object by setting up +% attributes of the data point. The second asks objects +% listening to this signal to transform the current transformation +% matrix. After the signals, we compute where +% the origin lies inside this transformed coordinate system. Then the +% two keys /data point/canvas x and /data point/canvas y are set to +% the values of this position. The local scope ends (but the settings +% of the keys persist by a bit of magic), thus restoring the +% transformation matrix to its original value. + +\def\pgfdvmapdatapointtocanvas{% + {% + \pgfpointdvlocaldatapoint + % Smuggle outside group + \expandafter% + }% + \edef\pgf@marshal{% + \noexpand\pgfkeyssetvalue{/data point/canvas x}{\the\pgf@x} + \noexpand\pgfkeyssetvalue{/data point/canvas y}{\the\pgf@y} + }% + \pgf@marshal% +}% + +% Help functions for locating a canvas data point +% +% Description: +% +% The first function returns the data point computed by a +% canvasposition... call. + +\def\pgfpointdvdatapoint{% + \pgfqpoint{\pgfkeysvalueof{/data point/canvas x}}{\pgfkeysvalueof{/data point/canvas y}}% +}% + +\def\pgfpointdvlocaldatapoint{% + {% + % only returns the data point in \pgf@x and \pgf@y, does not set + % canvas x and canvas y + \pgf@signalmapdatapoint.emit()% + \pgftransformreset% + \pgf@signaltransformdatapoint.emit()% + \pgfpointtransformed{\pgfpointorigin}% + }% +}% + + + +% +% Special path constructions commands +% +% +% The following commands are used to construct paths based on +% datapoints. +% +% Normally, all these methods need to do is to compute the current +% canvas position of the current data point and then do a moveto, +% lineto or some other operation to this position. +% +% However, when the coordinate system is weird, like with polar +% coordinates or spherical or log-polar coordinate or whatever, a +% straight line "along an attribute" is no longer a straight line. +% In this case, two actions can be taken: +% +% 1. The problem can be ignored, resulting in a wrong line. +% 2. Some clever algorithm kicks in an replaces the straight line by +% the correct replacement. +% +% As always, when a computer tries to be clever, things can go +% wrong... Nevertheless, some support for the second method is +% given. This works as follows: Objects can register to the data +% visualization path signal. This signal is emitted every time the +% lineto or moveto commands are executed. If an object reacts to such +% a command and handles it, it should set +% \pgfdvhandledtrue. + +\newif\ifpgfdvhandled + +\def\pgfdv@generic#1#2#3{% + {% + \pgfdvhandledfalse% + \pgf@signalmapdatapoint.emit()% + #1.emit(#2)% + \ifpgfdvhandled% + \else% + \pgf@process{% + \pgftransformreset% + \pgf@signaltransformdatapoint.emit()% + \pgfpointtransformed{\pgfpointorigin}% + }% + #3{}% + \fi% + }% +}% + + +% Handle a moveto +% +% This command has no parameters since the target of the moveto is +% given by the canvas position of the current local data point. +\def\pgfpathdvmoveto{% + \pgfdv@generic{\pgf@signalpath}{\pgfdvpathmovetotoken}{\pgfpathmoveto} +}% + + +% Handle a lineto +% +% This command has no parameters since the target of the moveto is +% given by the canvas position of the current local data point. +\def\pgfpathdvlineto{% + \pgfdv@generic{\pgf@signalpath}{\pgfdvpathlinetotoken}{\pgfpathlineto} +}% + + + +% Compute a normalized vector at the current point pointing along a +% line from #1 to #2. +% +% #1 = code for setting the attributes of the first point +% #2 = code for setting the attributes of the second point +% +% The current point should be a point on a line from #1 to +% #2. Both points as well as the current point are +% localized on the canvas and a vector pointing from the first point +% to the second one is returned. However, for instance in polar +% coordinates the vector may actually "point along" the angle axis. +% +% Internally, this command first sets up the first data point and then +% emits a "direction" signal with the \pgfdvpathdirectionfromtoken set and +% then sets up the second point and emits the corresponding +% ...totoken. Finally, the \pgfdvpathdirectionattoken is emitted. +% + +\def\pgfpointdvdirection#1#2{% + { + { + #1 + \pgfdv@generic{\pgf@signaldirection}{\pgfdvdirectionfromtoken}{\xdef\pgf@dv@from{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}} + } + { + #2 + \pgfdv@generic{\pgf@signaldirection}{\pgfdvdirectiontotoken}{\xdef\pgf@dv@to{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}} + } + {\pgfdv@generic{\pgf@signaldirection}{\pgfdvdirectionattoken}{% + \pgf@process{ + \pgfpointnormalised{% + \pgfpointdiff{\pgf@dv@from}{\pgf@dv@to} + } + }}} + } +}% + + + + + +% +% +% Data parsing and formatting +% +% + + + +% Run the rendering pipeline on a dataset and/or save the dataset. +% +% #1 = options with path /pgf/data/ +% #2 = optionally data given inline in curly braces. +% +% Description: +% +% This command is used to define a data set. For a single data +% visualization, multiple data sets can be defined, they will +% accumulate. Data can be in different formats, as specified by the +% "format" key, and you can define new formats. +% +% The settings of the following keys are important: +% +% /pgf/data visualization/obj +% contains a handle to the dv-object +% +% /pgf/data/format +% stores the format (see below) +% +% /pgf/data/read from file +% determines where the data is. +% +% /pgf/data/store in group +% when nonempty, the data is stored here instead of being fed to the +% rendering pipeline +% +% /pgf/data/use group +% when nonempty, data that has previously been stored here using +% |define group| is read from here and all other keys are ignored +% +% If "read from file" is empty, the data is stored in the argument that +% follows. Otherwise, the data is read from the specified source file. +% This data is stored in an internal variable, which is local to the +% current group. +% +% After the group, the key "/pgf/data/continue code" will be +% executed. +% +% When the data is actually used later on (during a survey or a +% visualization), independently of what source is used, a format +% handler is started for each data set. This works as follows: first, +% the handler's startup code is executed. Then for each line of the +% data file/the data given inline, the line handler function is +% called. Finally, the data format end handler is called. +% +% The format handler's job is to call \pgfdatapoint each time a +% complete data point has been produced. +% +% Example: +% +% \pgfoonew \dv=new data visualization() +% \pgfoonew \obj=new attribute mapper(...) +% ... +% +% \pgfkeys{/pgf/data/.cd, +% column 1=dax/low, +% column 2=dax/high, +% column 3=dax/entry, +% column 4=dax/exit} +% +% \pgfdata[format=space separated columns] +% { +% % today +% 2000 2300 2100 2200 +% 2000 2350 2200 2500 +% 2200 2300 2250 2260 +% 1800 2260 2260 1900 +% 2000 2300 2100 2200 +% } +% +% \pgfdata[format=comma separated columns] +% { +% % yesterday +% 2000, 2350, 2200, 2250 +% 2200, 2300, 2250, 2260 +% } +% +% \pgfdata[read from file=data.csv,format=comma separated columns] +% +% \dv.survey() +% \dv.visualize() + +\def\pgfdata{\pgfutil@ifnextchar[{\pgf@datagroup@data@opt}{\pgf@datagroup@data@opt[]}}%}% +\def\pgf@datagroup@data@opt[#1]{% + % Ok, add one data thing... + \begingroup% + \pgfkeys{/pgf/data/.cd,/pgf/every data/.try,#1}% + \pgfkeysgetvalue{/pgf/data/continue code}\pgf@dv@cont@code% + \global\let\pgf@dv@cont@code\pgf@dv@cont@code% + \pgfkeysgetvalue{/pgf/data/use group}\pgf@dv@use@group% + \ifx\pgf@dv@use@group\pgfutil@empty% + \pgf@dv@do@adddata{\pgfkeysvalueof{/pgf/data visualization/obj}.add data(\pgf@do@data{#1})}% + \expandafter\pgf@datagroup@data@opt@cont% + \else% + \expandafter\ifx\csname pgf@dv@group@@\pgf@dv@use@group\endcsname\relax% + \pgferror{Undefined data group '\pgf@dv@use@group'}% + \else% + {% + \pgfkeys{/pgf/data/define group/.code=}% no redefinitions during use + \csname pgf@dv@group@@\pgf@dv@use@group\endcsname% + }% + \fi% + \endgroup% + \expandafter\pgf@dv@cont@code% + \fi% +}% +\def\pgf@datagroup@data@opt@cont{% + \pgfkeysgetvalue{/pgf/data/format}\pgf@dv@format% + \expandafter\let\expandafter\pgf@dv@format@catcodes\csname pgfdv@format@\pgf@dv@format @catcodes\endcsname% + \ifx\pgf@dv@format@catcodes\relax + \pgferror{Unknown data format '\pgf@dv@format'}% + \else% + \pgfkeysgetvalue{/pgf/data/read from file}\pgf@dv@source% + \ifx\pgf@dv@source\pgfutil@empty% + \let\pgf@next\pgf@datagroup@grab@inline% + \else% + \let\pgf@next\pgf@datagroup@grab@external% + \fi% + \pgf@next% +}% + +\def\pgf@datagroup@grab@inline{% + \pgfutil@ifnextchar\bgroup{% + \begingroup% + \catcode`\^^M=\active% + \pgf@dv@format@catcodes% + \pgf@datagroup@grab@@inline}% + {\pgferror{Opening brace expected}}% +}% +\def\pgf@datagroup@grab@external{% + \fi% + \endgroup% + \pgf@dv@do@adddata{\pgfkeysvalueof{/pgf/data visualization/obj}.add data({{{\pgf@datagroup@do@external}}})}% + \pgf@dv@cont@code% +}% + +\def\pgf@dv@do@adddata#1{% + \pgfkeysgetvalue{/pgf/data/store in group}\pgf@dv@temp% + \ifx\pgf@dv@temp\pgfutil@empty% + #1% + \else% + \expandafter\pgfutil@g@addto@macro\csname pgf@dv@group@@\pgf@dv@temp\endcsname{#1}% + \fi% +}% + +{\catcode`\^=7 \catcode`\^^M=13 + \gdef\pgf@datagroup@grab@@inline#1{% + \endgroup% + \pgf@dv@do@adddata{\pgfkeysvalueof{/pgf/data visualization/obj}.add data({{{\pgf@datagroup@do@inline#1^^M\pgf@@eol}}})}% + \fi% + \endgroup% + \pgf@dv@cont@code% + }% +}% +\def\pgf@@eol{\pgf@eol}% + +\def\pgf@do@data#1#2{% + \begingroup% + \pgfkeys{/pgf/data/.cd,/pgf/every data/.try,#1}% + \pgfkeysgetvalue{/pgf/data/format}\pgf@dv@format% + \expandafter\let\expandafter\pgf@dv@format@line\csname pgfdv@format@\pgf@dv@format @line\endcsname% + \expandafter\let\expandafter\pgf@dv@format@emptyline\csname pgfdv@format@\pgf@dv@format @empty\endcsname% + \csname pgfdv@format@\pgfkeysvalueof{/pgf/data/format}@startup\endcsname% + #2% + \csname pgfdv@format@\pgfkeysvalueof{/pgf/data/format}@end\endcsname% + \endgroup% +}% + + +% +% Read external file +% + +\def\pgf@datagroup@do@external{% + \csname pgfdv@format@\pgfkeysvalueof{/pgf/data/format}@catcodes\endcsname% + \immediate\openin\r@pgf@reada=\pgfkeysvalueof{/pgf/data/read from file} % + \ifeof\r@pgf@reada\relax + \pgferror{Data file '\pgfkeysvalueof{/pgf/data/read from file}' not found}% + \else + \pgf@datagroup@readline% + \fi + \immediate\closein\r@pgf@reada% +}% + +\def\pgf@partext{\par}% +\def\pgf@datagroup@readline{% + \endlinechar=-1 % suppress white space at end; this is local to `\pgfdata` + \immediate\read\r@pgf@reada to \pgf@temp% + \ifx\pgf@temp\pgf@partext% + \pgf@dv@format@emptyline% + \else% + \ifx\pgf@temp\pgfutil@empty% + \pgf@dv@format@emptyline% + \else% + \expandafter\pgf@dv@format@line\pgf@temp\pgfeol% + \fi% + \fi% + \ifeof\r@pgf@reada\else\expandafter\pgf@datagroup@readline\fi% +}% + + +% +% Read inline data +% + +\def\pgf@datagroup@do@inline{% + \pgf@dv@handle@line% +}% + +{\catcode`\^=7 \catcode`\^^M=13 +\gdef\pgf@dv@handle@line{% + \pgfutil@ifnextchar^^M{\pgf@dv@format@emptyline\expandafter\pgf@dv@handle@line\pgfutil@gobble}% + {\pgfutil@ifnextchar\pgf@@eol{\pgfutil@gobble}{\pgf@dv@handle@nonemptyline}}% +}% +\gdef\pgf@dv@handle@nonemptyline#1^^M{% + \pgf@dv@format@line#1\pgfeol% + \pgf@dv@handle@line% +}% +}% + +\pgfkeys{ + /pgf/data/data visualization obj/.initial=\undefined, + /pgf/data/format/.initial=table,% the default format + /pgf/data/read from file/.initial=, + /pgf/data/inline/.style={read from file=}, + /pgf/data/continue code/.initial=, + /pgf/data/store in group/.initial=, + /pgf/data/new group/.code={\expandafter\global\expandafter\let\csname pgf@dv@group@@#1\endcsname\pgfutil@empty}, + /pgf/data/use group/.initial=, +}% + + + + + + +% Define a data format +% +% #1 = format name +% #2 = catcode code +% #3 = startup code +% #4 = line arguments +% #5 = line code +% #6 = empty line code +% #7 = end code +% +% Description: +% +% This command defines a new data format for data visualization. When +% a data set is visualized and the format is set to #1, this handler +% is used to parse the data. +% +% In detail, the \pgfdata command will select a source. Before this +% source is read, #2 will be executed to setup the +% catcodes. Additionally, each time the data is parsed, #3 will be +% called. Then, for each nonempty line of the source, the +% command #5 is executed, where the line will be matched against the +% argument pattern given in #4. For empty lines, #6 will be executed +% instead. At the end of the source, #7 will be executed. + +\def\pgfdeclaredataformat#1#2#3#4#5#6#7{% + \expandafter\def\csname pgfdv@format@#1@catcodes\endcsname{#2}% + \expandafter\def\csname pgfdv@format@#1@startup\endcsname{#3}% + \expandafter\def\csname pgfdv@format@#1@line\endcsname#4\pgfeol{#5}% + \expandafter\def\csname pgfdv@format@#1@empty\endcsname{#6}% + \expandafter\def\csname pgfdv@format@#1@end\endcsname{#7}% +}% + + + +% +% Predefined standard formats +% + +% TeX code format +% +% Description: +% +% The lines of the data set are assumed to contain executable TeX +% code that will call \pgfdatapoint. +% +% Example: +% +% \pgfdatavisualizationrender[format=TeX code] +% \dataset{ +% \pgfkeyssetvalue{/data point/x}{5} +% \pgfkeyssetvalue{/data point/y}{5} +% \pgfdatapoint +% \pgfkeyssetvalue{/data point/x}{6} +% \pgfkeyssetvalue{/data point/y}{6} +% \pgfdatapoint +% } + +\pgfdeclaredataformat{TeX code}{}{}{#1}{#1 }{}{}% + + + +% Key-value lines format +% +% Description: +% +% The lines of the data set are passed to \pgfkeys with the path set +% to /data point. +% +% Example: +% +% \data[format=named] { +% x=5, y=6, hi=9 +% x=7, y=6, lo=10 +% } + +\pgfdeclaredataformat{named}{}{}{#1}{\pgf@dv@parse@named#1,\pgf@stop}{}{}% +\def\pgf@dv@parse@named{% + \pgfutil@ifnextchar\pgf@stop{\pgfdatapoint\pgfutil@gobble}{\pgf@dv@parse@named@} +}% +\def\pgf@dv@parse@named@#1=#2,#3\pgf@stop{% + \foreach \pgf@dv@temp in {#2} {% + \def\pgf@dv@tempb{/data point/#1=}% + \expandafter\expandafter\expandafter\pgfkeys% + \expandafter\expandafter\expandafter{\expandafter\pgf@dv@tempb\pgf@dv@temp}% + \pgf@dv@parse@named#3\pgf@stop% + } +}% + + + + + +% Table format +% +% Description: +% +% A table consists of a head line, which contains the attribute names +% of the values that will be found in the following lines. Each line +% (except for the headline) contains one data point. +% +% Inside each line the data points are separated by a separator like a +% comma or a space or a colon. The separator can be configured using +% the key /pgf/data/separator (leave the separator empty or set it to +% \space for a space as separator). The default separator is a comma. +% +% Some tables will miss the headline. In this case, using the +% /pgf/data/headline key you can provide a headline yourself. +% +% Example: +% +% \data[format=table] { +% x y +% 10 5 +% 11 6 +% 6 7 +% } +% +% \data[format=table,separator={,}] { +% x, y +% 10, 5 +% 11, 6 +% 6, 7 +% } +% +% \data[format=table,headline=x y] { +% 10 5 +% 11 6 +% 6 7 +% } + +\pgfdeclaredataformat{table} +{} % no catcodes +{ + \pgfkeysgetvalue{/pgf/data/headline}\pgf@dv@headline + \pgfkeysgetvalue{/pgf/data/separator}\pgf@dv@separator + \ifx\pgf@dv@separator\pgfutil@empty + \let\pgf@dv@separator\space + \fi + \ifx\pgf@dv@separator\pgf@dv@spacetext + \let\pgf@dv@separator\space + \fi + \expandafter\def\expandafter\pgf@dv@till@separator% + \expandafter##\expandafter1\pgf@dv@separator{\pgf@dv@table@handle{##1}} + \expandafter\def\expandafter\pgf@dv@till@separator@head% + \expandafter##\expandafter1\pgf@dv@separator{\pgf@dv@head@handle{##1}} + \ifx\pgf@dv@headline\pgfutil@empty + % Ok, read headline from file + \pgfdv@firstlinetrue + \else + % Headline already set + \pgfdv@firstlinefalse + \expandafter\pgf@dv@parse@headline\expandafter{\pgf@dv@headline} + \fi +} +{#1} % special line pattern, so read everything +{ + \ifpgfdv@firstline + \pgfdv@firstlinefalse + \pgf@dv@parse@headline{#1} + \else + { + \c@pgf@counta=0\relax% keep track of attribute number + \def\pgf@marshal{\pgf@dv@parse@table#1} + \expandafter\expandafter\expandafter\pgf@marshal% + \expandafter\pgf@dv@separator\expandafter\pgfeol\pgf@dv@separator + \pgfdatapoint + } + \fi +} +{}% ignore empty lines +{}% no special end code + +\newif\ifpgfdv@firstline + +\pgfkeys{ + /pgf/data/separator/.initial={,}, + /pgf/data/headline/.initial= +}% +\def\pgf@dv@spacetext{\space}% + +\def\pgf@dv@parse@table{% + \pgfutil@ifnextchar"% Special, but ignored right now... + {\pgferror{csv with quotes not yet implemented}} + {\pgf@dv@till@separator} +}% +\def\pgf@dv@table@handle#1{% + \def\pgf@temp{#1}% + \ifx\pgf@temp\pgfeoltext% + % Bingo! Stop + \let\pgf@next=\relax% + \else% + \advance\c@pgf@counta by1\relax% + \pgfkeysgetvalue{/pgf/data/table/attribute \the\c@pgf@counta}\pgf@dv@target + \ifx\pgf@dv@target\relax% + \edef\pgf@dv@target{attribute \the\c@pgf@counta} + \pgfkeyslet{/pgf/data/table/attribute \the\c@pgf@counta}\pgf@dv@target + \fi + \pgfkeyssetvalue{/data point/\pgf@dv@target}{#1} + \let\pgf@next=\pgf@dv@parse@table% + \fi% + \pgf@next% +}% + + + +\def\pgf@dv@parse@headline#1{ + \c@pgf@counta=0\relax% keep track of attribute number + \def\pgf@marshal{\pgf@dv@doparse@headline#1} + \expandafter\expandafter\expandafter\pgf@marshal% + \expandafter\pgf@dv@separator\expandafter\pgfeol\pgf@dv@separator +}% + +\def\pgf@dv@doparse@headline{% + \pgfutil@ifnextchar"% Special, but ignored right now... + {\pgferror{csv with quotes not yet implemented}} + {\pgf@dv@till@separator@head} +}% +\def\pgf@dv@head@handle#1{% + \def\pgf@temp{#1}% + \ifx\pgf@temp\pgfeoltext% + % Bingo! Stop + \let\pgf@next=\relax% + \else% + \advance\c@pgf@counta by1\relax% + \pgfkeyssetvalue{/pgf/data/table/attribute \the\c@pgf@counta}{#1} + \let\pgf@next=\pgf@dv@doparse@headline% + \fi% + \pgf@next% +}% + +\def\pgfeoltext{\pgfeol}% + + +% +% +% Micro math kernel for dv +% +% + +% The idea behind the following methods is to "abstract away" which +% math engine is used. In the future, something fancy build on luatex +% might be used, so it would we should not rely directly on the +% fpu library code. +% +% The system works as follows: First, you "enter" a number into the +% engine. When you do so, it is converted into some internal +% representation that cannot be accessed any more and is called a +% "math variable" henceforth. All commands of the +% math system only work on math variables. +% +% In order to "exit" a value stored in a math variable from the +% engine, some other macros can be used. +% +% Operations like addition or multiplication take two math variables +% and store the result in another math variable. + + + +% Store a value in a math variable of the math engine +% +% #1 = a math variable (a macro name) that is to store the value +% #2 = an expression describing a number +% +% +% Description: +% +% The #2 should be an expression that evaluates to a number. However, +% due to the way the pgf math engine(s) work, currently, there are +% several restrictions/the syntax is not as simple as one might +% hope. Fortunately, the syntax is hopefully compatible with +% easier-to-use future systems. +% +% The syntax is as follows: #2 may be +% +% 1. a simple high-precision number. This number is currently directly +% passed to \pgfmathfloatparsenumber, so typical permissible values +% are "100" or "10000000000000" or "nan" or "-5.673543345345e9000" +% +% 2. a mathematical expression, which *must* be surrounded by +% parentheses. From a "mathematical" point of view these +% parentheses are superfluous, but the they are used to help pgf +% distinguish expressions from high-precision numbers. (If, in the +% future, expressions may contain high precision numbers, the +% parentheses will simply be ignored.) +% +% The expression is evaluated using \pgfmathparse, which means that +% TeX-precision is used. This means, for instance, that numbers +% larger than about 16500 will create error messages. +% +% The advantage is that you may say write things like "(5+6)" or +% "(pi/2)". +% +% 3. In case #2 starts with an exclamation mark (indicating an +% "escape"), everything following the mark is simply evaluated by +% TeX. The effect of evaluating #2 should be that the math +% variable "\pgfdvmathvalue" is set to some value. This value will +% then be transferred to #1. +% +% 4. In case #2 is the result of calling \pgfdvmathexitbyserializing, the +% "serialized" value is restored. The idea is that the serialized +% will turn its internal representation of a number into something +% that can be stored as a text somewhere. +% +% Currently, "@" at the beginning of #2 is used to indicate that +% what follows is a serialized version of a math variable. +% +% In order to determine which of the above cases is to be used for a +% variable, the first symbol #2 is checked, *after* the first symbol +% of #2 has been expanded once (using \expandafter once). +% +% Example: +% +% \pgfdvmathenter{\mymacro}{20000000000000} +% \pgfdvmathenter{\mymacro}{(5+pi)} +% \pgfdvmathenter{\mymacro}{!\pgfdvmathadd{\pgfdvmathvalue}{\a}{\b}} +% +% \pgfdvmathenter{\foo}{5} +% \pgfdvmathexitbyserializing{\somevalue}{\foo} +% \pgfdvmathenter{\mymacro}{\somevalue} + +\def\pgfdvmathenter#1#2{% + \expandafter\pgfdvmathenter@check\expandafter#1#2\pgfdvmath@end% +}% +\def\pgfdvmathenter@check#1{% + \pgfutil@ifnextchar @{\pgfdvmath@internal#1}{% + \pgfutil@ifnextchar ({\pgfdvmath@expression#1}{%) + \pgfutil@ifnextchar !{\pgfdvmath@execute#1}{\pgfdvmath@normal#1}}}% +}% + +\def\pgfdvmath@internal#1@#2\pgfdvmath@end{% + \def#1{#2}% +}% +\def\pgfdvmath@expression#1(#2)#3\pgfdvmath@end{% + \pgfmathparse{#2}% + \pgfmathfloatparsenumber{\pgfmathresult}% + \let#1=\pgfmathresult% +}% +\def\pgfdvmath@execute#1!#2\pgfdvmath@end{% + #2% + \let#1=\pgfdvmathvalue% +}% +\def\pgfdvmath@normal#1#2\pgfdvmath@end{% + \pgfmathfloatparsenumber{#2}% + \let#1=\pgfmathresult% +}% + + + +% Print a math variable in the math engine using \pgfmathprintnumber +% +% #1 = math variable +% +% Description: +% +% See the description of \pgfmathprintnumber +% +% Example: +% +% \pgfdvmathenter{\mymacro}{20000000000000} +% \pgfdvmathexitbyprinting{\mymacro} + +\def\pgfdvmathexitbyprinting#1{% + \pgfmathprintnumber{#1}% +}% + + + +% Exit a math variable as a number in a scientific and portable number +% format in a macro +% +% #1 = macro in which to store the value +% #2 = math variable +% +% Description: +% +% You can exit a number using this operation. You can then directly +% reenter the number using \pgfdvmathenter +% +% Example: +% +% \pgfdvmathenter{\mynumber}{20000000000000} +% \pgfdvmathexitbyscientificformat{\mymacro}{\mynumber} + +\def\pgfdvmathexitbyscientificformat#1#2{% + \edef#1{\pgfmathfloatvalueof#2}% +}% + + + +% Exit a math variable as a "serialized" (= string-based) +% version. +% +% #1 = macro in which to store the value +% #2 = math variable +% +% Description: +% +% The serialized version can be reentered using +% \pgfmathenter and is similar to \pgfdvmathexitbyscientificformat, +% but exiting and entering are (a bit) faster. +% +% Example: +% +% \pgfdvmathenter{\mynumber}{20000000000000} +% \pgfdvmathexitbyserializing{\mymacro}{\mynumber} + +\def\pgfdvmathexitbyserializing#1#2{% + \edef#1{@#2}% +}% + + + + + +% Operations on two numbers +% +% #1 = macro that should store the result of applying the +% corresponding operation to the following: +% #2 = entered number +% #3 = entered number +% +% Description: +% +% Sets #1 to the result of applying an operation to #2 and #3 +% +% Example: +% +% \pgfdvmathenter{\a}{2000} +% \pgfdvmathenter{\b}{5+6} +% \pgfdvmathadd{\c}{\a}{\b} + +\def\pgfdvmathadd#1#2#3{% + \pgfmathfloatadd{#2}{#3}% + \let#1=\pgfmathresult% +}% +\def\pgfdvmathsub#1#2#3{% + \pgfmathfloatsubtract{#2}{#3}% + \let#1=\pgfmathresult% +}% +\def\pgfdvmathmul#1#2#3{% + \pgfmathfloatmultiply{#2}{#3}% + \let#1=\pgfmathresult% +}% +\def\pgfdvmathdiv#1#2#3{% + \pgfmathfloatdivide{#2}{#3}% + \let#1=\pgfmathresult% +}% + +% Multiply by small number +% +% #1 = macro that should store the result of applying the +% corresponding operation to the following: +% #2 = entered number +% #3 = fixed point number like 2 or 0.5, but not 10000000000 +% +% Description: +% +% Sets #1 to the result of applying an operation to #2 and #3 +% +% Example: +% +% \pgfdvmathenter{\a}{2000} +% \pgfdvmathadd{\b}{\a}{0.2} +% % \b now stores 400 + +\def\pgfdvmathmulfixed#1#2#3{% + \pgfmathfloatmultiplyfixed{#2}{#3}% + \let#1=\pgfmathresult% +}% + + + +% Log, ln and exponent functions +% +% #1 = a math variable +% #2 = a math variable +% +% Description +% +% \pgfdvmathln will compute #1 = ln(#2) +% \pgfdvmathlog will compute #1 = log_{10}(#2) +% \pgfdvmathexp will compute #1 = exp(#2) +% \pgfdvmathpowten will compute #1 = 10^{#2} + +\def\pgfdvmathln#1#2{% + \pgfmathfloatln{#2}% + \let#1=\pgfmathresult% +}% + +\def\pgfdvmathlog#1#2{% + \pgfmathfloatln{#2}% + \pgfmathfloatmultiply{\pgfmathresult}{\pgfdvmathalwaysloge}% + \let#1=\pgfmathresult% +}% + +\def\pgfdvmathexp#1#2{% + \pgfmathfloatexp{#2}% + \let#1=\pgfmathresult% +}% + +\def\pgfdvmathpowten#1#2{% + \pgfmathfloattofixed{#2}% + \let\pgf@dv@temp=\pgfmathresult% + \pgfmathround{\pgf@dv@temp}% + \let\pgf@dv@temp@round=\pgfmathresult% + \pgf@x=\pgf@dv@temp@round pt% + \pgf@x=-\pgf@x% + \advance\pgf@x by\pgf@dv@temp pt\relax% + \ifdim\pgf@x>0.0002pt% + \pgfmathfloatmultiply{#2}{\pgfdvmathalwayslnten}% + \pgfmathfloatexp{\pgfmathresult}% + \else + \ifdim\pgf@x<-0.0002pt% + \pgfmathfloatmultiply{#2}{\pgfdvmathalwayslnten}% + \pgfmathfloatexp{\pgfmathresult}% + \else + \pgf@x=\pgf@dv@temp@round sp% + \c@pgf@counta=\pgf@x% + \pgfmathfloatcreate{1}{1.0}{\the\c@pgf@counta}% + \fi + \fi + \let#1=\pgfmathresult% +}% + + +% Floor functions +% +% #1 = a math variable +% #2 = a math variable +% +% Description +% +% Sets #1 to the largest integer less or equal to #2 + +\def\pgfdvmathfloor#1#2{% + \pgfmathifisint{#2}{% + \let#1=#2% + }{% + \pgfmathfloatgetflags#2\c@pgf@counta% + \ifcase\c@pgf@counta + \let#1=\pgfdvmathalwayszero% + \or + \pgfmathfloatfloor{#2}% + \let#1=\pgfmathresult% + \or + \pgfmathfloatsubtract{\pgfdvmathalwaysone}{#2}% + \pgfmathfloatfloor{\pgfmathresult}% + \pgfmathfloatsubtract{\pgfdvmathalwayszero}{\pgfmathresult}% + \let#1=\pgfmathresult% + \or\let#1=#2% + \or\let#1=#2% + \or\let#1=#2% + \fi% + }% +}% + + +% Advanced unary operations +% +% #1 = macro that should store the result of applying the +% operation to an entered number +% #2 = the operation (as a string like "ln") +% #3 = entered number +% +% Description: +% +% Sets #1 to the result of applying #2 to #3 +% +% Example: +% +% \pgfdvmathenter{\a}{2000} +% \pgfdvmathunaryop{\b}{ln}{\a} + +\def\pgfdvmathunaryop#1#2#3{% + \csname pgfmathfloat#2\endcsname{#3}% + \let#1=\pgfmathresult% +}% + + +% Advanced binary operations +% +% #1 = macro that should store the result of applying the +% operation to two entered number +% #2 = the operation (as a string like "veclen") +% #3 = first argument (an entered number) +% #4 = second argument (an entered number) +% +% Description: +% +% Sets #1 to the result of applying #2 to #3 +% +% Example: +% +% \pgfdvmathenter{\a}{3} +% \pgfdvmathenter{\b}{4} +% \pgfdvmathbinop{\c}{veclen}{\a}{\b} + +\def\pgfdvmathbinop#1#2#3#4{% + \csname pgfmathfloat#2\endcsname{#3}{#4}% + \let#1=\pgfmathresult% +}% + + + +% Comparing values +% +% #1 = first math variable +% #2 = second math variable +% #3 = code to-be-executed if #1 < #2 +% #4 = code to-be-executed if #1 >= #2 +% +% +% Example: +% +% \pgfdvmathenter{\a}{3} +% \pgfdvmathenter{\b}{4} +% \pgfdvmathifless{\a}{\b}{yes}{should not happen} + +\def\pgfdvmathifless#1#2#3#4{% + \pgfmathfloatlessthan{#1}{#2}% + \ifpgfmathfloatcomparison#3\else#4\fi% +}% + + + + +% +% +% Standard objects for data visualization +% +% + + +% +% Transformers +% + +\pgfooclass{linear transformer} +{% + % Class linear transformer + % + % This class is a transformer class. It reacts to the transform + % datapoint signal. When this signal is raised, it will shift the + % coordinate system as follows: If the current value of /data + % point/this.attribute is 0, then the system is shifted to the + % current value of this.origin. If the current value is 1, the + % system is shifted to this.origin + this.unit vector. For other + % values of /data point/this.attribuate, the shift is interpolated + % between these two values. + + + \attribute attribute;% + % The attribute (/data point/this.attribute) by which + % the unit vector is multiplied. If it is empty (which is + % different from 0), no transformation is done at all. + + \attribute origin = \pgfpointorigin;% + % The shift in case the /data point/this.attribute is 0. + + \attribute unit vector;% + % The coordinate system is additionally shifted by this amount + % times the current value of /data point/this.attribute. + + + % Constructor + % + % #1 = attribute that is being transformed. Example: x + % #2 = a vector corresponding to one unit of #1. + % Example: \pgfpoint{1cm}{0cm} + \method linear transformer(#1,#2) { + \pgfooset{attribute}{#1} + \pgf@process{#2} + \edef\pgf@temp{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}} + \pgfoolet{unit vector}\pgf@temp% + }% + + % Method + \method default connects() { + \pgfoothis.get handle(\pgf@dv@me) + \pgfkeysvalueof{/pgf/data visualization/obj}.connect(\pgf@dv@me,transform,transform datapoint signal) + }% + + % Setter + \method set origin(#1) { + \pgf@process{#1} + \edef\pgf@temp{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}} + \pgfoolet{origin}\pgf@temp% + }% + + % Slot + % + % This slot should be connected to the transform datapoint + % signal. When this signal is emitted, the coordinate system will be + % shifted according to the current value of the attribute. + \method transform() { + \pgfkeysgetvalue{/data point/\pgfoovalueof{attribute}}\pgf@dv@val% + \ifx\pgf@dv@val\pgfutil@empty% + \else% + \ifx\pgf@dv@val\relax% + \else% + \pgfdvmathenter{\pgf@dv@math@var}{\pgf@dv@val}% + \pgfdvmathexitbyscientificformat{\pgf@dv@val}{\pgf@dv@math@var}% + \pgftransformshift{\pgfoovalueof{origin}}% + \pgfmathfloatgetexponent\pgf@dv@math@var\c@pgf@counta% + \ifnum\c@pgf@counta<-3\relax% + \else% + \pgftransformshift{\pgfpointscale{\pgf@dv@val}{\pgfoovalueof{unit vector}}}% + \fi% + \fi% + \fi% + }% +}% + + +% +% Numerical mapping of attributes to other attributes +% + +\pgfooclass{interval mapper} +{% + % Class interval mapper + % + % This interval mapper reacts to the map datapoint signal. Its purpose is to + % map one attribute to another attribute. For the first attribute, + % an intervals is specified. + % + % In addition to the in interval [a,b] there is also an outinterval + % [c,d]. Values in the range [a,b] are linearly mapped to the + % range [c,d] and the result is stored in the second attribute. + % + % For instance, if the first range is [10,20] and the second range + % is [0,100], then 10 is mapped to 0, 11 is mapped to 10, 20 is + % mapped to 100 and 30 is mapped to 200. + % + % It is permissible to specify an additional non-linear + % transformation function f. In this case, for an input value x the + % position of f(x) inside the interval [f(a),f(b)] is determined and + % this position is linearly mapped to [c,d]. + % + % For example, if f(x) = log_10 (x) and the first range [a,b] = + % [10,1000] and the second range is [1,2], then 10 is mapped to 1, + % 100 is mapped to 1.5 (since f(100) = 3 lies in the middle + % between f(10) = 2 and f(1000) = 4) and 100000 is mapped to 3. + + + \attribute in;% + % The name of the input attribute. If the value of this attribute + % is empty or undefined, no mapping is done. + % + % The in attribute may have a subkey called .../const. If this key + % has a value different from \relax, it will always be used instead + % of the in attribute itself. This can be useful to temporarily + % set a constant value to a key inside a mapping chain. + % + % If the subkey /is min or max is set to "min" or "max", the mapping + % will always be done to the respective out min or out max values. + + \attribute out;% + % The name of the output attribute. + + \attribute trans;% + % Stores (more or less) the transformation function. + + \attribute out min;% + % Start of the second range (the value of c). + + \attribute out min;% + % End of the second range (the value of d). + + \attribute trans in min;% + % Transformed value of the start of the first range (the value + % f(a)). + + \attribute scale;% + % The scaling factor, that is, the value of (f(b)-f(a))/(d-c). + + + % Constructor + % + % #1 = input attribute. Example: velocity. + % #2 = output attribute. Example: x + % #3 = optional transformation function. The input value for this + % function is stored in entered number \pgfvalue. + % The output of the function should be stored in the + % same entered number. + % + % The intervals are set using setter methods later on. + % + \method interval mapper(#1,#2,#3) { + \pgfooset{in}{#1} + \pgfooset{out}{#2} + \def\pgf@temp{#3}% + \ifx\pgf@temp\pgfutil@empty% + \else% + \pgfooset{trans}{% + #3% + }% + \fi% + }% + + % Method + \method default connects() { + \pgfoothis.get handle(\pgf@dv@me) + \pgfkeysvalueof{/pgf/data visualization/obj}.connect(\pgf@dv@me,map,map datapoint signal) + }% + + % Method + % + % #1 = in interval minimum + % #2 = in interval maximum + % #3 = out interval minimum + % #4 = out interval maximum + % + % This method will (re)compute the correct mappings for the interval + % mapper. + \method set interval values(#1,#2,#3,#4) { + {% + \pgfdvmathenter{\pgf@in@interval@min}{#1} + \pgfdvmathenter{\pgf@in@interval@max}{#2} + \pgfdvmathenter{\pgf@out@interval@min}{#3} + \pgfdvmathenter{\pgf@out@interval@max}{#4} + % Let's start with the output, it's easier... + \pgfoolet{out min}{\pgf@out@interval@min} + \pgfoolet{out max}{\pgf@out@interval@max} + \pgfdvmathifless{\pgf@in@interval@min}{\pgf@in@interval@max}{% + \pgfdvmathsub{\pgf@out@diff}{\pgf@out@interval@max}{\pgf@out@interval@min} + \let\pgfvalue=\pgf@in@interval@min + \pgfoovalueof{trans} + \let\pgf@in@interval@min@transformed=\pgfvalue + \pgfoolet{trans in min}{\pgf@in@interval@min@transformed}% + \let\pgfvalue=\pgf@in@interval@max + \pgfoovalueof{trans} + \let\pgf@in@interval@max@transformed=\pgfvalue + \pgfdvmathsub{\pgf@diff@in@transformed}{\pgf@in@interval@max@transformed}{\pgf@in@interval@min@transformed} + % + % Precompute the scaling + % + \pgfdvmathdiv{\pgf@scale}{\pgf@out@diff}{\pgf@diff@in@transformed}% + \pgfoolet{scale}{\pgf@scale}% + }{% + \pgfoolet{scale}\pgf@dv@no@scale% + }% + }% + }% + \def\pgf@dv@no@scale{noscale}% + + % Slot + \method map() { + \pgfooget{scale}\pgf@dv@scale + \ifx\pgf@dv@scale\pgfutil@empty% + % not yet setup + \else% + \pgfkeysgetvalue{/data point/\pgfoovalueof{in}/is min or max}\pgf@dv@is% + \ifx\pgf@dv@is\pgf@min@text% + \pgf@dv@set@out@min% + \else% + \ifx\pgf@dv@is\pgf@max@text% + \pgf@dv@set@out@max% + \else% + \ifx\pgf@dv@scale\pgf@dv@no@scale% + \pgf@dv@mapper@goto@mid% + \else% + \pgfkeysgetvalue{/data point/\pgfoovalueof{in}/const}\pgf@dv@external@value% + \ifx\pgf@dv@external@value\relax% + \pgfkeysgetvalue{/data point/\pgfoovalueof{in}}\pgf@dv@external@value% + \fi% + \ifx\pgf@dv@external@value\pgfutil@empty% + \pgf@dv@mapper@pos@check% + \else% + \ifx\pgf@dv@external@value\relax% + \pgf@dv@mapper@pos@check% + \else% + \pgfdvmathenter{\pgfvalue}{\pgf@dv@external@value}% + \pgf@dv@mapper@trans + \fi% + \fi% + \fi% + \fi% + \fi% + \fi% + }% + + \def\pgf@dv@set@out@min{% + \pgfkeyslet{/data point/\pgfoovalueof{out}/is min or max}\pgf@min@text% + \pgfooget{out min}{\pgf@minmax@temp}% + \pgf@dv@set@out@now% + }% + \def\pgf@dv@set@out@max{% + \pgfkeyslet{/data point/\pgfoovalueof{out}/is min or max}\pgf@max@text% + \pgfooget{out max}{\pgf@minmax@temp}% + \pgf@dv@set@out@now% + }% + \def\pgf@dv@set@out@now{% + \pgfkeysgetvalue{/data point/\pgfoovalueof{out}/offset}\pgf@dv@external@offset% + \ifx\pgf@dv@external@offset\relax\else% + \ifx\pgf@dv@external@offset\pgfutil@empty\else% + \pgfdvmathenter{\pgfvalue}{\pgf@minmax@temp}% + \pgfdvmathenter{\pgf@dv@offset}{\pgf@dv@external@offset}% + \pgfdvmathadd{\pgfvalue}{\pgfvalue}{\pgf@dv@offset} + \pgfdvmathexitbyserializing{\pgf@minmax@temp}{\pgfvalue}% + \fi + \fi + \pgfkeyslet{/data point/\pgfoovalueof{out}}\pgf@minmax@temp% + }% + + \def\pgf@dv@mapper@trans{ + \pgfooget{trans in min}{\pgf@dv@trans@in@min}% + \pgfooget{scale}{\pgf@dv@scale}% + \pgfooget{out min}{\pgf@dv@out@min}% + \pgfoovalueof{trans}% + \pgfdvmathsub{\pgfvalue}{\pgfvalue}{\pgf@dv@trans@in@min}% + \pgfdvmathmul{\pgfvalue}{\pgfvalue}{\pgf@dv@scale}% + \pgfdvmathadd{\pgfvalue}{\pgfvalue}{\pgf@dv@out@min}% + \pgfkeysgetvalue{/data point/\pgfoovalueof{out}/offset}\pgf@dv@external@offset% + \ifx\pgf@dv@external@offset\relax\else% + \ifx\pgf@dv@external@offset\pgfutil@empty\else% + \pgfdvmathenter{\pgf@dv@offset}{\pgf@dv@external@offset}% + \pgfdvmathadd{\pgfvalue}{\pgfvalue}{\pgf@dv@offset} + \fi + \fi + \pgfdvmathexitbyserializing{\pgf@temp}{\pgfvalue}% + \pgfkeyslet{/data point/\pgfoovalueof{out}}\pgf@temp% + }% + + \def\pgf@dv@mapper@pos@check{% + \pgfkeysgetvalue{/data point/\pgfoovalueof{in}/out pos}\pgf@dv@pos@value% + \ifx\pgf@dv@pos@value\relax\else\if\pgf@dv@pos@value\pgfutil@empty\else% + \pgfmathsetmacro{\pgf@dv@pos@num}{\pgf@dv@pos@value}% + \pgfmathsetmacro{\pgf@dv@pos@num@prime}{1-\pgf@dv@pos@num}% + % Ok, compute transformed min position: + \pgfooget{out min}{\pgf@out@interval@min} + \pgfooget{out max}{\pgf@out@interval@max} + \pgfdvmathmulfixed{\pgf@out@interval@min}{\pgf@out@interval@min}{\pgf@dv@pos@num@prime} + \pgfdvmathmulfixed{\pgf@out@interval@max}{\pgf@out@interval@max}{\pgf@dv@pos@num} + \pgfdvmathadd{\pgf@out@val}{\pgf@out@interval@min}{\pgf@out@interval@max}% + \pgfdvmathexitbyserializing{\pgf@temp}{\pgf@out@val}% + \pgfkeyslet{/data point/\pgfoovalueof{out}}\pgf@temp% + \fi\fi% + }% + + \def\pgf@dv@mapper@goto@mid{% + % Ok, compute transformed min position: + \pgfooget{out min}{\pgf@out@interval@min} + \pgfooget{out max}{\pgf@out@interval@max} + \pgfdvmathmulfixed{\pgf@out@interval@min}{\pgf@out@interval@min}{.5} + \pgfdvmathmulfixed{\pgf@out@interval@max}{\pgf@out@interval@max}{.5} + \pgfdvmathadd{\pgf@out@val}{\pgf@out@interval@min}{\pgf@out@interval@max}% + \pgfdvmathexitbyserializing{\pgf@temp}{\pgf@out@val}% + \pgfkeyslet{/data point/\pgfoovalueof{out}}\pgf@temp% + }% +}% + + + + + +%\newif\ifpgf@dvclip +\newif\ifpgf@dvignore + +\pgfooclass{scaling mapper} +{% + % Class scaling mapper + % + % A scaling mapper is (mainly) used to scale an attribute in such a way + % that it can be rendered easily on a page. The idea is that you + % specify some attribute and a desired target interval size (like + % [0,5]). The scaling mapper will then survey the data and will setup a + % interval mapper in such a way that the minimum value present in the data is + % mapped to 0 and the maximum to 5. However, it is also possible to, + % say, only have the maximum value mapped to 5 and have 0 be mapped + % to 0. Other, more complicated specifications are also possible. + % + % The scaling mapper internally creates several objects: First, a surveyor + % for determining the minimum and maximum values of the attribute + % and a interval mapper for then mapping the values to the scaled + % values. Additionally, interval objects are needed. + + \attribute in;% + % The to-be-scaled attribute; + + \attribute out;% + % The attribute storing the scaled value. + + \attribute in range obj;% + % This interval keep track of the range of the in attribute + + \attribute scaling spec;% + % A specification of how the scaling should be performed. + % + % The format is the following: + % + % #1 at #2 and #3 at #4 + % + % Here, #2 and #4 must be numbers. #1 and #3 can either be numbers + % or #1 can be the text "min" and #3 can be the text "max". + % + % The effect of such a specification is the following: The value #1 + % is mapped to #2, the value #3 is mapped to #4 and values between + % #1 and #3 (or outside this range) are mapped linearly to a value + % between (or outside) #2 and #4. In case #1 is set to "min", the + % smallest observed value of the in-attribute is mapped to #3 and + % similarly for a value of "max" for #3. + % + % Additionally, as for a interval mapper, a function f might be specified + % that deforms the linear mapping. In case f is specified, + % f(#1) is mapped to #2 and f(#3) is mapped to #4. + + \attribute spec maps min;% + % When the scaling spec maps "min" to some value, rather than some + % specific value to that value, then spec maps min will be set to + % \pgfutil@firstoftwo, otherwise to \pgfutil@secondoftwo + + \attribute spec maps max;% + % Like spec maps min + + \attribute function;% + % Stores the function + + \attribute interval mapper;% + % Stores the interval mapper object + + \attribute range surveyor;% + % Stores the range surveyor object + + + % Constructor + % + % #1 = in attribute + % #2 = out attribute + % #3 = scaling specification + % #4 = optional function + % + \method scaling mapper(#1,#2,#3,#4) { + % + % Save the parameters in attributes + % + \pgfooset{in}{#1} + \pgfooset{out}{#2} + \pgfooset{scaling spec}{#3} + \pgfooset{function}{#4} + % + % Now setup the objects for the before survey phase + % + % + % First, the in range interval + \pgfoonew \pgf@dv@in@range@obj=new interval(,) + \pgfoolet{in range obj}\pgf@dv@in@range@obj + % Second, the in range surveyor. We do not need to store this + % object, it will "do its work in the background" + \pgfoonew \pgf@dv@range@obj=new range surveyor(#1,\pgf@dv@in@range@obj) + \pgf@dv@range@obj.default connects() + \pgfoolet{range surveyor}\pgf@dv@range@obj + % + % + % The interval mapper: + % + \pgfoonew{interval mapper}=new interval mapper(#1,#2,#4) + \pgfoovalueof{interval mapper}.default connects() + }% + + % Method + \method default connects() { + \pgfoothis.get handle(\pgf@dv@me) + \pgfkeysvalueof{/pgf/data visualization/obj}.connect(\pgf@dv@me,phase,phase signal) + }% + + % Getter + % + % Returns an interval object that stores the range of the in + % attribute. + \method get in range interval() { + \pgfooget{in range obj}\pgfdvinrangeinterval + }% + + + % Getter + % + % Returns the out attribute. + % + \method get out() { + \pgfooget{out}\pgfdvout + }% + + % Getter + % + % Returns the function attribute. + % + \method get function(#1) { + \pgfooget{function}#1 + }% + + % Method + % + % #1 = a included in value + % + % This method only affects the survey. The value #1 is recorded as + % if it were encountered for the in attribute at some point. + % + \method include in value(#1) { + \pgfoovalueof{range surveyor}.include attribute value({#1}) + }% + + + % Method + % + % #1 = value for the attribute + % + % Sets the attribute to the (evaluated) given value (evaluation + % using \pgfmathparse). When the value is evaluated, the values of + % \pgfdvmin and \pgfdvmax will be set to the minimum and maximum + % values of the in interval. (See \pgfdvmathenter with the "!"-notation). + % + % In case #1 is set to the special value "min", it evaluates to + % \pgfdvmin, when set to "max" it evaluates to \pgfdvmax. + % + \method set in to(#1) {% + \edef\pgf@temp{#1}% + \ifx\pgf@temp\pgf@min@text% + \pgf@dv@lib@set@mm% + \let\pgf@dv@value\pgfdvmin% + \pgfoovalueof{spec maps min}{\pgfkeyslet{/data point/\pgfoovalueof{in}/is min or max}\pgf@min@text}{}% + \else% + \ifx\pgf@temp\pgf@max@text% + \pgf@dv@lib@set@mm% + \let\pgf@dv@value\pgfdvmax% + \pgfoovalueof{spec maps min}{\pgfkeyslet{/data point/\pgfoovalueof{in}/is min or max}\pgf@max@text}{}% + \else% + \pgfdvmathenter{\pgf@dv@value}{\pgf@temp}% + \fi% + \fi% + \pgfdvmathexitbyserializing{\pgf@dv@serial}{\pgf@dv@value}% + \pgfkeyslet{/data point/\pgfoovalueof{in}}\pgf@dv@serial% + }% + \def\pgf@dv@lib@set@mm{ + \pgfooget{in range obj}\pgfdvinrangeinterval + \pgfdvinrangeinterval.get min and max()% + }% + + + % Slot + \method phase(#1) { + \ifx#1\pgfdvendsurvey + % Ok, we setup the interval mapper. + % + % We start by computing the interval borders: + \pgfoovalueof{in range obj}.get min and max() + \ifx\pgfdvmin\pgfutil@empty% undefined, since no data points + \else% + \pgfooget{scaling spec}\pgf@temp% + \expandafter\pgf@lib@dv@parse@scaling\pgf@temp\pgf@stop% + \pgfoovalueof{interval mapper}.set interval values(% + \pgf@lib@dv@min,\pgf@lib@dv@max,% + \pgf@lib@dv@min@at,\pgf@lib@dv@max@at) + \fi + \fi + }% + + + % Internal helper + \def\pgf@lib@dv@parse@scaling#1 at#2and #3 at#4\pgf@stop{% + \def\pgf@lib@dv@min{#1}% + \pgfdvmathenter{\pgf@lib@dv@min@at@mv}{#2} + \def\pgf@lib@dv@max{#3}% + \pgfdvmathenter{\pgf@lib@dv@max@at@mv}{#4} + \ifx\pgf@lib@dv@min\pgf@min@text% + \let\pgf@lib@dv@min@mv\pgfdvmin + \pgfoolet{spec maps min}\pgfutil@firstoftwo% + \else + \pgfdvmathenter{\pgf@lib@dv@min@mv}{\pgf@lib@dv@min} + \pgfoolet{spec maps min}\pgfutil@secondoftwo% + \fi + \ifx\pgf@lib@dv@max\pgf@max@text% + \let\pgf@lib@dv@max@mv\pgfdvmax + \pgfoolet{spec maps max}\pgfutil@firstoftwo% + \else + \pgfdvmathenter{\pgf@lib@dv@max@mv}{\pgf@lib@dv@max} + \pgfoolet{spec maps max}\pgfutil@secondoftwo% + \fi + \pgfdvmathexitbyserializing{\pgf@lib@dv@min}{\pgf@lib@dv@min@mv} + \pgfdvmathexitbyserializing{\pgf@lib@dv@min@at}{\pgf@lib@dv@min@at@mv} + \pgfdvmathexitbyserializing{\pgf@lib@dv@max}{\pgf@lib@dv@max@mv} + \pgfdvmathexitbyserializing{\pgf@lib@dv@max@at}{\pgf@lib@dv@max@at@mv} + }% + \def\pgf@min@text{min}% + \def\pgf@max@text{max}% +}% + + + + + +% +% Surveyors +% + +\pgfooclass{range surveyor} +{% + % Class range surveyor + % + % A range surveyor is used only in the survey phase. Its job is to + % determine the minimum and maximum values of an attribute that are + % "seen" during the survey phase. Based on this value, the size of, + % say, an axis can be determined later on. + % + % You can also set the key "sub attributes" of the attribute. In + % this case, this key should contain -- as the name suggests -- a + % list of subkeys that should also be taken into account for the + % computation of the minimum and maximum value. Inside the "sub + % attributes" key, each subkey should be surrounded by a call of + % \pgfdvsub. For instance, if there are two sub attributes "/data + % point/velocity/min" and "/data point/velocity/max" for a key + % "/data point/velocity", then "/data point/velocity/sub attributes" + % should be set to "\pgfdvsub{/min}\pgfdvsub{/max}". + + \attribute attribute;% + % The to-be-surveyed attribute + + \attribute interval obj;% + % The interval object that protocols the minimum and maximum + % values. + + + % Constructor + % + % #1 = the attribute + % #2 = handle to the interval object + % + \method range surveyor(#1,#2) { + \pgfooset{attribute}{#1} + \pgfoolet{interval obj}#2 + }% + + % Method + \method default connects() { + \pgfoothis.get handle(\pgf@dv@me) + \pgfkeysvalueof{/pgf/data visualization/obj}.connect(\pgf@dv@me,survey,survey datapoint signal) + }% + + % Method + % + % #1 = a value + % + % Adjust the interval as if during the survey this value were + % encountered + \method include attribute value(#1) { + \pgf@dv@assign@val@stripped#1\pgf@stop% + \pgfoovalueof{interval obj}.adjust(\pgf@dv@range@surv) + }% + \def\pgf@dv@assign@val@stripped#1\pgf@stop{% + \def\pgf@dv@val{#1} + }% + + % Slot + % + % This slot should be connected to the survey datapoint signal. For + % each datapoint, this method will call the adjust method of the + % interval. + % + \method survey() { + \let\pgfdvsub\pgf@dv@range@surv@sub + \pgfdvsub{}% + \pgfkeysvalueof{/data point/\pgfoovalueof{attribute}/sub attributes} + }% + + \def\pgf@dv@range@surv@sub#1{ + \pgfkeysgetvalue{/data point/\pgfoovalueof{attribute}#1}\pgf@dv@val% + \ifx\pgf@dv@val\relax% + \else% + \edef\pgf@dv@val{\pgf@dv@val}% + \ifx\pgf@dv@val\pgfutil@empty% + \else% + \pgfoovalueof{interval obj}.adjust(\pgf@dv@range@surv)% + \fi% + \fi% + }% + + \def\pgf@dv@range@surv{% + \pgfdvmathenter{\pgf@dv@value}{\pgf@dv@val}% + % Now, protocol value + \ifx\pgfdvmin\pgfutil@empty% + \let\pgfdvmin\pgf@dv@value% + \else% + \pgfdvmathifless{\pgf@dv@value}{\pgfdvmin}{\let\pgfdvmin\pgf@dv@value}{}% + \fi% + \ifx\pgfdvmax\pgfutil@empty% + \let\pgfdvmax\pgf@dv@value% + \else% + \pgfdvmathifless{\pgfdvmax}{\pgf@dv@value}{\let\pgfdvmax\pgf@dv@value}{}% + \fi% + }% +}% + + + +% +% Visualizers +% + +\usepgflibrary{plothandlers}% + + + +\pgfooclass{plot handler visualizer} +{% + % Class plot handler visualizer + % + % This visualizer uses one or more plot handlers to visualize data points. + % As the data points are processed, the canvas positions of the data + % points are recorded. At the end of a group of data points, the + % recorded stream of canvas position is visualized (rendered) + % using plot handlers. For instance, when the lineto plot handler + % is used, the data points are connected by straight lines, when the + % curveto plot handler is used, they are connected by smooth curves + % and so on. + % + % Since plot lines are not drawn immediately, but only at the end, + % when everything has been collected, multiple plot handlers can + % coexist peacefully at the same time. + % + % You can specify a "filter", which will cause only those data + % points to be visualized by the visualizer that pass the test. The + % filter should set \pgfdvfilterpassedfalse for to-be-filtered data + % point. + % + % You can specify "projection code" that changes the canvas x and + % canvas y positions. This projection code allows you to draw, say, + % projected points instead of the actual data point. + % + % The keys /data point/<name>/execute at begin and /data + % point/<name>/execute at end, where <name> is the name of + % the visualizer, should store code that is to be executed directly + % before and after the stream of coordinates is created. Typically, + % these keys will store commands to use the path created by the plot + % handler. + + \attribute handlers;% + % The to-be-used handlers + + \attribute name;% + % The attribute + + \attribute filter=\pgfdvnamedvisualizerfilter;% + % Filter code + + \attribute positions;% + % An internal protocol of the positions on the stream + + \attribute cache;% + % An internal cache. Positions are first inserted into this + % cache. Every 100 steps this cache is moved to positions. The idea + % behind this is that it will avoid the quadratic time increase that + % is normally caused when a protocol is build in TeX. + + \attribute projection code;% + % This code is empty by default. However, you can set it for + % instance to the following + % + % \pgfkeyssetvalue{/data point/y=0}\pgfdvmapdatapointtocanvas + % + % in order to visualize the data point with its y-coordinate + % replaced by 0. + + % Constructor + % + % #1 = A name + % #2 = A comma-separated list of handlers like + % "\pgfplothandlerlineto,\pgfplothandlercurveto" + % + \method plot handler visualizer(#1,#2) { + \pgfooset{name}{#1} + \pgfooset{handlers}{#2} + }% + + % Method + \method default connects() { + \pgfoothis.get handle(\pgf@dv@me) + \pgfkeysvalueof{/pgf/data visualization/obj}.connect(\pgf@dv@me,protocol,visualize datapoint signal) + \pgfkeysvalueof{/pgf/data visualization/obj}.connect(\pgf@dv@me,phase,phase signal) + }% + + % Setter + \method set filter(#1) { + \pgfooset{filter}{#1} + }% + + % Setter + \method set projection code(#1) { + \pgfooset{projection code}{#1} + }% + + % Slot + \method protocol() { + \pgfdvfilterpassedtrue + \pgfoovalueof{filter} + \ifpgfdvfilterpassed% + \pgf@dv@line@cache@it + \fi% + }% + + \def\pgf@dv@line@cache@it{ + % Cache it + \pgfooget{cache}\pgf@dv@cache + \pgfkeysgetvalue{/data point/outlier}\pgf@temp% + \ifx\pgf@temp\pgfutil@empty + {% + \pgfoovalueof{projection code} + \xdef\pgf@dv@add{\noexpand\pgf@dv@ph{\pgfkeysvalueof{/data + point/canvas x}}{\pgfkeysvalueof{/data point/canvas y}}}% + } + \else% + % An outlier! + \gdef\pgf@dv@add{\pgf@dv@outliner}% + \fi% + \expandafter\expandafter\expandafter\def% + \expandafter\expandafter\expandafter\pgf@dv@cache% + \expandafter\expandafter\expandafter{\expandafter\pgf@dv@cache\pgf@dv@add}% + % Possibly flush cache + \global\advance\pgf@lib@dv@cache@count by1\relax% + \ifnum\pgf@lib@dv@cache@count=100\relax% + \global\pgf@lib@dv@cache@count=0\relax% + % Append cache to positions + \pgfooget{positions}\pgf@dv@temp + \expandafter\expandafter\expandafter\def% + \expandafter\expandafter\expandafter\pgf@dv@temp% + \expandafter\expandafter\expandafter{\expandafter\pgf@dv@temp\pgf@dv@cache}% + \pgfoolet{positions}\pgf@dv@temp + \let\pgf@dv@cache\pgfutil@empty + \fi + \pgfoolet{cache}\pgf@dv@cache + }% + + % Slot + \method phase(#1) {% + \ifx#1\pgfdvendvisualization% + \pgfoothis.render()% + \fi% + }% + + % Internal method + \method render() { + \pgfscope + \pgfooget{handlers}\tikz@dv@temp@list + \foreach\tikz@dv@temp in\tikz@dv@temp@list{ + \ifx\tikz@dv@temp\pgfutil@empty + \else + \pgfkeysvalueof{/data point/\pgfoovalueof{name}/execute at begin} + \tikz@dv@temp + \pgfdvnewstreamtrue + \pgfoovalueof{positions} + \pgfoovalueof{cache} + \ifpgfdvnewstream\else\pgfplotstreamend\fi + \pgfkeysvalueof{/data point/\pgfoovalueof{name}/execute at end} + \fi + } + \endpgfscope + \pgfoolet{positions}\pgfutil@empty + \pgfoolet{cache}\pgfutil@empty + }% + + \def\pgf@dv@ph#1#2{\ifpgfdvnewstream\pgfplotstreamstart\pgfdvnewstreamfalse\fi\pgfplotstreampoint{\pgfqpoint{#1}{#2}}}% + \def\pgf@dv@outliner{\ifpgfdvnewstream\else\pgfplotstreamend\pgfdvnewstreamtrue\fi}% +}% + +\newif\ifpgfdvnewstream +\newcount\pgf@lib@dv@cache@count + +\pgfkeys{% + /data point/outlier/.initial=, + /data point/outlier/.default=true, +}% + + +% A possible filter for unnamed visualizers +% +% #1 = Text to be compared with key /data point/set +% +% Description: +% +% In a data visualization with multiple visualizers, data points +% typically "belong" only to a single visualizer. It is, thus, +% necessary to filter data points according to the current +% visualizer. This current visualizer is assumed to be stored in /data +% point/visualizer. +% +% When objects allow you to set a filter, you can use this filter to +% test against /data point/set being equal to #1. + +\def\pgfdvvisualizerfilter#1{% + \pgfkeysgetvalue{/data point/set}\pgf@dv@visualizer% + \def\pgf@temp{#1}% + \ifx\pgf@temp\pgf@dv@visualizer% + \else + \pgfdvfilterpassedfalse + \fi% +}% + + + +% A filter for named visualizer +% +% Description: +% +% This filter works like \pgfdvvisualizerfilter, only it assumes the +% the object defines a "name" attribute. In this case, the value of +% /data point/set is tested against this attribute. + +\def\pgfdvnamedvisualizerfilter{% + \pgfkeysgetvalue{/data point/set}\pgf@dv@visualizer% + \pgfooget{name}\pgf@temp% + \ifx\pgf@temp\pgf@dv@visualizer% + \else + \pgfdvfilterpassedfalse + \fi% +}% + + + +\pgfooclass{rectangle visualizer} +{% + % Class rectangle visualizer + % + % This visualizer visualizes a datapoint as a rectangle. It needs + % a name, so that /data point/<name>/attribute 1 and /data + % point/<name>/attribute 2 store two attributes names a1 to a2. Then, + % provided the current value of /data point/set equals name, for + % each data point a rectangle between (a1/min,a2/min) and + % (a1/max,a2/max) is created. + + + \attribute name;% + % The name + + \attribute a1;% + \attribute a2;% + % The attributes, filled from /data point/<name>/attribute 1 and 2 + + + % Constructor + % + % #1 = first attribute + % #2 = second attribute + % + \method rectangle visualizer(#1) { + \pgfooset{name}{#1} + }% + + % Method + \method default connects() { + \pgfoothis.get handle(\pgf@dv@me) + \pgfkeysvalueof{/pgf/data visualization/obj}.connect(\pgf@dv@me,visualize,visualize datapoint signal) + \pgfkeysvalueof{/pgf/data visualization/obj}.connect(\pgf@dv@me,phase,phase signal) + }% + + % Slot + \method phase(#1) {% + \ifx#1\pgfdvbeginsurvey% + \pgfooeset{a1}{/data point/\pgfkeysvalueof{/data point/\pgfoovalueof{name}/attribute 1}} + \pgfooeset{a2}{/data point/\pgfkeysvalueof{/data point/\pgfoovalueof{name}/attribute 2}} + \pgfkeyssetvalue{\pgfoovalueof{a1}/sub attributes}{\pgfdvsub{/min}\pgfdvsub{/max}} + \pgfkeyssetvalue{\pgfoovalueof{a2}/sub attributes}{\pgfdvsub{/min}\pgfdvsub{/max}} + \fi% + }% + + % Slot + \method visualize() { + { + \pgfkeysgetvalue{/data point/set}\pgf@dv@visualizer% + \pgfooget{name}\pgf@temp% + \ifx\pgf@temp\pgf@dv@visualizer% + \pgf@dv@rect@do + \fi + } + }% + + \def\pgf@dv@rect@do{ + \pgfkeysgetvalue{\pgfoovalueof{a1}/min}\pgf@dv@amin + \ifx\pgf@dv@amin\relax\else\ifx\pgf@dv@amin\pgfutil@empty\else + \pgfkeysgetvalue{\pgfoovalueof{a1}/max}\pgf@dv@amax + \ifx\pgf@dv@amax\relax\else\ifx\pgf@dv@amax\pgfutil@empty\else + \pgfkeysgetvalue{\pgfoovalueof{a2}/min}\pgf@dv@bmin + \ifx\pgf@dv@bmin\relax\else\ifx\pgf@dv@bmin\pgfutil@empty\else + \pgfkeysgetvalue{\pgfoovalueof{a2}/max}\pgf@dv@bmax + \ifx\pgf@dv@bmax\relax\else\ifx\pgf@dv@bmax\pgfutil@empty\else + \pgfkeysvalueof{/data point/\pgfoovalueof{name}/execute at begin} + \pgfkeyssetvalue{\pgfoovalueof{a1}}{\pgf@dv@amin} + \pgfkeyssetvalue{\pgfoovalueof{a2}}{\pgf@dv@bmin} + \pgfpathdvmoveto + \pgfkeyssetvalue{\pgfoovalueof{a2}}{\pgf@dv@bmax} + \pgfpathdvlineto + \pgfkeyssetvalue{\pgfoovalueof{a1}}{\pgf@dv@amax} + \pgfpathdvlineto + \pgfkeyssetvalue{\pgfoovalueof{a2}}{\pgf@dv@bmin} + \pgfpathdvlineto + \pgfkeyssetvalue{\pgfoovalueof{a1}}{\pgf@dv@amin} + \pgfpathdvlineto + \pgfpathclose + \pgfkeysvalueof{/data point/\pgfoovalueof{name}/execute at end} + \fi\fi + \fi\fi + \fi\fi + \fi\fi + }% +}% + +\newcount\pgf@lib@dv@cache@count + + + +% +% Label positioning classes +% + +\pgfooclass{label visualizer} +{% + % Class label visualizer + % + % The job of this class is to visualize a label of a curve or some + % other object. + % + % The idea is as follows: This class monitors a certain + % attribute. When this attribute reaches a certain value for the + % first time, the following happens: A normalized vector of the line + % connecting the current canvas position to the canvas position of + % the previous data point is computed and stored in pgf@x/pgf@y. The + % coordinate system is shifted so that the current data point is at + % the origin. Then, some drawing code is executed. + + \attribute attribute;% + % The monitored attribute + + \attribute filter;% + % Filter code, see plot handler visualizer doc. + + \attribute threshold;% + % When the attribute exceeds this threshold, the drawing code is + % executed + + \attribute before canvas position=;% + % Stores canvas position of the data point before the interesting one + + \attribute canvas position=;% + % Stores canvas position of the data point + + \attribute code;% + % To-be-executed code when the label should be drawn + + + % Constructor + % + % #1 = attribute + % #2 = a macro, storing the threshold + % #3 = code + % #4 = filter code + % + \method label visualizer(#1,#2,#3,#4) { + \pgfooset{attribute}{#1} + \expandafter\pgfdvmathenter\expandafter{\expandafter\pgf@temp\expandafter}\expandafter{#2} + \pgfoolet{threshold}\pgf@temp + \pgfooset{code}{#3} + \pgfooset{filter}{#4} + }% + + % Method + \method default connects() { + \pgfoothis.get handle(\pgf@dv@me) + \pgfkeysvalueof{/pgf/data visualization/obj}.connect(\pgf@dv@me,test,visualize datapoint signal) + \pgfkeysvalueof{/pgf/data visualization/obj}.connect(\pgf@dv@me,phase,phase signal) + }% + + % Slot + \method test() { + \pgfdvfilterpassedtrue + \pgfoovalueof{filter} + \ifpgfdvfilterpassed% + \pgfooget{attribute}\pgf@dv@attribute + \ifx\pgf@dv@attribute\pgfutil@empty% + % nothing to do (anymore) + \else% + \pgf@dv@label@vis@record + \fi% + \fi% + }% + + \def\pgf@dv@label@vis@record{ + % Record current position + \edef\pgf@dv@pos{% + \noexpand\pgfqpoint% + {\pgfkeysvalueof{/data point/canvas x}}% + {\pgfkeysvalueof{/data point/canvas y}}}% + \pgfooget{canvas position}\pgf@dv@temp% + \pgfoolet{before canvas position}\pgf@dv@temp% + \pgfoolet{canvas position}\pgf@dv@pos% + % Now check threshold + \pgfooget{threshold}\pgf@dv@thres% + \ifx\pgf@dv@thres\pgfutil@empty% + \else% + % Hmm, have to check, whether the value is, indeed, correct + \pgfkeysgetvalue{/data point/\pgfoovalueof{attribute}}\pgf@dv@val + \ifx\pgf@dv@val\relax% + \else\ifx\pgf@dv@val\pgfutil@empty% + \else% + \pgfdvmathenter{\pgf@dv@value}{\pgf@dv@val}% + \pgfooget{threshold}{\pgf@dv@thres} + % Now, protocol value + \pgfdvmathifless{\pgf@dv@value}{\pgf@dv@thres}{}{% + \pgfoolet{attribute}\pgfutil@empty% Stop! + }% + \fi\fi% + \fi% + }% + + % Slot + \method phase(#1) {% + \ifx#1\pgfdvendvisualization% + \scope + \pgfcoordinate{label visualizer coordinate}{\pgfoovalueof{canvas position}} + \pgfcoordinate{label visualizer coordinate'}{\pgfoovalueof{before canvas position}} + \pgfoovalueof{code} + \endscope + \fi% + }% +}% + + + +% +% Style sheets +% +\pgfooclass{style attribute} +{% + % Class style attribute + % + % Instances of this class are used to select a style based on the + % current value of an attribute. + % + % Instances of this class monitor an attribute. Whenever the "style" + % signal is raised, the object investigates the current value of the + % attribute. It then looks up /pgf/data visualization/style + % sheets/<name of style sheet>/<value of the attribute> and executes + % this key, provided it is defined. If it not defined, the key + % /pgf/data visualization/style sheets/<name of style sheet>/default + % style is executed with the value as a parameter. + + \attribute attribute;% + % The name of the attribute + + \attribute style sheet name;% + % The styling function + + % Constructor + % + % #1 = attribute (including, if present, /data point/) + % #2 = style sheet name + % + \method style attribute(#1,#2) { + \pgfooset{attribute}{#1} + \pgfooset{style sheet name}{#2} + }% + + % Method + \method default connects() { + \pgfoothis.get handle(\pgf@dv@me) + \pgfkeysvalueof{/pgf/data visualization/obj}.connect(\pgf@dv@me,invoke style,style signal) + }% + + % Slots + \method invoke style() { + \pgfooget{attribute}\pgf@dv@temp + \pgfkeysgetvalue{\pgf@dv@temp}\pgf@dv@temp@val + \ifx\pgf@dv@temp@val\pgfutil@empty%do nothing + \else\ifx\pgf@dv@temp@val\relax%do nothing + \else% + \pgfkeysifdefined{\pgf@dv@temp/\pgf@dv@temp@val}{\pgfkeysgetvalue{\pgf@dv@temp/\pgf@dv@temp@val}\pgf@dv@temp@val}{}% redirect + \edef\pgf@temp{/pgf/data visualization/style sheets/\pgfoovalueof{style sheet name}} + \pgfkeysifassignable{\pgf@temp/\pgf@dv@temp@val}{% + \pgfkeysalso{\pgf@temp/\pgf@dv@temp@val}}{% + \pgfkeysalso{\pgf@temp/default style/.expand once=\pgf@dv@temp@val} + } + \fi\fi% + }% +}% + + +% Declares a new style sheet +% +% #1 = name of style sheet +% #2 = settings +% +% Description: +% +% This is a shortcut for doing a /.cd to /pgf/data visualization/style sheets/#1 + +\def\pgfdvdeclarestylesheet#1#2{ + \pgfkeys{/pgf/data visualization/style sheets/#1/.cd,#2} +}% + + + + + + + + + +% +% Legend container class +% + +\pgfooclass{legend} +{% + % Class legend + % + % This class is used to collect entries into a legend. The idea is + % that, say, for each line of a picture you would like to have an + % entry in a legend. Then you prepare these entries and send + % them to a legend container object. It will collect the entries + % and, at the end, arrange them in rows and columns by + % separating them using \pgfdvnextcell and \pgfdvendrow. + % + % By surrounding the resulting arrangement by a call to \pgfmatrix or + % a call to \halign you can then create a legend. + % + % To specify how the entries should be arranged, a size + % specification and two directions are needed. + % + % For the size specification you specify either the ideal number of + % columns and the maximum number of rows or you specify the ideal + % number of rows and the maximum number of columns. In the first + % case, the class first tries to create as many columns as specified + % by the "ideal" parameter. If there are less entries than this + % parameter, you obviously get less columns, namely as many as there + % are entries, each column containing only one entry. If there are + % more entries that the ideal number of columns, you get multiple + % columns, each containing as many entries to make sure that the + % ideal number is met. However, if a column would get more than the + % specified maximum number of elements per column, you get more + % columns that the ideal number -- to ensure that no column contains + % more than the desired number. The case is symmetric, but with the + % roles of columns and rows exchanged. + % + % Here is an example: You specify that you want 2 columns, ideally, + % each having a maximum of 4 entries. Here is what you would get: + % + % Number of entries & Number of resulting columns ... & ... and rows + % 1 & 1 & 1 + % 2 & 2 & 1 + % 3 & 2 & 2 + % 4 & 2 & 2 + % 5 & 2 & 3 + % 6 & 2 & 3 + % 7 & 2 & 4 + % 8 & 2 & 4 + % 9 & 3 & 4 + % 10 & 3 & 4 + % 11 & 3 & 4 + % 12 & 3 & 4 + % 13 & 4 & 4 + % + % + % Once the number of columns and rows has been computed, the next + % step is to "fill" the table. For this, eight strategies may be + % specified: right then down, right then up, left then down, left + % then up, up then right, up then left, down then right, down then + % left. A strategy like "right then up" means the following: Each + % new entry should placed right of the previous entry in the + % arrangement. However, once the maximum number of elements in a row + % has been reached, the a new row should be started that is placed + % above ("up") then just-assembled row. + % + % + % + % + % Example: + % + % You create an object \legend and then call + % + % \pgfoofnew \legend=new legend(columns,2,3,down then right) + % \legend.add entry(first) + % \legend.add entry(second) + % \legend.add entry(third) + % \legend.add entry(fourth) + % \legend.add entry(fifth) + % \legend.add entry(sixth) + % \legend.add entry(seventh) + % \legend.get arrangement(\arrangement) + % + % Then \arrangment will expand to + % + % first \pgfdvnextcell fourth \pgfdvnextcell seventh \pgfdvendrow + % second \pgfdvnextcell fifth \pgfdvnextcell third \pgfdvendrow + % third \pgfdvendrow sixth \pgfdvnextcell \pgfdvendrow + % + % By saying \let\pgfdvendrow=\\ and \let\pgfdvnextcell=&, you + % can use \arrangment inside a table. + + + \attribute entries;% + % Collects the entries + + \attribute columns or rows;% + % Specifies whether columns or rows are specified + + \attribute ideal;% + % The ideal number of columns/rows + + \attribute max;% + % The maximum number of entries per column/row + + \attribute strategy;% + % Specifies how the matrix should be filled + + \attribute number of entries=0;% + % Counts the number of entries that have been stored + + + % Constructor + % + % #1 = "columns" or "rows" -> specifies whether the ideal number and + % the max entries number refer to columns or the row + % #2 = the ideal number of #1's + % #3 = the maximum number of entries in a #1 + % #4 = one of the eight strategies + % + \method legend(#1,#2,#3,#4) { + \pgfooset{columns or rows}{#1} + \pgfooset{ideal}{#2} + \pgfooset{max}{#3} + \pgfooset{strategy}{#4} + }% + + % Method + \method default connects() { + }% + + % Method + \method add entry(#1) { + \pgfooappend{entries}{{#1}} + \c@pgf@counta=\pgfoovalueof{number of entries}\relax% + \advance\c@pgf@counta by1\relax% + \pgfooeset{number of entries}{\the\c@pgf@counta} + }% + + % Method + \method get arrangement(#1) { + { + % + % Step 1: Compute the desired number of rows or columns + % + % + % Compute the ideal number of entries per row/column (called + % "target" in the following) and entries per colum/row (called + % "per target" in the following) + % + % if (number of entries > ideal) + \ifnum\pgfoovalueof{number of entries}>\pgfoovalueof{ideal}\relax% + % { + % if (ideal * max > number of entries) + \c@pgf@counta=\pgfoovalueof{ideal}\relax% + \multiply\c@pgf@counta by\pgfoovalueof{max}\relax% + \ifnum\c@pgf@counta>\pgfoovalueof{number of entries}\relax + % { + % target = ideal; + % per target = ceil (number of entries / ideal); + \c@pgf@counta=\pgfoovalueof{ideal}\relax% + \c@pgf@countb=\pgfoovalueof{number of entries}\relax + \advance\c@pgf@countb by-1\relax + \divide\c@pgf@countb by\c@pgf@counta\relax% + \advance\c@pgf@countb by1\relax% + % } + % else + % { + % target = ceil(number of entries / max); + % per target = max; + \else + \c@pgf@countb=\pgfoovalueof{max}\relax% + \c@pgf@counta=\pgfoovalueof{number of entries}\relax + \advance\c@pgf@counta by-1\relax + \divide\c@pgf@counta by\c@pgf@countb\relax% + \advance\c@pgf@counta by1\relax% + \fi + % } + % } + % else + % { + % target = number of entries; + % per target = 1; + % } + \else + \c@pgf@counta=\pgfoovalueof{number of entries}\relax% + \c@pgf@countb=1\relax% + \fi% + % + % Ensure that counta = rows, countb = columns + % + \pgfooget{columns or rows}\pgf@temp + \ifx\pgf@temp\pgf@dv@columnstext% + \c@pgf@countc=\c@pgf@counta\relax + \c@pgf@counta=\c@pgf@countb\relax + \c@pgf@countb=\c@pgf@countc\relax + \fi + %\edef\temp{rows: \the\c@pgf@counta, columns: \the\c@pgf@countb}\temp + % + % Now arrange the matrix + % + \csname pgf@dv@legend@init@\pgfoovalueof{strategy}\endcsname + \expandafter\let\expandafter\pgf@dv@updater\csname pgf@dv@legend@update@\pgfoovalueof{strategy}\endcsname + \pgfooget{entries}\pgf@temp + \expandafter\pgf@dv@legend@arrange\pgf@temp\pgf@stop + % + % Now assemble the matrix + % + \edef\pgf@dv@row@list{1,...,\the\c@pgf@counta} + \edef\pgf@dv@column@list{1,...,\the\c@pgf@countb} + \global\let\pgf@dv@matrix\pgfutil@empty + \foreach \pgf@dv@row in \pgf@dv@row@list + { + \global\let\pgf@dv@matrix@row\pgf@dv@first@mark + \foreach \pgf@dv@column in \pgf@dv@column@list + { + \expandafter\let\expandafter\pgf@temp\csname pgf@dv@legend@entry@\pgf@dv@row @\pgf@dv@column\endcsname + \ifx\pgf@temp\relax + \let\pgf@temp\pgfutil@empty + \fi + \ifx\pgf@dv@matrix@row\pgf@dv@first@mark + \global\let\pgf@dv@matrix@row\pgf@temp + \else + \expandafter\pgfutil@g@addto@macro\expandafter\pgf@dv@matrix@row\expandafter{\expandafter\pgfdvnextcell\pgf@temp} + \fi + } + \pgfutil@g@addto@macro\pgf@dv@matrix@row{\pgfdvendrow} + \expandafter\pgfutil@g@addto@macro\expandafter\pgf@dv@matrix\expandafter{\pgf@dv@matrix@row} + } + \global\let\pgf@dv@matrix@row\relax + } + \let#1\pgf@dv@matrix + \global\let\pgf@dv@matrix\relax + }% + + \def\pgf@dv@first@mark{\pgf@dv@first@mark}% + + \def\pgf@dv@legend@arrange{% + \pgfutil@ifnextchar\pgf@stop{\pgfutil@gobble}{\pgf@dv@legend@handle@entry} + }% + + \def\pgf@dv@legend@handle@entry#1{ + \expandafter\def\csname pgf@dv@legend@entry@\the\c@pgf@countc @\the\c@pgf@countd\endcsname{#1}% + \pgf@dv@updater% + \pgf@dv@legend@arrange% + }% + + \expandafter\def\csname pgf@dv@legend@init@down then right\endcsname{% + \c@pgf@countc=1\relax% + \c@pgf@countd=1\relax% + }% + + \expandafter\def\csname pgf@dv@legend@update@down then right\endcsname{% + \advance\c@pgf@countc by1\relax + \ifnum\c@pgf@countc>\c@pgf@counta\relax% + \c@pgf@countc=1\relax% + \advance\c@pgf@countd by1\relax% + \fi% + }% + + \expandafter\def\csname pgf@dv@legend@init@right then down\endcsname{% + \c@pgf@countc=1\relax% + \c@pgf@countd=1\relax% + }% + + \expandafter\def\csname pgf@dv@legend@update@right then down\endcsname{% + \advance\c@pgf@countd by1\relax + \ifnum\c@pgf@countd>\c@pgf@countb\relax% + \c@pgf@countd=1\relax% + \advance\c@pgf@countc by1\relax% + \fi% + }% + + \expandafter\def\csname pgf@dv@legend@init@up then right\endcsname{% + \c@pgf@countc=\c@pgf@counta\relax% + \c@pgf@countd=1\relax% + }% + + \expandafter\def\csname pgf@dv@legend@update@up then right\endcsname{% + \advance\c@pgf@countc by-1\relax + \ifnum\c@pgf@countc=0\relax% + \c@pgf@countc=\c@pgf@counta\relax% + \advance\c@pgf@countd by1\relax% + \fi% + }% + + \expandafter\def\csname pgf@dv@legend@init@right then up\endcsname{% + \c@pgf@countc=\c@pgf@counta\relax% + \c@pgf@countd=1\relax% + }% + + \expandafter\def\csname pgf@dv@legend@update@right then up\endcsname{% + \advance\c@pgf@countd by1\relax + \ifnum\c@pgf@countd>\c@pgf@countb\relax% + \c@pgf@countd=1\relax% + \advance\c@pgf@countc by-1\relax% + \fi% + }% + + \expandafter\def\csname pgf@dv@legend@init@left then up\endcsname{% + \c@pgf@countc=\c@pgf@counta\relax% + \c@pgf@countd=\c@pgf@countb\relax% + }% + + \expandafter\def\csname pgf@dv@legend@update@left then up\endcsname{% + \advance\c@pgf@countd by-1\relax + \ifnum\c@pgf@countd=0\relax% + \c@pgf@countd=\c@pgf@countb\relax% + \advance\c@pgf@countc by-1\relax% + \fi% + }% + + \expandafter\def\csname pgf@dv@legend@init@up then left\endcsname{% + \c@pgf@countc=\c@pgf@counta\relax% + \c@pgf@countd=\c@pgf@countb\relax% + }% + + \expandafter\def\csname pgf@dv@legend@update@up then left\endcsname{% + \advance\c@pgf@countc by-1\relax + \ifnum\c@pgf@countc=0\relax% + \c@pgf@countc=\c@pgf@counta\relax% + \advance\c@pgf@countd by-1\relax% + \fi% + }% + + \expandafter\def\csname pgf@dv@legend@init@left then down\endcsname{% + \c@pgf@countc=1\relax% + \c@pgf@countd=\c@pgf@countb\relax% + }% + + \expandafter\def\csname pgf@dv@legend@update@left then down\endcsname{% + \advance\c@pgf@countd by-1\relax + \ifnum\c@pgf@countd=0\relax% + \c@pgf@countd=\c@pgf@countb\relax% + \advance\c@pgf@countc by1\relax% + \fi% + }% + + \expandafter\def\csname pgf@dv@legend@init@down then left\endcsname{% + \c@pgf@countc=1\relax% + \c@pgf@countd=\c@pgf@countb\relax% + }% + + \expandafter\def\csname pgf@dv@legend@update@down then left\endcsname{% + \advance\c@pgf@countc by1\relax + \ifnum\c@pgf@countc>\c@pgf@countb\relax% + \c@pgf@countc=1\relax% + \advance\c@pgf@countd by-1\relax% + \fi% + }% + + \def\pgf@dv@columnstext{columns}% +}% + + + +% +% Help classes +% + +\pgfooclass{interval} +{% + % Class interval + % + % Instances of this class store intervals. When either the min or + % the max value is empty, this corresponds to "not yet set", (not to + % "infinity). + + \attribute min;% + % The minimum value of the interval + + \attribute max;% + % The maximum value of the interval + + % Constructor + % + % #1 = initial minimum value + % #2 = initial maximum value + % + \method interval(#1,#2) { + \pgfooset{min}{#1} + \pgfooset{max}{#2} + }% + + % Method + \method default connects() { + }% + + % Sets the minimum/maximum to a new value + \method set min(#1) { + \pgfooset{min}{#1} + }% + \method set max(#1) { + \pgfooset{max}{#1} + }% + \method let min(#1) { + \pgfoolet{min}{#1} + }% + \method let max(#1) { + \pgfoolet{max}{#1} + }% + + % Getter + % + % Returns the current values of the minimum and maximum in the + % macros \pgfdvmin and \pgfdvmax. + \method get min and max() { + \pgfooget{min}\pgfdvmin + \pgfooget{max}\pgfdvmax + }% + + % Adjusts the minimum and maximum values + % + % #1 = some code. When this code is executed, the values of + % \pgfdvmin and \pgfdvmax will be set to the current values of + % the minimum/maximum. The code may change these values. The + % changed values will then be stored. + % + % This method does nothing that cannot be achieved also by calling + % set/get methods, but it is easier to use and faster. + \method adjust(#1) { + {% + \pgfooget{min}\pgfdvmin + \pgfooget{max}\pgfdvmax + #1% + \pgfoolet{min}\pgfdvmin + \pgfoolet{max}\pgfdvmax + }% + }% +}% + + + +\pgfooclass{count} +{% + % Class count + % + % This class can be used to count the number of data points that are + % present or that have a certain property. By default, for every + % data point a counter is incremented and the value of this counter + % is stored in an attribute. You can also specify an increment for + % the counter. You can specify that certain data points should + % not have an effect on the counter. + + + \attribute attribute;% + % The attribute that will be set to the current value of the counter + + \attribute val=0;% + % The current value of the counter + + \attribute max=0;% + % The current value of the counter + + \attribute step=1;% + % The stepping by which the counter is incremented for each + % datapoint + + \attribute start val=0;% + % The start value of the counter + + \attribute filter=;% + % This attribute stores some code that is executed for each + % data point. If the data point sets the TeX-if ifpgfdvfilterpassed to + % true, then the counter is not incremented. + + + % Constructor + % + % #1 = The attribute in which the counter value is stored + % + \method count(#1) { + \pgfooset{attribute}{#1} + }% + + % Method + \method default connects() { + \pgfoothis.get handle(\pgf@dv@me) + \pgfkeysvalueof{/pgf/data visualization/obj}.connect(\pgf@dv@me,apply,prepare datapoint signal) + \pgfkeysvalueof{/pgf/data visualization/obj}.connect(\pgf@dv@me,phase,phase signal) + }% + + % Getter + \method get max(#1) { + \pgfooget{max}{#1} + }% + + % Setter + \method set value(#1) { + \pgfooset{val}{#1} + \pgfkeyssetvalue{/data point/\pgfoovalueof{attribute}}{#1}% + }% + + % Setter + \method set start value(#1) { + \pgfooset{start val}{#1} + \pgfooset{max}{#1} + }% + + % Setter + \method set filter(#1) { + \pgfooset{filter}{#1} + }% + + % Setter + \method set step(#1) { + \pgfooset{step}{#1} + }% + + % Slot + \method apply() { + \pgfdvfilterpassedtrue% + \pgfoovalueof{filter}% + \ifpgfdvfilterpassed% + \pgfmathparse{\pgfoovalueof{val}+\pgfoovalueof{step}}% + \pgfoolet{val}\pgfmathresult% + \pgfkeyslet{/data point/\pgfoovalueof{attribute}}\pgfmathresult% + \ifdim\pgfmathresult pt>\pgfoovalueof{max}pt\relax% + \pgfoolet{max}\pgfmathresult% + \fi% + \pgfooget{max}\pgfmathresult% + \pgfkeyslet{/data point/\pgfoovalueof{attribute}/max}\pgfmathresult% + \fi% + }% + + % Slot + \method phase(#1) { + \ifx#1\pgfdvbeginsurvey + \pgfooeset{val}{\pgfoovalueof{start val}} + \else \ifx#1\pgfdvbeginvisualization + \pgfooeset{val}{\pgfoovalueof{start val}} + \fi\fi + }% +}% + +\newif\ifpgfdvfilterpassed + + + + +\pgfooclass{accumulator} +{% + % Class accumulator + % + % This class is used to keep track of the sum of the values of some + % attribute. When this object is connected to some attribute, the + % subkey attribute/sum will always contain the sum of all values in + % all previous data points with respect to this + % attribute. Furthermore, the subkey attribute/prev key will hold + % the previous sum, which is sometimes also useful to have access + % to. + + \attribute attribute;% + % The attribute to-be-accumulated + + \attribute sum;% + % The initial value of the sum + + % Constructor + % + % #1 = to-be-accumulated attribute + \method accumulator(#1) { + \pgfooset{attribute}{#1} + \pgfoothis.reset() + }% + + % Method + \method default connects() { + \pgfoothis.get handle(\pgf@dv@me) + \pgfkeysvalueof{/pgf/data visualization/obj}.connect(\pgf@dv@me,apply,prepare datapoint signal) + \pgfkeysvalueof{/pgf/data visualization/obj}.connect(\pgf@dv@me,phase,phase signal) + }% + + % Slot + \method apply() {% + % Save old sum + \pgfkeysgetvalue{/data point/\pgfoovalueof{attribute}/sum}\pgf@temp% + \pgfkeyslet{/data point/\pgfoovalueof{attribute}/prev sum}\pgf@temp% + % Add new attribute... + \pgfkeysgetvalue{/data point/\pgfoovalueof{attribute}}{\pgf@temp@b}% + \pgfdvmathenter{\pgf@dv@new@val}{\pgf@temp@b}% + % ... to sum + \pgfooget{sum}\pgf@dv@sum% + \pgfdvmathadd{\pgf@dv@sum}{\pgf@dv@new@val}{\pgf@dv@sum}% + \pgfoolet{sum}\pgf@dv@sum + % Record in attribute + \pgfdvmathexitbyscientificformat{\pgf@dv@serial@sum}{\pgf@dv@sum} + \pgfkeyslet{/data point/\pgfoovalueof{attribute}/sum}\pgf@dv@serial@sum + }% + + % Method + % + % Use this method to reset the sum at any point. + % + \method reset() { + \pgfdvmathenter{\pgf@dv@temp}{0} + \pgfoolet{sum}{\pgf@dv@temp} + \pgfdvmathexitbyserializing{\pgf@dv@zero}{\pgf@dv@temp} + \pgfkeyslet{/data point/\pgfoovalueof{attribute}/prev sum}{\pgf@dv@zero} + \pgfkeyslet{/data point/\pgfoovalueof{attribute}/sum}{\pgf@dv@zero} + }% + + % Slot + \method phase(#1) { + \ifx#1\pgfdvbeginsurvey + \pgfoothis.reset() + \else \ifx#1\pgfdvbeginvisualization + \pgfoothis.reset() + \fi\fi + }% +}% + + + + + + + +% +% +% Help keys and, attributes +% +% + +\pgfkeys{% do not even think of changing the values of the following: + /data point/always true/.initial=true, + /data point/always false/.initial=false, + /data point/always 0/.initial=0, + /data point/always 1/.initial=1, + /data point/always empty/.initial=, + /data point/always pgfusepath stroke/.initial=\pgfusepath{stroke}, +}% + +\pgfdvmathenter{\pgfdvmathalwaysloge}{0.434294481903252}% +\pgfdvmathenter{\pgfdvmathalwayslnten}{2.302585092994046}% +\pgfdvmathenter{\pgfdvmathalwayszero}{0}% +\pgfdvmathenter{\pgfdvmathalwaysone}{1}% + + +\endinput diff --git a/graphics/pgf/base/tex/generic/modules/pgfmoduledecorations.code.tex b/graphics/pgf/base/tex/generic/modules/pgfmoduledecorations.code.tex new file mode 100644 index 0000000000..c119dd622f --- /dev/null +++ b/graphics/pgf/base/tex/generic/modules/pgfmoduledecorations.code.tex @@ -0,0 +1,2191 @@ +% Copyright 2019 by Mark Wibrow and Till Tantau +% +% This file may be distributed and/or modified +% +% 1. under the LaTeX Project Public License and/or +% 2. under the GNU Public License. +% +% See the file doc/generic/pgf/licenses/LICENSE for more details. + + +% The following naming conventions are (more or less) observed: +% +% `decorated' : the path that is being decorated. +% `decoration' : any path created/left by the automaton +% `decorate' : generic use where distinction is probably unimportant +% (e.g. internal macros). + + + +% These should be moved. +% +\def\pgfgetpath#1{\pgfsyssoftpath@getcurrentpath{#1}}% +\def\pgfsetpath#1{\pgfsyssoftpath@setcurrentpath{#1}}% + + +% Required registers. +% +\newdimen\pgfdecoratedcompleteddistance +\newdimen\pgfdecoratedremainingdistance +\newdimen\pgfdecoratedinputsegmentcompleteddistance +\newdimen\pgfdecoratedinputsegmentremainingdistance +\newdimen\pgf@decorate@distancetomove + +\newcount\pgf@decorate@repeatstate + + +% Extra options used in all libraries + +\newdimen\pgfdecorationsegmentamplitude +\newdimen\pgfdecorationsegmentlength +\pgfdecorationsegmentamplitude2.5pt +\pgfdecorationsegmentlength10pt +\def\pgfdecorationsegmentangle{45}% +\def\pgfdecorationsegmentaspect{0.5}% +\def\pgfmetadecorationsegmentamplitude{2.5pt}% +\def\pgfmetadecorationsegmentlength{1cm}% + +\pgfkeys{% + /pgf/decoration/.code={\pgfkeys{/pgf/decoration/.cd,#1}}, + /pgf/decoration/.cd, + amplitude/.code={\pgfmathsetlength\pgfdecorationsegmentamplitude{#1}}, + meta-amplitude/.store in=\pgfmetadecorationsegmentamplitude, + segment length/.code={\pgfmathsetlength\pgfdecorationsegmentlength{#1}}, + meta-segment length/.store in=\pgfmetadecorationsegmentlength, + angle/.code={\pgfmathparse{#1}\let\pgfdecorationsegmentangle\pgfmathresult}, + aspect/.code={\pgfmathparse{#1}\let\pgfdecorationsegmentaspect\pgfmathresult}, + start radius/.initial=2.5pt, + end radius/.initial=2.5pt, + radius/.style={start radius={#1},end radius={#1}}, + path has corners/.is if=pgfdecoratepathhascorners, + reverse path/.is if=pgf@decorate@inputsegmentobjects@reverse, +}% +\newif\ifpgfdecoratepathhascorners +\newif\ifpgf@decorate@inputsegmentobjects@reverse + +% Declare a decoration +% +% #1 = name of the decoration +% #2 = initial state +% #3 = states +% +% This command declares a new decoration for later use. +% Inside the code of #3 the command \state may be used. This command +% will only be defined while #3 is executed. +% +% Example: +% +% \pgfdeclaredecoration{zigzag}{q} +% { +% \state{q}[width=10pt] +% { +% \pgfpathlineto{\pgfpoint{2.5pt}{2.5pt}} +% \pgfpathlineto{\pgfpoint{7.5pt}{-2.5pt}} +% \pgfpathlineto{\pgfpoint{10pt}{0pt}} +% } +% \state{final} +% { +% \pgfpathlineto{\pgfpointdecoratedpathlast} +% } +% } + +\long\def\pgfdeclaredecoration#1#2#3{% + \let\pgf@mod@dec@next=\pgf@mod@dec@firstofone% + \pgfifmetadecoration{#1}{% + \pgferror{You cannot declare a decoration of the same + name as a meta-decoration}% + \let\pgf@mod@dec@next=\pgfutil@gobble% + }{}% + \pgf@mod@dec@next + {% + \def\pgf@decorate@name{#1}% + \pgfutil@namedef{pgf@decorate@@#1@initial}{#2}% + \let\pgf@orig@state\state% + \let\state\pgf@decorate@state + #3% + \let\state\pgf@orig@state% + }% +}% + +\def\pgf@decorate@state#1{\pgfutil@ifnextchar[{\pgf@decorate@@start#1}{\pgf@decorate@@start#1[]}}%}% +\def\pgf@decorate@@start#1[#2]#3{% + \pgfutil@namedef{pgf@decorate@@\pgf@decorate@name @#1@options}{#2}% + \pgfutil@namedef{pgf@decorate@@\pgf@decorate@name @#1@code}{#3}% +}% +\long\def\pgf@mod@dec@firstofone#1{#1}% + + +% Test whether something is a decoration +% +% #1 = name +% #2 = if-code +% #3 = else-code +% +% Example: +% +% \pgfifdecoration{zigzag}{Exists}{Does not exist} + +\def\pgfifdecoration#1#2#3{\pgfutil@ifundefined{pgf@decorate@@#1@initial}{#3}{#2}}% + + + +% Declare a meta decoration +% +% #1 = name of the meta decoration +% #2 = initial state +% #3 = states +% +% This command declares a new meta decoration for later use. +% Inside the code of #3 the command \state may be used. + +\long\def\pgfdeclaremetadecoration#1#2#3{% + \let\pgf@mod@dec@next=\pgf@mod@dec@firstofone% + \pgfifdecoration{#1}{% + \pgferror{You cannot declare a meta-decoration of the + same name as a decoration}% + \let\pgf@mod@dec@next=\pgfutil@gobble% + }{}% + \pgf@mod@dec@next{% + \def\pgf@metadecoration@name{#1}% + \pgfutil@namedef{pgf@metadecoration@@#1@initial}{#2}% + \let\pgf@orig@state\state% + \let\state\pgf@metadecoration@state + #3% + \let\state\pgf@orig@state% + }% +}% + +\def\pgf@metadecoration@state#1{\pgfutil@ifnextchar[{\pgf@metadecoration@@state#1}{\pgf@metadecoration@@state#1[]}}%}% +\def\pgf@metadecoration@@state#1[#2]#3{% + \pgfutil@namedef{pgf@metadecoration@@\pgf@metadecoration@name @#1@options}{#2}% + \pgfutil@namedef{pgf@metadecoration@@\pgf@metadecoration@name @#1@code}{#3}% +}% + + +% Test whether something is a meta-decoration +% +% #1 = name +% #2 = if-code +% #3 = else-code +% +% Example: +% +% \pgfifmetadecoration{zigzag}{Exists}{Does not exist} + +\def\pgfifmetadecoration#1#2#3{\pgfutil@ifundefined{pgf@metadecoration@@#1@initial}{#3}{#2}}% + + + + + +\let\pgfdecoratebeforecode\pgfutil@empty +\let\pgfdecorateaftercode\pgfutil@empty + +% \pgfdecoratepath +% +% Decorate a path. +% +% \pgfdecoratebeforecode and \pgfdecorateaftercode can be used to +% execute code before and after the decoration. +% +% #1 - the name of the decoration. +% #2 - a (non-empty) path specification. +% +% Example: +% +% \pgfdeclaredecoration{circles}{red circles}{ +% \state{red circles}[width=10pt, repeat state=2, next state=blue circles] +% { +% \pgfmathparse{round(rnd*100)} +% \pgfsetstrokecolor{red!\pgfmathresult!brown} +% \pgfsetfillcolor{orange!\pgfmathresult!yellow} +% \pgfpathcircle{\pgfpointorigin}{2.5pt} +% \pgfusepath{stroke,fill} +% } +% \state{blue circles}[width=10pt, repeat state=1, next state=red circles] +% { +% \pgfmathparse{round(rnd*100)} +% \pgfsetstrokecolor{blue!\pgfmathresult!gray} +% \pgfsetfillcolor{blue!\pgfmathresult!white} +% \pgfpathcircle{\pgfpointorigin}{2.5pt} +% \pgfusepath{stroke,fill} +% } +% \state{final}{} +% } +% +% \pgfpicture +% \pgfdecoratepath{circles} +% { +% \pgfpathmoveto{\pgfpointorigin} +% \pgfpathcurveto{\pgfpoint{0cm}{3cm}}{\pgfpoint{4cm}{0cm}}{\pgfpoint{4cm}{3cm}} +% } +% \endpgfpicture +% +\long\def\pgfdecoratepath#1#2{% + \pgfdecoration{{#1}{\pgfdecoratedpathlength}{\pgfdecoratebeforecode}{\pgfdecorateaftercode}}% + #2% + \endpgfdecoration}% + + + + +% \pgfdecoratecurrentpath +% +% Decorate the current/existing path, but (unavoidably) destroys it. +% +% \pgfdecoratebeforecode and \pgfdecorateaftercode can be used to +% execute code before and after the decoration. +% +% #1 - the name of the decoration. +% +% Example: +% +% \pgfdeclaredecoration{stars}{move}{ +% \state{move}[width=7.5pt, next state=star]{} +% \state{star}[width=7.5pt, next state=move] +% { +% \pgfmathparse{round(rnd*100)} +% \pgfsetfillcolor{yellow!\pgfmathresult!orange} +% \pgfsetstrokecolor{yellow!\pgfmathresult!red} +% \pgfmathparse{rnd*.75+.25} +% \pgftransformscale{\pgfmathresult} +% \pgfnode{star}{center}{}{}{\pgfusepath{stroke,fill}} +% } +% \state{final} +% { +% \pgfpathmoveto{\pgfpointdecoratedpathlast} +% } +% } +% +% \pgfpicture +% \pgfpathmoveto{\pgfpointorigin} +% \pgfpathcurveto{\pgfqpoint{0cm}{2cm}}{\pgfqpoint{3cm}{2cm}}{\pgfqpoint{3cm}{0cm}} +% \pgfpathcurveto{\pgfqpoint{3cm}{-3cm}}{\pgfqpoint{0cm}{0cm}}{\pgfqpoint{0cm}{-3cm}} +% \pgfpathcurveto{\pgfqpoint{0cm}{-5cm}}{\pgfqpoint{3cm}{-5cm}}{\pgfqpoint{3cm}{-3cm}} +% \pgfdecoratecurrentpath{stars} +% \endpgfpicture +% +\def\pgfdecoratecurrentpath#1{% + \pgfgetpath\pgf@decorate@currentpath% + \pgfsetpath\pgfutil@empty% + \pgfdecoration{{#1}{\pgfdecoratedpathlength}{\pgfdecoratebeforecode}{\pgfdecorateaftercode}}% + \pgfsetpath\pgf@decorate@currentpath% + \endpgfdecoration}% + + + + +% Environment \pgfdecoration \endpgfdecoration +% +% Decorates a path with multiple decorations. +% +% Definitions: +% +% `existing path' +% - any unused path existing prior to the environment. +% +% `decorated path' +% - the path that is being decorated. +% +% `decoration path' +% - any unused path left by the decoration automaton. +% +% The action of the environment is as follows: +% +% 1. Any existing path is saved. +% 2. Construction commands are executed forming the decorated path. +% 3. If the first command in the decorated path is not a `move to', +% the last known position from the existing path is inserted. +% 4. The saved existing path is reinstalled. +% 5. The decoration automaton is executed along the decorated path. +% +% Parameters: +% +% #1 - a comma separated list of decoration specifications. +% +% A decoration specification takes the format: +% +% {<name>}{<distance>}{<before code>}{<after code>} +% +% The decoration <name> is applied to a section of the decorated +% path from the last point on the decorated path the decoration +% automaton reached to <distance> from that point. +% +% When <distance>} is parsed, the dimension +% \pgfdecoratedreminingdistance holds the distance remaining on the +% entire decorated path, and the macro \pgfdecoratedpathlength holds +% the total length of the entire decorated path. +% After <distance> is parsed, both are set to to the value of +% <distance> for the automaton to decorate current section of the +% decorated path. +% +% <before code> is executed just before the decoration automaton +% starts and <after code> is executed just after the automaton stops. +% If <before code> or <after code> are empty, they can be omitted. +% The macros \pgfdecoratedpathfirst and \pgfdecoratedpathlast +% will be set up as the first and last points on the current section +% of the decorated path. +% +% When the environment exits, the following macros will be set up: +% +% \pgfdecorateexistingpath +% - the existing path. +% +% \pgfdecoratedpath +% - the decorated path. +% +% \pgfdecorationpath +% - any unused decoration path. +% +% \pgfpointdecoratedpathlast +% - the last point on the decorated path. +% +% Examples (require pgfdecorations library): +% +% \pgfpicture +% \pgfdecoration +% { +% {lineto}{\pgfdecoratedpathlength/3}, +% {zigzag}{\pgfdecoratedpathlength/3}, +% {lineto}{\pgfdecoratedpathlength/3} +% } +% \pgfpathmoveto{\pgfpointorigin} +% \pgfpathcurveto{\pgfpoint{0cm}{4cm}}{\pgfpoint{5cm}{0cm}}{\pgfpoint{5cm}{4cm}} +% \endpgfdecoration +% \pgfusepath{stroke} +% \endpgfpicture +% +% +% \pgfpicture +% \pgfdecoration +% { +% {lineto}{\pgfdecoratedpathlength/5}{}{\pgfusepath{stroke}}, +% {zigzag}{\pgfdecoratedpathlength/5} +% { +% \pgfpathmoveto{\pgfpointdecoratedpathfirst} +% } +% { +% \pgfsetstrokecolor{red} +% \pgfusepath{stroke} +% }, +% {lineto}{\pgfdecoratedpathlength/5} +% { +% \pgfpathmoveto{\pgfpointdecoratedpathfirst} +% } +% { +% \pgfsetstrokecolor{black} +% \pgfusepath{stroke} +% }, +% {zigzag}{\pgfdecoratedpathlength/5} +% { +% \pgfpathmoveto{\pgfpointdecoratedpathfirst} +% } +% { +% \pgfsetstrokecolor{red} +% \pgfusepath{stroke} +% }, +% {lineto}{\pgfdecoratedpathlength/5} +% { +% \pgfpathmoveto{\pgfpointdecoratedpathfirst} +% } +% { +% \pgfsetstrokecolor{black} +% \pgfusepath{stroke} +% } +% } +% \pgfpathmoveto{\pgfpointorigin} +% \pgfpathcurveto{\pgfpoint{0cm}{4cm}}{\pgfpoint{5cm}{0cm}}{\pgfpoint{5cm}{4cm}} +% \endpgfdecoration +% +% \endpgfpicture +% +% +% +% \pgfdeclaredecoration{polygons}{move}{ +% \state{move}[width=7.5pt, next state=polygon]{} +% \state{polygon}[width=7.5pt, next state=move] +% { +% \pgfmathparse{round(rnd*100)} +% \pgfsetfillcolor{green!\pgfmathresult!yellow} +% \pgfsetstrokecolor{green!\pgfmathresult!brown} +% \pgfnode{regular polygon}{center}{}{}{\pgfusepath{stroke,fill}} +% } +% \state{final} +% { +% \pgfpathmoveto{\pgfpointdecoratedpathlast} +% } +% } +% +% \pgfpicture +% \pgfdecoration +% { +% {polygons}{45pt}, +% {lineto}{30pt} +% {\pgfpathmoveto{\pgfpointdecoratedpathfirst}} +% { +% \pgfsetstrokecolor{yellow!50!orange} +% \pgfusepath{stroke} +% }, +% {polygons}{45pt}, +% {lineto}{30pt} +% {\pgfpathmoveto{\pgfpointdecoratedpathfirst}} +% { +% \pgfsetstrokecolor{green!50!blue} +% \pgfusepath{stroke} +% }, +% {polygons}{\pgfdecoratedremainingdistance} +% } +% \pgfpathmoveto{\pgfpointorigin} +% \pgfpathcurveto{\pgfpoint{0cm}{4cm}}{\pgfpoint{5cm}{0cm}}{\pgfpoint{5cm}{4cm}} +% \endpgfdecoration +% \endpgfpicture +% +% +% \pgfpicture +% \pgfpathmoveto{\pgfpointorigin} +% \pgfpathcurveto{\pgfpoint{0cm}{4cm}}{\pgfpoint{5cm}{0cm}}{\pgfpoint{5cm}{4cm}} +% \pgfgetpath\temppath +% \pgfsetstrokecolor{red} +% \pgfusepath{stroke} +% +% \pgfset{decoration text={Here is some text all the way along this curve!}} +% \pgfdecoration{{text}{\pgfdecoratedremainingdistance}} +% \pgfsetpath\temppath +% \endpgfdecoration +% \endpgfpicture +% +\def\pgfdecoration#1{% + \begingroup% + \def\pgf@decorate@decorationlist{#1}% + \pgf@decoration@env% +}% + +\def\endpgfdecoration{% + \pgf@decoration@endenv% + \ifx\pgfdecoratedpath\pgfutil@empty% + \else% + % + % Perform the decoration(s). + % + \pgf@decorate@for\pgf@temp:=\pgf@decorate@decorationlist\do{% + \ifx\pgf@temp\pgfutil@empty% + \else% + \expandafter\pgf@decorate@invoke\expandafter{\pgf@temp}% + \fi% + }% + \fi% + \pgfgetpath\pgfdecorationpath% + % + % Take stuff outside the group. + % + \global\let\pgf@decorate@decorationpathtemp\pgfdecorationpath% + \global\let\pgf@decorate@decoratedpathtemp\pgfdecoratedpath% + \global\let\pgf@decorate@existingpathtemp\pgfdecorateexistingpath% + \global\let\pgfpoint@decorated@pathlasttemp\pgfpoint@decorated@pathlast% + \endgroup% + % + % Are we in LaTeX? + % + \pgfutil@ifnextchar\@checkend{\aftergroup\pgf@decorate@installmacrosatend}% + {\pgf@decorate@installmacrosatend}% +}% + + + +% Environment \pgfmetadecoration \endpgfmetadecoration +% +% Decorate a path with decoration automatons! +% +% Example: +% +% \pgfdeclaremetadecoration{fancy line}{line to}{ +% \state{line to}[width=1cm, next state=zigzag] +% { +% \decoration{lineto} +% \beforedecoration{\pgfpathmoveto{\pgfpointdecoratedpathfirst}} +% \afterdecoration +% { +% \pgfsetstrokecolor{black} +% \pgfusepath{stroke} +% } +% } +% \state{zigzag}[width=2cm, next state=line to] +% { +% \decoration{zigzag} +% \beforedecoration{\pgfpathmoveto{\pgfpointdecoratedpathfirst}} +% \afterdecoration +% { +% \pgfsetstrokecolor{red} +% \pgfusepath{stroke} +% } +% } +% \state{final} +% { +% \decoration{lineto} +% \beforedecoration{\pgfpathmoveto{\pgfpointdecoratedpathfirst}} +% \afterdecoration +% { +% \pgfsetstrokecolor{black} +% \pgfusepath{stroke} +% } +% } +% } +% +% \pgfpicture +% \pgfmetadecoration{fancy line} +% \pgfpathmoveto{\pgfpointorigin} +% \pgfpathcurveto{\pgfqpoint{0cm}{2cm}}{\pgfqpoint{3cm}{2cm}}{\pgfqpoint{3cm}{0cm}} +% \pgfpathcurveto{\pgfqpoint{3cm}{-3cm}}{\pgfqpoint{0cm}{0cm}}{\pgfqpoint{0cm}{-3cm}} +% \pgfpathcurveto{\pgfqpoint{0cm}{-5cm}}{\pgfqpoint{3cm}{-5cm}}{\pgfqpoint{3cm}{-3cm}} +% \endpgfmetadecoration +% \endpgfpicture +% +\def\pgfmetadecoration#1{% + \begingroup% + \let\decoration\pgf@metadecoration@decoration% + \let\beforedecoration\pgf@metadecoration@beforedecoration% + \let\afterdecoration\pgf@metadecoration@afterdecoration% + \def\pgf@metadecoration@name{#1}% + \pgf@decoration@env% +}% + +\def\endpgfmetadecoration{% + \pgf@decoration@endenv% + \ifx\pgfdecoratedpath\pgfutil@empty% + \else% + \let\pgfmetadecoratedpathlength\pgf@decorate@totalpathlength% + \def\pgfmetadecoratedinputsegmentremainingdistance{\pgfdecoratedremainingdistance}% + \def\pgfmetadecoratedinputsegmentcompleteddistance{\pgfdecoratedcompleteddistance}% + % + % Perform the meta decoration... + % + \expandafter\let\expandafter\pgf@metadecoration@current@state% + \csname pgf@metadecoration@@\pgf@metadecoration@name @initial\endcsname% + \pgf@metadecoration@run% + % + % ..until the final state. + % + \let\pgf@decorate@tempname\pgfutil@empty% + \let\pgf@decorate@tempbefore\pgfutil@empty% + \let\pgf@decorate@tempafter\pgfutil@empty% + \csname pgf@metadecoration@@\pgf@metadecoration@name @\pgf@metadecoration@current@state @code\endcsname% + \ifx\pgf@decorate@tempname\pgfutil@empty% + \def\pgf@decorate@tempname{moveto} + \fi% + \pgf@decorate@invoke{% + {\pgf@decorate@tempname}{\pgfdecoratedremainingdistance}% + {\pgf@decorate@tempbefore}{\pgf@decorate@tempafter}% + }% + \fi% + \pgfgetpath\pgfdecorationpath% + % + % Take stuff outside the group. + % + \global\let\pgf@decorate@decorationpathtemp\pgfdecorationpath% + \global\let\pgf@decorate@decoratedpathtemp\pgfdecoratedpath% + \global\let\pgf@decorate@existingpathtemp\pgfdecorateexistingpath% + \global\let\pgfpoint@decorated@pathlasttemp\pgfpoint@decorated@pathlast% + \endgroup% + % + % Are we in LaTeX? + % + \pgfutil@ifnextchar\@checkend{\aftergroup\pgf@decorate@installmacrosatend}% + {\pgf@decorate@installmacrosatend}% +}% + + + + + + +% Internal macro for the decoration environment. +% +\def\pgf@decoration@env{% + \pgfgetpath\pgfdecorateexistingpath% + \pgfsetpath\pgfutil@empty% + \let\pgfdecorationpath\pgfutil@empty% + \let\pgfdecoratedpath\pgfutil@empty% + \let\pgfpoint@decorated@pathlast\pgfpointorigin% + \edef\pgfpoint@decorate@existingpathlast{\pgf@x\the\pgf@path@lastx\pgf@y\the\pgf@path@lasty}% + % + % Begin a group so transformations don't mess things up. + % + \bgroup% +}% + +\def\pgf@decorate@path@check@moveto#1{% + \expandafter\pgf@decorate@path@@check@moveto#1\pgf@decorate@stop\pgf@decorate@@stop}% + +\def\pgf@decorate@token@stop{\pgf@decorate@stop}% +\def\pgf@decorate@path@@check@moveto#1#2#3#4\pgf@decorate@@stop#5#6{% + \def\pgf@decorate@temp{#4}% + \pgf@x=#2\relax% + \pgf@y=#3\relax% + \ifx\pgf@decorate@temp\pgf@decorate@token@stop% + #5% + \else% + #6% + \fi% +}% + +\def\pgf@decoration@endenv{% + \egroup% + \pgftransformreset% + % + % Save the existing soft path and restore the existing path. + % + \pgfgetpath\pgfdecoratedpath% + \pgfsetpath\pgfdecorateexistingpath% + % + \ifx\pgfdecoratedpath\pgfutil@empty% + \pgferror{I cannot decorate an empty path}% + \else% + % + % If the path consists of a single moveto token, make it + % a very small horizontal line. + % + \pgf@decorate@path@check@moveto\pgfdecoratedpath{% + \advance\pgf@x by0.0001pt\relax% + \edef\pgfdecoratedpath{% + \expandafter\noexpand\pgfdecoratedpath% + \noexpand\pgfsyssoftpath@linetotoken{\the\pgf@x}{\the\pgf@y}% + }% + }% + {}% + % + % Remove special round tokens and get points. + % + \pgfprocessround{\pgfdecoratedpath}{\pgfdecoratedpath}% + % + % Parse the soft path into a series of decorated input segment objects. + % + \pgf@decorate@parsesoftpath{\pgfdecoratedpath}{\pgf@decorate@inputsegmentobjects}% + % + % Setup further options + % + \pgfkeys{/pgf/every decoration/.try}% + % + % Reverse objects if necessary. + % + \ifpgf@decorate@inputsegmentobjects@reverse% + \pgf@decorate@inputsegmentobjects@reverse{\pgf@decorate@inputsegmentobjects}{\pgf@decorate@inputsegmentobjects}% + \fi% + % + \let\pgf@decorated@remainingdistance\pgf@decorate@totalpathlength% + % + \let\pgfpoint@decorated@totalpathfirst\pgfpoint@decorated@firstparsed% + \let\pgfpoint@decorated@totalpathlast\pgfpoint@decorate@lastnonmovetoparsed% + % + \let\pgfpoint@decorated@pathfirst\pgfpoint@decorated@totalpathfirst + \let\pgfpoint@decorated@pathlast\pgfpoint@decorated@totalpathlast% + % + % Set up the first input segment. + % + \let\pgf@decorate@currentinputsegmentobjects\pgf@decorate@inputsegmentobjects% + \let\pgf@decorate@transformtoinputsegment\pgfutil@empty% + \pgf@decorate@getnextinputsegmentobject\pgf@decorate@nextinputsegmentobject% + \pgf@decorate@processnextinputsegmentobject% + \pgf@decorate@distancetomove0pt\relax% + \fi% +}% + + + + +\def\pgf@decorate@installmacrosatend{% + \let\pgfdecorationpath\pgf@decorate@decorationpathtemp% + \let\pgfdecoratedpath\pgf@decorate@decoratedpathtemp% + \let\pgfdecorateexistingpath\pgf@decorate@existingpathtemp% + \let\pgfpoint@decorated@pathlast\pgfpoint@decorated@pathlasttemp% +}% + +\let\pgfdecorationpath\pgfutil@empty +\let\pgfdecoratedpath\pgfutil@empty +\let\pgfdecorateexisitingpath\pgfutil@empty + +\let\pgfpoint@decorated@pathlast\pgfpointorigin + + + +% A version of \pgfutil@for which doesn't expand each value in #3. +% +\def\pgf@decorate@for#1:=#2\do#3{% + \def\pgf@decorate@for@var{#1}% + \def\pgf@decorate@for@action{#3}% + \expandafter\pgf@decorate@@for#2,\pgf@stop,}% + +\def\pgf@decorate@@for#1,{% + \ifx#1\pgf@stop% + \expandafter\let\pgf@decorate@for@var\pgfutil@empty% + \else% + \expandafter\def\pgf@decorate@for@var{#1}% + \pgf@decorate@for@action% + \expandafter\pgf@decorate@@for% + \fi% +}% + + +% Invoke a decoration. +% +% #1 - {<name>}{<distance>}{<before code>}{<after code>} +% +\def\pgf@decorate@invoke#1{% + \pgf@decorate@@invoke#1\pgf@decorate@invoke\pgf@decorate@invoke\pgf@decorate@invoke\pgf@stop}% + +\def\pgf@decorate@@invoke#1#2#3#4#5\pgf@stop{% + \pgfutil@ifundefined{pgf@decorate@@#1@initial}% + {% + \pgferror{Unknown decoration `#1'}% + }% + {% + \def\pgf@decorate@name{#1}% + \ifx\pgf@decorate@invoke#3% + \let\pgf@decorate@beforecode\pgfutil@empty% + \else% + \def\pgf@decorate@beforecode{#3}% + \fi% + \ifx\pgf@decorate@invoke#4% + \let\pgf@decorate@aftercode\pgfutil@empty% + \else% + \def\pgf@decorate@aftercode{#4}% + \fi% + % + % Make remaining distance and the decorated path length `public'. + % + \pgfdecoratedremainingdistance\pgf@decorated@remainingdistance\relax% + \let\pgfdecoratedpathlength\pgf@decorate@totalpathlength% + % + \pgfmathsetlength\pgf@xa{#2}% + \ifdim\pgf@xa>\pgf@decorated@remainingdistance\relax% + \pgf@xa\pgf@decorated@remainingdistance\relax% + \fi% + \edef\pgf@decorate@currentpathlength{\the\pgf@xa}% + % + % Calculate the distance remaining to the end of the entire path. + % + \pgf@xa-\pgf@decorate@currentpathlength\relax% + \advance\pgf@xa\pgf@decorated@remainingdistance\relax% + \edef\pgf@decorated@remainingdistance{\the\pgf@xa}% + % + % Set up the macros and distances for the current section + % of the decorated path. + % + \let\pgfdecoratedpathlength\pgf@decorate@currentpathlength% + \pgfdecoratedremainingdistance\pgfdecoratedpathlength\relax% + \pgfdecoratedcompleteddistance0pt\relax% + % + % Execute user-defined code beore decoration. + % + \pgf@decorate@beforecode% + % + % Run the decoration... + % + \expandafter\let\expandafter\pgf@decorate@current@state% + \csname pgf@decorate@@\pgf@decorate@name @initial\endcsname% + \pgf@decorate@run% + % + % ...until the final state. + % + \ifdim\pgf@decorated@remainingdistance<1pt\relax% Should be =0pt, but need to control for inaccuracies. + \let\pgfpoint@decorated@pathlast\pgfpoint@decorated@totalpathlast% + \else% + % + % Set up \pgfpoint@decorated@pathlast if the end of + % the total decorated path has not been reached. + % + {% + \pgf@decorate@movealonginputsegment{\the\pgfdecoratedremainingdistance}% + \pgf@decorate@transformtoinputsegment% + \pgfpointorigin% + \pgf@pos@transform@glob% + \global\pgf@x\pgf@x% + \global\pgf@y\pgf@y% + }% + \edef\pgfpoint@decorated@pathlast{\pgf@x\the\pgf@x\pgf@y\the\pgf@y}% + \fi% + {% + \pgftransformreset% + \pgf@decorate@transformtoinputsegment% + \pgf@decorate@additionaltransform% + \csname pgf@decorate@@\pgf@decorate@name @final@code\endcsname% + }% + \pgf@decorate@movealonginputsegment{\the\pgfdecoratedremainingdistance}% + % + % Execute user-defined code after decoration. + % + \pgf@decorate@aftercode% + % + % Update the input segment objects. + % + \let\pgf@decorate@additionaltransform\pgfutil@empty% + \let\pgf@decorate@inputsegmentobjects\pgf@decorate@inputsegmentobjects@aftersplit% + \let\pgfpoint@decorated@pathfirst\pgfpoint@decorated@pathlast% + }% +}% + + + +\let\pgf@decorate@transformtoinputsegment\pgfutil@empty% +\let\pgf@decorate@additionaltransform\pgfutil@empty% + + +% \pgfpointdecoratedpathfirst +% +\def\pgfpointdecoratedpathfirst{% + {% + \pgftransforminvert% + \pgfpoint@decorated@pathfirst% + \pgf@pos@transform@glob% + \global\pgf@x\pgf@x% + \global\pgf@y\pgf@y% + }% +}% + +% \pgfpointdecoratedpathlast +% +\def\pgfpointdecoratedpathlast{% + {% + \pgftransforminvert% + \pgfpoint@decorated@pathlast% + \pgf@pos@transform@glob% + \global\pgf@x\pgf@x% + \global\pgf@y\pgf@y% + }% +}% + +% \pgfpointdecoratedinputsegmentfirst +% +% The first point of the current sub-path. +% +\def\pgfpointdecoratedinputsegmentfirst{% + {% + \pgftransforminvert% + \pgf@decorate@inputsegment@first% + \pgf@pos@transform@glob% + \global\pgf@x\pgf@x% + \global\pgf@y\pgf@y% + }% +}% + +% \pgfpointdecoratedinputsegmentlast +% +% The final point of the current sub-path. +% +\def\pgfpointdecoratedinputsegmentlast{% + {% + \pgftransforminvert% + \pgf@decorate@inputsegment@last% + \pgf@pos@transform@glob% + \global\pgf@x\pgf@x% + \global\pgf@y\pgf@y% + }% +}% + + +\def\pgf@final@text{final}% + +% Run a normal decoration. +% +\def\pgf@decorate@run{% + \let\pgf@decorate@next\pgf@decorate@do@state% + \ifx\pgf@decorate@current@state\pgf@final@text% + \let\pgf@decorate@next\relax% + \fi% + \pgf@decorate@next% +}% + +\def\pgf@decorate@do@state{% + \let\pgf@decorate@next\relax% + \let\pgf@decorate@next@state\pgf@decorate@current@state% + % + % Get the options. + % + \let\pgf@decorate@persistent@pre=\relax% + \let\pgf@decorate@persistent@post=\relax% + \expandafter\expandafter\expandafter\pgf@decorate@setter + \expandafter\expandafter\expandafter{\csname pgf@decorate@@\pgf@decorate@name @\pgf@decorate@current@state @options\endcsname}% + \ifx\pgf@decorate@next\relax% + \let\pgf@decorate@next\pgf@decorate@do@code% + \fi% + \pgf@decorate@next% +}% + +\def\pgf@decorate@setter{\pgfqkeys{/pgf/decoration automaton}}% + +\pgf@decorate@repeatstate-1\relax + +\def\pgf@decorate@do@code{% + % + % Execute code. + % + \pgf@decorate@persistent@pre% + {% + \pgftransformreset% + \pgf@decorate@transformtoinputsegment% + \pgf@decorate@additionaltransform% + \csname pgf@decorate@@\pgf@decorate@name @\pgf@decorate@current@state @code\endcsname% + }% + \pgf@decorate@persistent@post% + \pgf@decorate@movealongpath{\pgf@decorate@width}% + % + % Next iteration. + % + \ifnum\pgf@decorate@repeatstate>0\relax% + \advance\pgf@decorate@repeatstate-1\relax% + \else% + \pgf@decorate@repeatstate-1\relax% + \let\pgf@decorate@current@state\pgf@decorate@next@state% + \fi% + \pgf@decorate@run% +}% + +\pgfkeys{ + /pgf/decoration automaton/width/.code=\def\pgf@decorate@width{#1}\pgf@decorate@switch@if#1 to final\pgf@stop,% + /pgf/decoration automaton/switch if less than/.code=\pgf@decorate@switch@if#1\pgf@stop,% + /pgf/decoration automaton/switch if input segment less than/.code=\pgf@decorate@switch@ifinputsegment#1\pgf@stop,% + /pgf/decoration automaton/next state/.store in=\pgf@decorate@next@state,% + /pgf/decoration automaton/persistent precomputation/.store in=\pgf@decorate@persistent@pre,% + /pgf/decoration automaton/persistent postcomputation/.store in=\pgf@decorate@persistent@post,% + /pgf/decoration automaton/repeat state/.code={% + \ifnum\pgf@decorate@repeatstate=-1\relax% + \pgfmathsetcount\pgf@decorate@repeatstate{#1}% + \fi% + },% + /pgf/decoration automaton/if input segment is closepath/.code={% + \ifpgf@decorate@is@closepath@% + \pgfkeysalso{#1}% + \fi% + }, + /pgf/decoration automaton/auto end on length/.code=\pgf@decorate@auto@end{#1}, + /pgf/decoration automaton/auto corner on length/.code=\pgf@decorate@auto@corner{#1}, +}% + +\def\pgf@decorate@auto@end#1{% + \ifx\pgf@decorate@next\relax% + \pgfmathsetlength\pgf@x{#1}% + \ifpgf@decorate@is@closepath@% + \ifdim\pgfdecoratedremainingdistance>\pgf@x% + \ifdim\pgfdecoratedinputsegmentremainingdistance>\pgf@x% + \else% + \pgfpathclose% + \pgf@decorate@movealongpath{\pgfdecoratedinputsegmentremainingdistance}% + \pgf@decorate@repeatstate-1\relax% + \let\pgf@decorate@next\pgf@decorate@run% + \fi% + \else% + \pgfpathclose% + \def\pgf@decorate@current@state{final}% + \def\pgf@decorate@width{\pgfdecoratedremainingdistance} + \pgf@decorate@repeatstate-1\relax% + \let\pgf@decorate@next\pgf@decorate@run% + \fi% + \else% + \ifdim\pgfdecoratedremainingdistance>\pgf@x% + \else% + {% + \pgftransformreset% + \pgf@decorate@transformtoinputsegment% + \pgf@decorate@additionaltransform% + \pgfpathlineto{\pgfpoint{\pgfdecoratedremainingdistance}{0pt}} + }% + \def\pgf@decorate@current@state{final}% + \def\pgf@decorate@width{\pgfdecoratedremainingdistance} + \pgf@decorate@repeatstate-1\relax% + \let\pgf@decorate@next\pgf@decorate@run% + \fi% + \fi% + \fi% +}% + +\def\pgf@decorate@auto@corner#1{% + \ifpgfdecoratepathhascorners% + \ifx\pgf@decorate@next\relax% + \pgfmathsetlength\pgf@x{#1}% + \ifdim\pgfdecoratedinputsegmentremainingdistance>\pgf@x% + \else% + {% + \pgftransformreset% + \pgf@decorate@transformtoinputsegment% + \pgf@decorate@additionaltransform% + \pgfpathlineto{\pgfqpoint{\pgfdecoratedinputsegmentremainingdistance}{0pt}} + }% + \pgf@decorate@movealongpath{\pgfdecoratedinputsegmentremainingdistance}% + \pgf@decorate@repeatstate-1\relax% + \let\pgf@decorate@next\pgf@decorate@run% + \fi% + \fi% + \fi% +}% + +\def\pgf@decorate@switch@if#1to #2\pgf@stop{% + \ifx\pgf@decorate@next\relax% + \ifdim\pgfdecoratedremainingdistance=0pt\relax% + \def\pgf@decorate@current@state{#2}% + \pgf@decorate@repeatstate-1\relax% + \let\pgf@decorate@next\pgf@decorate@run% + \else% + \pgfmathsetlength\pgf@x{#1}% + \ifdim\pgfdecoratedremainingdistance<\pgf@x% + \def\pgf@decorate@current@state{#2}% + \pgf@decorate@repeatstate-1\relax% + \let\pgf@decorate@next\pgf@decorate@run% + \fi% + \fi% + \fi% +}% + +\def\pgf@decorate@switch@ifinputsegment#1to #2\pgf@stop{% + \ifx\pgf@decorate@next\relax% + \pgfmathsetlength\pgf@x{#1}% + \ifdim\pgfdecoratedinputsegmentremainingdistance<\pgf@x% + \def\pgf@decorate@current@state{#2}% + \pgf@decorate@repeatstate-1\relax% + \let\pgf@decorate@next\pgf@decorate@run% + \fi% + \fi% +}% + + + + +% \pgfsetdecorationsegmenttransformation +% +% Set the additional transform for each decoration state. +% +% Example: +% +% \pgfsetdecorationsegmenttransformation{\pgftransformyshift{5pt}} +% +\def\pgfsetdecorationsegmenttransformation#1{\def\pgf@decorate@additionaltransform{#1}}% +\let\pgf@decorate@additionaltransform\pgfutil@empty% + + + + +\def\pgf@metadecoration@decoration#1{% + \edef\pgf@decorate@tempname{#1}% +}% +\def\pgf@metadecoration@beforedecoration#1{% + \def\pgf@decorate@tempbefore{#1}% +}% +\def\pgf@metadecoration@afterdecoration#1{% + \def\pgf@decorate@tempafter{#1}% +}% + + +\def\pgf@final@text{final}% + +% Run a meta decoration. +% +\def\pgf@metadecoration@run{% + \let\pgf@metadecoration@next\pgf@metadecoration@do@state% + \ifx\pgf@metadecoration@current@state\pgf@final@text% + \let\pgf@metadecoration@next\relax% + \fi% + \pgf@metadecoration@next% +}% + +\def\pgf@metadecoration@do@state{% + \let\pgf@metadecoration@next\relax% + \let\pgf@metadecoration@next@state\pgf@metadecoration@current@state% + % + % Set up some macros. + % + \let\pgfmetadecoratedremainingdistance\pgf@decorated@remainingdistance% + \pgf@x-\pgfmetadecoratedremainingdistance\relax% + \advance\pgf@x\pgf@decorate@totalpathlength\relax% + \edef\pgfmetadecoratedcompleteddistance{\the\pgf@x}% + % + % Get the options. + % + \expandafter\expandafter\expandafter\pgf@metadecoration@setter + \expandafter\expandafter\expandafter{\csname pgf@metadecoration@@\pgf@metadecoration@name @\pgf@metadecoration@current@state @options\endcsname}% + \ifx\pgf@metadecoration@next\relax% + \let\pgf@metadecoration@next\pgf@metadecoration@do@code% + \fi% + \pgf@metadecoration@next% +}% + +\def\pgf@metadecoration@do@code{% + % + % Execute code. + % + \let\pgf@decorate@tempname\pgfutil@empty% + \let\pgf@decorate@tempbefore\pgfutil@empty% + \let\pgf@decorate@tempafter\pgfutil@empty% + \let\pgfpointmetadecoratedpathfirst\pgfpointdecoratedpathfirst% + \let\pgfpointmetadecoratedpathlast\pgfpointdecoratedpathlast% + \csname pgf@metadecoration@@\pgf@metadecoration@name @\pgf@metadecoration@current@state @code\endcsname% + \ifx\pgf@decorate@tempname\pgfutil@empty% + \def\pgf@decorate@tempname{moveto} + \fi% + \pgf@decorate@invoke{% + {\pgf@decorate@tempname}{\pgf@metadecoration@width}% + {\pgf@decorate@tempbefore}{\pgf@decorate@tempafter}% + }% + % + % Next iteration. + % + \let\pgf@metadecoration@current@state\pgf@metadecoration@next@state% + \pgf@metadecoration@run% +}% + +\def\pgf@metadecoration@setter{\pgfqkeys{/pgf/meta-decoration automaton}}% + +\pgfkeys{ + /pgf/meta-decoration automaton/width/.code=\def\pgf@metadecoration@width{#1}\pgf@metadecoration@switch@if#1 to final\pgf@stop,% + /pgf/meta-decoration automaton/switch if less than/.code=\pgf@metadecoration@switch@if#1\pgf@stop,% + /pgf/meta-decoration automaton/switch if input segment less than/.code=\pgf@metadecoration@switch@ifinputsegment#1\pgf@stop,% + /pgf/meta-decoration automaton/next state/.store in=\pgf@metadecoration@next@state,% +}% + +\def\pgf@metadecoration@switch@if#1to #2\pgf@stop{% + \ifx\pgf@metadecoration@next\relax% + \pgfmathsetlength\pgf@x{#1}% + \ifdim\pgf@decorated@remainingdistance<\pgf@x% + \def\pgf@metadecoration@current@state{#2}% + \let\pgf@metadecoration@next\pgf@metadecoration@run% + \fi% + \fi% +}% + +\def\pgf@metadecoration@switch@ifinputsegment#1to #2\pgf@stop{% + \ifx\pgf@metadecoration@next\relax% + \pgfmathsetlength\pgf@x{#1}% + \ifdim\pgfdecoratedinputsegmentremainingdistance<\pgf@x% + \def\pgf@metadecoration@current@state{#2}% + \let\pgf@metadecoration@next\pgf@metadecoration@run% + \fi% + \fi% +}% + + + +% Move along the path by a specified distance. +% +\def\pgf@decorate@movealongpath#1{% + \pgfmathsetlength\pgf@decorate@distancetomove{#1}% + \advance\pgfdecoratedcompleteddistance\pgf@decorate@distancetomove% + \advance\pgfdecoratedremainingdistance-\pgf@decorate@distancetomove% + \pgf@decorate@@movealongpath% +}% +\def\pgf@decorate@@movealongpath{% + \advance\pgfdecoratedinputsegmentcompleteddistance\pgf@decorate@distancetomove% + \advance\pgfdecoratedinputsegmentremainingdistance-\pgf@decorate@distancetomove% + \ifdim\pgfdecoratedinputsegmentremainingdistance>0pt\relax% + \let\pgf@next\pgf@decorate@@@movealongpath% + \else% + \pgf@decorate@distancetomove-\pgfdecoratedinputsegmentremainingdistance% + \pgf@decorate@processnextinputsegmentobject% + \ifx\pgf@decorate@currentinputsegmentobjects\pgfutil@empty% + \pgfdecoratedremainingdistance0pt\relax% + \let\pgf@next\relax% + \else% + \let\pgf@next\pgf@decorate@@movealongpath% + \fi% + \fi% + \pgf@next% +}% + +\def\pgf@decorate@@@movealongpath{% + % + % Move along input segment for real. + % + \pgf@decorate@movealonginputsegment{\the\pgf@decorate@distancetomove}% + \pgf@decorate@distancetomove0pt\relax% + % + % Grrr. Hacking to control some inaccuracies. + % + \ifdim\pgf@decorate@inputsegmenttime pt>1pt\relax% + \let\pgf@decorate@inputsegmenttimetemp\pgf@decorate@inputsegmenttime% + \pgf@decorate@processnextinputsegmentobject% + \pgf@x\pgf@decorate@inputsegmenttimetemp pt\relax% + \advance\pgf@x-1pt\relax% + \edef\pgf@decorate@inputsegmenttime{\pgfmath@tonumber{\pgf@x}}% + \ifx\pgf@decorate@currentinputsegmentobjects\pgfutil@empty% + \pgfdecoratedremainingdistance0pt\relax% + \fi% + \fi% +}% + +% Return the next input segment object in a macro +% +\def\pgf@decorate@getnextinputsegmentobject#1{% + \ifx\pgf@decorate@currentinputsegmentobjects\pgfutil@empty% + \let\pgf@next\relax% + \else% + \def\pgf@decorate@temp{#1}% + \let\pgf@next\pgf@decorate@@getnextinputsegmentobject% + \fi% + \pgf@next% +}% +\def\pgf@decorate@@getnextinputsegmentobject{% + \expandafter\pgf@decorate@@@getnextinputsegmentobject\pgf@decorate@currentinputsegmentobjects\pgf@stop}% +\def\pgf@decorate@@@getnextinputsegmentobject#1#2\pgf@stop{% + \expandafter\def\pgf@decorate@temp{#1}% + \def\pgf@decorate@currentinputsegmentobjects{#2}}% + +% Process the next input segment object. +% +\def\pgf@decorate@processnextinputsegmentobject{% + \let\pgfdecorationpreviousinputsegment\pgfdecoratecurrentinputsegment% + \let\pgf@decorate@currentinputsegmentobject\pgf@decorate@nextinputsegmentobject% + \pgf@decorate@getnextinputsegmentobject\pgf@decorate@nextinputsegmentobject% + % + % If the current input segment object is a moveto, execute the + % object macro and get the next input segment object. + % + \pgf@decorate@is@closepath@false% + \pgf@decorate@currentinputsegmentobject% + \ifx\pgfdecorationcurrentinputsegment\pgfdecorationinputsegmentmoveto% + \pgfpathmoveto{\pgf@decorate@inputsegment@first}% + \let\pgfdecorationpreviousinputsegment\pgfdecorationcurrentinputsegment% + \let\pgf@decorate@currentinputsegmentobject\pgf@decorate@nextinputsegmentobject% + \pgf@decorate@getnextinputsegmentobject\pgf@decorate@nextinputsegmentobject% + \pgf@decorate@is@closepath@false% + \pgf@decorate@currentinputsegmentobject% + \fi% + % + % Teensy hack in case a path goes nowhere. + % + \ifdim\pgfdecoratedinputsegmentlength=0pt\relax% + \def\pgfdecoratedinputsegmentlength{0.05pt}% Arbitrary choice >0pt. + \fi% + \pgfdecoratedinputsegmentremainingdistance\pgfdecoratedinputsegmentlength\relax% + \pgfdecoratedinputsegmentcompleteddistance0pt\relax% + \def\pgf@decorate@inputsegmenttime{0}% + % + % Get the angle at the start of the input segment. + % + \ifx\pgfdecorationcurrentinputsegment\pgfdecorationinputsegmentcurveto% + \pgfmathanglebetweenpoints{\pgf@decorate@inputsegment@first}{\pgf@decorate@inputsegment@supporta}% + \let\pgfdecoratedangle\pgfmathresult% + \let\pgfdecoratedinputsegmentstartangle\pgfmathresult% + \pgfmathanglebetweenpoints{\pgf@decorate@inputsegment@supportb}{\pgf@decorate@inputsegment@last}% + \let\pgfdecoratedinputsegmentendangle\pgfmathresult% + \else% + \pgfmathanglebetweenpoints{\pgf@decorate@inputsegment@first}{\pgf@decorate@inputsegment@last}% + \let\pgfdecoratedangle\pgfmathresult% + \let\pgfdecoratedinputsegmentstartangle\pgfmathresult% + \let\pgfdecoratedinputsegmentendangle\pgfmathresult% + \fi% + % + % Get the angle to the next input segment. + % + \begingroup% + \pgf@decorate@nextinputsegmentobject% + \global\let\pgf@decorate@temp\pgfdecorationcurrentinputsegment% + \ifx\pgfdecorationcurrentinputsegment\pgfdecorationinputsegmentmoveto% + \expandafter\pgf@decorate@currentinputsegmentobject% + \fi% + \ifx\pgfdecorationcurrentinputsegment\pgfdecorationinputsegmentcurveto% + \pgfmathanglebetweenpoints{\pgf@decorate@inputsegment@first}{\pgf@decorate@inputsegment@supporta}% + \else% + \pgfmathanglebetweenpoints{\pgf@decorate@inputsegment@first}{\pgf@decorate@inputsegment@last}% + \fi% + \pgf@x-\pgfdecoratedangle pt\relax% + \advance\pgf@x\pgfmathresult pt\relax% + \edef\pgfdecoratedangletonextinputsegment{\the\pgf@x}% + \pgfmath@smuggleone\pgfdecoratedangletonextinputsegment% + \endgroup% + \let\pgfdecorationnextinputsegmentobject\pgf@decorate@temp% +}% + + +% +% Input segment objects +% + +\newif\ifpgf@decorate@is@closepath@% + +% Input segment types. +% +% These allow comparison with \ifx using +% +% \pgfdecorationcurrentinputsegment +% \pgfdecorationnextinputsegment +% \pgfdecorationcurrentprevioussegment +% +\def\pgfdecorationinputsegmentmoveto{moveto}% +\def\pgfdecorationinputsegmentlineto{lineto}% +\def\pgfdecorationinputsegmentcurveto{curveto}% +\def\pgfdecorationinputsegmentclosepath{closepath}% +\def\pgfdecorationinputsegmentlast{last}% + +% Input segment object moveto. +% +\def\pgf@decorate@inputsegmentobject@moveto#1{% + \def\pgf@decorate@inputsegment@first{#1}% + \def\pgf@decorate@inputsegment@supporta{#1}% + \def\pgf@decorate@inputsegment@supportb{#1}% + \def\pgf@decorate@inputsegment@last{#1}% + \edef\pgf@decorate@lastmoveto{#1}% + \def\pgfdecoratedinputsegmentlength{0pt}% + \let\pgfdecorationcurrentinputsegment\pgfdecorationinputsegmentmoveto% +}% + +% Input segment object lineto. +% +\def\pgf@decorate@inputsegmentobject@lineto#1#2#3{% + \def\pgfdecoratedinputsegmentlength{#1}% + \def\pgf@decorate@inputsegment@first{#2}% + \def\pgf@decorate@inputsegment@last{#3}% + % + % Supports should be defined like this, so if treated as a curve, + % equal time steps will correspond to equal distances. + % + \pgfpointdiff{#2}{#3}% + \pgf@xa\pgf@x% + \pgf@ya\pgf@y% + \pgfextract@process\pgf@decorate@inputsegment@supporta{% + \pgf@process{#2}% + \advance\pgf@x0.333333\pgf@xa% + \advance\pgf@y0.333333\pgf@ya% + }% + \pgfextract@process\pgf@decorate@inputsegment@supportb{% + \pgf@process{#2}% + \advance\pgf@x0.666666\pgf@xa% + \advance\pgf@y0.666666\pgf@ya% + }% + \let\pgf@decorate@movealonginputsegment\pgf@decorate@movealonginputsegment@line% + \let\pgf@decorate@transformtoinputsegment\pgf@decorate@transformtoinputsegment@line% + \let\pgfdecorationcurrentinputsegment\pgfdecorationinputsegmentlineto% +}% + +% Input segment object curveto. +% +\def\pgf@decorate@inputsegmentobject@curveto#1#2#3#4#5{% + \def\pgfdecoratedinputsegmentlength{#1}% + \def\pgf@decorate@inputsegment@first{#2}% + \def\pgf@decorate@inputsegment@supporta{#3}% + \def\pgf@decorate@inputsegment@supportb{#4}% + \def\pgf@decorate@inputsegment@last{#5}% + % + \let\pgf@decorate@movealonginputsegment\pgf@decorate@movealonginputsegment@curve% + \let\pgf@decorate@transformtoinputsegment\pgf@decorate@transformtoinputsegment@curve% + \let\pgfdecorationcurrentinputsegment\pgfdecorationinputsegmentcurveto% +}% + +% Input segment object closepath. +% +% Treated like a lineto, but flag that it is a closepath. +% +\def\pgf@decorate@inputsegmentobject@closepath#1#2#3{% + \pgf@decorate@is@closepath@true% + % + \def\pgfdecoratedinputsegmentlength{#1}% + \def\pgf@decorate@inputsegment@first{#2}% + \def\pgf@decorate@inputsegment@last{#3}% + % + \pgfpointdiff{#2}{#3}% + \pgf@xa\pgf@x% + \pgf@ya\pgf@y% + \pgfextract@process\pgf@decorate@inputsegment@supporta{% + \pgf@process{#2}% + \advance\pgf@x0.333333\pgf@xa% + \advance\pgf@y0.333333\pgf@ya% + }% + \pgfextract@process\pgf@decorate@inputsegment@supportb{% + \pgf@process{#2}% + \advance\pgf@x0.666666\pgf@xa% + \advance\pgf@y0.666666\pgf@ya% + }% + % + \let\pgf@decorate@movealonginputsegment\pgf@decorate@movealonginputsegment@line% + \let\pgf@decorate@transformtoinputsegment\pgf@decorate@transformtoinputsegment@line% + \let\pgfdecorationcurrentinputsegment\pgfdecorationinputsegmentclosepath% +}% + + +% Input segment object endofinputsegments. +% +\def\pgf@decorate@inputsegmentobject@endofinputsegments{% + \let\pgfdecorationcurrentinputsegment\pgfdecorationinputsegmentlast% +}% + + + + +% This macro controls how the automaton moves along a line sub-path +% +\def\pgf@decorate@movealonginputsegment@line#1{}% Nothing special needed. + +% This macro controls how the automaton moves along a curve sub-path. +% +\def\pgf@decorate@movealonginputsegment@curve#1{% + \ifdim#1=0pt\relax% + \else% + \pgfpointcurveattime{\pgf@decorate@inputsegmenttime}% + {\pgf@decorate@inputsegment@first}{\pgf@decorate@inputsegment@supporta}% + {\pgf@decorate@inputsegment@supportb}{\pgf@decorate@inputsegment@last}% + \pgf@xa\pgf@x% + \pgf@ya\pgf@y% + \pgf@xb\pgf@decorate@inputsegmenttime pt\relax% + % + % Reduce the initial time step, depending on the length + % of the path. To do: Optimise this step (should also depend on #1). + % + \ifdim\pgf@decorate@currentpathlength<128pt\relax% + \pgf@yb.03125pt\relax% + \else% + \ifdim\pgf@decorate@currentpathlength<512pt\relax% + \pgf@yb.015625pt\relax% + \else% + \ifdim\pgf@decorate@currentpathlength<2048pt\relax% + \pgf@yb.00390625pt\relax% + \else% + \pgf@yb.0009765625pt\relax% + \fi% + \fi% + \fi% + \c@pgf@counta1\relax% + \pgfutil@tempdima0pt\relax% + \pgfmathloop% + \advance\pgf@xb\c@pgf@counta\pgf@yb% + % + % We would like to use \pgfpointcurveattime. However, we must not + % overuse \pgf@process or \pgfmath stuff within this loop, + % otherwise we increase the chances of save stack overflow. + % + \edef\pgf@decorate@temp{% + \pgf@xa\the\pgf@xa% + \pgf@xb\the\pgf@xb% + \pgf@ya\the\pgf@ya% + \pgf@yb\the\pgf@yb% + }% + % + \pgfpoint@decorate@curveattime{\pgfmath@tonumber{\pgf@xb}}% + {\pgf@decorate@inputsegment@first}{\pgf@decorate@inputsegment@supporta}% + {\pgf@decorate@inputsegment@supportb}{\pgf@decorate@inputsegment@last}% + \pgf@decorate@temp% + % + \pgf@xc\pgf@xa% + \pgf@yc\pgf@ya% + \pgf@xa\pgf@x% + \pgf@ya\pgf@y% + \advance\pgf@x-\pgf@xc% + \advance\pgf@y-\pgf@yc% + \pgfmathveclen@{\pgfmath@tonumber{\pgf@x}}{\pgfmath@tonumber{\pgf@y}}% + \pgfutil@tempdimb\pgfmathresult pt\relax% + \advance\pgfutil@tempdima\c@pgf@counta\pgfutil@tempdimb% + \ifnum\c@pgf@counta>0\relax% + \ifdim\pgfutil@tempdima>#1\relax% + \c@pgf@counta-\c@pgf@counta% + \pgf@yb.5\pgf@yb% + \fi% + \else% + \ifdim\pgfutil@tempdima<#1\relax% + \c@pgf@counta-\c@pgf@counta% + \pgf@yb.5\pgf@yb% + \fi% + \fi% + \ifdim\pgf@yb=0pt\relax% *Should* be OK. + \else% + \repeatpgfmathloop% + \edef\pgf@decorate@inputsegmenttime{\pgfmath@tonumber{\pgf@xb}}% + % + % Now calculate the angle at the new time on the curve. + % + % We can use \pgfpointcurveattime safely here. + % + \pgfpointcurveattime{\pgf@decorate@inputsegmenttime}% + {\pgf@decorate@inputsegment@first}{\pgf@decorate@inputsegment@supporta}% + {\pgf@decorate@inputsegment@supportb}{\pgf@decorate@inputsegment@last}% + \edef\pgf@marshal{% + \noexpand\pgfmathanglebetweenpoints% + {\pgf@x\the\pgf@xb\pgf@y\the\pgf@yb}% + {\pgf@x\the\pgf@xa\pgf@y\the\pgf@ya}% + }% + \pgf@marshal% + \let\pgfdecoratedangle\pgfmathresult% + \fi% +}% + +% \pgfpoint@decorate@curveattime +% +% A `quick' version of \pgfpointcurveattime +% +% No parsing and no use of \pgf@process. +% This prevents save stack build up when used inside a loop. +% +\def\pgfpoint@decorate@curveattime#1#2#3#4#5{% + \edef\pgf@time@s{#1}% + \pgf@x-\pgf@time@s pt\relax% + \advance\pgf@x1pt\relax% + \edef\pgf@time@t{\pgf@sys@tonumber{\pgf@x}}% + #5\relax% + \pgf@xc=\pgf@x% + \pgf@yc=\pgf@y% + #4\relax% + \pgf@xb=\pgf@x% + \pgf@yb=\pgf@y% + #3\relax% + \pgf@xa=\pgf@x% + \pgf@ya=\pgf@y% + #2\relax% + % First iteration: + \pgf@x=\pgf@time@t\pgf@x\advance\pgf@x by\pgf@time@s\pgf@xa% + \pgf@y=\pgf@time@t\pgf@y\advance\pgf@y by\pgf@time@s\pgf@ya% + \pgf@xa=\pgf@time@t\pgf@xa\advance\pgf@xa by\pgf@time@s\pgf@xb% + \pgf@ya=\pgf@time@t\pgf@ya\advance\pgf@ya by\pgf@time@s\pgf@yb% + \pgf@xb=\pgf@time@t\pgf@xb\advance\pgf@xb by\pgf@time@s\pgf@xc% + \pgf@yb=\pgf@time@t\pgf@yb\advance\pgf@yb by\pgf@time@s\pgf@yc% + % Second iteration: + \pgf@x=\pgf@time@t\pgf@x\advance\pgf@x by\pgf@time@s\pgf@xa% + \pgf@y=\pgf@time@t\pgf@y\advance\pgf@y by\pgf@time@s\pgf@ya% + \pgf@xa=\pgf@time@t\pgf@xa\advance\pgf@xa by\pgf@time@s\pgf@xb% + \pgf@ya=\pgf@time@t\pgf@ya\advance\pgf@ya by\pgf@time@s\pgf@yb% + % Save x/y + \pgf@xb=\pgf@x% + \pgf@yb=\pgf@y% + % Third iteration: + \pgf@x=\pgf@time@t\pgf@x\advance\pgf@x by\pgf@time@s\pgf@xa% + \pgf@y=\pgf@time@t\pgf@y\advance\pgf@y by\pgf@time@s\pgf@ya% +}% + + +% Applies the necessary transform for a line sub-path. +% +\def\pgf@decorate@transformtoinputsegment@line{% + \pgftransformshift{% + \pgfpointlineatdistance{\pgfdecoratedinputsegmentcompleteddistance}{\pgf@decorate@inputsegment@first}{\pgf@decorate@inputsegment@last} + }% + \pgftransformrotate{\pgfdecoratedangle}% +}% + +% Applies the necessary transform for a curve sub-path. +% +\def\pgf@decorate@transformtoinputsegment@curve{% + \pgfslopedattimetrue% + \pgfallowupsidedownattimetrue% + \pgftransformcurveattime{\pgf@decorate@inputsegmenttime}% + {\pgf@decorate@inputsegment@first}{\pgf@decorate@inputsegment@supporta}% + {\pgf@decorate@inputsegment@supportb}{\pgf@decorate@inputsegment@last}% +}% + + + + + +% \pgf@decorate@parsesoftpath +% +% Parses an instance of a soft path into intermediate line and curve +% sub-path objects which the decoration automaton can use to traverse +% the decorated path. +% +% The soft path should be well formed and only contain `primative' +% soft path tokens, that is: moveto, lineto, curvetosupporta, +% curvetosupportb, curveto, rectcorner, rectsize, and closepath. +% (i.e., all specialround tokens should have removed using +% \pgfprocessround) +% +% The overall length of the path is also calculated and returned +% in the macro \pgfdecoratedpathlength. +% +% Parameters: +% +% #1 - a macro containing a well formed soft path. +% #2 - a macro to store the intermediate sub-path representations. +% +% Example: +% +% \pgfpathmoveto{\pgfpointorigin} +% \pgfpathlineto{\pgfpoint{0pt}{12pt}} +% \pgfpathlineto{\pgfpoint{12pt}{0pt}} +% \pgfgetpath\softpath +% +% \pgf@decorate@parsesoftpath{\softpath}{\parsedsoftpath} +% +% results in: +% +% \parsedsoftpath -> +% {\pgf@decorate@inputsegmentobject@lineto{12pt}{\pgf@x 0pt\pgf@y 0pt}{\pgf@x 0pt\pgf@y 12pt}} +% {\pgf@decorate@inputsegmentobject@lineto{12pt}{\pgf@x 0pt\pgf@y 12pt}{\pgf@x 12pt\pgf@y 12pt}} +% +% \pgfdecoratedpathlength -> +% 24pt +% + +\def\pgf@decorate@parsesoftpath#1#2{% + \def\pgf@decorate@inputsegmentobjectsmacro{#2}% + \let\pgf@decorate@inputsegmentobjects\pgfutil@empty% + \pgfutil@tempdima0pt\relax% + \let\pgfpoint@decorate@lastparsed\pgfpoint@decorate@existingpathlast% + \let\pgfpoint@decorate@lastnonmovetoparsed\pgfpoint@origin% + \let\pgf@decorate@queueinputsegmentobject\pgfutil@empty% + \let\pgfpoint@decorated@firstparsed\pgfutil@empty% + \expandafter\pgf@decorate@@parsesoftpath#1\pgf@stop% +}% + +\def\pgf@decorate@@parsesoftpath#1{% + \ifx#1\pgf@stop% + \let\pgf@decorate@queueinputsegmentobject\pgfutil@empty% <- removes final moveto (may not be desirable). + \pgf@decorate@addtoinputsegmentobjects{\pgf@decorate@inputsegmentobject@endofinputsegments}% + \pgf@decorate@addtoinputsegmentobjects{\pgf@decorate@inputsegmentobject@endofinputsegments}% + \expandafter\let\pgf@decorate@inputsegmentobjectsmacro\pgf@decorate@inputsegmentobjects% + \edef\pgf@decorate@totalpathlength{\the\pgfutil@tempdima}% + \let\pgf@next\relax% + \else% + \ifx#1\pgfsyssoftpath@movetotoken% + \let\pgf@next\pgf@decorate@parsemoveto% + \else% + \ifx#1\pgfsyssoftpath@linetotoken% + \let\pgf@next\pgf@decorate@parselineto% + \else% + \ifx#1\pgfsyssoftpath@curvetosupportatoken% + \let\pgf@next\pgf@decorate@parsecurveto% + \else% + \ifx#1\pgfsyssoftpath@closepathtoken% + \let\pgf@next\pgf@decorate@parseclosepath% + \else% + \ifx#1\pgfsyssoftpath@rectcornertoken% + \let\pgf@next\pgf@decorate@parserect% + \else% + \pgferror{Unrecognised soft path token `#1'}% + \fi% + \fi% + \fi% + \fi% + \fi% + \fi% + \pgf@next}% + + +\def\pgf@decorate@addtoinputsegmentobjects#1{% + % + % If there is an input segment object waiting (i.e. a moveto), insert it here. + % + \ifx\pgf@decorate@queueinputsegmentobject\pgfutil@empty% + \else% + \let\pgf@decorate@temp\pgf@decorate@queueinputsegmentobject% + \let\pgf@decorate@queueinputsegmentobject\pgfutil@empty% + \expandafter\pgf@decorate@addtoinputsegmentobjects\expandafter{\pgf@decorate@temp}% + \fi% + \ifx\pgfpoint@decorated@firstparsed\pgfutil@empty% + #1% + \let\pgfpoint@decorated@firstparsed\pgf@decorate@inputsegment@first% + \fi% + \expandafter\def\expandafter\pgf@decorate@inputsegmentobjects\expandafter% + {\pgf@decorate@inputsegmentobjects{#1}}% +}% + +% Convert \pgfsyssoftpath@movetotoken{<X>}{<Y>} into the following +% representation: +% +% \pgf@decorate@inputsegmentobject@moveto{\pgf@x X\pgf@y Y} +% +% The moveto input segment object is not added immediately, so that +% only the last of multiple movetos is inserted. +% +\def\pgf@decorate@parsemoveto#1#2{% + \def\pgf@decorate@queueinputsegmentobject{\pgf@decorate@inputsegmentobject@moveto{\pgf@x#1\pgf@y#2}}% + \def\pgfpoint@decorate@lastparsed{\pgf@x#1\pgf@y#2}% + \pgf@decorate@@parsesoftpath% +}% + +% Convert \pgfsyssoftpath@linetotoken{<X>}{<Y>} into the following +% representation: +% +% \pgf@decorate@inputsegmentobject@lineto{<length>}{\pgf@x <Last X> \pgf@y <Last Y}{\pgf@x <X> \pgf@y <Y>} +% +\def\pgf@decorate@parselineto#1#2{% + \pgf@decorate@linelength{\pgfpoint@decorate@lastparsed}{\pgf@x#1\pgf@y#2}% + \advance\pgfutil@tempdima\pgfmathresult pt\relax% + \edef\pgf@decorate@temp{% + \noexpand\pgf@decorate@inputsegmentobject@lineto{\pgfmathresult pt}{\pgfpoint@decorate@lastparsed}{\pgf@x#1\pgf@y#2}% + }% + \edef\pgfpoint@decorate@lastparsed{\pgf@x#1\pgf@y#2}% + \let\pgfpoint@decorate@lastnonmovetoparsed\pgfpoint@decorate@lastparsed% + \expandafter\pgf@decorate@addtoinputsegmentobjects\expandafter{\pgf@decorate@temp}% + \pgf@decorate@@parsesoftpath% +}% + +% Convert \pgfsyssoftpath@curvetosupportatoken{<Xa>}{<Ya>}...etc +% into the following representation: +% +% \pgf@decorate@inputsegmentobject@curveto{<length>}{\pgf@x <Last X> \pgf@y <Last Y} +% {\pgf@x <Xa> \pgf@y <Ya>}{\pgf@x <Xb> \pgf@y <Yb>}{\pgf@x <X> \pgf@y <Y>} +% +\def\pgf@decorate@parsecurveto#1#2\pgfsyssoftpath@curvetosupportbtoken#3#4\pgfsyssoftpath@curvetotoken#5#6{% + \pgf@decorate@curvelength{\pgfpoint@decorate@lastparsed}{\pgf@x#1\pgf@y#2}{\pgf@x#3\pgf@y#4}{\pgf@x#5\pgf@y#6}% + \advance\pgfutil@tempdima\pgfmathresult pt\relax% + \edef\pgf@decorate@temp{% + \noexpand\pgf@decorate@inputsegmentobject@curveto{\pgfmathresult pt}{\pgfpoint@decorate@lastparsed}% + {\pgf@x#1\pgf@y#2}{\pgf@x#3\pgf@y#4}{\pgf@x#5\pgf@y#6}% + }% + \expandafter\pgf@decorate@addtoinputsegmentobjects\expandafter{\pgf@decorate@temp}% + \edef\pgfpoint@decorate@lastparsed{\pgf@x#5\pgf@y#6}% + \let\pgfpoint@decorate@lastnonmovetoparsed\pgfpoint@decorate@lastparsed% + \pgf@decorate@@parsesoftpath% +}% + + +\def\pgf@decorate@parseclosepath#1#2{% + \pgf@decorate@linelength{\pgfpoint@decorate@lastparsed}{\pgf@x#1\pgf@y#2}% + \advance\pgfutil@tempdima\pgfmathresult pt\relax% + \edef\pgf@decorate@temp{% + \noexpand\pgf@decorate@inputsegmentobject@closepath{\pgfmathresult pt}{\pgfpoint@decorate@lastparsed}{\pgf@x#1\pgf@y#2}% + }% + \expandafter\pgf@decorate@addtoinputsegmentobjects\expandafter{\pgf@decorate@temp}% + \edef\pgfpoint@decorate@lastparsed{\pgf@x#1\pgf@y#2}% + \let\pgfpoint@decorate@lastnonmovetoparsed\pgfpoint@decorate@lastparsed% + \pgf@decorate@@parsesoftpath% +}% + +% Convert \pgfsyssoftpath@rectcornertoken{<X>}{<Y>}\pgfsyssoftpath@rectsizetoken{<Xa>}{<Ya>} +% into either (clockwise) +% +% \pgf@decorate@inputsegmentobject@moveto{<X>}{<Y>} +% \pgf@decorate@inputsegmentobject@lineto{<length 1>}{\pgf@x <X> \pgf@y <Y>}{\pgf@x <X> \pgf@y <Ya+Y>} +% \pgf@decorate@inputsegmentobject@lineto{<length 2>}{\pgf@x <X> \pgf@y <Ya+Y>}{\pgf@x <Xa+X> \pgf@y <Ya+Y>} +% \pgf@decorate@inputsegmentobject@lineto{<length 1>}{\pgf@x <Xa+X> \pgf@y <Ya+X>}{\pgf@x <Xa+X> \pgf@y <Y>} +% \pgf@decorate@inputsegmentobject@lineto{<length 2>}{\pgf@x <Xa+X> \pgf@y <Y>}{\pgf@x <X> \pgf@y <Y>} +% \pgf@decorate@inputsegmentobject@moveto{<Xa>}{<Ya>} +% +% ...or (anti-clockwise) +% +% \pgf@decorate@inputsegmentobject@moveto{<X>}{<Y>} +% \pgf@decorate@inputsegmentobject@lineto{<length 2>}{\pgf@x <X> \pgf@y <Y>}{\pgf@x <Xa+X> \pgf@y <Y>} +% \pgf@decorate@inputsegmentobject@lineto{<length 1>}{\pgf@x <Xa+X> \pgf@y <Y>}{\pgf@x <Xa+X> \pgf@y <Ya+Y>} +% \pgf@decorate@inputsegmentobject@lineto{<length 2>}{\pgf@x <Xa+X> \pgf@y <Ya+Y>}{\pgf@x <X> \pgf@y <Ya+Y>} +% \pgf@decorate@inputsegmentobject@lineto{<length 1>}{\pgf@x <X> \pgf@y <Ya+Y>}{\pgf@x <X> \pgf@y <Y>} +% \pgf@decorate@inputsegmentobject@moveto{<Xa>}{<Ya>} +% +\newif\ifpgfdecoraterectangleclockwise +\def\pgf@decorate@parserect#1#2\pgfsyssoftpath@rectsizetoken#3#4{% + % + % Subvert \pgf@decorate@parsesoftpath so parsing macros will + % return here instead of continuing to parse the soft path. + % + \let\pgf@decorate@orig@@parsesoftpath\pgf@decorate@@parsesoftpath% + \let\pgf@decorate@@parsesoftpath\relax% + \pgf@decorate@parsemoveto{#1}{#2}% + \pgf@xa#1\relax% + \pgf@ya#2\relax% + \ifpgfdecoraterectangleclockwise% + \advance\pgf@ya#4% + \pgf@yb\pgf@ya% + \edef\pgf@temp{{\the\pgf@xa}{\the\pgf@ya}}% + \expandafter\pgf@decorate@parselineto\pgf@temp% + \advance\pgf@xa#3% + \pgf@xb\pgf@xa% + \edef\pgf@temp{{\the\pgf@xa}{\the\pgf@ya}}% + \expandafter\pgf@decorate@parselineto\pgf@temp% + \advance\pgf@ya-#4% + \edef\pgf@temp{{\the\pgf@xa}{\the\pgf@ya}}% + \expandafter\pgf@decorate@parselineto\pgf@temp% + \advance\pgf@xa-#3% + \edef\pgf@temp{{\the\pgf@xa}{\the\pgf@ya}}% + \expandafter\pgf@decorate@parseclosepath\pgf@temp% + \else% + \advance\pgf@xa#3% + \edef\pgf@temp{{\the\pgf@xa}{\the\pgf@ya}}% + \pgf@xb\pgf@xa% + \expandafter\pgf@decorate@parselineto\pgf@temp% + \advance\pgf@ya#4% + \pgf@yb\pgf@ya% + \edef\pgf@temp{{\the\pgf@xa}{\the\pgf@ya}}% + \expandafter\pgf@decorate@parselineto\pgf@temp% + \advance\pgf@xa-#3% + \edef\pgf@temp{{\the\pgf@xa}{\the\pgf@ya}}% + \expandafter\pgf@decorate@parselineto\pgf@temp% + \advance\pgf@ya-#4% + \edef\pgf@temp{{\the\pgf@xa}{\the\pgf@ya}}% + \expandafter\pgf@decorate@parseclosepath\pgf@temp% + \fi% + % + % Restore the parsing. + % + \let\pgf@decorate@@parsesoftpath\pgf@decorate@orig@@parsesoftpath% + \edef\pgf@marshal{\noexpand\pgf@decorate@parsemoveto{\the\pgf@xb}{\the\pgf@yb}}% + \pgf@marshal% +}% + + + + +% \pgf@decorate@linelength +% +% Define \pgfmathresult as the distance between #1 and #2 (without units). +% +\def\pgf@decorate@linelength#1#2{% + \pgf@process{\pgfpointdiff{#1}{#2}}% + \pgfmathveclen@{\pgfmath@tonumber{\pgf@x}}{\pgfmath@tonumber{\pgf@y}}% +}% + +% \pgf@decorate@curvelength +% +% Define \pgfmathresult as the length (without units) of the cubic +% bezier described by #1,#2,#3 and #4. +% +% To approximate the length of the curve P (sort of) use: +% +% curveLength(P,q) +% Q = subdivideLeft(P); +% R = subdivideRight(P); +% return curveLength'(Q,q) + curveLength'(R,q); +% +% curveLength'(P,q) +% <P1,P2,P3,P4> <- P; +% P' = P4 - P1; +% x = P'.x(); +% y = P'.y(); +% if (x < q) && (y < q) +% return |P'|; +% else% +% Q = subdivideLeft(P); +% R = subdivideRight(P); +% return curveLength'(Q,q) + curveLength'(R,q); +% +% Where q is some small value (`tolerance'). +% +\newif\ifpgf@decorate@subdivide +\def\pgf@decorate@curvelength#1#2#3#4{% + % + % Always begin with a subdivision in case #1 = #4. + % + \begingroup% + \pgf@decorate@recursive@subdividecurve@left{#1}{#2}{#3}{#4}% + \pgfutil@tempdimb\pgfmathresult pt\relax% + \pgf@decorate@recursive@subdividecurve@right{#1}{#2}{#3}{#4}% + \advance\pgfutil@tempdimb\pgfmathresult pt\relax% + \pgfmath@returnone\pgfutil@tempdimb% + \endgroup% +}% + +\def\pgf@decorate@@curvelength#1#2#3#4{% + \begingroup% + \pgf@decorate@curvecordlength{#1}{#2}{#3}{#4}% + \ifpgf@decorate@subdivide% + \pgf@decorate@recursive@subdividecurve@left{#1}{#2}{#3}{#4}% + \pgfutil@tempdimb\pgfmathresult pt\relax% + \pgf@decorate@recursive@subdividecurve@right{#1}{#2}{#3}{#4}% + \advance\pgfutil@tempdimb\pgfmathresult pt\relax% + \expandafter\pgfmath@returnone\expandafter\pgfutil@tempdimb% + \else% + \expandafter\pgfmath@returnone\expandafter\pgfmathresult\expandafter p\expandafter t\expandafter% + \fi% + \endgroup% +}% + +\def\pgf@decorate@curvelength@tolerance{1pt}% + +\def\pgf@decorate@curvecordlength#1#2#3#4{% + % + % Calculate the curve cord vector. + % + #4\relax% + \pgf@xa\pgf@x\pgf@ya\pgf@y% + #1\relax% + \advance\pgf@xa-\pgf@x% + \advance\pgf@ya-\pgf@y% + \ifdim\pgf@xa<0pt\relax\pgf@xa-\pgf@xa\fi% + \ifdim\pgf@ya<0pt\relax\pgf@ya-\pgf@ya\fi% + % + % Always subdivide, unless... + % + \pgf@decorate@subdividetrue% + % + % The cord vector is small. + % + \ifdim\pgf@xa<\pgf@decorate@curvelength@tolerance\relax% + \ifdim\pgf@ya<\pgf@decorate@curvelength@tolerance\relax% + \pgfmathveclen@{\pgfmath@tonumber{\pgf@xa}}{\pgfmath@tonumber{\pgf@ya}}% + \pgf@decorate@subdividefalse% + \fi% + \fi% +}% + +\def\pgf@decorate@recursive@subdividecurve@left#1#2#3#4{% + {% + % + % The left curve (from t=0 to t=.5) + % + #1\relax% + \pgfutil@tempdima\pgf@x% + \pgfutil@tempdimb\pgf@y% + \pgf@xa.5\pgf@x\pgf@ya.5\pgf@y% + \pgf@xb.25\pgf@x\pgf@yb.25\pgf@y% + \pgf@xc.125\pgf@x\pgf@yc.125\pgf@y% + #2\relax% + \advance\pgf@xa.5\pgf@x\advance\pgf@ya.5\pgf@y% + \advance\pgf@xb.5\pgf@x\advance\pgf@yb.5\pgf@y% + \advance\pgf@xc.375\pgf@x\advance\pgf@yc.375\pgf@y% + #3\relax% + \advance\pgf@xb.25\pgf@x\advance\pgf@yb.25\pgf@y% + \advance\pgf@xc.375\pgf@x\advance\pgf@yc.375\pgf@y% + #4\relax% + \advance\pgf@xc.125\pgf@x\advance\pgf@yc.125\pgf@y% + \xdef\pgf@marshal{% + \noexpand\pgf@decorate@@curvelength% + {\pgf@x\the\pgfutil@tempdima\pgf@y\the\pgfutil@tempdimb}% + {\pgf@x\the\pgf@xa\pgf@y\the\pgf@ya}{\pgf@x\the\pgf@xb\pgf@y\the\pgf@yb} + {\pgf@x\the\pgf@xc\pgf@y\the\pgf@yc}% + }% + }% + \pgf@marshal% +}% + +\def\pgf@decorate@recursive@subdividecurve@right#1#2#3#4{% + {% + % + % The right curve (from t=0.5 to t=1) + % + #1\relax% + \pgfutil@tempdima.125\pgf@x\pgfutil@tempdimb.125\pgf@y% + #2\relax% + \advance\pgfutil@tempdima.375\pgf@x\advance\pgfutil@tempdimb.375\pgf@y% + \pgf@xa.25\pgf@x\pgf@ya.25\pgf@y% + #3\relax% + \advance\pgfutil@tempdima.375\pgf@x\advance\pgfutil@tempdimb.375\pgf@y% + \advance\pgf@xa.5\pgf@x\advance\pgf@ya.5\pgf@y% + \pgf@xb.5\pgf@x\pgf@yb.5\pgf@y% + #4\relax% + \advance\pgfutil@tempdima.125\pgf@x\advance\pgfutil@tempdimb.125\pgf@y% + \advance\pgf@xa.25\pgf@x\advance\pgf@ya.25\pgf@y% + \advance\pgf@xb.5\pgf@x\advance\pgf@yb.5\pgf@y% + \pgf@xc\pgf@x\pgf@yc\pgf@y% + \xdef\pgf@marshal{% + \noexpand\pgf@decorate@@curvelength% + {\pgf@x\the\pgfutil@tempdima\pgf@y\the\pgfutil@tempdimb}% + {\pgf@x\the\pgf@xa\pgf@y\the\pgf@ya}{\pgf@x\the\pgf@xb\pgf@y\the\pgf@yb} + {\pgf@x\the\pgf@xc\pgf@y\the\pgf@yc}% + }% + }% + \pgf@marshal% +}% + + +% Macro to reverse a set of input segment objects. +% +% #1 - a macro holding the input segment objects. +% #2 - a macro holding the reversed input segment objects. +% +\def\pgf@decorate@inputsegmentobjects@reverse#1#2{% + \def\pgf@decorate@path@storein{#2}% + \def\pgf@decorate@inputsegments@@temp{}% + \expandafter\pgf@decorate@inputsegments@@reverse@pass@first#1\pgf@stop}% + +\def\pgf@decorate@inputsegments@@reverse@pass@first#1#2\pgf@stop{% + \def\pgf@decorate@temp{#1}% + \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter% + \pgf@decorate@inputsegments@@temp\expandafter\expandafter\expandafter% + {\expandafter\pgf@decorate@temp\pgf@decorate@inputsegments@@temp}% + \def\pgf@decorate@test{#2} + \ifx\pgf@decorate@test\pgfutil@empty% + \def\pgf@decorate@next{% + \expandafter\pgf@decorate@inputsegmentobjects@reverse@pass@second\pgf@decorate@inputsegments@@temp\pgf@stop% + }% + \else% + \def\pgf@decorate@next{\pgf@decorate@inputsegments@@reverse@pass@first#2\pgf@stop}% + \fi% + \pgf@decorate@next% +}% +\def\pgf@decorate@inputsegmentobjects@reverse@pass@second#1#2#3\pgf@stop{% + \def\pgf@decorate@inputsegments@temp{#3}% + \def\pgf@decorate@inputsegments@@temp{}% + \let\pgf@decorate@current@inputsegmentobject=\pgfutil@empty + \let\pgf@decorate@last@object=\pgfutil@empty + \pgf@decorate@inputsegmentobjects@reverse@moveto@waitingtrue% + \pgf@decorate@inputsegmentobjects@reverse@closepath@waitingfalse% + \expandafter\pgf@decorate@path@@reverse@pass@second\pgf@decorate@inputsegments@temp\pgf@stop}% + + +\def\pgf@decorate@path@@reverse@pass@second#1{% + \ifx#1\pgf@stop% + \expandafter\def\expandafter\pgf@decorate@inputsegments@@temp\expandafter{% + \pgf@decorate@inputsegments@@temp% + {\pgf@decorate@inputsegmentobject@endofinputsegments}% + {\pgf@decorate@inputsegmentobject@endofinputsegments} + }% + \expandafter\let\pgf@decorate@path@storein=\pgf@decorate@inputsegments@@temp% + \let\pgf@decorate@next=\relax% + \else% + \ifx#1\pgf@decorate@inputsegmentobject@moveto% + \let\pgf@decorate@next=\pgf@decorate@inputsegmentobjects@reverse@moveto% + \else% + \ifx#1\pgf@decorate@inputsegmentobject@lineto% + \let\pgf@decorate@next=\pgf@decorate@inputsegmentobjects@reverse@lineto% + \else% + \ifx#1\pgf@decorate@inputsegmentobject@curveto% + \let\pgf@decorate@next=\pgf@decorate@inputsegmentobjects@reverse@curveto% + \else% + \ifx#1\pgf@decorate@inputsegmentobject@closepath% + \let\pgf@decorate@next=\pgf@decorate@inputsegmentobjects@reverse@closepath% + \else% + \fi% + \fi% + \fi% + \fi% + \fi% + \pgf@decorate@next% +}% + +\newif\ifpgf@decorate@inputsegmentobjects@reverse@moveto@waiting + +\def\pgf@decorate@inputsegmentobjects@reverse@moveto#1{% + \ifpgf@decorate@inputsegmentobjects@reverse@closepath@waiting% + \edef\pgf@decorate@current@inputsegmentobject{% + {\noexpand\pgf@decorate@inputsegmentobject@closepath% + {\pgf@decorate@reverse@closepath@length}% + {\pgf@decorate@last}% + {#1}% + }% + }% + \pgf@decorate@inputsegmentobjects@reverse@closepath@waitingfalse% + \else% + \let\pgf@decorate@current@inputsegmentobject=\pgfutil@empty% + \fi% + \pgf@decorate@inputsegmentobjects@reverse@moveto@waitingtrue% + \pgf@decorate@path@@@reverse@pass@second% +}% + +\newif\ifpgf@decorate@inputsegmentobjects@reverse@closepath@waiting + +\def\pgf@decorate@inputsegmentobjects@reverse@closepath#1#2#3{% + \pgf@decorate@inputsegmentobjects@reverse@closepath@waitingtrue% + \def\pgf@decorate@reverse@closepath@length{#1}% + \def\pgf@decorate@current@inputsegmentobject{}% + \pgf@decorate@inputsegmentobjects@reverse@lineto{#1}{#2}{#3}% +}% + +\def\pgf@decorate@inputsegmentobjects@reverse@lineto#1#2#3#4{% + \ifpgf@decorate@inputsegmentobjects@reverse@moveto@waiting% + \def\pgf@decorate@current@inputsegmentobject{{\pgf@decorate@inputsegmentobject@moveto{#3}}}% + \pgf@decorate@inputsegmentobjects@reverse@moveto@waitingfalse% + \else% + \let\pgf@decorate@current@inputsegmentobject=\pgfutil@empty% + \fi% + \def\pgf@decorate@append@inputsegmentobject{{\pgf@decorate@inputsegmentobject@lineto{#1}{#3}{#2}}}% + \ifx#4\pgf@decorate@inputsegmentobject@moveto% + \ifpgf@decorate@inputsegmentobjects@reverse@closepath@waiting% + \def\pgf@decorate@append@inputsegmentobject{{\pgf@decorate@inputsegmentobject@closepath{#1}{#3}{#2}}}% + \pgf@decorate@inputsegmentobjects@reverse@closepath@waitingfalse% + \else% + \def\pgf@decorate@append@inputsegmentobject{{\pgf@decorate@inputsegmentobject@lineto{#1}{#3}{#2}}}% + \fi% + \fi% + \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter% + \pgf@decorate@current@inputsegmentobject\expandafter\expandafter\expandafter% + {\expandafter\pgf@decorate@current@inputsegmentobject\pgf@decorate@append@inputsegmentobject} + \def\pgf@decorate@last{#2}% + \pgf@decorate@path@@@reverse@pass@second#4% +}% + +\def\pgf@decorate@inputsegmentobjects@reverse@curveto#1#2#3#4#5{% + \ifpgf@decorate@inputsegmentobjects@reverse@moveto@waiting% + \def\pgf@decorate@current@inputsegmentobject{% + {\pgf@decorate@inputsegmentobject@moveto{#5}}% + {\pgf@decorate@inputsegmentobject@curveto{#1}{#5}{#4}{#3}{#2}}% + }% + \pgf@decorate@inputsegmentobjects@reverse@moveto@waitingfalse% + \else% + \def\pgf@decorate@current@inputsegmentobject{{\pgf@decorate@inputsegmentobject@curveto{#1}{#5}{#4}{#3}{#2}}}% + \fi% + \def\pgf@decorate@last{#2}% + \pgf@decorate@path@@@reverse@pass@second% +}% + +\def\pgf@decorate@path@@@reverse@pass@second{% + \ifx\pgf@decorate@current@inputsegmentobject\pgfutil@empty% + \else% + \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter% + \pgf@decorate@inputsegments@@temp\expandafter\expandafter\expandafter% + {\expandafter\pgf@decorate@inputsegments@@temp\pgf@decorate@current@inputsegmentobject}% + \fi% + \pgf@decorate@path@@reverse@pass@second% +}% + + + + + +\let\startpgfdecoration=\pgfdecoration +\let\stoppgfdecoration=\endpgfdecoration + +\let\startpgfmetadecoration=\pgfmetadecoration +\let\stoppgfmetadecoration=\endpgfmetadecoration + + + +% +% Predefined decorations +% + + +% Simple decorations. + +\pgfdeclaredecoration{lineto}{initial}{% + \state{initial}[switch if input segment less than=\pgfdecoratedremainingdistance to input segment, + width=\pgfdecoratedremainingdistance] + { + }% + \state{input segment}[width=\pgfdecoratedinputsegmentlength,next state=initial] + { + \pgfpathlineto{\pgfpointdecoratedinputsegmentlast} + }% + + \state{final}{\pgfpathlineto{\pgfpointdecoratedpathlast}}% +}% + +\pgfdeclaredecoration{moveto}{initial}{% + \state{initial}[width=\pgfdecoratedremainingdistance]{}% + \state{final}{\pgfpathmoveto{\pgfpointdecoratedpathlast}}% +}% + +% This decorations curves to the end along the line. + +\pgfdeclaredecoration{curveto}{initial}{% + \state{initial}[width=\pgfdecoratedinputsegmentlength/100] + { + \pgfpathlineto{\pgfpointorigin} + }% + \state{final}{\pgfpathlineto{\pgfpointdecoratedpathlast}}% +}% + + + + +% Compatibility with old "snakes": + +\let\pgfsnakeremainingdistance=\pgfdecoratedremainingdistance +\let\pgfsnakecompleteddistance=\pgfdecoratedcompleteddistance +\def\pgfsnakeangle{\pgfdecoratedangle}% +\let\pgfdeclaresnake=\pgfdeclaredecoration +\let\pgfsetsnakesegmenttransformation=\pgfsetdecorationsegmenttransformation +\def\pgfpathsnakesto#1#2{% + \edef\pgf@temp{#1}% + \expandafter\pgfdecoration\expandafter{\pgf@temp}% + \pgfpathlineto{#2}% + \endpgfdecoration% +}% +\def\pgfpathsnaketo#1#2{\pgfpathsnakesto{{#1}{\pgfsnakeremainingdistance}}{#2}}% +\def\pgfpathsnakealongvector#1#2#3{% + \pgf@process{#3}% + \pgf@xa=\pgf@x% + \pgf@ya=\pgf@y% + \pgfmathparse{#2}% + \pgf@xa=\pgfmathresult\pgf@xa% + \pgf@ya=\pgfmathresult\pgf@ya% + \advance\pgf@xa by\pgf@path@lastx% + \advance\pgf@ya by\pgf@path@lasty% + \edef\pgf@snake@lib@temp{\noexpand\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}% + \pgfpathsnaketo{#1}{\pgf@snake@lib@temp}% +}% + +\let\pgfsnakesegmentamplitude=\pgfdecorationsegmentamplitude +\let\pgfsnakesegmentlength=\pgfdecorationsegmentlength +\def\pgfsnakesegmentangle{\pgfdecorationsegmentangle}% +\def\pgfsnakesegmentobjectlength{\pgfkeysvalueof{/pgf/decoration/shape start width}}% +\def\pgfsnakesegmentaspect{\pgfdecorationsegmentaspect}% + +\pgfset{% + /pgf/segment amplitude/.style={/pgf/decoration={amplitude={#1},shape height={2*(#1)}}}, + /pgf/segment length/.style={/pgf/decoration={segment length={#1}}}, + /pgf/segment angle/.style={/pgf/decoration={angle={#1}}}, + /pgf/segment aspect/.style={/pgf/decoration={aspect={#1}}}, + /pgf/segment object length/.style={/pgf/decoration={shape width={#1},radius={#1}}}}% + + + +\endinput diff --git a/graphics/pgf/base/tex/generic/modules/pgfmodulematrix.code.tex b/graphics/pgf/base/tex/generic/modules/pgfmodulematrix.code.tex new file mode 100644 index 0000000000..166eb47093 --- /dev/null +++ b/graphics/pgf/base/tex/generic/modules/pgfmodulematrix.code.tex @@ -0,0 +1,660 @@ +% Copyright 2019 by Till Tantau +% +% This file may be distributed and/or modified +% +% 1. under the LaTeX Project Public License and/or +% 2. under the GNU Public License. +% +% See the file doc/generic/pgf/licenses/LICENSE for more details. + +\ProvidesFileRCS{pgfmodulematrix.code.tex} + +\usepgfmodule{shapes}% + +\newif\ifpgfmatrix +\newif\ifpgf@matrix@correct@call + +\newif\ifpgf@matrix@fixed + +\newcount\pgfmatrixcurrentrow + +\newcount\pgfmatrixcurrentcolumn +\newcount\pgf@matrix@numberofcolumns + +\let\pgf@matrix@cell=\pgfnodeparttextbox % alias +\let\pgf@matrix@box=\pgf@matrix@cell % alias + +\long\def\pgfmatrixbeforeassemblenode#1{% + \long\def\pgf@matrix@before@assemnble@node{#1}} +\pgfmatrixbeforeassemblenode{} + +% Sets the default row or column sep +% +% #1 = default row or column sep list +% +% Description: +% +% Sets the macro \pgfmatrixrowsep or \pgfmatrixcolumnsep to #1. +% +% Example: +% +% \pgfsetmatrixrowsep{5pt} + +\def\pgfsetmatrixrowsep#1{\def\pgfmatrixrowsep{#1}}% +\def\pgfsetmatrixcolumnsep#1{\def\pgfmatrixcolumnsep{#1}}% + +\def\pgfmatrixrowsep{0pt}% +\def\pgfmatrixcolumnsep{0pt}% + + + +% The pgfmatrix command creates a node that contains a matrix. Like +% the \pgfnode command, it is to be used inside a pgfpicture +% environment. +% +% #1 = shape type +% #2 = anchor +% #3 = name for later reference +% #4 = path usage command +% #5 = inner shift +% #6 = startup code +% #7 = the matrix code (may contain verbatim stuff) +% +% Description: +% +% This command creates a node that contains a matrix. For +% each cell of the matrix, drawing commands may be given. These +% commands will create a cell picture and a bounding box is computed +% for each cell picture. The cell picture is then placed inside the +% cell. The position inside the cell is computed according to the +% following rules: +% +% 1. In each column, the origins of the cell pictures are all on a +% vertical line. +% 2. In each row, the origins of the cell pictures are all on a +% horizontal line. +% 3. All cells in the same row have the same height and depth. +% 4. All cells in the same column have the same width. +% 5. The size of cells are chosen minimal such that the boxes fit into +% the cells and still meet the above requirement. +% +% Next, the whole resulting matrix, which is given as #7, is placed +% inside a node whose name is given as #3, whose shape is #1, and +% whose anchor is #2. This means that the resulting node is shifted +% such that the given anchor lies at the origin. Then, as for a normal +% node, the #4 path usage command is used for the node. +% +% The parameter #5 is an additional negative shift for the node. Normally, such +% a shift could be given beforehand (that is, the shift could be +% preapplied to the current transformation matrix). However, when #5 +% is evaluated, you can refer to temporary positions of nodes inside +% the matrix. In detail, the following happens: When the matrix has +% been typeset, all nodes in the matrix temporarily get assigned their +% positions in the matrix box. The origin of this coordinate system is +% at the left baseline end of the matrix box, which corresponds to the +% text anchor. The position #5 is then interpreted inside this +% coordinate system and then used for shifting. +% +% The idea behind using the parameter #5 is the following: If you +% specify "text" as anchor #2 and specify \pgfpointanchor{inner +% node}{some anchor} for parameter #5, where inner node is a node that +% is created in the matrix, then the whole matrix will be shifted such +% that inner node.some anchor lies at the origin of the whole picture. +% +% At the beginning and at the end of each cell, the macros +% \pgfmatrixbegincode and \pgfmatrixendcode are called. Inside these +% macros you can use \pgfmatrixcurrentcolumn and \pgfmatrixcurrentrow +% to access the current row and column. +% +% Before each column except for the first, an extra space given by the +% macro \pgfmatrixcolumnsep is added. Additionally, an extra space +% given by the optional argument of the \pgfmatrixnextcell commands in +% the first line are added, which may vary between columns. Between +% rows, an extra space given by the macro \pgfmatrixrowsep is +% added. You can also change this value for each row by using a \gdef. +% +% +% Lines are ended using the command \pgfmatrixendrow. The command \\ +% is \let to be the same, but \\ is sometimes overridden by, say, +% minipage, so it's good to have \pgfmatrixendrow as a "fallback". +% +% Inside the matrix, \\ must be used to end every line ***including +% the last***. You can pass an optional parameter to \\ as in \\[2pt] +% to give an additional space after this particular line. +% +% The usage of \\ at the end of the matrix is important since this +% command is used to find the end of the matrix. What happens is that +% \\ followed by a closing brace ends the matrix. +% +% Inside the matrix, you must use \pgfmatrixnextcell to separate +% columns. The reason is that two columns are used internally for +% every single column shown. The macro \pgfmatrixnextcell is yields +% (more or less) two && (and some further trickery). +% +% The parameter #6 is executed at the beginning of the matrix +% group. It is inside the protecting group of the matrix, but outside +% the matrix itself. It can be used for different purposes: 1) It can be +% used to simplify the next cell macro. 2) It can be used to issue an +% \aftergroup command. 3) It can be used to setup local settings. For +% example, by setting #6 to \let\&=\pgfmatrixnextcell you can use \& +% instead of \pgfmatrixnextcell. You can also set the catcode of & to +% active. +% +% +% Example: +% +% \begin{tikzpicture} +% +% \draw (0,0) circle (1mm); % b.north will lie on the center of this circle +% +% \pgfmatrix{rectangle}{text}{mymatrix}{\pgfusepath{}}{\pgfpointanchor{b}{north}}{\let\&=\pgfmatrixnextcell} +% { +% \draw (0,0) circle (1cm); \& \node (a) [draw] {Hallo}; \\ +% \node (b) [draw] {Welt}; \& \draw (0,0) circle (5mm); \\ +% } +% +% \draw [->] (a) -- (b); +% +% \end{tikzpicture} + +\def\pgfmatrix#1#2#3#4#5#6{% + \ifpgfmatrix% + \pgferror{You cannot nest pgfmatrix environments, yet} + \fi% + \bgroup% + \everycr{}% + #6% + \def\pgf@matrix@par@shape{#1}% + \def\pgf@matrix@par@anchor{#2}% + \def\pgf@matrix@par@name{#3}% + \def\pgf@matrix@par@usage{#4}% + \def\pgf@matrix@par@shift{#5}% + % the matrix must have a name: + \ifx\pgf@matrix@par@name\pgfutil@empty% + \def\pgf@matrix@par@name{pgf@matrix@internal}% + \fi% + \pgfmatrixcurrentrow=0\relax% + \pgfmatrixcurrentcolumn=0\relax% + \pgf@matrix@numberofcolumns=0\relax% + \let\\=\pgfmatrixendrow% + \tabskip=0pt% + \offinterlineskip% + \pgfmatrixtrue% + \let\pgf@matrix@node@list=\pgfutil@empty% + \expandafter\let\csname pgf@matrix@column@finish@1\endcsname=\pgfutil@empty% + \let\pgf@matrix@row@finish=\pgfutil@empty% + % + % Get rid of bgroup + % + \afterassignment\pgf@matrix@cont\let\pgf@next=% +}% +\def\pgf@matrix@cont{% + \setbox\pgf@matrix@box=\hbox\bgroup\vbox\bgroup% + \halign\bgroup% + \pgf@matrix@init@row% + \pgf@matrix@step@column% + {% + \pgf@matrix@startcell% + ##% + \pgf@matrix@endcell% + }% + &% + ##\pgf@matrix@padding&&% + \pgf@matrix@step@column% + {% + \pgf@matrix@startcell% + ##% + \pgf@matrix@endcell% + }&% + ##\pgf@matrix@padding% + \cr% +}% + +\def\pgf@end@matrix{% + \egroup\egroup% + \egroup% + % + % Inject custom tokens before assembling the node holding the matrix. Can + % be used to apply additional settings. + % + \pgf@matrix@before@assemnble@node + % + % If there are named nodes, we must adjust their coordinates. + % + \ifx\pgf@matrix@node@list\pgfutil@empty% + \else% + % Ok, this means we got a lot of work to do... + \pgf@matrix@compute@origin% + \pgf@matrix@shift@nodes@initial% + \fi% + % + % Compute additional shift + % + \pgftransformresetnontranslations% + \pgftransformshift{\pgf@process{\pgftransformreset\pgf@matrix@par@shift\pgf@x=-\pgf@x\pgf@y=-\pgf@y}}% + % + % Create node + % + \pgfmultipartnode{\pgf@matrix@par@shape}{\pgf@matrix@par@anchor}{\pgf@matrix@par@name}{\pgf@matrix@par@usage}% + % + % If there are named nodes, we must adjust their coordinates, again. + % + \ifx\pgf@matrix@node@list\pgfutil@empty% + \else% + {% + \pgftransformreset% + \pgf@process{\pgfpointanchor{\pgf@matrix@par@name}{text}}% + \xdef\pgf@matrix@offset{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}% + \pgf@matrix@shift@nodes@secondary{\pgf@matrix@offset}% + }% + \fi% +}% + + + + +% Ending a cell +% +% #1 = (optional) spacing between previous and next column. This +% parameter only has an effect in the first row. +% +% Description: +% +% This command ends a cell of a matrix. +% +% Example: +% +% \pgfmatrix{rectangle}{text}{mymatrix}{\pgfusepath{}}{\pgfpointanchor{b}{north}}{} +% { +% \draw (0,0) circle (1cm); \pgfmatrixnextcell[1cm] \node (a) [draw] {Hallo}; \\ +% \node (b) [draw] {Welt}; \pgfmatrixnextcell \draw (0,0) circle (5mm); \\ +% } + +\def\pgfmatrixnextcell{% + % if the cell contains nothing, the following \let will be at the + % beginning (macro expansion has stopped here since neither \omit + % nor \span was found) + \let\pgf@matrix@signal@cell@end=\pgf@matrix@signal@cell@end% + \pgfutil@ifnextchar[{\pgf@matrix@cell@cont}{\pgf@matrix@cell@cont[0pt]}}%}% + +\def\pgf@matrix@cell@cont[#1]{% + \xdef\pgf@matrix@last@nextcell@options{#1}% + &\pgf@matrix@correct@calltrue&% +}% + + +\def\pgf@matrix@fixed@text{between origins}% +\def\pgf@matrix@natural@text{between borders}% + +\def\pgf@matrix@addtolength#1#2{\edef\pgf@temp{#2}\expandafter\pgf@matrix@addtolength@\expandafter#1#2,\pgf@stop}% +\def\pgf@matrix@addtolength@#1#2,#3\pgf@stop{% + \pgfkeys@spdef\pgf@temp{#2}% + \ifx\pgf@temp\pgfutil@empty% + \else% + \ifx\pgf@temp\pgf@matrix@fixed@text% + \global\pgf@matrix@fixedtrue% + \else% + \ifx\pgf@temp\pgf@matrix@natural@text% + \global\pgf@matrix@fixedfalse% + \else% + \pgfmathaddtolength{#1}{#2}% + \fi% + \fi% + \fi% + \def\pgf@temp{#3}% + \ifx\pgf@temp\pgfutil@empty% + \else% + \pgf@matrix@addtolength@#1#3\pgf@stop% + \fi% +}% + + + +% Init a line + +\def\pgf@matrix@init@row{% + \relax% + \global\advance\pgfmatrixcurrentrow by 1\relax% + \global\pgfmatrixcurrentcolumn=0\relax% + \expandafter\gdef\csname pgf@matrix@maxy\the\pgfmatrixcurrentrow\endcsname{0pt}% + \expandafter\gdef\csname pgf@matrix@miny\the\pgfmatrixcurrentrow\endcsname{0pt}% +}% + + + +% Step the row column number + +\def\pgf@matrix@step@column{% + \relax% + \global\advance\pgfmatrixcurrentcolumn by1\relax% +}% + + + +% Start a cell + +\def\pgf@matrix@startcell{% + % + % Step 1: Init the list of nodes for this cell + % + \let\pgf@nodecallback=\pgf@matrix@nodecallback% + % + % Step 2: Setup the bounding box + % + \pgfinterruptboundingbox% + % + % Step 3: Reset the transformation matrix + % + \pgftransformreset% + % + % Step 4: Collect everything in a cell box + % + \setbox\pgf@matrix@cell=\hbox\bgroup\bgroup% + % make sure that cell pictures are not affected if matrices have + % 'overlay' option on: + \pgf@relevantforpicturesizetrue + \pgfsys@beginpicture% + \normalbaselines% + % Find out whether the cell is empty: + \pgfutil@ifnextchar\let% + {% ok, candidate, check following symbol + \afterassignment\pgf@matrix@empty@check\let\pgf@matrix@temp=% get rid of \let + }% + {% no, not empty + \pgf@matrix@empty@cell@false% + \pgfmatrixbegincode% + }% +}% + +\def\pgf@matrix@empty@check{\futurelet\pgf@matrix@check@token\pgf@matrix@empty@check@cont}% +\def\pgf@matrix@empty@check@cont{% + \ifx\pgf@matrix@check@token\pgf@matrix@signal@cell@end% + \pgf@matrix@empty@cell@true% + \expandafter% + \pgfmatrixemptycode% + \expandafter% + \let% reinserted + \else% + \pgf@matrix@empty@cell@false% + \expandafter% + \pgfmatrixbegincode% + \expandafter + \let% reinserted + \fi% +}% + +\newif\ifpgf@matrix@empty@cell@ + + +% End a cell + +\def\pgf@matrix@endcell{% + \ifpgf@matrix@empty@cell@% + \else% + \expandafter\expandafter\expandafter\pgfmatrixendcode% + \expandafter\ifpgf@matrix@last@cell@in@row\expandafter\pgf@matrix@last@cell@in@rowtrue\expandafter\fi% + \fi% + \pgfsys@endpicture% + \ifnum\pgfmatrixcurrentcolumn<\pgf@matrix@numberofcolumns% + \else% + \ifpgf@matrix@last@cell@in@row\else% + {% + \global\pgf@matrix@fixedfalse% + \pgf@x=0pt% + \pgf@matrix@addtolength{\pgf@x}{\pgfmatrixcolumnsep}% + \pgf@matrix@addtolength{\pgf@x}{\pgf@matrix@last@nextcell@options}% + \ifpgf@matrix@fixed% + \expandafter\pgfutil@g@addto@macro\csname pgf@matrix@column@finish@\the\pgfmatrixcurrentcolumn\endcsname% + {\global\pgf@picmaxx=0pt}% + \fi% + \advance\pgfmatrixcurrentcolumn by1\relax % only temporary for the following: + \expandafter\xdef\csname pgf@matrix@column@sep@\the\pgfmatrixcurrentcolumn\endcsname{\the\pgf@x}% + \ifpgf@matrix@fixed% + \expandafter\gdef\csname pgf@matrix@column@finish@\the\pgfmatrixcurrentcolumn\endcsname{\global\pgf@picminx=0pt}% + \else% + \expandafter\global\expandafter\let\csname pgf@matrix@column@finish@\the\pgfmatrixcurrentcolumn\endcsname=\pgfutil@empty% + \fi% + }% + \fi\fi% + % Special case of the empty picture + \ifdim\pgf@picmaxx=-16000pt\relax% + \global\pgf@picmaxx=0pt\relax% + \global\pgf@picminx=0pt\relax% + \global\pgf@picmaxy=0pt\relax% + \global\pgf@picminy=0pt\relax% + \fi% + \csname pgf@matrix@column@finish@\the\pgfmatrixcurrentcolumn\endcsname% + \pgf@matrix@row@finish% + \egroup\egroup% + % + % Step 5: Horizontal gap + % + \ifnum\pgfmatrixcurrentcolumn>1\relax% + \pgf@y=\csname pgf@matrix@column@sep@\the\pgfmatrixcurrentcolumn\endcsname\relax% + \advance\pgf@picminx by-\pgf@y% + \fi% + % + % Step 6: Protocol and adjust height and depth + % + % Step 6.1: Protocol... + % + \ifdim\pgf@picmaxy>\csname pgf@matrix@maxy\the\pgfmatrixcurrentrow\endcsname% + \expandafter\xdef\csname pgf@matrix@maxy\the\pgfmatrixcurrentrow\endcsname{\the\pgf@picmaxy}% + \fi% + \ifdim\pgf@picminy<\csname pgf@matrix@miny\the\pgfmatrixcurrentrow\endcsname% + \expandafter\xdef\csname pgf@matrix@miny\the\pgfmatrixcurrentrow\endcsname{\the\pgf@picminy}% + \fi% + % + % Step 6.2: and setup. + % + \ht\pgf@matrix@cell=\pgf@picmaxy% + \dp\pgf@matrix@cell=-\pgf@picminy% + % + % Step 7: Protocol and adjust left and right width + % + % Step 7.1: Protocol... + % + \ifnum\pgfmatrixcurrentcolumn>\pgf@matrix@numberofcolumns\relax% + \expandafter\xdef\csname pgf@matrix@maxx\the\pgfmatrixcurrentcolumn\endcsname{\the\pgf@picmaxx}% + \expandafter\xdef\csname pgf@matrix@minx\the\pgfmatrixcurrentcolumn\endcsname{\the\pgf@picminx}% + \global\pgf@matrix@numberofcolumns=\pgfmatrixcurrentcolumn\relax% + \else% + \ifdim\pgf@picmaxx>\csname pgf@matrix@maxx\the\pgfmatrixcurrentcolumn\endcsname% + \expandafter\xdef\csname pgf@matrix@maxx\the\pgfmatrixcurrentcolumn\endcsname{\the\pgf@picmaxx}% + \fi% + \ifdim\pgf@picminx<\csname pgf@matrix@minx\the\pgfmatrixcurrentcolumn\endcsname% + \expandafter\xdef\csname pgf@matrix@minx\the\pgfmatrixcurrentcolumn\endcsname{\the\pgf@picminx}% + \fi% + \fi% + % + % Step 7.2: and setup. + % + \xdef\pgf@matrix@paddingskip{\the\pgf@picmaxx}% + % + % Step 8: Put in the box now + % + \hfil\hskip-\pgf@picminx% + \wd\pgf@matrix@cell=0pt% + \box\pgf@matrix@cell% + % + % Step 9: End bounding box + \endpgfinterruptboundingbox% +}% + +% Default code for the start and end code +\let\pgfmatrixbegincode=\relax +\let\pgfmatrixendcode=\relax +\let\pgfmatrixemptycode=\relax + + +% The following callback is called for every node that is produced +% inside a cell + +\def\pgf@matrix@nodecallback#1{% + \def\pgf@temp{#1}% + \ifx\pgf@temp\pgfutil@empty% + \else% + \xdef\pgf@matrix@node@list{\pgf@matrix@node@list,{#1}}% + \expandafter\xdef\csname pgf@matrix@node@location@#1\endcsname{{\the\pgfmatrixcurrentrow}{\the\pgfmatrixcurrentcolumn}}% + \fi% +}% + + + +% Padding code + +\def\pgf@matrix@padding{% + \ifpgf@matrix@correct@call% + \else% + \pgferror{Single ampersand used with wrong catcode}% + \fi% + \hskip\pgf@matrix@paddingskip\hfil% +}% + + +% Compute the real positions of the origins + +% We must now compute the real positions of the origins of all the +% small pictures. To this end, we need to compute prefix sums. After +% the procedure is done, the minx and the maxy will contain the origin +% positions. + +\def\pgf@matrix@compute@origin{% + % + % Inverse prefix sum on the vertical positions + % + {% + \ifnum\pgfmatrixcurrentrow>0\relax% + \expandafter\gdef\csname pgf@matrix@miny\the\pgfmatrixcurrentrow\endcsname{0pt}% + \fi + \c@pgf@counta=\pgfmatrixcurrentrow\relax% + \loop% + \ifnum\c@pgf@counta>1\relax% + \pgf@y=\csname pgf@matrix@miny\the\c@pgf@counta\endcsname\relax% + \advance\pgf@y by\csname pgf@matrix@maxy\the\c@pgf@counta\endcsname\relax + \advance\c@pgf@counta by-1\relax% + \pgf@ya=\csname pgf@matrix@miny\the\c@pgf@counta\endcsname\relax% + \advance\pgf@y by-\pgf@ya\relax% + \expandafter\xdef\csname pgf@matrix@miny\the\c@pgf@counta\endcsname{\the\pgf@y}% + \repeat% + }% + % + % Prefix sum on the horizontal positions + % + {% + \ifnum\pgf@matrix@numberofcolumns>0\relax% + \pgf@x=\csname pgf@matrix@minx1\endcsname\relax% + \pgf@x=-\pgf@x% + \expandafter\xdef\csname pgf@matrix@minx1\endcsname{\the\pgf@x}% + \fi + \c@pgf@counta=1\relax% + \loop% + \ifnum\c@pgf@counta<\pgf@matrix@numberofcolumns\relax% + \pgf@x=\csname pgf@matrix@minx\the\c@pgf@counta\endcsname\relax% + \advance\pgf@x by\csname pgf@matrix@maxx\the\c@pgf@counta\endcsname\relax% + \advance\c@pgf@counta by1\relax% + \pgf@xa=\csname pgf@matrix@minx\the\c@pgf@counta\endcsname\relax% + \advance\pgf@x by-\pgf@xa\relax% + \expandafter\xdef\csname pgf@matrix@minx\the\c@pgf@counta\endcsname{\the\pgf@x}% + \repeat% + }% +}% + + +% Shift the nodes to their origins + +% The following procedure shifts all nodes in +% \pgf@matrix@node@list to their location inside a temporary +% picture. This picture will later be shifted again to its final +% position in the real picture. + +\def\pgf@matrix@shift@nodes@initial{% + {% + \pgfutil@for\pgf@matrix@node@name:=\pgf@matrix@node@list\do{% + \ifx\pgf@matrix@node@name\pgfutil@empty% + \else% + \expandafter\ifx\csname pgf@matrix@node@visited@\pgf@matrix@node@name\endcsname\relax% + \pgf@shift@node{\pgf@matrix@node@name}{% + \pgf@x=\csname pgf@matrix@minx% + \expandafter\expandafter\expandafter\pgfutil@secondoftwo\csname pgf@matrix@node@location@\pgf@matrix@node@name\endcsname\endcsname% + \pgf@y=\csname pgf@matrix@miny% + \expandafter\expandafter\expandafter\pgfutil@firstoftwo\csname pgf@matrix@node@location@\pgf@matrix@node@name\endcsname\endcsname% + }% + \expandafter\let\csname pgf@matrix@node@visited@\pgf@matrix@node@name\endcsname=\pgfutil@empty% + \fi% + \fi% + }% + }% +}% + + +% The second shifting, done in the following procedure, shifts all +% nodes to their real positions inside the real picture. + +\def\pgf@matrix@shift@nodes@secondary#1{% + \pgfutil@for\pgf@matrix@node@name:=\pgf@matrix@node@list\do{% + \ifx\pgf@matrix@node@name\pgfutil@empty% + \else% + \expandafter\ifx\csname pgf@matrix@node@visited@\pgf@matrix@node@name\endcsname\relax% + \pgf@shift@node{\pgf@matrix@node@name}{#1}% + \expandafter\let\csname pgf@matrix@node@visited@\pgf@matrix@node@name\endcsname=\pgfutil@empty% + \fi% + \fi% + }% +}% + + +% End of line +\def\pgfmatrixendrow{% + % if the cell contains nothing, the following \let will be at the + % beginning (macro expansion has stopped here since neither \omit + % nor \span was found) + \let\pgf@matrix@signal@cell@end=\pgf@matrix@signal@cell@end + \pgf@matrix@last@cell@in@rowtrue% + \xdef\pgf@matrix@rowsep{\pgfmatrixrowsep}% + &\pgf@matrix@correct@calltrue% + \global\pgf@matrix@fixedfalse% + \pgf@y=0pt% + \pgf@matrix@addtolength\pgf@y{\pgf@matrix@rowsep}% + \pgfutil@ifnextchar[{\pgfmatrixendrow@skip}{\pgf@matrix@finish@line}% +}% +\newif\ifpgf@matrix@last@cell@in@row + +\def\pgf@matrix@signal@cell@end{\pgf@matrix@signal@cell@end}% +% exact definition does not matter, only needs to be unique. + +\def\pgfmatrixendrow@skip[#1]{% + \pgf@matrix@addtolength\pgf@y{#1}% + \pgf@matrix@finish@line% +}% + +\def\pgf@matrix@finish@line{% + \global\pgf@y=\pgf@y% + \pgf@ya=-\pgf@y% + \global\advance\pgf@ya by\csname pgf@matrix@miny\the\pgfmatrixcurrentrow\endcsname% + \expandafter\xdef\csname pgf@matrix@miny\the\pgfmatrixcurrentrow\endcsname{\the\pgf@ya}% + \pgfutil@ifnextchar\egroup{\cr\pgf@end@matrix}{\pgf@matrix@no@eom@found}% +}% + +\def\pgf@matrix@eom@found{% + \cr% + \pgf@end@matrix% +}% + +\def\pgf@matrix@no@eom@found{% + \cr% + \noalign{\vskip\pgf@y% + \ifpgf@matrix@fixed% + \vskip\csname pgf@matrix@miny\the\pgfmatrixcurrentrow\endcsname% unskip + \vskip\pgf@y% + \pgf@y=-\pgf@y% + \expandafter\xdef\csname pgf@matrix@miny\the\pgfmatrixcurrentrow\endcsname{\the\pgf@y}% + \gdef\pgf@matrix@row@finish{\global\pgf@picmaxy=0pt}% + \else% + \global\let\pgf@matrix@row@finish=\pgfutil@empty% + \fi% + }% +}% + + +\endinput diff --git a/graphics/pgf/base/tex/generic/modules/pgfmodulenonlineartransformations.code.tex b/graphics/pgf/base/tex/generic/modules/pgfmodulenonlineartransformations.code.tex new file mode 100644 index 0000000000..55c9d6f651 --- /dev/null +++ b/graphics/pgf/base/tex/generic/modules/pgfmodulenonlineartransformations.code.tex @@ -0,0 +1,342 @@ +% Copyright 2019 by Till Tantau +% +% This file may be distributed and/or modified +% +% 1. under the LaTeX Project Public License and/or +% 2. under the GNU Public License. +% +% See the file doc/generic/pgf/licenses/LICENSE for more details. + +\ProvidesFileRCS{pgfmodulenonlineartransformations.code.tex} + + +% +% This file defines commands for nonlinear coordinate systems. This is +% needed, for instance, for bend arrow heads. +% +% +% Non-linear coordinate systems transform coordinates, in addition to +% the normal linear transformations, in a, well, non-linear way. An +% example are polar coordinates. +% +% Like the linear transformations, non-linear transformations are +% applied on a low level and they are integrated into the whole path +% construction mechanism. So, while \pgfpointpolar also "does +% something nonlinear", it just provides a way of computing a +% coordinate. In contrast, installing a polar transformation will +% transform everything being drawn using the \pgfpath... commands an +% will even turn a straight line into a circle. +% +% Since computing non-linear transformations is expensive, they are +% only applied "if really necessary". + + + + +% Return a transformed point +% +% #1 = a point +% +% Description: +% +% Applies the current transformation and the current nonlinear +% transformation to the given point and returns the result in \pgf@x/y. + +\def\pgfpointtransformednonlinear#1{% + \pgf@process{% + #1% + \pgf@pos@transform@glob% + \pgf@nlt@list% + } +}% + + + + +% Approximate the nonlinear translation locally +% +% Description: +% +% Does a "local synchronization" of the nonlinear transformation with +% the linear transformation regarding the translation part of the +% current nonlinear transformation. The *linear* transformation will be +% updated so that it translates things to the point to which the +% nonlinear transformation transforms the origin. The nonlinear +% transformation will be reset. +% +% The net effect is that you can now draw things near the origin as +% before, but with just a linear transformation in force. Note that +% the coordinate system will not be rotated, only a shift is done (use +% the "real" local sync for a full sync). + +\def\pgfapproximatenonlineartranslation{% + \ifx\pgf@nlt@list\pgfutil@empty% + \else% + \pgfpointtransformednonlinear{\pgfpointorigin}% + \pgf@pt@x=\pgf@x% + \pgf@pt@y=\pgf@y% + \let\pgf@nlt@list\pgfutil@empty% + \let\pgf@nlt@moveto\pgf@lt@moveto% + \let\pgf@nlt@lineto\pgf@lt@lineto% + \let\pgf@nlt@curveto\pgf@lt@curveto% + \let\pgf@nlt@closepath\pgf@lt@closepath% + \fi% +}% + + + +% Local sync translation +% +% Description: +% +% Does a "complete local synchronization" of the nonlinear +% transformation with the linear transformation. The *linear* +% transformation will be updated so that it transforms things to the +% point to which the nonlinear transformation transforms the +% origin. The nonlinear transformation will be reset. +% +% The net effect is that you can now draw things near the origin as +% before, but with just a linear transformation in force. + +\def\pgfapproximatenonlineartransformation{% + \ifx\pgf@nlt@list\pgfutil@empty% + \else% + \pgfpointtransformednonlinear{\pgfpoint{1pt}{0pt}}% + \pgf@xa\pgf@x + \pgf@ya\pgf@y + \pgfpointtransformednonlinear{\pgfpoint{0pt}{1pt}}% + \pgf@xb\pgf@x + \pgf@yb\pgf@y + \pgfpointtransformednonlinear{\pgfpointorigin}% + \advance\pgf@xa by-\pgf@x + \advance\pgf@xb by-\pgf@x + \advance\pgf@ya by-\pgf@y + \advance\pgf@yb by-\pgf@y + \pgfsettransformentries{\pgf@sys@tonumber\pgf@xa}{\pgf@sys@tonumber\pgf@ya}{\pgf@sys@tonumber\pgf@xb}{\pgf@sys@tonumber\pgf@yb}{\pgf@x}{\pgf@y}% + \let\pgf@nlt@list\pgfutil@empty% + \let\pgf@nlt@moveto\pgf@lt@moveto% + \let\pgf@nlt@lineto\pgf@lt@lineto% + \let\pgf@nlt@curveto\pgf@lt@curveto% + \let\pgf@nlt@closepath\pgf@lt@closepath% + \fi% +}% + + + +% Adds a non-linear transformation to the current list of +% transformations. +% +% #1 = code +% +% Description: +% +% When the code #1 is called, \pgf@x and \pgf@y will be set to some +% values, let us call this the point p. The nonlinear transformation +% will now map this point to a new point f(p). The coordinates of this +% point should be returned in \pgf@x and \pgf@y. Furthermore, consider +% the two "unit tangents" (going in $x$-direction and in +% $y$-direction) at point p. The nonlinear transformation will map +% these to new tangents, t_x and t_y. The (not necessarily normalized) +% vectors t_x and t_y should be returned in pgf@xa and -ya and pgf@xb +% and pgf@yb. The macro should not do any global assignments except to +% \pgf@x and \pgf@y, but may do arbitrary local assignments. +% +% The effect of installing a new transformation is the following: As +% long as there is at least one nonlinear transformation installed, +% whenever the path construction commands add a curve or line to the +% softpath, the line or curve will be "bend" by applying all of the +% nonlinear transformation installed. In particular, straight lines +% will be replaced by curves. +% +% Note that the normal linear transformation are always applied before +% any nonlinear transformations are applied. + +\def\pgftransformnonlinear#1{% + \expandafter\def\expandafter\pgf@nlt@list\expandafter{\pgf@nlt@list#1}% + \let\pgf@nlt@moveto\pgf@nlt@moveto@nlt + \let\pgf@nlt@lineto\pgf@nlt@lineto@nlt + \let\pgf@nlt@curveto\pgf@nlt@curveto@nlt + \let\pgf@nlt@closepath\pgf@nlt@closepath@nlt +}% + +\def\pgf@nlt@moveto@nlt#1#2{% + {% + \pgfutil@tempdima#1% + \pgfutil@tempdimb#2% + \pgf@x\pgfutil@tempdima% + \pgf@y\pgfutil@tempdimb% + \xdef\pgf@nlt@last@moveto@orig{{\the\pgf@x}{\the\pgf@y}}% + \pgf@nlt@list% + \xdef\pgf@nlt@last@moveto@trans{{\the\pgf@x}{\the\pgf@y}}% + \xdef\pgf@nlt@last@moveto@xaxis{{\the\pgf@xa}{\the\pgf@ya}}% + \xdef\pgf@nlt@last@moveto@yaxis{{\the\pgf@xb}{\the\pgf@yb}}% + \pgf@protocolsizes{\pgf@x}{\pgf@y}% + \pgfsyssoftpath@moveto{\the\pgf@x}{\the\pgf@y}% + \global\let\pgf@nlt@last@coord@orig\pgf@nlt@last@moveto@orig% + \global\let\pgf@nlt@last@coord@trans\pgf@nlt@last@moveto@trans% + \global\let\pgf@nlt@last@coord@xaxis\pgf@nlt@last@moveto@xaxis% + \global\let\pgf@nlt@last@coord@yaxis\pgf@nlt@last@moveto@yaxis% + }% +}% + + +\def\pgf@nlt@lineto@nlt#1#2{% + {% + \edef\pgf@temp{\pgf@xc\the#1\pgf@yc\the#2}% + \pgf@temp% + \expandafter\pgfqpoint\pgf@nlt@last@coord@orig% + \expandafter\pgf@nlt@set@temps\pgf@nlt@last@coord@trans% + % Test, whether the points are quite near: + \pgf@xa\pgf@x\advance\pgf@xa by-\pgf@xc% + \pgf@ya\pgf@y\advance\pgf@ya by-\pgf@yc% + \pgfutil@tempswafalse% + \ifdim\pgf@xa<0.1pt\relax\ifdim\pgf@xa>-0.1pt\relax\ifdim\pgf@ya<0.1pt\relax\ifdim\pgf@ya>-0.1pt\relax\pgfutil@tempswatrue\fi\fi\fi\fi% + \ifpgfutil@tempswa% + \pgf@x\pgf@xc\pgf@y\pgf@yc% + \xdef\pgf@nlt@last@coord@orig{{\the\pgf@x}{\the\pgf@y}}% + \pgf@process{\pgf@nlt@list}% + \xdef\pgf@nlt@last@coord@trans{{\the\pgf@x}{\the\pgf@y}}% + \xdef\pgf@nlt@last@coord@xaxis{{\the\pgf@xa}{\the\pgf@ya}}% + \xdef\pgf@nlt@last@coord@yaxis{{\the\pgf@xb}{\the\pgf@yb}}% + \pgf@protocolsizes{\pgf@x}{\pgf@y}% + \pgfsyssoftpath@lineto{\the\pgf@x}{\the\pgf@y}% + \else% + % Compute support points + \pgf@xb=.333333\pgf@x% + \advance\pgf@xb by.666666\pgf@xc% + \pgf@yb=.333333\pgf@y% + \advance\pgf@yb by.666666\pgf@yc% + \pgf@xa=.333333\pgf@xc% + \advance\pgf@xa by.666666\pgf@x% + \pgf@ya=.333333\pgf@yc% + \advance\pgf@ya by.666666\pgf@y% + \pgf@nlt@inner@curve% + \fi% + }% +}% + +\def\pgf@nlt@set@temps#1#2{% + \pgfutil@tempdima#1\pgfutil@tempdimb#2% +}% + +\def\pgf@nlt@curveto@nlt#1#2#3#4#5#6{% + {% + \edef\pgf@temp{\pgf@xa\the#1\pgf@ya\the#2\pgf@xb\the#3\pgf@yb\the#4\pgf@xc\the#5\pgf@yc\the#6}% + \pgf@temp% + \pgf@nlt@inner@curve% + }% +}% + +\newdimen\pgftransformnonlinearflatness +\pgftransformnonlinearflatness=5pt +\def\pgfsettransformnonlinearflatness#1{\pgfmathsetlength\pgftransformnonlinearflatness{#1}}% + + + +% Compute a curve from \pgf@nlt@last@coord@orig to \pgf@xc/\pgf@yc via the +% controls \pgf@xa/\pgf@ya and \pgf@xb/\pgf@yb. +\def\pgf@nlt@inner@curve{% + \expandafter\pgfqpoint\pgf@nlt@last@coord@orig% + % Save delta of supports: + \pgfutil@tempdima\pgf@xa\advance\pgfutil@tempdima by-\pgf@xb + \pgfutil@tempdimb\pgf@ya\advance\pgfutil@tempdimb by-\pgf@yb + \begingroup + % Replace supports by relative supports + \advance\pgf@xa by-\pgf@x% + \advance\pgf@ya by-\pgf@y% + \advance\pgf@xb by-\pgf@xc% + \advance\pgf@yb by-\pgf@yc% + % + % Now, test whether the flatness is satisfied: + % + \pgfutil@tempswafalse + \ifdim\pgfutil@tempdima>\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% + \ifdim\pgfutil@tempdima<-\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% + \ifdim\pgfutil@tempdimb>\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% + \ifdim\pgfutil@tempdimb<-\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% + \ifdim\pgf@xa>\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% + \ifdim\pgf@xa<-\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% + \ifdim\pgf@ya>\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% + \ifdim\pgf@ya<-\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% + \ifdim\pgf@xb>\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% + \ifdim\pgf@xb<-\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% + \ifdim\pgf@yb>\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% + \ifdim\pgf@yb<-\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% + \ifpgfutil@tempswa% + \endgroup% Undo the adjustments... + {% + \edef\pgf@orig@xya{\pgf@xa\the\pgf@xa\pgf@ya\the\pgf@ya} + \edef\pgf@orig@xyb{\pgf@xb\the\pgf@xb\pgf@yb\the\pgf@yb} + \edef\pgf@orig@xyc{\pgf@xc\the\pgf@xc\pgf@yc\the\pgf@yc} + \pgfpointcurveattime{.5}% + {\expandafter\pgfqpoint\pgf@nlt@last@coord@orig}% + {\pgf@x\pgf@xa\pgf@y\pgf@ya}% + {\pgf@x\pgf@xb\pgf@y\pgf@yb}% + {\pgf@x\pgf@xc\pgf@y\pgf@yc}% + % First new curve: + % Start is at last@coord@orig, + % first support is at last@coord@orig*t + s*(original xa/ya) + % second support is at xb/yb + % target is at x/y + {% + % Target: + \pgf@xc\pgf@x\pgf@yc\pgf@y + % First support: + \expandafter\pgfqpoint\pgf@nlt@last@coord@orig% + \pgf@orig@xya% + \pgf@xa\pgf@time@s\pgf@xa\advance\pgf@xa by\pgf@time@t\pgf@x% + \pgf@ya\pgf@time@s\pgf@ya\advance\pgf@ya by\pgf@time@t\pgf@y% + \pgf@nlt@inner@curve% + }% + % Second new curve: + % Start is at (new) last@coord@orig + % first support is at xa/ya + % second support is at t*(original xb/yb) + s*(original xc/yc) + % target is at original xc/yc + {% + \pgf@orig@xyb% + \pgf@orig@xyc% + \pgf@xb\pgf@time@t\pgf@xb\advance\pgf@xb by\pgf@time@s\pgf@xc% + \pgf@yb\pgf@time@t\pgf@yb\advance\pgf@yb by\pgf@time@s\pgf@yc% + \pgf@nlt@inner@curve% + }% + }% + \else% + \endgroup% Snap back... + \pgf@nlt@do@inner@curve% + \fi% +}% + +\def\pgf@nlt@do@inner@curve{% + \pgf@process{\pgf@x\pgf@xa\pgf@y\pgf@ya\pgf@nlt@list}% + \pgf@xa\pgf@x\pgf@ya\pgf@y% + \pgf@process{\pgf@x\pgf@xb\pgf@y\pgf@yb\pgf@nlt@list}% + \pgf@xb\pgf@x\pgf@yb\pgf@y% + \xdef\pgf@nlt@last@coord@orig{{\the\pgf@xc}{\the\pgf@yc}}% + \pgf@process{\pgf@x\pgf@xc\pgf@y\pgf@yc\pgf@nlt@list}% + \pgf@xc\pgf@x\pgf@yc\pgf@y% + \xdef\pgf@nlt@last@coord@trans{{\the\pgf@xc}{\the\pgf@yc}}% + \pgf@protocolsizes{\pgf@xa}{\pgf@ya}% + \pgf@protocolsizes{\pgf@xb}{\pgf@yb}% + \pgf@protocolsizes{\pgf@xc}{\pgf@yc}% + \pgfsyssoftpath@curveto{\the\pgf@xa}{\the\pgf@ya}{\the\pgf@xb}{\the\pgf@yb}{\the\pgf@xc}{\the\pgf@yc}% +}% + + +\def\pgf@nlt@closepath@nlt{% + {% + \expandafter\pgfqpoint\pgf@nlt@last@moveto@orig% + \expandafter\pgf@nlt@set@temps\pgf@nlt@last@coord@orig% + \advance\pgfutil@tempdima by-\pgf@x% + \advance\pgfutil@tempdimb by-\pgf@y% + \pgfutil@tempswatrue% + \ifdim\pgfutil@tempdima<0.01pt\relax\ifdim\pgfutil@tempdima>-0.01pt\relax\ifdim\pgfutil@tempdimb<0.01pt\relax\ifdim\pgfutil@tempdimb>-0.01pt\relax\pgfutil@tempswafalse\fi\fi\fi\fi% + \ifpgfutil@tempswa% + \pgf@nlt@lineto@nlt{\pgf@x}{\pgf@y}% + \fi% + \pgfsyssoftpath@closepath% + }% +}% + + +\endinput diff --git a/graphics/pgf/base/tex/generic/modules/pgfmoduleoo.code.tex b/graphics/pgf/base/tex/generic/modules/pgfmoduleoo.code.tex new file mode 100644 index 0000000000..86c1e8e996 --- /dev/null +++ b/graphics/pgf/base/tex/generic/modules/pgfmoduleoo.code.tex @@ -0,0 +1,844 @@ +% Copyright 2019 by Till Tantau +% Copyright 2019 by Saso Zivanovic +% +% This file may be distributed and/or modified +% +% 1. under the LaTeX Project Public License and/or +% 2. under the GNU Public License. +% +% See the file doc/generic/pgf/licenses/LICENSE for more details. +% +% Based on pgfmoduleoo.code.tex v1.8 by Till Tantau. This version adds inheritance. + + + +% Support of object-oriented programming in TeX, with inheritance. + + +% The oo support works as follows: +% +% The main supported concepts are classes, objects, methods, +% attributes and signal/slots. A class defines a set of methods, which are, +% in the end, just normal TeX macros. Once a class has been created, +% it can be instantiated by calling the \pgfoonew command, resulting +% in a new object. Objects are local to their group. Given an object, +% you can send it a message, resulting in the method code of the +% object's method to be executed. While an object exists, it has a set +% of attributes whose values can change over time. Attributes values +% are not local to TeX groups, rather their life-cycle is the same as +% the object's (which, however, is local to the group in which the +% object was declared). +% +% The implementation is as follows: There is an ID counter that is +% increased each time an object is created. This counter is local to +% the group, which means that when a group ends the counter will +% revert to the previous value, allowing objects to the reused in +% subsequent groups. +% +% A method is just a normal \TeX macro, but to call it you write +% \objecthandle.methodname(parameters). The \objecthandle is a macro +% that is created when you say \pgfoonew. The special object +% \pgfoothis is the current object. +% +% Attributes are stored globally in internal TeX macros whose name is +% composed of the object number and the attribute name. + + + + +% Declares a class +% +% #1 = class name +% #2 = methods +% +% Description: +% +% This command declares a class for future use. Inside #2, the macro +% \method can be used to declare a method. The \method macro takes a +% method name, parameters (methods are normal TeX macros, after all) +% and body. + +% base classes are put in parenthesis before the new class name, but +% are optional. +\def\pgfooclass{% + \pgfutil@ifnextchar ({% + \pgfooclass@ + }{% + \pgfooclass@() + }% +}% +\long\def\pgfooclass@(#1)#2#3{% + \def\pgfoo@classname{#2}% + \expandafter\ifx\csname pgfooY\pgfoo@classname.@pgfooinit\endcsname\relax\else + \pgferror{class #2 is already defined}% + \fi + \pgfoo@ciii{#2}{#1}% + \expandafter\let\csname pgfooY#2@pgfoo@mro\endcsname\pgfoo@mro + \let\pgfoo@origmethod=\method% + \let\pgfoo@origattribute=\attribute% + \let\method=\pgfoo@declaremethod% + \let\attribute=\pgfoo@declareattribute% + \let\pgfoo@attributes=\pgfutil@empty% + \let\pgfoo@methods=\pgfutil@empty% + #3% + % inherit + \def\pgfoo@temp@baseclasses{#1}% + \pgfoo@inherit@methods + \pgfoo@inherit@attributes + % Always present (and never inherited) methods: + \expandafter\let\csname pgfooY\pgfoo@classname.get handle\endcsname\pgfoo@obj% + \expandafter\let\csname pgfooY\pgfoo@classname.get id\endcsname\pgfoo@id% + % for compatibility with previous versions of pgfoo, define method + % with the same name as the class as a synonym for init + \expandafter\let\expandafter\pgfoo@init\csname pgfooY\pgfoo@classname.init\endcsname + \expandafter\let\csname pgfooY\pgfoo@classname.\pgfoo@classname\endcsname\pgfoo@init + % Cleanup + \let\method=\pgfoo@origmethod% + \let\attribute=\pgfoo@origattribute% +}% + +\def\pgfoo@emptyinit(){}% +\def\pgfoo@escapeif#1#2\fi{\fi#1}% a little helper + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Compute MRO using C3 algorithm +% The sequences are enclosed in brackets; elements of sequences and +% the big list are terminated by a comma and a dot, respectively. (Why +% comma *and* a dot: TeX's parset---brackets don't form groups! Soooo, +% why not braces? Because I don't know how do delete excess "{},"s...) + +% #1 = current class, #2 = list of base classes +\def\pgfoo@ciii#1#2{% + % \pgfoo@seq will hold a list of sequences. + % (i) the first sequence has just one element: the current class + \def\pgfoo@seq{[#1,].}% + % (ii) every subclass has a sequence (in the order as they were + % given): the class' sequence is its mro. + \pgfutil@for\pgfoo@baseclass:={#2}\do{% + \edef\pgfoo@baseclass@mro{\csname pgfooY\pgfoo@baseclass @pgfoo@mro\endcsname}% + \expandafter\ifx\expandafter\relax\pgfoo@baseclass@mro\relax\else + \edef\pgfoo@seq{\pgfoo@seq[\pgfoo@baseclass@mro,].}% + \fi + }% + % The final sequence is a sequence of base classes, in the order as + % they were given. + \ifx\relax#2\relax\else + \edef\pgfoo@seq{\pgfoo@seq[#2,].}% + \fi + \edef\pgfoo@temp{()(\pgfoo@seq)}% + \expandafter\pgfoo@ciii@merge\pgfoo@temp +}% + +% #1 = MRO so far +% #2 = remaining sequences +% no empty sequences [] should arrive here: they should be removed +% before calling this macro +\def\pgfoo@ciii@merge(#1)(#2){% + \ifx\relax#2\relax + \pgfoo@ciii@done(#1)% + \else\pgfoo@escapeif{% split #2 + \pgfoo@ciii@merge@A(#1)(#2)% + }\fi% +}% +% a splitter: to be only called from \pgfoo@ciii@merge +\def\pgfoo@ciii@merge@A(#1)(#2.#3){% + \ifx\relax#3\relax % if #3 is empty, we can take the shortcut + \pgfoo@ciii@mergeA@done(#1)(#2)% + \else\pgfoo@escapeif{% + \pgfoo@ciii@merge@checkHead(#1)()(#2)()(#3)% + }\fi +}% + +\def\pgfoo@ciii@mergeA@done(#1)([#2]){% + \pgfoo@ciii@done(#1#2)% +}% + +% #1 = MRO so far +% #2 = sequences with bad heads (dot after each sequence) +% [#3,#4] = the sequence whose head we're testing right +% now---obviously, it should not be empty! (#3 has no comma, +% #4 has a comma after each class name) +% #5 = #3 does not occur in the tail of these sequences (dot after +% each sequence) +% #6 = the remaining sequences (dot after each sequence) +\def\pgfoo@ciii@merge@checkHead(#1)(#2)([#3,#4])(#5)(#6){% + \pgfutil@in@{[#3,}{#2}% + \ifpgfutil@in@\pgfoo@escapeif{% #3 is already blacklisted + \pgfoo@ciii@clean\pgfoo@ciii@merge@checkHead@P{(#1)(#2[#3,#4].)(#5#6)}% + }\else\pgfoo@escapeif{% + \ifx\relax#6\relax\pgfoo@escapeif{% no more sequences to test the head against! + % so, #3 is a good head, and we can recurse + \pgfoo@ciii@goodHeadFound(#1)(#3)(#2[#4].#5)% + }\else\pgfoo@escapeif{% split #6 and get to work + \pgfoo@ciii@merge@checkHead@A(#1)(#2)([#3,#4])(#5)(#6)% + }\fi + }\fi +}% +% splitter: to be only called from \pgfoo@ciii@merge@checkHead +% we will check if #3 occurs in the tail of #6 +\def\pgfoo@ciii@merge@checkHead@A(#1)(#2)([#3,#4])(#5)(#6.#7){% + \pgfoo@ciii@merge@checkHead@B(#1)(#2)([#3,#4])(#5)(#6)(#7)% +}% +% splitter: to be only called from \pgfoo@ciii@merge@checkHead@A +% &worker: we will check if #3 occurs in #7. If it does, #3 is a bad head. +\def\pgfoo@ciii@merge@checkHead@B(#1)(#2)([#3,#4])(#5)([#6,#7])(#8){% +% \def\pgfoo@ciii@tempA{#3}\def\pgfoo@ciii@tempB{#5}% +% \ifx\pgfoo@ciii@tempA\pgfoo@ciii@tempB +% \pgferror{An attempt to derive from the same class twice}% +% \else\pgfoo@escapeif{% + \pgfutil@in@{,#3,}{,#7}% + \ifpgfutil@in@\pgfoo@escapeif{% bad head! get next head and restart + % but: need to clean up first! + \pgfoo@ciii@clean\pgfoo@ciii@merge@checkHead@P{(#1)(#2[#3,#4].)(#5[#6,#7].#8)}% + }\else\pgfoo@escapeif{% so far, so good + \ifx\relax#8\relax\pgfoo@escapeif{% if #8 is empty, we won: + % #3 is a good head! we will move it to MRO + % but clean-up first! + \pgfoo@ciii@goodHeadFound(#1)(#3)(#2[#4].#5[#6,#7].)% + }\else\pgfoo@escapeif{% proceed to the first seq in #8 + \pgfoo@ciii@clean\pgfoo@ciii@merge@checkHead{(#1)(#2)([#3,#4])(#5[#6,#7].)(#8)}% + }\fi + }\fi +% }\fi +}% + +% #1 = MRO +% #2 = good head +% #3 = the remaining sequences (in need of removing the good head from +% them, and then also some general cleanup) +\def\pgfoo@ciii@goodHeadFound(#1)(#2)(#3){% + \pgfutil@in@{,#2,}{#3}% + \ifpgfutil@in@\pgfoo@escapeif{% + \def\pgfoo@ciii@removeGoodHead(##1)(##2)(##3,#2,##4){% + \pgfoo@ciii@goodHeadFound(##1)(##2)(##3,##4)}% + \pgfoo@ciii@removeGoodHead(#1)(#2)(#3)% + }\else\pgfoo@escapeif{% + \pgfutil@in@{[#2,}{#3}% + \ifpgfutil@in@\pgfoo@escapeif{% + \def\pgfoo@ciii@removeGoodHead(##1)(##2)(##3[#2,##4){% + \pgfoo@ciii@goodHeadFound(##1)(##2)(##3[##4)}% + \pgfoo@ciii@removeGoodHead(#1)(#2)(#3)% + }\else\pgfoo@escapeif{% + \pgfoo@ciii@clean\pgfoo@ciii@merge{(#1#2,)(#3)}% + }\fi + }\fi +}% + +% mediates between the bad head exit of \pgfoo@ciii@merge@checkHead@B +% and \pgfoo@ciii@merge@checkHead; we need the mediator because #3 +% below might have been affected by cleaning. +\def\pgfoo@ciii@merge@checkHead@P(#1)(#2)(#3){% + \if\relax#3\relax % no more good head candidates! + \pgferror{Bad MRO: There is no good (monotonic etc.) + Method Resolution Order for your class hierarchy}% + \else\pgfoo@escapeif{% + \pgfoo@ciii@merge@checkHead@Q(#1)(#2)(#3)% + }\fi +}% +% splitter for \pgfoo@ciii@merge@checkHead@P +% #3 = the sequence containing the newest good head candidate +\def\pgfoo@ciii@merge@checkHead@Q(#1)(#2)(#3.#4){% + \pgfoo@ciii@merge@checkHead(#1)(#2)(#3)()(#4)% +}% + +% cyclically removes all occurrences of "[]," and "[]." (i.e., empty +% sequences) in #2. When done, calls #1 with whatever remains. +\def\pgfoo@ciii@clean#1#2{% clean commas + \pgfutil@in@{[].}{#2}% clean dots + \ifpgfutil@in@\pgfoo@escapeif{% + \pgfoo@ciii@clean@dot#1#2\pgfoo@ciii@clean@END + }\else\pgfoo@escapeif{% + #1#2% + }\fi +}% +\def\pgfoo@ciii@clean@dot#1#2[].#3\pgfoo@ciii@clean@END{% + \pgfoo@ciii@clean#1{#2#3}% +}% + +\def\pgfoo@ciii@done(#1,){% + \def\pgfoo@mro{#1}% +}% + +% end of C3 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +% Declare a method +% +% Description: +% +% Defines a method. To use/invoke this method for an object \object, +% you write \object.methodname(parameters). This will cause the method +% body to be invoked with the argument "(parameters)". +% +% To define the method \method should be directly followed by the +% method name and, then, by (, followed by a parameter pattern, +% followed by ), followed by the message body. Spaces are allowed only +% after "\method" and after the closing ). +% +% Example: +% +% \pgfooclass{MyPlot}{ +% +% \attribute x=0; +% \attribute y=0; +% +% \method MyPlot() { +% } +% +% \method getX(#1) { +% \pgfooget{x}{#1} +% } +% +% \method setPoint(#1,#2) { +% \pgfooset{x}{#1} +% \pgfooset{y}{#2} +% } +% } + +% define the method macro and append the method to the method collection +\long\def\pgfoo@declaremethod#1(#2)#3{% + \def\pgfoo@method{#1}% + \ifx\pgfoo@classname\pgfoo@method + \def\pgfoo@method{init}% + \fi + \expandafter\long\expandafter\def\csname pgfooY\pgfoo@classname.\pgfoo@method\endcsname(#2){#3}% + \edef\pgfoo@methods{\pgfoo@methods,\pgfoo@method}% +}% + +\def\pgfoo@inherit@methods{% + % for non-derived classes: + \expandafter\ifx\expandafter\relax\pgfoo@temp@baseclasses\relax + % if init is not defined, define an empty one + \expandafter\ifx\csname pgfooY\pgfoo@classname.init\endcsname\relax + \expandafter\let\csname pgfooY\pgfoo@classname.init\endcsname\pgfoo@emptyinit% + \edef\pgfoo@methods{,init\pgfoo@methods}% + \fi + \fi + \if\relax\pgfoo@methods\relax\else + % gobble the initial comma + \edef\pgfoo@methods{\expandafter\pgfutil@gobble\pgfoo@methods}% + \fi + % remember the list of collected methods + \expandafter\let\csname pgfooY\pgfoo@classname @pgfoo@methods\endcsname\pgfoo@methods + % get MRO + \expandafter\let\expandafter\pgfoo@mro\csname pgfooY\pgfoo@classname @pgfoo@mro\endcsname + % loop throough base classes in MRO + \pgfutil@for\pgfoo@baseclass:=\pgfoo@mro\do{% + \ifx\pgfoo@baseclass\pgfoo@classname % skip self + \else\pgfoo@escapeif{% + % get methods defined in this base class + \expandafter\let\expandafter\pgfoo@methods\csname pgfooY\pgfoo@baseclass @pgfoo@methods\endcsname + \pgfutil@for\pgfoo@method:=\pgfoo@methods\do{% for each method + % check if it is already defined in our class + \expandafter\ifx\csname pgfooY\pgfoo@classname.\pgfoo@method\endcsname\relax + % if not, link the method name from our class to base class + \edef\pgfoo@temp{\noexpand\let + \expandafter\noexpand\csname pgfooY\pgfoo@classname.\pgfoo@method\endcsname + \expandafter\noexpand\csname pgfooY\pgfoo@baseclass.\pgfoo@method\endcsname}% + \pgfoo@temp + \fi + }% + }\fi + }% +}% + +\def\pgfoosuper(#1,#2).#3({% + #2.get id(\pgfoo@temp@id)% + \edef\pgfoo@classname{\csname pgfooX\pgfoo@temp@id @class\endcsname}% + \def\pgfoo@super@A##1,#1,##2.##3({% + \pgfoo@super@B##2.##3(% + }% + \expandafter\let\expandafter\pgfoo@mro\csname pgfooY\pgfoo@classname @pgfoo@mro\endcsname + \expandafter\pgfoo@super@A\expandafter,\pgfoo@mro,.#3(% +}% +\def\pgfoo@super@B#1,#2.#3({% + \expandafter\ifx\csname pgfooY#1.#3\endcsname\relax + \pgfoo@escapeif{\pgfoo@super@B#2.#3(}% + \else + \pgfoo@escapeif{\expandafter\pgfoo@caller\pgfoo@temp@id.#1.#3(}% + \fi +}% + + + +% Declare an attribute +% +% #1 = attribute name +% #2 = optional default value +% +% Description: +% +% Declares the attribute #1 for the current class. If the attribute +% name is followed by =, the text following the equal sign is the +% default value. + +\def\pgfoo@declareattribute#1;{% + \pgfutil@in@{ =}{#1}% + \ifpgfutil@in@% + \pgfoo@declareunpackspace#1;% + \else% + \pgfutil@in@={#1}% + \ifpgfutil@in@% + \pgfoo@declareunpack#1;% + \else% + \pgfoo@declareattribute@\let{#1}\pgfutil@empty + \fi% + \fi% +}% +\def\pgfoo@declareunpack#1=#2;{\pgfoo@declareattribute@\def{#1}{{#2}}}% +\def\pgfoo@declareunpackspace#1 =#2;{\pgfoo@declareattribute@\def{#1}{{#2}}}% + +% put the initial value in the class's namespace +% put the attr name in a list +\def\pgfoo@declareattribute@#1#2#3{% + \expandafter#1\csname pgfooY\pgfoo@classname @#2\endcsname#3% + \expandafter\def\expandafter\pgfoo@attributes\expandafter{\pgfoo@attributes,#2}% +}% + +% for each base class: +% for each attribute declared in that class: +% add the attr on a list if it's not there yet +% convert the list of (attr,class which it comes from) into an +% efficient sequence of triples \op{attrname}\pointer-to-initial-value +% (\op is then set differently in init and garbage collection) +\def\pgfoo@inherit@attributes{% + % store the list of attributes declared in this class for use in + % derived classes + \if\relax\pgfoo@attributes\relax\else + \edef\pgfoo@attributes{\expandafter\pgfutil@gobble\pgfoo@attributes}% gobble comma + \fi + \expandafter\let\csname pgfooY\pgfoo@classname.@pgfoo@attributes\endcsname\pgfoo@attributes% + % get MRO + \expandafter\let\expandafter\pgfoo@mro\csname pgfooY\pgfoo@classname @pgfoo@mro\endcsname + \def\pgfoo@allattributes{}% here we will store the attrs declared in base classes + \pgfutil@for\pgfoo@baseclass:=\pgfoo@mro\do{% for each base class in MRO order + % the attributes declared in this base class + \expandafter\let\expandafter\pgfoo@attributes\csname pgfooY\pgfoo@baseclass.@pgfoo@attributes\endcsname + \pgfutil@for\pgfoo@attribute:=\pgfoo@attributes\do{% for each attribute + % check if we have already found it in some previous base class + \edef\pgfoo@temp{{,\pgfoo@attribute=}{\pgfoo@allattributes,}}% + \expandafter\pgfutil@in@\pgfoo@temp + \ifpgfutil@in@\else % don't overwrite + % append to the list + \edef\pgfoo@allattributes{\pgfoo@allattributes,\pgfoo@attribute=\pgfoo@baseclass}% + \fi + }% + }% + \if\relax\pgfoo@allattributes\relax\else + \edef\pgfoo@allattributes{\expandafter\pgfutil@gobble\pgfoo@allattributes}% gobble comma + \fi + % this macro will hold a list of attributes from all base classes + % we run this macro at init and gc + \def\pgfoo@process@attributes{}% + \pgfutil@for\pgfoo@attributeclass:=\pgfoo@allattributes\do{% for each (attribute, class) pair + % append to the attribute processor + \expandafter\pgfoo@inherit@attributes@appendattribute\pgfoo@attributeclass.% + }% + \expandafter\let\csname pgfooY\pgfoo@classname .@pgfoo@process@attributes\endcsname\pgfoo@process@attributes +}% + + +% #1=attr, #2=class which the attr comes from +\def\pgfoo@inherit@attributes@appendattribute#1=#2.{% + \def\pgfoo@tempA{\pgfoo@attribute@op{#1}}% + \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter\pgfoo@tempB\expandafter\expandafter\expandafter{\expandafter\pgfoo@tempA\csname pgfooY#2@#1\endcsname}% + \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter\pgfoo@process@attributes\expandafter\expandafter\expandafter{\expandafter\pgfoo@process@attributes\pgfoo@tempB}% + % above is just this: + % \def\pgfoo@process@attributes{\pgfoo@process@attributes\pgfoo@attribute@op{#1}% + % \csname pgfooY#2@#1\endcsname} + % with \pgfoo@process@attributes and \csname expanded exactly once: +}% + +% \pgfoo@attribute@op for garbage collection +\def\pgfoo@gc@attribute#1#2{\pgfoolet{#1}\relax}% + + +\newcount\pgfoo@objectcount +\newcount\pgfoothis@count + +% The attribute value method +% +% #1 = attr +% +% This method inserts the current value of the given attribute for the +% current object. + +\def\pgfoovalueof#1{% + \csname pgfooX\the\pgfoothis@count @#1\endcsname% +}% + + +% The attribute get method +% +% #1 = attr +% #2 = macro +% +% This method makes the macro equal to the current value of the +% attribute for the current object. + +\def\pgfooget#1#2{% + \expandafter\let\expandafter#2\csname pgfooX\the\pgfoothis@count @#1\endcsname% +}% + + +% The attribute set method +% +% #1 = attr +% #2 = value +% +% This method sets the given attribute for the current object to the +% given value. + +\long\def\pgfooset#1#2{% + \expandafter\gdef\csname pgfooX\the\pgfoothis@count @#1\endcsname{#2}% +}% + + +% The attribute set method (expanded version) +% +% #1 = attr +% #2 = value +% +% This method sets the given attribute for the current object to the +% expanded version of the given value. + +\long\def\pgfooeset#1#2{% + \expandafter\xdef\csname pgfooX\the\pgfoothis@count @#1\endcsname{#2}% +}% + + +% The attribute let method +% +% #1 = attr +% #2 = value +% +% This method sets the given attribute for the current object to the +% given value using \let. + +\def\pgfoolet#1#2{% + \expandafter\global\expandafter\let\csname pgfooX\the\pgfoothis@count @#1\endcsname#2% +}% + + +% Add something to an attribute at the end +% +% #1 = attr +% #2 = code +% +% This method adds the give code to the attr at the end. + +\def\pgfooappend#1#2{% + \expandafter\expandafter\expandafter\def% + \expandafter\expandafter\expandafter\pgf@oo@temp\expandafter\expandafter\expandafter{\csname pgfooX\the\pgfoothis@count @#1\endcsname#2}% + \expandafter\global\expandafter\let\csname pgfooX\the\pgfoothis@count @#1\endcsname\pgf@oo@temp% +}% + + +% Add something to an attribute at the beginning +% +% #1 = attr +% #2 = code +% +% This method adds the give code to the attr at the beginning. + +\def\pgfooprefix#1#2{% + \pgfooget{#1}\pgf@oo@temp% + \def\pgf@oo@@temp{#2}% + \expandafter\expandafter\expandafter\def% + \expandafter\expandafter\expandafter\pgf@oo@temp% + \expandafter\expandafter\expandafter{\expandafter\pgf@oo@@temp\pgf@oo@temp}% + \pgfoolet{#1}\pgf@oo@temp% +}% + + + +% Help macro for scanning arguments +\newtoks\pgfoo@toks + +\def\pgfoo@collect@args{% + \pgfutil@ifnextchar\bgroup\pgfoo@collect@args@group\pgfoo@collect@args@nogroup% +}% +\def\pgfoo@collect@args@nogroup#1){% + \pgfoo@toks{#1}% + \pgfoo@continue% +}% +\def\pgfoo@collect@args@group#1{% + \pgfutil@ifnextchar){\pgfoo@toks{{#1}}\expandafter\pgfoo@continue\pgfutil@gobble}{\pgfoo@collect@args@nogroup{#1}}% +}% + + +% Instantiate an object +% +% Possible syntax: +% +% 1) \pgfoonew new <class name>(<constructor parameters) +% 2) \pgfoonew \<objectname>=new <class name>(<constructor parameters) +% 3) \pgfoonew{attribute}=new <class name>(<constructor parameters) +% +% Description: +% +% Creates an object. After the object has been created, the method +% called <class name> (the constructor) is invoked. If the +% \<objectname>= part is present, the macro is assigned to the newly +% created object. + +\def\pgfoonew{% + \pgfutil@ifnextchar n{% + \pgfoo@new\pgfoo@dummy=% + }{% + \pgfutil@ifnextchar\bgroup{% + \pgfoo@new@attribute% + }{% + \pgfoo@new% + }% + }% +}% +\def\pgfoo@new#1={% + \def\pgfutil@reserved@a{#1}% + \let\pgfutil@next\pgfoo@@new + \futurelet\pgfutil@let@token\pgfutil@ignorespaces}% +\def\pgfoo@@new new #1({% + \expandafter\ifx\csname pgfooY#1.get id\endcsname\relax% + \pgferror{Unknown class '#1'}% + \else% + \expandafter\pgfoo@new@create\pgfutil@reserved@a{#1}% + {% + \pgfoothis@count\pgfoo@objectcount% + \let\pgfoo@attribute@op\pgfoolet + \csname pgfooY#1.@pgfoo@process@attributes\endcsname% + }% + \let\pgfoo@continue=\pgf@oo@new@cont% + \expandafter\pgfoo@collect@args% + \fi% +}% + +\def\pgf@oo@new@cont{% + \expandafter\pgfoolastobj\expandafter.\expandafter i\expandafter n\expandafter i\expandafter t\expandafter(\the\pgfoo@toks)% + \aftergroup\pgfoogc% cleanup after group +}% + +\def\pgfoo@new@attribute#1={% + \def\pgfutil@reserved@a{#1}% + \let\pgfutil@next\pgfoo@@new@attribute + \futurelet\pgfutil@let@token\pgfutil@ignorespaces}% +\def\pgfoo@@new@attribute new #1({% + \expandafter\ifx\csname pgfooY#1.get id\endcsname\relax% + \pgferror{Unknown class '#1'}% + \else% + \pgfoo@new@create\pgfoo@temp{#1}% + {% + \pgfoothis@count\pgfoo@objectcount% + \let\pgfoo@attribute@op\pgfoolet% + \csname pgfooY#1.@pgfoo@process@attributes\endcsname% + }% + \expandafter\pgfoolet\expandafter{\pgfutil@reserved@a}\pgfoo@temp% + \let\pgfoo@continue=\pgf@oo@new@cont% + \expandafter\pgfoo@collect@args% + \fi% +}% + +\def\pgfoo@new@create#1#2{% + \advance\pgfoo@objectcount by 1\relax% + \edef\pgfoolastobj{\noexpand\pgfoo@caller{\the\pgfoo@objectcount}}% + \expandafter\gdef\csname pgfooX\the\pgfoo@objectcount @class\endcsname{#2}% + \let#1\pgfoolastobj% +}% + +\def\pgfoo@caller#1.#2({% + \expandafter\let\expandafter\pgfoo@caller@temp\csname pgfooY\csname pgfooX#1@class\endcsname.#2\endcsname% + \ifx\pgfoo@caller@temp\relax% + % assume that #2 is of form "superclass.method" + \expandafter\let\expandafter\pgfoo@caller@temp\csname pgfooY#2\endcsname% + \ifx\pgfoo@caller@temp\relax% + \pgferror{Object #1 has no method '#2'}% + \fi% + \fi% + \def\pgf@marshal{% + \pgfoothis@count=#1\relax% + }% + \let\pgfoo@continue\pgfoo@caller@cont% + \pgfoo@collect@args% +}% +\def\pgfoo@caller@cont{% + \edef\pgf@marshal{% + \pgf@marshal% + \noexpand\pgfoo@caller@temp(\the\pgfoo@toks)% + }% + \expandafter\pgf@marshal\expandafter\pgfoothis@count\the\pgfoothis@count\relax% +}% +\let\pgfoo@orig@caller=\pgfoo@caller + +% The special "this" object + +\def\pgfoothis.#1({% + \expandafter\let\expandafter\pgfoo@caller@temp\csname pgfooY\csname pgfooX\the\pgfoothis@count @class\endcsname.#1\endcsname% + \ifx\pgfoo@caller@temp\relax% + % assume that #1 is of form "superclass.method" + \expandafter\let\expandafter\pgfoo@caller@temp\csname pgfooY#1\endcsname% + \fi% + \let\pgfoo@continue\pgfoothis@cont% + \pgfoo@collect@args% +}% +\def\pgfoothis@cont{% + \expandafter\pgfoo@caller@temp\expandafter(\the\pgfoo@toks)% +}% + + + +% Get the object id +% +% #1 = macro to store the id +% +% Description: +% +% This special method allows you to access the object id. You can then +% use \pgfoocall to call a method using this id. This is mainly useful +% when you wish to store the id for a longer time. + +\def\pgfoo@id(#1){% + \edef#1{\the\pgfoothis@count}% +}% + +% Yields the object with the given id +% +% #1 = id +% +% Description: +% +% Given an object id, \pgfooobj{<id>} will yield the object having +% this id. + +\def\pgfooobj#1{% + \pgfoo@caller{#1}% +}% + + +% Get the object +% +% #1 = macro to store the object +% +% Description: +% +% This special method allows you to get a new handle to a given +% object. If \obj is an object, you could normally just say +% \let#1=\obj. However, if \obj happens to be \pgfoothis, then you may +% wish to get a handle to the object itself, not to the special macro +% \pgfoothis. In this case you can say \obj.get handle(#1). + +\def\pgfoo@obj(#1){% + \edef#1{\noexpand\pgfoo@caller{\the\pgfoothis@count}}% +}% + + + +% The garbage collector +% +% Description: +% +% This method frees space occupied by unused objects. Garbage are +% objects that have been destroyed because of the end of the scope in +% which they were created. In this case, however, the memory used by +% this object is not immediately reused because the attributes of the +% object are actually stored in global variables. When the garbage +% collector is called, it will set all these global variables to +% \relax, thereby ensuring that no memory is needed for them. + +\def\pgfoogc{% + {% + % We do this in a group... + \pgfoothis@count\pgfoo@objectcount% this is temporary... + \let\pgfoo@next=\pgfoo@dogc% + \pgfoo@next% + }% +}% +\def\pgfoo@dogc{% + \advance\pgfoothis@count by 1\relax% + \expandafter\ifx\csname pgfooX\the\pgfoothis@count @class\endcsname\relax% + \let\pgfoo@next=\relax% + \else% + % Cleanup this object: + % The following is the fast version of \pgfooobj{\the\pgfoo@objectcount}.@pgfoogc: + \let\pgfoo@attribute@op\pgfoo@gc@attribute + \csname pgfooY\csname pgfooX\the\pgfoothis@count @class\endcsname.@pgfoo@process@attributes\endcsname% + \expandafter\global\expandafter\let\csname pgfooX\the\pgfoothis@count @class\endcsname\relax% + \fi% + \pgfoo@next% +}% + + + +% +% +% Object class +% defines method "copy" +% +% + +\pgfooclass{object}{% + \method copy(#1) {% + % create a new object + \edef\pgfoo@temp@classname{\csname pgfooX\the\pgfoothis@count @class\endcsname}% + \expandafter\pgfoo@new@create\expandafter#1\expandafter{\pgfoo@temp@classname}% + \def\pgfoo@copy@attributes##1##2{% + \pgfooget{##1}\pgfoo@attrvalue + \expandafter\let\csname pgfooX\the\pgfoo@objectcount @##1\endcsname\pgfoo@attrvalue + }% + \let\pgfoo@attribute@op\pgfoo@copy@attributes + \csname pgfooY\pgfoo@temp@classname .@pgfoo@process@attributes\endcsname + \aftergroup\pgfoogc% cleanup after group + }% +}% + + +% +% +% Signal class +% +% + +\pgfooclass{signal} +{% + % + % This class implements signals. + % + % After you have created a signal object, you can call + % connect to connect a slot. Then, whenever the emit method is + % called, all connected methods get called. + + % Attribute + \attribute emitter;% + % Collects the objects that should be called. + + % Constructor + \method signal() {}% + + % Connect a slot (method) #2 of object #1 + \method connect(#1,#2) {% + {% + #1.get id(\pgf@tempid)% + % Save in emitter + \pgfooget{emitter}\pgf@temp% + \let\pgfoo@signal@call=\relax% avoid expansion + \edef\pgf@temp{\pgf@temp\pgfoo@signal@call{\pgf@tempid}{#2}}% + \pgfoolet{emitter}\pgf@temp% + }% + }% + + \def\pgfoo@signal@call#1#2{% + \def\pgf@temp{\pgfooobj{#1}.#2}% + \expandafter\pgf@temp\expandafter(\pgfoo@signal@args)% + }% + + % Emit a signal + \method emit(#1) {% + \def\pgfoo@signal@args{#1}% + \pgfoovalueof{emitter} + }% +}% + + + + +\endinput diff --git a/graphics/pgf/base/tex/generic/modules/pgfmoduleparser.code.tex b/graphics/pgf/base/tex/generic/modules/pgfmoduleparser.code.tex new file mode 100644 index 0000000000..ca2a597560 --- /dev/null +++ b/graphics/pgf/base/tex/generic/modules/pgfmoduleparser.code.tex @@ -0,0 +1,697 @@ +% Copyright 2019 by Till Tantau +% Copyright 2019 by Jonathan P. Spratte +% +% This file may be distributed and/or modified +% +% 1. under the LaTeX Project Public License and/or +% 2. under the GNU Public License. +% +% See the file doc/generic/pgf/licenses/LICENSE for more details. + +\ProvidesFileRCS{pgfmoduleparser.code.tex} + +% +% This file defines commands for defining a letter-by-letter parser +% based on a dfa. The idea is inspired by the pgf module parser, but extends it +% and eventually replaced it. +% + +% options +\long\def\pgfparserset#1% + {% + \pgfqkeys{/pgfparser}{#1}% + } +\pgfparserset + {% + ,silent/.is if=\pgfparser@ifname{every parser silent}% + ,status/.is if=\pgfparser@ifname{pgfparser status}% + } + +% logic helpers +\long\def\pgfparser@fi@Bn\fi#1{\fi} +\long\def\pgfparser@fi@Bg\fi#1#2{\fi} +\long\def\pgfparser@fi@BTb\fi#1#2#3{\fi#2} +\long\def\pgfparser@fi@BTc\fi#1#2#3{\fi#3} + +% opt arg parsing +\long\def\pgfparserifmark#1% + {% + \ifx\pgfparser@mark#1% + \pgfparser@fi@BTb + \fi + \pgfutil@secondoftwo + } +\pgfutil@protected\long\def\pgfparser@oarg@oarg#1% + {% + \pgfutil@ifnextchar[{\pgfparser@oarg@oarg@{#1}}{#1{\pgfparser@mark}}% + } +\long\def\pgfparser@oarg@oarg@#1[#2]% + {% + #1{#2}% + } +\pgfutil@protected\long\def\pgfparser@oarg@Oarg#1#2% + {% + \pgfutil@ifnextchar[{\pgfparser@oarg@oarg@{#2}}{#2{#1}}% + } +\pgfutil@protected\long\def\pgfparser@oarg@darg#1#2#3% + {% + \long\def\pgfparser@oarg@darg@##1#1##2#2{##1{##2}}% + \pgfutil@ifnextchar#1{\pgfparser@oarg@darg@{#3}}{#3{\pgfparser@mark}}% + } +\pgfutil@protected\long\def\pgfparser@oarg@Darg#1#2#3#4% + {% + \long\def\pgfparser@oarg@darg@##1#1##2#2{##1{##2}}% + \pgfutil@ifnextchar#1{\pgfparser@oarg@darg@{#4}}{#4{#3}}% + } +\pgfutil@protected\long\def\pgfparser@oarg@targ@#1% + {% + \def\pgfparser@oarg@targ@next{#1}% + \afterassignment\pgfparser@oarg@targ@next + \let\pgfparser@ignored@token=% + } +\pgfutil@protected\long\def\pgfparser@oarg@targ#1#2% + {% + \pgfutil@ifnextchar#1% + {\pgfparser@oarg@targ@{#2{\pgfparser@mark}}}{#2{\pgfparser@stop}}% + } + +% status messages +\pgfutil@protected\def\pgfparser@msg#1% + {% + \immediate\write0{(pgfparser) #1}% + } +\pgfutil@protected\def\pgfparser@status@start#1% + {% + \pgfparser@if{pgfparser status}{\pgfparser@msg{parser '#1' started}}{}% + } +\pgfutil@protected\def\pgfparser@status@state#1% + {% + \pgfparser@if{pgfparser status} + {\pgfparser@msg{|= switched to state '#1'}} + {}% + } +\pgfutil@protected\def\pgfparser@status@rule + {% + \pgfparser@if{pgfparser status} + {\pgfparser@msg{| \space|= rule '\meaning\pgfparsertoken' executed}} + {}% + } +\pgfutil@protected\def\pgfparser@status@rule@all + {% + \pgfparser@if{pgfparser status} + {% + \pgfparser@msg + {| \space|= (all) rule '\meaning\pgfparsertoken' executed}% + } + {}% + } +\pgfutil@protected\def\pgfparser@status@rule@undefined + {% + \pgfparser@if{pgfparser status} + {% + \pgfparser@msg + {| \space|= unknown for '\meaning\pgfparsertoken' executed}% + } + {}% + } +\pgfutil@protected\def\pgfparser@status@rule@undefined@all + {% + \pgfparser@if{pgfparser status} + {% + \pgfparser@msg + {| \space|= (all) unknown for '\meaning\pgfparsertoken' executed}% + } + {}% + } + +% macros for string comparison +\begingroup +\lccode`\P=`\P \lccode`\G=`\G \lccode`\F=`\F +\lccode`\:=`\X \lccode`\;=`\r \lccode`\A=`\- +\lowercase + {% + \endgroup + \def\pgfparser@mark{APGFparse;:mark-}% + \def\pgfparser@stop{APGFparse;:stop-}% + }% +\def\pgfparser@final{final} +\def\pgfparser@noarg{noarg} +\def\pgfparser@blankspace{blank space} + +% counters +\newcount\pgfparserdef@arg@count + +% shortcuts for special names +\def\pgfparser@name#1% + {% + pgfparser #1% + } +\def\pgfparser@rule#1% + {% + \pgfparser@current\space #1\space \meaning\pgfparsertoken + } +\def\pgfparser@type#1% + {% + \pgfparser@rule{#1} type% + } +\def\pgfparser@code#1% + {% + \pgfparser@rule{#1} code% + } +\def\pgfparser@undefined#1% + {% + \pgfparser@current\space #1\space unknown% + } +\def\pgfparser@ifname#1% + {% + pgfparserconditional #1% + } + +% conditionals +\def\pgfparser@if#1% + {% + \pgfutil@ifundefined{if\pgfparser@ifname{#1}} + {% + \pgferror{Undefined conditional '#1'. Falling back to false}% + \iffalse + } + {\csname if\pgfparser@ifname{#1}\endcsname}% + \pgfparser@fi@BTb + \fi + \pgfutil@secondoftwo + } +\def\pgfparser@newif#1% + {% + \csname newif\expandafter\endcsname + \csname if\pgfparser@ifname{#1}\endcsname + } +\pgfparser@newif{every parser silent} +\pgfparser@newif{pgfparserdef abort} +\pgfparser@newif{pgfparser status} + +% pgfparserdef +\pgfutil@protected\def\pgfparserdef#1#2% + {% + \def\pgfparserdef@twoargs{{#1}{#2}}% + \pgfparserdef@a + } +\pgfutil@protected\def\pgfparserdef@a + {% + \futurelet\pgfparserdef@arg\pgfparserdef@b + } +\def\pgfparserdef@b + {% + \ifx\pgfparserdef@arg\pgfutil@sptoken + % will also gobble the \pgfparserdef@c after \fi + \pgfparserdef@gobble@space + \fi + \pgfparserdef@c + } +\pgfutil@protected\def\pgfparserdef@gobble@space\fi#1% + {% + \fi + \afterassignment\pgfparserdef@a + \let\pgfparser@ignored@token= % space after = to get spaces, too + } +\pgfutil@protected\def\pgfparserdef@c#1% + {% + \ifx\pgfparserdef@arg\bgroup + \pgfparser@fi@BTb + \fi + \pgfutil@secondoftwo + {% + \def\pgfparserdef@arg{#1}% + \ifx\pgfparser@blankspace\pgfparserdef@arg + \pgfparser@fi@BTb + \fi + \pgfutil@secondoftwo + {\expandafter\pgfparserdef@d\pgfparserdef@twoargs{blank space \space}} + {\expandafter\pgfparserdef@d\pgfparserdef@twoargs{#1}}% + } + {% + \expandafter\pgfparserdef@d\pgfparserdef@twoargs + {\meaning\pgfparserdef@arg}% + }% + } +\long\def\pgfparserdef@d#1#2#3% + {% + \pgfparser@oarg@Oarg{}{\pgfparserdef@e{#1}{#2}{#3}}% + } +\pgfutil@protected\long\def\pgfparserdef@e#1#2#3#4#5% + {% + \pgfparser@initiate@parser{#1}% + \csname\pgfparser@ifname{pgfparserdef abort}false\endcsname + % check how many arguments are used, also parses whether the argument string + % is valid + \pgfparserdef@arg@count=0 + \pgfparserdef@argcount#4\pgfparser@mark + \ifnum\pgfparserdef@arg@count>9 + \pgferror + {% + Too many arguments for parser rule. A maximum of 9 parameters is + supported.% + }% + \csname\pgfparser@ifname{pgfparserdef abort}true\endcsname + \fi + % define what the code does + \pgfparser@if{pgfparserdef abort}{} + {% + % use a macro as flag that there is a rule for the specified state and + % hold information how to parse it + \ifnum\pgfparserdef@arg@count=0 + \pgfutil@namedef{\pgfparser@name{#1} #2 #3 type}{noarg}% + \pgfutil@namedef{\pgfparser@name{#1} #2 #3 code}% + {#5\pgfparser@getnexttoken}% + \else + \pgfutil@namedef{\pgfparser@name{#1} #2 #3 type}{#4}% + \expandafter\pgfparserdef@argstring@def\the\pgfparserdef@arg@count + \pgfutil@namedef{\pgfparser@name{#1} #2 #3 code\expandafter}% + \pgfparserdef@argstring##1##2##3##4##5##6##7##8##9% + {#5\pgfparser@getnexttoken}% + \fi + }% + } +\def\pgfparserdef@argstring@def#1% + {% + \def\pgfparserdef@argstring##1#1##2##{##1#1}% + } +\def\pgfparserdef@argcount#1% + {% + \ifx\pgfparser@mark#1% + \pgfparser@fi@Bg + \fi + \pgfutil@firstofone + {% + \pgfutil@IfUndefined{pgfparserdef@argcount@#1} + {% + \pgferror{Unknown argument type '#1'. Aborting rule definition}% + \pgfparserdef@argcount@abort + } + {\csname pgfparserdef@argcount@#1\endcsname}% + }% + } +\pgfutil@protected\long\def\pgfparserdef@argcount@abort#1\pgfparser@mark + {% + \csname\pgfparser@ifname{pgfparserdef abort}true\endcsname + } +\def\pgfparserdef@argcount@m + {% + \advance\pgfparserdef@arg@count1 + \pgfparserdef@argcount + } +\let\pgfparserdef@argcount@o\pgfparserdef@argcount@m +\def\pgfparserdef@argcount@O#1% + {% + \advance\pgfparserdef@arg@count1 + \pgfparserdef@argcount + } +\let\pgfparserdef@argcount@r\pgfparserdef@argcount@O +\let\pgfparserdef@argcount@t\pgfparserdef@argcount@O +\def\pgfparserdef@argcount@d#1#2% + {% + \advance\pgfparserdef@arg@count1 + \pgfparserdef@argcount + } +\def\pgfparserdef@argcount@D#1#2#3% + {% + \advance\pgfparserdef@arg@count1 + \pgfparserdef@argcount + } + +\pgfutil@protected\def\pgfparser@initiate@parser#1% + {% + % if that is the first rule for the parser + \pgfutil@IfUndefined{if\pgfparser@ifname{#1 silent}}% + {% + % define space to be a no-op + \pgfutil@namedef + {\pgfparser@name{#1} all blank space \space\space type}{noarg}% + \pgfutil@namedef + {\pgfparser@name{#1} all blank space \space\space code}% + {\pgfparser@getnexttoken}% + % define a new conditional whether the parser silently ignores unknown + % tokens + \pgfparser@newif{#1 silent}% + \pgfparserset + {% + #1/silent/.is if=\pgfparser@ifname{#1 silent}% + }% + } + {}% + } + +% pgfparserlet +\pgfutil@protected\def\pgfparserlet#1#2% + {% + \def\pgfparserlet@parser@a{#1}% + \def\pgfparserlet@state@a{#2}% + \pgfparserlet@a + } +\pgfutil@protected\def\pgfparserlet@a + {% + \futurelet\pgfparserlet@arga\pgfparserlet@b + } +\def\pgfparserlet@b + {% + \ifx\pgfparserlet@arga\pgfutil@sptoken + % will also gobble the \pgfparserlet@c after \fi + \pgfparserlet@gobble@space\pgfparserlet@a + \fi + \pgfparserlet@c + } +\pgfutil@protected\def\pgfparserlet@gobble@space#1\fi#2% + {% + \fi + \afterassignment#1% + \let\pgfparser@ignored@token= % space after = to get spaces, too + } +\pgfutil@protected\def\pgfparserlet@c#1% + {% + \ifx\pgfparserlet@arga\bgroup + \pgfparser@fi@BTb + \fi + \pgfutil@secondoftwo + {% + \def\pgfparserlet@arga{#1}% + \ifx\pgfparser@blankspace\pgfparserlet@arga + \def\pgfparserlet@arga{blank space \space}% + \fi + } + {\edef\pgfparserlet@arga{\meaning\pgfparserlet@arga}}% + \pgfparser@oarg@oarg\pgfparserlet@d + } +\pgfutil@protected\def\pgfparserlet@d#1% + {% + \pgfparserifmark{#1} + {% + \let\pgfparserlet@parser@b\pgfparserlet@parser@a + \let\pgfparserlet@state@b\pgfparserlet@state@a + \pgfparserlet@f + } + {\pgfparser@oarg@oarg{\pgfparserlet@e{#1}}}% + } +\pgfutil@protected\def\pgfparserlet@e#1#2% + {% + \pgfparserifmark{#2} + {% + \let\pgfparserlet@parser@b\pgfparserlet@parser@a + \def\pgfparserlet@state@b{#1}% + } + {% + \def\pgfparserlet@parser@b{#1}% + \def\pgfparserlet@state@b{#2}% + } + \pgfparserlet@f + } +\pgfutil@protected\def\pgfparserlet@f + {% + \futurelet\pgfparserlet@argb\pgfparserlet@g + } +\def\pgfparserlet@g + {% + \ifx\pgfparserlet@argb\pgfutil@sptoken + % will also gobble the \pgfparserlet@h after \fi + \pgfparserlet@gobble@space\pgfparserlet@f + \fi + \pgfparserlet@h + } +\pgfutil@protected\def\pgfparserlet@h#1% + {% + \ifx\pgfparserlet@argb\bgroup + \pgfparser@fi@BTb + \fi + \pgfutil@secondoftwo + {% + \def\pgfparserlet@argb{#1}% + \ifx\pgfparser@blankspace\pgfparserlet@argb + \def\pgfparserlet@argb{blank space \space}% + \fi + } + {\edef\pgfparserlet@argb{\meaning\pgfparserlet@argb}}% + \pgfparserlet@i + \pgfparserlet@parser@a + \pgfparserlet@state@a + \pgfparserlet@arga + \pgfparserlet@parser@b + \pgfparserlet@state@b + \pgfparserlet@argb + } +\def\pgfparserlet@i#1#2#3#4#5#6% + {% + \pgfutil@IfUndefined{\pgfparser@name{#4} #5 #6 type} + {% + \pgferror + {% + There is no rule defined for parser '#4' in state '#5' for '#6'. + Aborting \string\pgfparserlet + }% + } + {% + \pgfparser@initiate@parser{#1}% + \pgfparserlet@let{#1}{#2}{#3}{#4}{#5}{#6}{type}% + \pgfparserlet@let{#1}{#2}{#3}{#4}{#5}{#6}{code}% + }% + } +\def\pgfparserlet@let#1#2#3#4#5#6#7% + {% + \expandafter\let\csname\pgfparser@name{#1} #2 #3 #7\expandafter\endcsname + \csname\pgfparser@name{#4} #5 #6 #7\endcsname + } + +\pgfutil@protected\long\def\pgfparserdefunknown#1#2#3% + {% + \pgfparser@initiate@parser{#1}% + \pgfutil@namedef{\pgfparser@name{#1} #2 unknown}{#3}% + } +\pgfutil@protected\long\def\pgfparserdeffinal#1#2% + {% + \pgfutil@namedef{\pgfparser@name{#1} final action}{#2}% + } +\pgfutil@protected\def\pgfparserswitch#1% + {% + \pgfparser@status@state{#1}% + \def\pgfparserstate{#1}% + } +\pgfutil@protected\def\pgfparserparse#1% + {% + \pgfutil@IfUndefined{if\pgfparser@ifname{#1 silent}} + {\pgferror{No parser named '#1' defined}} + {% + \edef\pgfparser@current{\pgfparser@name{#1}}% + \def\pgfparser@usersname{#1}% + \pgfparser@status@start{#1}% + \pgfparserswitch{initial}% + \pgfparser@getnexttoken + }% + } +% get next token +\pgfutil@protected\def\pgfparser@getnexttoken + {% + \ifx\pgfparserstate\pgfparser@final + \pgfparser@fi@BTb + \fi + \pgfutil@secondoftwo + {% + \pgfutil@IfUndefined{\pgfparser@current\space final action} + {} + {\csname \pgfparser@current\space final action\endcsname}% + } + {\futurelet\pgfparsertoken\pgfparser@getnexttoken@a}% + } +\pgfutil@protected\def\pgfparser@getnexttoken@a + {% + % check the problematic categories based on the meaning of the token + \edef\pgfparser@tokencategory{\pgfparser@extractmeaning\pgfparsertoken}% + \pgfparser@getnexttoken@ifcategory\pgfparser@category@i\bgroup + {% + \pgfparser@getnexttoken@ifcategory\pgfparser@category@ii\egroup + {% + \pgfparser@getnexttoken@ifcategory\pgfparser@category@x{ }% + {% + \pgfparser@getnexttoken@ifcategory\pgfparser@category@vi{####}% + {\pgfparser@getnexttoken@defchar}% + }% + }% + }% + } +\begingroup +\def\pgfparser@extractmeaning#1% + {% + \endgroup + \def\pgfparser@extractmeaning##1% + {\expandafter\pgfparser@extractmeaning@\meaning##1#1#1\pgfparser@mark}% + \def\pgfparser@extractmeaning@##1#1##2#1##3\pgfparser@mark{##1#1##2}% + } +\catcode`\ =12\pgfparser@extractmeaning{ } % ends the group +\edef\pgfparser@category@i{\pgfparser@extractmeaning\bgroup} +\edef\pgfparser@category@ii{\pgfparser@extractmeaning\egroup} +\edef\pgfparser@category@vi{\pgfparser@extractmeaning#} +\edef\pgfparser@category@x{\pgfparser@extractmeaning\pgfutil@sptoken} +\pgfutil@protected\def\pgfparser@getnexttoken@ifcategory#1#2% + {% + \ifx\pgfparser@tokencategory#1% + \def\pgfparserletter{#2}% + \pgfparser@getnexttoken@gobble + \fi + \pgfutil@firstofone + } +\pgfutil@protected\def\pgfparser@getnexttoken@gobble\fi#1#2% + {% + \fi + \afterassignment\pgfparser@getnexttoken@b + \let\pgfparser@ignored@token= % gobble should gobble a space + } +\pgfutil@protected\long\def\pgfparser@getnexttoken@defchar#1% + {% + \def\pgfparserletter{#1}% + \pgfparser@getnexttoken@b + } +\pgfutil@protected\def\pgfparser@getnexttoken@b + {% + \pgfutil@IfUndefined{\pgfparser@type{\pgfparserstate}} + {% + \pgfutil@IfUndefined{\pgfparser@type{all}} + {% + \pgfparser@if{every parser silent}{} + {% + \pgfparser@if{\pgfparser@usersname\space silent}{} + {% + \pgferror + {% + No rule for parser '\pgfparser@usersname' in state + '\pgfparserstate' for '\meaning\pgfparsertoken'. + Ignoring token% + }% + }% + }% + \pgfutil@IfUndefined{\pgfparser@undefined{\pgfparserstate}} + {% + \pgfutil@IfUndefined{\pgfparser@undefined{all}} + {} + {% + \pgfparser@status@rule@undefined@all + \csname\pgfparser@undefined{all}\endcsname + }% + } + {% + \pgfparser@status@rule@undefined + \csname\pgfparser@undefined{\pgfparserstate}\endcsname + }% + \pgfparser@getnexttoken + } + {% + \pgfparser@status@rule@all + \pgfparser@handle{all}% + }% + } + {% + \pgfparser@status@rule + \pgfparser@handle{\pgfparserstate}% + }% + } + +% handle arguments +\pgfutil@protected\def\pgfparser@handle#1% + {% + \expandafter\ifx\csname\pgfparser@type{#1}\endcsname\pgfparser@noarg + \pgfparser@fi@BTb + \fi + \pgfutil@secondoftwo + {\csname\pgfparser@code{#1}\endcsname} + {\pgfparser@handle@args{#1}}% + } +\pgfutil@protected\def\pgfparser@handle@args#1% + {% + \def\pgfparser@stateorall{#1}% + \expandafter\expandafter\expandafter\pgfparser@handle@args@a + \csname\pgfparser@type{#1}\endcsname\pgfparser@mark\pgfparser@stop + } +\def\pgfparser@handle@args@a + {% + \pgfparser@handle@args@b{}% + } +\pgfutil@protected\long\def\pgfparser@handle@args@b#1#2#3\pgfparser@stop + {% + \pgfparserifmark{#2} + {\csname\pgfparser@code{\pgfparser@stateorall}\endcsname#1} + {% + \pgfutil@IfUndefined{pgfparser@handle@args@#2} + {\pgferror{Unknown argument type '#2'}} + {\csname pgfparser@handle@args@#2\endcsname{#1}{#3}}% + }% + } +\long\def\pgfparser@handle@args@c#1#2% + {% + \pgfparserifmark{#2} + {\csname\pgfparser@code{\pgfparser@stateorall}\endcsname#1} + {\pgfparser@handle@args@b{#1}#2\pgfparser@stop}% + } +\long\def\pgfparser@handle@args@m#1#2#3% + {% + \pgfparser@handle@args@c{#1{#3}}{#2}% + } +\pgfutil@protected\long\def\pgfparser@handle@args@o#1#2% + {% + \pgfparser@oarg@oarg{\pgfparser@handle@args@m{#1}{#2}}% + } +\long\def\pgfparser@handle@args@O#1#2% + {% + \pgfparser@handle@args@O@{#1}#2\pgfparser@stop + } +\pgfutil@protected\long\def\pgfparser@handle@args@O@#1#2#3\pgfparser@stop + {% + \pgfparser@oarg@Oarg{#2}{\pgfparser@handle@args@m{#1}{#3}}% + } +\long\def\pgfparser@handle@args@d#1#2% + {% + \pgfparser@handle@args@d@{#1}#2\pgfparser@stop + } +\pgfutil@protected\long\def\pgfparser@handle@args@d@#1#2#3#4\pgfparser@stop + {% + \pgfparser@oarg@darg{#2}{#3}{\pgfparser@handle@args@m{#1}{#4}}% + } +\long\def\pgfparser@handle@args@D#1#2% + {% + \pgfparser@handle@args@D@{#1}#2\pgfparser@stop + } +\pgfutil@protected\long\def\pgfparser@handle@args@D@#1#2#3#4#5\pgfparser@stop + {% + \pgfparser@oarg@Darg{#2}{#3}{#4}{\pgfparser@handle@args@m{#1}{#5}}% + } +\long\def\pgfparser@handle@args@r#1#2% + {% + \pgfparser@handle@args@r@a{#1}#2\pgfparser@stop + } +\pgfutil@protected\long\def\pgfparser@handle@args@r@a#1#2#3\pgfparser@stop + {% + \def\pgfparser@handle@args@r@b##1#2% + {\pgfparser@handle@args@c{#1{##1}}{#3}}% + \pgfparser@handle@args@r@b + } +\long\def\pgfparser@handle@args@t#1#2% + {% + \pgfparser@handle@args@t@{#1}#2\pgfparser@stop + } +\pgfutil@protected\long\def\pgfparser@handle@args@t@#1#2#3\pgfparser@stop + {% + \pgfparser@oarg@targ{#2}{\pgfparser@handle@args@m{#1}{#3}}% + } + +\pgfutil@protected\long\def\pgfparserreinsert#1% + {% + \ifx\pgfparser@getnexttoken#1% + \pgfparser@fi@BTb + \fi + \@secondoftwo + {\expandafter\pgfparser@getnexttoken\pgfparserletter} + {% + \pgferror + {% + Misuse of \string\pgfparserreinsert\space detected. You should only + use it at the the end of an action code% + }% + #1% + }% + } + +\endinput diff --git a/graphics/pgf/base/tex/generic/modules/pgfmoduleplot.code.tex b/graphics/pgf/base/tex/generic/modules/pgfmoduleplot.code.tex new file mode 100644 index 0000000000..0afea0ae34 --- /dev/null +++ b/graphics/pgf/base/tex/generic/modules/pgfmoduleplot.code.tex @@ -0,0 +1,561 @@ +% Copyright 2019 by Till Tantau +% +% This file may be distributed and/or modified +% +% 1. under the LaTeX Project Public License and/or +% 2. under the GNU Public License. +% +% See the file doc/generic/pgf/licenses/LICENSE for more details. + +\ProvidesFileRCS{pgfmoduleplot.code.tex} + + +% PGF's plotting interface works as follows: +% +% In order to plot something, two things need to be done. First, you +% need to provide the coordinates (obviously) of the points that +% should be plotted. The coordinates are given via a long stream of +% commands. These commands are \pgfplotstreamstart, which is +% given exactly once at the beginning, the commands +% \pgfplotstreampoint, \pgfplotstreampointoutlier, +% \pgfplotstreampointundefined, \pgfplotstreamnewdataset, of which +% there are numerous in the middle, the special \pgfplotstreamspecial, +% of which there may be numerous in the middle, and \pgfplotstreamend, +% which must be given at the end. Between these commands arbitrary +% other commands may be given. Here is an example: +% +% ... +% \pgfplotstreamstart +% \pgfplotstreampoint{\pgfpointxy{0}{0}} +% \pgfplotstreampoint{\pgfpointxy{1}{1}} +% \pgfplotstreamnewdataset +% \pgfplotstreampoint{\pgfpointxy{2}{4}} +% \relax +% \pgfplotstreampointoutlier{\pgfpointxy{3}{9}} +% \pgfplotstreamspecial{some handler-dependent special stuff} +% \pgfplotstreamend +% +% By themselves, the \pgfplotstreamxxxx commands do not do anything by +% default. Rather, to ``use'' such a stream, you must first install a +% stream handler. For example, the ``lineto'' handler will simply +% basically every \pgfplotstreampoint into a \pgfpathlineto. +% +% One special things is the handling of "jumps" in a stream. For +% instance, when a lineto handler encounters an "outlier" or a new +% data set, the current line should end and a new subpath should start +% (unless configured otherwise). For this, the special jump handler is +% important. +% +% Example: +% +% \pgfpathmoveto{\pgfpointorigin} +% +% \pgfplothandlerlineto +% \pgfplotstreamstart +% \pgfplotstreampoint{\pgfpointxy{0}{0}} +% \pgfplotstreampoint{\pgfpointxy{1}{1}} +% \pgfplotstreampoint{\pgfpointxy{2}{4}} +% \relax +% \pgfplotstreampoint{\pgfpointxy{3}{9}} +% \pgfplotstreamend + + +% The stream commands actually call their ``internal'' versions, which +% are set by the handlers: +\def\pgfplotstreamstart{\pgf@plotstreamstart}% +\def\pgfplotstreampoint#1{\gdef\pgfplotlastpoint{#1}\pgf@plotstreampoint{#1}}% +\def\pgfplotstreampointoutlier#1{\pgfplot@outliers{}{\pgf@plotstreamjump}{\pgfplotstreampoint{#1}}}% +\def\pgfplotstreampointundefined{\pgfplot@outliers{}{\pgf@plotstreamjump}{}}% +\def\pgfplotstreamnewdataset{\pgfplot@outliers{}{\pgf@plotstreamjump}{}}% +\def\pgfplotstreamspecial{\pgf@plotstreamspecial}% +\def\pgfplotstreamend{\pgf@plotstreamend}% + +\def\pgfplot@ignorer#1#2#3{#1}% +\def\pgfplot@jumper#1#2#3{#2}% +\def\pgfplot@plotter#1#2#3{#3}% + +\let\pgfplot@outliers\pgfplot@jumper +\let\pgfplot@undefined\pgfplot@jumper +\let\pgfplot@newdata\pgfplot@jumper + +\pgfset{ + handle outlier points in plots/.is choice, + handle outlier points in plots/ignore/.code=\let\pgfplot@outliers\pgfplot@ignorer, + handle outlier points in plots/jump/.code=\let\pgfplot@outliers\pgfplot@jumper, + handle outlier points in plots/plot/.code=\let\pgfplot@outliers\pgfplot@plotter, + handle undefined points in plots/.is choice, + handle undefined points in plots/ignore/.code=\let\pgfplot@undefined\pgfplot@ignorer, + handle undefined points in plots/jump/.code=\let\pgfplot@undefined\pgfplot@jumper, + handle new data sets in plots/.is choice, + handle new data sets in plots/ignore/.code=\let\pgfplot@newdata\pgfplot@ignorer, + handle new data sets in plots/jump/.code=\let\pgfplot@newdata\pgfplot@jumper, +}% + + +% Declares a new plot handler +% +% #1 = macro that should install the stream handler subsequently +% #2 = parameter list of said macro. +% #3 = keys as described below +% +% Description: +% +% When you declare a new plot handler, you provide, through keys, +% different "actions" that should be taken when the different stream +% commands are used: +% +% start = some code to be executed when \pgfplotstreamstart is called +% for this plothandler +% end = same for the end of the stream +% point = code to be executed for a point of the stream +% jump = code to be executed for a "jump" in the stream +% special = code to be executed for \pgfplotstreamspecial +% start macro = here and for the following keys, the to-be-executed +% code is stored in the given macro +% end macro = see above +% jump macro = see above +% point macro = see above +% special macro = see above +% +% Example: +% +% \pgfdeclareplothandler{\ultrasimplehandler}{}{ +% point=\pgfpathlineto{##1} +% } + +\def\pgfdeclareplothandler#1#2#3{% + \def#1#2{% + \pgfkeys{% + /pgf/plots/@handler options/.cd, + start=\relax, + end macro=\relax, + point macro=\pgfutil@gobble, + jump macro=\relax, + special macro=\pgfutil@gobble,% + #3% + }% + }% +}% + +\pgfkeys{% + /pgf/plots/@handler options/.cd, + start/.code=% + \gdef\pgf@plotstreamstart{% + \global\pgf@plot@startedfalse% + \global\let\pgf@plotstreamend\pgf@plotstreamend@init% + \global\let\pgf@plotstreampoint\pgf@plotstreampoint@init% + \global\let\pgf@plotstreamjump\pgf@plotstreamjump@init% + \global\let\pgf@plotstreamspecial\pgf@plotstreamspecial@init% + #1% + },% + start macro/.code=% + \gdef\pgf@plotstreamstart{% + \global\pgf@plot@startedfalse% + \global\let\pgf@plotstreamend\pgf@plotstreamend@init% + \global\let\pgf@plotstreampoint\pgf@plotstreampoint@init% + \global\let\pgf@plotstreamjump\pgf@plotstreamjump@init% + \global\let\pgf@plotstreamspecial\pgf@plotstreamspecial@init% + #1% + },% + end/.code=\gdef\pgf@plotstreamend@init{#1}, + end macro/.code=\global\let\pgf@plotstreamend@init#1, + point/.code=\gdef\pgf@plotstreampoint@init##1{#1}, + point macro/.code=\global\let\pgf@plotstreampoint@init#1, + jump/.code=\gdef\pgf@plotstreamjump@init{#1}, + jump macro/.code=\global\let\pgf@plotstreamjump@init#1, + special/.code=\gdef\pgf@plotstreamspecial@init##1{#1}, + special macro/.code=\global\let\pgf@plotstreamspecial@init#1, +}% + +\newif\ifpgf@plot@started + + + + +% Sets the action taken for the first point of a plot to a lineto. +% +% Description: +% +% For certain handlers it makes sense either the start a plot by +% moving to the first point of the plot or to do a lineto to that +% first point. Using this command this action can be set to a lineto. +% +% Example: +% +% \pgfsetlinetofirstplotpoint + +\def\pgfsetlinetofirstplotpoint{\let\pgf@plot@first@action=\pgfpathlineto}% + + +% Sets the action taken for the first point of a plot to a moveto. +% +% Example: +% +% \pgfsetmovetofirstplotpoint + +\def\pgfsetmovetofirstplotpoint{\let\pgf@plot@first@action=\pgfpathmoveto}% + +\let\pgf@plot@first@action=\pgfpathmoveto + + + + +% +% Handlers +% + + +% This handler converts each plot stream command into a lineto +% command, except for the first, which is converted to the action that +% has previously been specified using \pgfsetlinetofirstplotpoint or +% \pgfsetmovetofirstplotpoint. +% +% Example: +% +% \pgfplothandlerlineto +% \pgfplotxyfile{mytable} + +\pgfdeclareplothandler{\pgfplothandlerlineto}{}{ + point macro=\pgf@plot@line@handler, + jump=\global\let\pgf@plotstreampoint\pgf@plot@line@handler@move% +}% + +\def\pgf@plot@line@handler#1{% + \pgf@plot@first@action{#1}% + \global\let\pgf@plotstreampoint=\pgfpathlineto% +}% + +\def\pgf@plot@line@handler@move#1{% + \pgfpathmoveto{#1}% + \global\let\pgf@plotstreampoint=\pgfpathlineto% +}% + + + +% This handler turns creates a series of lineto commands, with the +% last command being a closepath, resulting in a closed path. If a +% jump is encountered, the current subpath is closed and a new subpath +% is started. +% +% Example: +% +% \pgfplothandlerpolygon +% \pgfplotxyfile{mytable} + +\pgfdeclareplothandler{\pgfplothandlerpolygon}{}{% + point macro=\pgf@plot@line@handler@close, + jump macro=\pgf@plot@next@close@and@moveto, + end macro=\pgf@plot@polygon@stop +}% + +\def\pgf@plot@line@handler@close#1{% + \pgfpathmoveto{#1}% + \global\pgf@plot@startedtrue% + \global\let\pgf@plotstreampoint=\pgfpathlineto% +}% + +\def\pgf@plot@polygon@stop{% + \ifpgf@plot@started% + \pgfpathclose% + \fi% + \global\pgf@plot@startedfalse% +}% + +\def\pgf@plot@next@close@and@moveto{% + \ifpgf@plot@started% + \pgfpathclose% + \fi% + \global\let\pgf@plotstreampoint\pgf@plot@line@handler@close% +}% + + +% More handlers are defined in pgflibraryplothandlers + + + + + +% This handler discards the plot. +% +% Example: +% +% \pgfplothandlerdiscard +% \pgfplotxyfile{mytable} + +\pgfdeclareplothandler{\pgfplothandlerdiscard}{}{}% + + + +% This handler records each plot stream command to a macro. This is +% useful if plot commands are difficult to generate and need to be +% ``recycled'' later on. +% +% Example: +% +% \pgfplothandlerrecord{\myplot} +% \pgfplotxyfile{mytable} % stored in \myplot now +% \pgfplothandlerline +% \myplot +% \pgftransformxshift{1cm} +% \myplot + +\pgfdeclareplothandler{\pgfplothandlerrecord}{#1}{% + start=\gdef#1{\pgfplotstreamstart}, + point=\expandafter\gdef\expandafter#1\expandafter{#1\pgfplotstreampoint{##1}}, + jump=\expandafter\gdef\expandafter#1\expandafter{#1\pgf@plotstreamjump}, + special=\expandafter\gdef\expandafter#1\expandafter{#1\pgfplotstreamspecial{##1}}, + end=\expandafter\gdef\expandafter#1\expandafter{#1\pgfplotstreamend}, +}% + + + +% Read a plot stream from a file and plot it. +% +% #1 = file from which to read things +% +% File format: +% +% Each line of the file should begin with two numbers separated by a +% space. Such a line with number #1 and #2 is converted to a +% \pgfplotstreampoint{\pgfpointxy{#1}{#2}}. Extra characters following +% on the line are ignored. +% +% Lines starting with ``%'' and ``#'' are ignored. +% +% Example: +% +% \pgfplotxyfile{tableformgnuplot.dat} + +\def\pgfplotxyfile#1{% + \begingroup% + \def\b@pgfplotsxyfile@scanning@for@first{1}% + \pgfplotstreamstart% + \openin\r@pgf@reada=#1 + \ifeof\r@pgf@reada + \pgfwarning{Plot data file `#1' not found.} + \else + \catcode`\#=14 + \catcode`\^^M=5 + \pgf@readxyfile% + \fi + \pgfplotstreamend% + \endgroup% +}% + +\let\pgf@savedpar=\par +\def\pgf@partext{\par}% +\def\pgf@readxyfile{% + \pgfutil@read\r@pgf@reada to \pgf@temp% + \let\par=\pgf@savedpar% + \edef\pgf@temp{\pgf@temp}% + \ifx\pgf@temp\pgfutil@empty% + \if1\b@pgfplotsxyfile@scanning@for@first + \else + \ifeof\r@pgf@reada\else\pgfplotstreamnewdataset\fi% + \fi + \else\ifx\pgf@temp\pgf@partext% + \if1\b@pgfplotsxyfile@scanning@for@first + \else + \ifeof\r@pgf@reada\else\pgfplotstreamnewdataset\fi% + \fi + \else% + \expandafter\pgf@parsexyline\pgf@temp\pgf@stop% + \fi\fi% + \ifeof\r@pgf@reada\else\expandafter\pgf@readxyfile\fi% +}% + +\def\pgf@parsexyline#1 #2 #3\pgf@stop{% + \def\b@pgfplotsxyfile@scanning@for@first{0}% + \edef\pgf@xyline@flag@val{#3}% + \ifx\pgf@xyline@flag@val\pgf@xyline@flag@undef% + \pgfplotstreampointundefined% + \else\ifx\pgf@xyline@flag@val\pgf@xyline@flag@out% + \pgfplotstreampointoutlier{\pgfpointxy{#1}{#2}}% + \else% + \pgfplotstreampoint{\pgfpointxy{#1}{#2}}% + \fi\fi% +}% + +\edef\pgf@xyline@flag@out{o\space}% +\edef\pgf@xyline@flag@undef{u\space}% + + + + +% Read a plot stream from a file and plot it. +% +% #1 = file from which to read things +% +% File format: +% +% Like xy, except that each line contains three numbers, which are +% converted to xyz coordinates. +% +% Example: +% +% \pgfplotxyfile{tableformgnuplot.dat} + +\def\pgfplotxyzfile#1{% + \begingroup% + \pgfplotstreamstart% + \openin\r@pgf@reada=#1 + \ifeof\r@pgf@reada + \pgfwarning{Plot data file `#1' not found.} + \else + \catcode`\#=14 + \catcode`\^^M=5 + \pgf@readxyzfile% + \fi + \pgfplotstreamend% + \endgroup% +}% + +\def\pgf@readxyzfile{% + \pgfutil@read\r@pgf@reada to \pgf@temp% + \ifx\pgf@temp\pgfutil@empty% + \if1\b@pgfplotsxyfile@scanning@for@first + \else + \ifeof\r@pgf@reada\else\pgfplotstreamnewdataset\fi% + \fi + \else\ifx\pgf@temp\pgf@partext% + \if1\b@pgfplotsxyfile@scanning@for@first + \else + \ifeof\r@pgf@reada\else\pgfplotstreamnewdataset\fi% + \fi + \else% + \expandafter\pgf@parsexyzline\pgf@temp\pgf@stop% + \fi\fi% + \ifeof\r@pgf@reada\else\expandafter\pgf@readxyzfile\fi% +}% + +\def\pgf@parsexyzline#1 #2 #3 #4\pgf@stop{% + \def\b@pgfplotsxyfile@scanning@for@first{0}% + \edef\pgf@xyline@flag@val{#4}% + \ifx\pgf@xyline@flag@val\pgf@xyline@flag@undef% + \pgfplotstreampointundefined% + \else\ifx\pgf@xyline@flag@val\pgf@xyline@flag@out% + \pgfplotstreampointoutlier{\pgfpointxyz{#1}{#2}{#3}}% + \else% + \pgfplotstreampoint{\pgfpointxyz{#1}{#2}{#3}}% + \fi\fi% +}% + + + + +% Render a function using gnuplot. +% +% #1 = filename prefix for .gnuplot and .table files (optional, +% default is \jobname) +% #2 = gnuplot function text +% +% Description: +% +% This command will write a file called #1.gnuplot that sets up +% some gnuplot commands to write output to a file called +% #1.table. Then it calls gnuplot (using the \write18 mechanism) +% to execute the file. Then it reads #2.table using \pgfplotxyfile. +% +% Example: +% +% \pgfplothandlerlineto +% \pgfplotgnuplot[\jobname]{plot [x=0:5] x*sin(x)} + +{% + \catcode`\%=12 + \catcode`\"=12 + \xdef\pgf@gnuplot@head{set table \noexpand\pgf@plottablefile@quoted; set format "%.5f"} +}% + +\let\pgf@plotwrite=\w@pgf@writea +\newif\ifpgf@resample@plot + +\pgfkeys{% + /pgf/plot/gnuplot call/.initial={gnuplot}}% + +\def\pgfplotgnuplot{\pgfutil@ifnextchar[{\pgf@plotgnuplot}{\pgf@plotgnuplot[\jobname]}}%}% +\def\pgf@plotgnuplot[#1]#2{% + \pgf@resample@plottrue% + \pgfutilpreparefilename{#1.gnuplot}% + \let\pgf@plotgnuplotfile=\pgfretval + \pgfutilpreparefilename{#1.table}% + \let\pgf@plottablefile=\pgfretval + \let\pgf@plottablefile@quoted=\pgfretvalquoted + % Check, whether it is up-to-date + \openin\pgfutil@inputcheck=\pgf@plotgnuplotfile\relax + \ifeof\pgfutil@inputcheck% + \else% + \pgfutil@read\pgfutil@inputcheck to\pgf@temp% ignored + \pgfutil@read\pgfutil@inputcheck to\pgf@plot@line% + \closein\pgfutil@inputcheck + \edef\pgf@plot@code{#2\space}% + \ifx\pgf@plot@code\pgf@plot@line% + \openin\pgfutil@inputcheck=\pgfretval\relax + \ifeof\pgfutil@inputcheck% + \else% + \closein\pgfutil@inputcheck + \pgf@resample@plotfalse% + \fi% + \fi% + \fi + \ifpgf@resample@plot% + \immediate\openout\pgf@plotwrite=\pgf@plotgnuplotfile\relax + \immediate\pgfutil@write\pgf@plotwrite{\pgf@gnuplot@head}% + \immediate\pgfutil@write\pgf@plotwrite{#2}% + \immediate\closeout\pgf@plotwrite% + \pgfutil@shellescape{% + \pgfkeysvalueof{/pgf/plot/gnuplot call} \pgf@plotgnuplotfile}% + \fi% +% \let\pgf@savedparsexyline=\pgf@parsexyline% +% \let\pgf@parsexyline=\pgf@parsegnuplotxyline% + \pgfplotxyfile{\pgf@plottablefile}% +% \let\pgf@parsexyline=\pgf@savedparsexyline% +}% + +% \def\pgf@parsegnuplotxyline#1 #2 #3\pgf@stop{% +% \edef\pgf@xyline@flag@val{#3}% +% \edef\pgf@xyline@flag@out{o\space}% +% \edef\pgf@xyline@flag@undef{u\space}% +% \ifx\pgf@xyline@flag@val\pgf@xyline@flag@undef% +% \pgfplotstreampointundefined% +% \else\ifx\pgf@xyline@flag@val\pgf@xyline@flag@out% +% \pgfplotstreampointoutlier{\pgfpointxy{#1}{#2}}% +% \else% +% \pgfplotstreampoint{\pgfpointxy{#1}{#2}}% +% \fi\fi% +% }% + +% This producer handler plots a function using pgf's mathematical engine. +% +% #1 = variable +% #2 = domain for the variable +% #3 = point, typically defined in terms of the value of the variable +% +% Description: +% +% This producer will iterate the variable #1 over all variables in #2 +% (using the \foreach statement). For each value, a plot coordinate +% #3 is created. +% +% Note that this command is pretty slow. +% +% Example: +% +% \pgfplothandlerlineto +% \pgfplotfunction{\x}{0,0.1,...,3.141}{\pgfpointxy{\x}{sin(\x)}} + +\def\pgfplotfunction#1#2#3{% + \pgfplotstreamstart% + \foreach#1in{#2}% + {% + \pgf@process{#3}% + \edef\pgf@marshal{\noexpand\pgfplotstreampoint{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}}% + \pgf@marshal% + } + \pgfplotstreamend% +}% + + + + +\endinput diff --git a/graphics/pgf/base/tex/generic/modules/pgfmoduleshapes.code.tex b/graphics/pgf/base/tex/generic/modules/pgfmoduleshapes.code.tex new file mode 100644 index 0000000000..6be10d1464 --- /dev/null +++ b/graphics/pgf/base/tex/generic/modules/pgfmoduleshapes.code.tex @@ -0,0 +1,1334 @@ +% Copyright 2019 by Till Tantau +% +% This file may be distributed and/or modified +% +% 1. under the LaTeX Project Public License and/or +% 2. under the GNU Public License. +% +% See the file doc/generic/pgf/licenses/LICENSE for more details. + +\ProvidesFileRCS{pgfmoduleshapes.code.tex} + + + +\newbox\pgfnodeparttextbox + + +% Creates a node +% +% #1 = shape type +% #2 = anchor +% #3 = name for later reference +% #4 = path usage command +% +% Description: +% +% This command draws a shape that has previously been defined using +% \pgfdeclareshape. The shape is shifted such that the anchor is at +% the origin. In order to transform the whole shape, you should setup +% pgf's transformation matrix prior to calling this command. +% +% When this command is called, the shape-specific TeX boxes of the +% node must have been setup correctly. +% +% Example: +% +% \pgftransformshift{\pgfpoint{1cm}{1cm}} +% \pgfmultipartnode{rectangle}{center}{hellonode}{\pgfusepath{stroke}} + +\def\pgfmultipartnode#1#2#3#4{% + \pgfutil@ifundefined{pgf@sh@s@#1}% + {\pgferror{Unknown shape ``#1''}}% + {% + {% + \ifpgflatenodepositioning% + \pgfsys@beginscope% + \fi% + \pgf@outer@adjust@hook% + \let\pgf@sh@savedmacros=\pgfutil@empty% MW + \let\pgf@sh@savedpoints=\pgfutil@empty% + \def\pgf@sm@shape@name{#1}% CJ % TT added prefix! + \csname pgf@sh@s@#1\endcsname% + \pgf@sh@savedpoints% + \pgf@sh@savedmacros% MW + \pgftransformshift{% + \pgf@sh@reanchor{#1}{#2}% + \pgf@x=-\pgf@x% + \pgf@y=-\pgf@y% + }% + \expandafter\pgfsavepgf@process\csname pgf@sh@sa@#3\endcsname{% + \pgf@sh@reanchor{#1}{#2}% FIXME : this is double work! + }% + % Save the saved points and the transformation matrix + \edef\pgf@node@name{#3}% + \ifpgflatenodepositioning% + \pgf@shapes@late@pos@begin% + \fi% + \ifx\pgf@node@name\pgfutil@empty% + \else% + \expandafter\xdef\csname pgf@sh@ns@\pgf@node@name\endcsname{#1}% + \edef\pgf@sh@@temp{\noexpand\gdef\expandafter\noexpand\csname pgf@sh@np@\pgf@node@name\endcsname}% + \expandafter\pgf@sh@@temp\expandafter{\pgf@sh@savedpoints}% + \edef\pgf@sh@@temp{\noexpand\gdef\expandafter\noexpand\csname pgf@sh@ma@\pgf@node@name\endcsname}% MW + \expandafter\pgf@sh@@temp\expandafter{\pgf@sh@savedmacros}% MW + \pgfgettransform\pgf@temp% + \expandafter\xdef\csname pgf@sh@nt@\pgf@node@name\endcsname{\pgf@temp}% + \expandafter\xdef\csname pgf@sh@pi@\pgf@node@name\endcsname{\pgfpictureid}% + \fi% + \pgfutil@ifundefined{pgf@sh@bbg@#1}{} + {{\pgfusetype{.behind background}\pgfidscope\pgfscope\csname pgf@sh@bbg@#1\endcsname\endpgfscope\endpgfidscope}}% + \pgfutil@ifundefined{pgf@sh@bg@#1}{% + \global\let\pgfpositionnodelaterpath\pgfutil@empty% + }% + {\pgfpushtype% + \pgfusetype{.background}\csname pgf@sh@bg@#1\endcsname% + \ifpgflatenodepositioning% + \pgfsyssoftpath@getcurrentpath\pgfpositionnodelaterpath% + \pgfprocessround{\pgfpositionnodelaterpath}{\pgfpositionnodelaterpath}% + \global\let\pgfpositionnodelaterpath\pgfpositionnodelaterpath% + \fi% + #4\pgfpoptype}% + \pgfutil@ifundefined{pgf@sh@fbg@#1}{} + {{\pgfusetype{.before background}\pgfidscope\pgfscope\csname pgf@sh@fbg@#1\endcsname\endpgfscope\endpgfidscope}}% + {% + \expandafter\pgfutil@for\expandafter\pgf@shape@com\expandafter:\expandafter=\csname pgf@sh@boxes@#1\endcsname\do{% + {% + \pgfusetype{.\pgf@shape@com}% + \pgftransformshift{\pgf@sh@reanchor{#1}{\pgf@shape@com}}% + \pgfapproximatenonlineartransformation% + \expandafter\pgfqboxsynced\expandafter{\csname pgfnodepart\pgf@shape@com box\endcsname}% + }% + }% + }% + \pgfutil@ifundefined{pgf@sh@bfg@#1}{}% + {{\pgfusetype{.behind foreground}\pgfidscope\pgfscope\csname pgf@sh@bfg@#1\endcsname\endpgfscope\endpgfidscope}}% + \pgfutil@ifundefined{pgf@sh@fg@#1}{}% + {\pgfpushtype\pgfusetype{.foreground}\csname pgf@sh@fg@#1\endcsname#4\pgfpoptype}% + \pgfutil@ifundefined{pgf@sh@ffg@#1}{}% + {{\pgfusetype{.before foreground}\pgfidscope\pgfscope\csname pgf@sh@ffg@#1\endcsname\endpgfscope\endpgfidscope}}% + \ifpgflatenodepositioning% + \pgf@shapes@late@pos@end% + \pgfsys@endscope% + \else% + \expandafter\pgf@nodecallback\expandafter{\pgf@node@name}% + \fi% + }% + }% +}% +\let\pgf@outer@adjust@hook\relax + +\def\pgf@shapes@late@pos@begin{% + % Rename node + \edef\pgf@node@name{not yet positionedPGFINTERNAL\pgf@node@name}% + % Interrupt bounding box! + \pgfinterruptboundingbox% + % Put everything in our box: + \pgf@relevantforpicturesizetrue% + \setbox\pgfpositionnodelaterbox=\hbox% + \bgroup% + \pgfsys@beginscope% +}% + +\def\pgf@shapes@late@pos@end{% + \pgfsys@endscope% + \egroup% Close box + \ifdim\pgf@picminx>\pgf@picmaxx\relax% happens for empty nodes + \def\pgfpositionnodelaterminx{0.0pt}% + \let\pgfpositionnodelaterminy\pgfpositionnodelaterminx% + \let\pgfpositionnodelatermaxx\pgfpositionnodelaterminx% + \let\pgfpositionnodelatermaxy\pgfpositionnodelaterminx% + \else% + \edef\pgfpositionnodelaterminx{\the\pgf@picminx}% + \edef\pgfpositionnodelaterminy{\the\pgf@picminy}% + \edef\pgfpositionnodelatermaxx{\the\pgf@picmaxx}% + \edef\pgfpositionnodelatermaxy{\the\pgf@picmaxy}% + \fi% + \let\pgfpositionnodelatername=\pgf@node@name% + \pgf@positionnodelater@macro% + \endpgfinterruptboundingbox% +}% + +\def\pgf@shift@node#1#2{% + % This internal command shifts the recorded coordinates for node #1 + % by the vector #2. It is used to + % correct the position of the node if the recorded coordinate + % happens to be wrong + {% + \pgfsettransform{\csname pgf@sh@nt@#1\endcsname}% + \pgf@process{#2}% + \advance\pgf@pt@x by\pgf@x% + \advance\pgf@pt@y by\pgf@y% + \pgfgettransform{\pgf@temp}% + \expandafter\xdef\csname pgf@sh@nt@#1\endcsname{\pgf@temp}% + }% +}% + + + +% Creates a node +% +% #1 = shape type +% #2 = anchor +% #3 = normal TeX text +% #4 = name for later reference +% #5 = path usage command +% +% Description: +% +% This command draws a shape that has previously been defined using +% \pgfdeclareshape. The shape is shifted such that the anchor is at +% the origin. In order to transform the whole shape, you should setup +% pgf's transformation matrix prior to calling this command. +% +% Example: +% +% \pgftransformshift{\pgfpoint{1cm}{1cm}} +% \pgfnode{rectangle}{center}{Hello}{hellonode}{\pgfusepath{stroke}} + +\def\pgfnode#1#2#3#4#5{% + {% + \setbox\pgfnodeparttextbox=\hbox% + {% + \pgfinterruptpicture% + {#3}% + \endpgfinterruptpicture% + }% + \pgfmultipartnode{#1}{#2}{#4}{#5}% + } +}% + + + +% Create a node that is positioned later +% +% #1 = macro to be called after the next node has been created +% +% Description: +% +% You can use this command to create a node that is "positioned +% later". The idea is the following: Normally, when a node is created +% using a command like \pgfnode, the node is immediately inserted into +% the current picture. In particular, you have no chance to change the +% position of a created node after it has been create. +% +% Using \pgfpositionnodelater, you can change the default +% behaviour. This command is not a replacement for \pgfnode, but is +% called before such a command and will cause the following to happen: +% The node is created, but not inserted into the picture. Instead, it +% is stored in the box \pgfpositionnodelaterbox. Furthermore, the node +% is not relevant for the picture's bounding box, but a bounding box +% for the node is computed and stored in the macros +% \pgfpositionnodelaterboundboxminx to +% \pgfpositionnodelaterboundboxmaxy. Finally, the +% macro #1 is called with the following macros setup: +% +% \pgfpositionnodelaterbox +% A box register (box 0 by default) that stores the node's paths and +% texts. You should move the contents of this box to a box of your +% choice. +% +% \pgfpositionnodelatername +% The name of the just-created-node. This name will be the +% originally "desired" name of the box plus the prefix "not yet +% positioned boxPGFINTERNAL". +% +% \pgfpositionnodelaterminx +% \pgfpositionnodelatermaxx +% \pgfpositionnodelaterminy +% \pgfpositionnodelatermaxy +% These four macros store the bounding box as dimensions that are +% guarenteed to end with "pt". +% +% By setting #1 to \relax (which is the default), you can switch off +% the whole mechanism +% +% Once a late node has been created, you can add arbitrary code in the +% same picture. Then, at some later point, you call +% \pgfpositionnodenow to finally position the node at a given position. +% +% When \pgfpositionnodenow is called, the macros mentioned above +% should have the exact same values they had when #1 is called. + +\def\pgfpositionnodelater#1{% + \let\pgf@positionnodelater@macro=#1% + \ifx\pgf@positionnodelater@macro\relax% + \pgflatenodepositioningfalse% + \else% + \pgflatenodepositioningtrue% + \fi% +}% +\newif\ifpgflatenodepositioning +\pgfpositionnodelater{\relax}% +\def\pgfpositionnodelaterbox{0}% + + + + +% Position a node that has been scheduled for later insertion +% +% #1 = position in the picture, where the original anchor should go, +% now +% +% Description: +% +% This command works in concert with \pgfpositionnodelater, see the +% description there. When \pgfpositionnodenow is called, the macros +% \pgfpositionnodelaterbox and so on should be set to the values they +% had when the macro given to \pgfpositionnodelater was called. Then, +% the box described by these macros will be inserted into the picture +% at the position given #1. More precisely, the original node will +% have been positioned such that the anchor given upon invocation is +% at (0,0) and the node is now shifted by #1 before being inserted +% into the actual picture. + +\def\pgfpositionnodenow#1{% + \pgfinterruptpath% + {% + \pgfpointtransformed{#1}% + \edef\pgf@temp@shift{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}} + \pgftransformreset% + \pgftransformshift{\pgf@temp@shift}% + \pgfsys@pictureboxsynced\pgfpositionnodelaterbox% + \pgf@shift@node{\pgfpositionnodelatername}{\pgf@temp@shift}% + % Bounding box update... + \pgfpointtransformed{\pgfqpoint{\pgfpositionnodelaterminx}{\pgfpositionnodelaterminy}}% + \pgf@protocolsizes{\pgf@x}{\pgf@y} + \pgfpointtransformed{\pgfqpoint{\pgfpositionnodelatermaxx}{\pgfpositionnodelatermaxy}}% + \pgf@protocolsizes{\pgf@x}{\pgf@y} + % Naming and callbacks + \expandafter\pgfpositionnodenow@rename\pgfpositionnodelatername\relax% + }% + % Late setup% + {% + \csname pgf@lns@\pgfpositionnodelatername\endcsname% + \expandafter\global\expandafter\let\csname pgf@lns@\pgfpositionnodelatername\endcsname\relax% + }% + \endpgfinterruptpath% +}% + +\def\pgfpositionnodenow@rename not yet positionedPGFINTERNAL#1\relax{% + \pgfnoderename{#1}{not yet positionedPGFINTERNAL#1} +}% + + + +% "Fake" a node +% +% #1 = node name +% +% Description: +% +% This command will setup things so that it "looks like" a node named +% #1 has been created, but, in reality, no node is created. The idea +% is that a node created in this way can be referenced by systems +% using the late positioning, which will only create the node later +% on. +% +\def\pgffakenode#1{% + \edef\pgf@temp{\ifpgflatenodepositioning not yet positionedPGFINTERNAL\fi#1}% + \expandafter\gdef\csname pgf@sh@ns@\pgf@temp\endcsname{coordinate}% + \expandafter\gdef\csname pgf@sh@np@\pgf@temp\endcsname{\def\centerpoint{\pgfpointorigin}}% + \expandafter\gdef\csname pgf@sh@nt@\pgf@temp\endcsname{{1}{0}{0}{1}{0pt}{0pt}}% + \expandafter\global\expandafter\let\csname pgf@sh@ma@\pgf@temp\endcsname\pgfutil@empty% + \expandafter\xdef\csname pgf@sh@pi@\pgf@temp\endcsname{\pgfpictureid}% +}% + + + +% Specify code that should be executed after a node has been created +% +% #1 = node name +% #2 = later code +% +% Description: +% +% This macro allows you to provide some code #2 that will be executed +% right after a node has been positioned using \pgfpositionnodenow. If +% the node has already been positioned (if the late positioning is not +% switched on), nothing happens. + +\def\pgfnodepostsetupcode#1#2{% + \ifpgflatenodepositioning% + \expandafter\ifx\csname pgf@lns@not yet positionedPGFINTERNAL#1\endcsname\relax% + \expandafter\gdef\csname pgf@lns@not yet positionedPGFINTERNAL#1\endcsname{#2}% + \else% + \expandafter\pgfutil@g@addto@macro\csname pgf@lns@not yet positionedPGFINTERNAL#1\endcsname{#2}% + \fi% + \fi% +}% + + + +% Alias a node +% +% #1 = new node name +% #2 = existing node name +% +% Description: +% +% This command allows you to subsequently access the node #2 using the +% name #1. +% +% Example: +% +% \pgfnode{rectangle}{center}{Hallo}{hellonode}{} +% \pgfnodealias{mynode}{hellonode} + +\def\pgfnodealias#1#2{% + \pgf@node@gnamelet{pgf@sh@ns@#1}{pgf@sh@ns@#2}% + \pgf@node@gnamelet{pgf@sh@np@#1}{pgf@sh@np@#2}% + \pgf@node@gnamelet{pgf@sh@nt@#1}{pgf@sh@nt@#2}% + \pgf@node@gnamelet{pgf@sh@pi@#1}{pgf@sh@pi@#2}% + \pgf@node@gnamelet{pgf@sh@ma@#1}{pgf@sh@ma@#2}% + \pgf@nodecallback{#1}% +}% +\def\pgf@node@gnamelet#1#2{% + \expandafter\let\expandafter\pgf@temp\csname #2\endcsname% + \expandafter\global\expandafter\let\csname #1\endcsname=\pgf@temp% +}% + +% Rename a node +% +% #1 = new name +% #2 = old name +% +% Description: +% +% Renames a node. This is like an alias, only the memory used for the +% old name is freed. + +\def\pgfnoderename#1#2{% + \pgfnodealias{#1}{#2}% + \expandafter\global\expandafter\let\csname pgf@sh@ns@#2\endcsname\relax + \expandafter\global\expandafter\let\csname pgf@sh@np@#2\endcsname\relax + \expandafter\global\expandafter\let\csname pgf@sh@nt@#2\endcsname\relax + \expandafter\global\expandafter\let\csname pgf@sh@pi@#2\endcsname\relax + \expandafter\global\expandafter\let\csname pgf@sh@ma@#2\endcsname\relax +}% + + + + +% Quickly create a coordinate node +% +% #1 = name +% #2 = coordinate +% +% Description: +% +% Creates a node of shape coordinate. The following have the same +% effect, only the second is much faster: +% +% { \pgftransformshift{\pgfpoint{1cm}{2cm} \pgfnode{coordinate}{center}{}{foo}{} } +% \pgfcoordinate{foo}{\pgfpoint{1cm}{2cm}} + +\def\pgfcoordinate#1#2{% + \edef\pgf@temp{#1}% + \ifx\pgf@temp\pgfutil@empty% do nothing + \else% + \ifpgflatenodepositioning% + {\pgftransformshift{#2}\pgfnode{coordinate}{center}{}{#1}{}}% + \else% + \pgf@process{\pgfpointtransformed{#2}}% + \expandafter\gdef\csname pgf@sh@ns@#1\endcsname{coordinate}% + \expandafter\xdef\csname pgf@sh@np@#1\endcsname{% + \noexpand\def\noexpand\centerpoint{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}% + } + \expandafter\gdef\csname pgf@sh@nt@#1\endcsname{{1}{0}{0}{1}{0pt}{0pt}}% + \expandafter\global\expandafter\let\csname pgf@sh@ma@#1\endcsname\pgfutil@empty% + \expandafter\xdef\csname pgf@sh@pi@#1\endcsname{\pgfpictureid}% + \fi% + \fi% +}% + +\def\pgf@sh@redeferredanchor#1#2{% CJ + \expandafter\gdef\csname pgf@anchor@\pgf@sm@shape@name @#1\endcsname{#2}}% CJ + + + + + + +\def\pgf@sh@resavedanchor#1#2{% + \pgf@process{#2}% + \edef\pgf@sh@marshal{% + \noexpand\pgfutil@g@addto@macro\noexpand\pgf@sh@savedpoints{% + \noexpand\def\noexpand#1{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}% + }}% + \pgf@sh@marshal% +}% + + +\def\pgf@sh@resaveddimen#1#2{% + {#2\global\pgf@x=\pgf@x}% + \edef\pgf@sh@marshal{% + \noexpand\pgfutil@g@addto@macro\noexpand\pgf@sh@savedpoints{% + \noexpand\def\noexpand#1{\the\pgf@x}% + }}% + \pgf@sh@marshal% +}% + +\def\pgf@sh@resavedmacro#1#2{% + \let#1\pgfutil@empty% + \def\addtosavedmacro##1{% + \expandafter\def\expandafter\pgf@sh@addtomacro@temp\expandafter{#1\noexpand\def\noexpand##1{##1}}% + {\expandafter\pgfutil@toks@\expandafter{\pgf@sh@addtomacro@temp}\expandafter}% + \expandafter\def\expandafter#1\expandafter{\the\pgfutil@toks@}% + }% + #2\relax% + \edef\pgf@sh@marshal{% + \noexpand\pgfutil@g@addto@macro\noexpand\pgf@sh@savedmacros{% + \noexpand\def\noexpand#1{#1}% + }}% + \pgf@sh@marshal% +}% + +% Invoke an anchor +\def\pgf@sh@reanchor#1#2{% + \pgfutil@ifundefined{pgf@anchor@#1@#2}% + {% + \pgfutil@ifundefined{pgf@anchor@generic@#2}{% + \pgfmathsetcounter{pgf@counta}{#2}% + \csname pgf@anchor@#1@border\endcsname{\pgfqpointpolar{\the\c@pgf@counta}{1pt}}% + }{% + \csname pgf@anchor@generic@#2\endcsname{#1}% + }% + }% + {\csname pgf@anchor@#1@#2\endcsname}% +}% + +% Defines a generic anchor, i.e. one which gets the associated shape +% as first argument. +% +% #1: the anchor name. +% #2: the code of the anchor. It may depend upon '##1', the shape's +% name. +% +% The anchor will be defined locally in the current TeX scope. +% +% If the anchor will be referenced later by \pgfpointanchor, the macro \pgfreferencednodename +% can be used to query the referenced node's name. +% This macro is not defined during node creation. +\def\pgfdeclaregenericanchor#1#2{% + \expandafter\def\csname pgf@anchor@generic@#1\endcsname##1{#2}% +}% + + +% Retrieves an anchor of a shape +% +% #1 = name +% #2 = anchor or a number (corresponding to a degree) +% +% Description: +% +% This command yields a point at the anchor of the given named shape. +% +% Example: +% +% \pgfshape{rectangle}{center}{Hello}{hellonode}{\pgfusepath[stroke]} +% \pgfpathmoveto{\pgfpointanchor{hellonode}{center}} + +\def\pgfpointanchor#1#2{% + % Ok, check whether #1 is known! + \pgfutil@ifundefined{pgf@sh@ns@#1} + {\pgfutil@ifundefined{pgf@sh@ns@not yet positionedPGFINTERNAL#1}{% + \pgferror{No shape named `#1' is known}}{ + % else, fail silently + }% + \pgfpointorigin}% + {% + \pgf@process{% + \edef\pgfreferencednodename{#1}% for use inside of anchors. + % MW install special macros + \csname pgf@sh@ma@#1\endcsname% MW + % install special coordinates + \csname pgf@sh@np@#1\endcsname + \pgf@sh@reanchor{\csname pgf@sh@ns@#1\endcsname}{#2}% + {% + \pgfsettransform{\csname pgf@sh@nt@#1\endcsname}% + \pgf@pos@transform@glob% + }% + % Add inter picture transformation + \pgf@shape@interpictureshift{#1}% + % Undo current transformation + \pgftransforminvert% + \pgf@pos@transform@glob% + }% + }% +}% + +\def\pgf@shape@interpictureshift#1{% + \expandafter\ifx\csname pgf@sh@pi@#1\endcsname\pgfpictureid% + \else% + \pgfsys@getposition{\csname pgf@sh@pi@#1\endcsname}\pgf@shape@original@pos% + \pgfsys@getposition{\pgfpictureid}\pgf@shape@current@pos% + \pgf@xa=\pgf@x% + \pgf@ya=\pgf@y% + \pgf@process{\pgfpointorigin\pgf@shape@current@pos}% + \advance\pgf@xa by-\pgf@x% + \advance\pgf@ya by-\pgf@y% + \pgf@process{\pgfpointorigin\pgf@shape@original@pos}% + \advance\pgf@xa by\pgf@x% + \advance\pgf@ya by\pgf@y% + \pgf@x=\pgf@xa% + \pgf@y=\pgf@ya% + \fi% +}% + + + +% Retrieves a border point of a shape +% +% #1 = name +% #2 = another point +% +% Description: +% +% This command yields a point on the border of the shape that is on a +% straight line from the center to the point #2. +% +% Example: +% +% \pgfshape{rectangle}{center}{Hello}{hellonode}{\pgfusepath[stroke]} +% \pgfpathmoveto{\pgfpointshapeborder{hellonode}{\pgfpoint{2cm}{1cm}} +% \pgfpathlineto{\pgfpoint{2cm}{1cm}} +% \pgfusepath{stroke} + +\def\pgfpointshapeborder#1#2{% + % Ok, check whether #1 is known! + \pgfutil@ifundefined{pgf@sh@ns@#1} + {\pgferror{No shape named `#1' is known}% + \pgfpointorigin}% + {% + \pgf@process{% + \edef\pgfreferencednodename{#1}% for use inside of anchors. + % MW install special macros + \csname pgf@sh@ma@#1\endcsname% MW + % install special coordinates + \csname pgf@sh@np@#1\endcsname% + {% + \pgf@process{\pgfpointtransformed{#2}}% + \pgfsettransform{\csname pgf@sh@nt@#1\endcsname}% + \pgftransforminvert% + \pgf@pos@transform@glob% + \pgf@xa=\pgf@x% + \pgf@ya=\pgf@y% + \pgf@process{\csname pgf@anchor@\csname pgf@sh@ns@#1\endcsname @center\endcsname}% + \pgf@process{\pgf@shape@interpictureshift{#1}}%% + \advance\pgf@xa by-\pgf@x% + \advance\pgf@ya by-\pgf@y% + % Now (\pgf@xa, \pgf@ya) is a coord in shape's coord system, and + % relative to the anchor "center". If (xa, ya) is almost identical to + % the node center, which is determined by sqrt(xa^2+ya^2) < 0.02pt, + % just return the node center. + % + % \pgf@xb = 10*\pgf@xa if -1pt<\pgf@xa<1pt else 10pt + % - "10*\pgf@xa": reduce ruonding errors when \pgf@xa is small + % - "-1pt<\pgf@xa<1pt": guard against error "Dimension too large" + \pgf@xb=10pt + \ifdim\pgf@xa<1pt\ifdim\pgf@xa>-1pt + \pgf@xb=10\pgf@xa + \fi\fi + % \pgf@yb = 10*\pgf@ya if -1pt<\pgf@ya<1pt else 10pt + \pgf@yb=10pt + \ifdim\pgf@ya<1pt\ifdim\pgf@ya>-1pt + \pgf@yb=10\pgf@ya + \fi\fi + % if xb^2+yb^2 < 0.04pt, which means sqrt(xa^2+ya^2) < 0.02pt, + % return node center + \ifdim\dimexpr\expandafter\Pgf@geT\the\pgf@xb\pgf@xb + +\expandafter\Pgf@geT\the\pgf@yb\pgf@yb\relax<0.04pt + \expandafter\pgfutil@firstoftwo + \else + \expandafter\pgfutil@secondoftwo + \fi + {% return coord of "center" + \pgfwarning + {Returning node center instead of a point on node border. Did you + specify a point identical to the center of node + ``\pgfreferencednodename''?}% + \pgf@sh@reanchor{\csname pgf@sh@ns@#1\endcsname}{center}% + } + {% this calls the corresponding \anchorborder in shape declaration + \csname pgf@anchor@\csname pgf@sh@ns@#1\endcsname @border\endcsname + {\pgfqpoint{\pgf@xa}{\pgf@ya}}% + }% + \pgfsettransform{\csname pgf@sh@nt@#1\endcsname}% + \pgf@pos@transform@glob% + \global\pgf@x=\pgf@x% + \global\pgf@y=\pgf@y% + }% + % Add inter picture transformation + \pgf@shape@interpictureshift{#1}% + % Undo current transformation + \pgftransforminvert% + \pgf@pos@transform@glob% + }% + }% +}% + + +% Retrieve the list of node parts of a shape +% +% #1 = a macro +% #2 = a shape name +% +% Description: +% +% Stores the list of node parts of a shape #2 in the macro #1. +% +% Example: +% +% \pgfgetnodeparts\mylist{circle split} +% +% This will set \mylist\ to "text,lower" + +\def\pgfgetnodeparts#1#2{% + \expandafter\let\expandafter#1\csname pgf@sh@boxes@#2\endcsname% +}% + + +% Declares a shape type +% +% #1 = shape type +% #2 = shape specification +% +% Description: +% +% This command declares a new shape type. The shape specification +% should contain the following commands: +% +% - \savedanchor +% This command takes two arguments: a name and some code. The point +% specified by the code (the \pgf@x and \pgf@y values resulting from +% executing the code, to be precise), will be stored under the given +% name. Whenever, later on, an anchor is invoked these stored points +% will be available. The \saveddimen works like \savedanchor, only a +% single dimension is stored (taken from \pgf@x). +% When a savedanchor is calculated, the height/width/depth of the +% boxes mentioned in \nodeparts can be used. +% +% - \anchor +% Specifies an anchor. This command takes two arguments: an anchor +% name and some code for computing the anchor. When the anchor +% computation starts, all shape points and shape dimension will be +% setup. +% +% - \anchorborder +% This command should compute a point on the border of the +% shape. When the code is called, \pgf@x and \pgf@y will have been +% preinitialised with a direction. +% +% - \behindbackgroundpath +% Some things to be draw behind everything. +% +% - \backgroundpath +% A path that is drawn behind the main box. How this path is used +% depends on how \pgfshape is called. +% +% - \beforebackgroundpath +% Some things to be draw before the background path, but behind the +% main box. +% +% - \behindforegroundpath +% Some things to be draw on top of the main box. +% +% - \foregroundpath +% A path that is drawn on top of all of the above. How this path is +% used depends on how \pgfshape is called. +% +% - \beforeforegroundpath +% Some things to be draw before everything. +% +% - \nodeparts +% List of node parts that make up the shape. For each entry XYZ in +% the list there must be a box named \pgfnodepartXYZbox (possibly +% ``redirected'' to some different box using \let). Each such box +% will be placed at the anchor XYZ. +% By default, \nodeparts is just {text}. Thus, there will be one +% node part box named \pgfnodeparttextbox and this node will be +% placed at the anchor text. +% +% +% Example: +% +% See the definition of rectangle, below. + +\long\def\pgfdeclareshape#1#2{% + {% + \def\pgf@sm@shape@name{#1}% + \let\savedanchor=\pgf@sh@savedanchor + \let\saveddimen=\pgf@sh@saveddimen + \let\savedmacro=\pgf@sh@savedmacro% MW + \let\deferredanchor=\pgf@sh@deferredanchor% CJ + \let\anchor=\pgf@sh@anchor + \let\anchorborder=\pgf@sh@anchorborder + \let\behindbackgroundpath=\pgf@sh@behindbgpath + \let\backgroundpath=\pgf@sh@bgpath + \let\beforebackgroundpath=\pgf@sh@beforebgpath + \let\behindforegroundpath=\pgf@sh@behindfgpath + \let\foregroundpath=\pgf@sh@fgpath + \let\beforeforegroundpath=\pgf@sh@beforefgpath + \let\nodeparts=\pgf@sh@boxes + \let\inheritsavedanchors=\pgf@sh@inheritsavedanchors + \let\inheritanchor=\pgf@sh@inheritanchor + \let\inheritanchorborder=\pgf@sh@inheritanchorborder + \let\inheritbehindbackgroundpath=\pgf@sh@inheritbehindbgpath + \let\inheritbackgroundpath=\pgf@sh@inheritbgpath + \let\inheritbeforebackgroundpath=\pgf@sh@inheritbeforebgpath + \let\inheritbehindforegroundpath=\pgf@sh@inheritbehindfgpath + \let\inheritforegroundpath=\pgf@sh@inheritfgpath + \let\inheritbeforeforegroundpath=\pgf@sh@inheritbeforefgpath + \let\inheritnodeparts=\pgf@sh@inheritboxes + \anchorborder{\csname pgf@anchor@#1@center\endcsname}% + \anchor{text}{\pgfpointorigin}% + \nodeparts{text}% + \expandafter\global\expandafter\let\csname pgf@sh@s@\pgf@sm@shape@name\endcsname=\pgfutil@empty% + #2% + }% +}% + +\def\pgf@sh@savedanchor#1#2{% + \expandafter\pgfutil@g@addto@macro\csname pgf@sh@s@\pgf@sm@shape@name\endcsname{\pgf@sh@resavedanchor{#1}{#2}}}% +\def\pgf@sh@saveddimen#1#2{% + \expandafter\pgfutil@g@addto@macro\csname pgf@sh@s@\pgf@sm@shape@name\endcsname{\pgf@sh@resaveddimen{#1}{#2}}}% + \def\pgf@sh@savedmacro#1#2{% MW + \expandafter\pgfutil@g@addto@macro\csname pgf@sh@s@\pgf@sm@shape@name\endcsname{\pgf@sh@resavedmacro{#1}{#2}}}% MW +\def\pgf@sh@anchor#1#2{\expandafter\gdef\csname pgf@anchor@\pgf@sm@shape@name @#1\endcsname{#2}}% +\def\pgf@sh@deferredanchor#1#2{% CJ + \expandafter\pgfutil@g@addto@macro + \csname pgf@sh@s@\pgf@sm@shape@name\endcsname{\pgf@sh@redeferredanchor{#1}{#2}}}% CJ +\def\pgf@sh@anchorborder#1{\expandafter\gdef\csname pgf@anchor@\pgf@sm@shape@name @border\endcsname##1{\pgf@process{##1}#1}}% +\long\def\pgf@sh@behindbgpath#1{\expandafter\gdef\csname pgf@sh@bbg@\pgf@sm@shape@name\endcsname{#1}}% +\long\def\pgf@sh@bgpath#1{\expandafter\gdef\csname pgf@sh@bg@\pgf@sm@shape@name\endcsname{#1}}% +\long\def\pgf@sh@beforebgpath#1{\expandafter\gdef\csname pgf@sh@fbg@\pgf@sm@shape@name\endcsname{#1}}% +\long\def\pgf@sh@behindfgpath#1{\expandafter\gdef\csname pgf@sh@bfg@\pgf@sm@shape@name\endcsname{#1}}% +\long\def\pgf@sh@fgpath#1{\expandafter\gdef\csname pgf@sh@fg@\pgf@sm@shape@name\endcsname{#1}}% +\long\def\pgf@sh@beforefgpath#1{\expandafter\gdef\csname pgf@sh@ffg@\pgf@sm@shape@name\endcsname{#1}}% +\def\pgf@sh@boxes#1{\expandafter\gdef\csname pgf@sh@boxes@\pgf@sm@shape@name\endcsname{#1}}% + +\def\pgf@sh@inheritsavedanchors[from=#1]{% + \expandafter\pgfutil@g@addto@macro\csname pgf@sh@s@\pgf@sm@shape@name\endcsname{\csname pgf@sh@s@#1\endcsname}}% + +\def\pgf@sh@inheritanchor[from=#1]#2{% + \edef\pgf@marshal{\global\let\expandafter\noexpand\csname + pgf@anchor@\pgf@sm@shape@name @#2\endcsname=\expandafter\noexpand\csname + pgf@anchor@#1@#2\endcsname}% + \pgf@marshal% +}% +\def\pgf@sh@inheritanchorborder[from=#1]{% + \edef\pgf@marshal{\global\let\expandafter\noexpand\csname + pgf@anchor@\pgf@sm@shape@name @border\endcsname=\expandafter\noexpand\csname + pgf@anchor@#1@border\endcsname}% + \pgf@marshal% +}% +\def\pgf@sh@inheritor#1#2{% + \edef\pgf@marshal{\global\let\expandafter\noexpand\csname + pgf@sh@#1@\pgf@sm@shape@name\endcsname=\expandafter\noexpand\csname + pgf@sh@#1@#2\endcsname}% + \pgf@marshal% +}% +\def\pgf@sh@inheritbehindbgpath[from=#1]{\pgf@sh@inheritor{bbg}{#1}}% +\def\pgf@sh@inheritbgpath[from=#1]{\pgf@sh@inheritor{bg}{#1}}% +\def\pgf@sh@inheritbeforebgpath[from=#1]{\pgf@sh@inheritor{fbg}{#1}}% +\def\pgf@sh@inheritbehindfgpath[from=#1]{\pgf@sh@inheritor{bfg}{#1}}% +\def\pgf@sh@inheritfgpath[from=#1]{\pgf@sh@inheritor{fg}{#1}}% +\def\pgf@sh@inheritbeforefgpath[from=#1]{\pgf@sh@inheritor{ffg}{#1}}% +\def\pgf@sh@inheritboxes[from=#1]{\pgf@sh@inheritor{boxes}{#1}}% + + + +% +% Coordinate. This shape is special: It is only used to reference a +% point for later usage. Nothing is drawn, all anchors are identical +% to the center. +% + +\pgfdeclareshape{coordinate} +{% + \savedanchor\centerpoint{% + % x + \pgf@x=.5\wd\pgfnodeparttextbox% + % y + \pgf@y=.5\ht\pgfnodeparttextbox% + \advance\pgf@y by -.5\dp\pgfnodeparttextbox% + }% + + \anchor{center}{\centerpoint}% + \anchor{north}{\centerpoint}% + \anchor{north west}{\centerpoint}% + \anchor{north east}{\centerpoint}% + \anchor{west}{\centerpoint}% + \anchor{east}{\centerpoint}% + \anchor{mid}{\centerpoint}% + \anchor{mid west}{\centerpoint}% + \anchor{mid east}{\centerpoint}% + \anchor{base}{\centerpoint}% + \anchor{base west}{\centerpoint}% + \anchor{base east}{\centerpoint}% + \anchor{south}{\centerpoint}% + \anchor{south west}{\centerpoint}% + \anchor{south east}{\centerpoint}% + \anchorborder{\centerpoint}% + + \nodeparts{}% no text +}% + + +% Value keys for shapes: +% +% /pgf/inner xsep : recommended inner x separation +% /pgf/inner ysep : recommended inner y separation +% /pgf/outer xsep : recommended outer x separation +% /pgf/outer ysep : recommended outer y separation +% /pgf/minimum width : recommended minimum width +% /pgf/minimum height : recommended minimum height + +\pgfset{ + inner xsep/.initial =.3333em, + inner ysep/.initial =.3333em, + inner sep/.style ={/pgf/inner xsep={#1},/pgf/inner ysep={#1}}, + outer xsep/.initial =.5\pgflinewidth, + outer ysep/.initial =.5\pgflinewidth, + outer sep/.code =\pgf@handle@outer@sep{#1}, + minimum width/.initial =1pt, + minimum height/.initial =1pt, + minimum size/.style ={/pgf/minimum width={#1},/pgf/minimum height={#1}}, +}% + + + +\def\pgf@handle@outer@sep#1{% + \def\pgf@temp{#1}% + \ifx\pgf@temp\pgf@auto@text% + \def\pgf@outer@adjust@hook{% + \pgftransformationadjustments% + \pgfkeyssetvalue{/pgf/outer xsep}{.5\pgflinewidth*\pgfhorizontaltransformationadjustment}% + \pgfkeyssetvalue{/pgf/outer ysep}{.5\pgflinewidth*\pgfverticaltransformationadjustment}% + \pgf@outer@auto@adjust@hook% + }% + \else% + \pgfkeyssetvalue{/pgf/outer xsep}{#1}% + \pgfkeyssetvalue{/pgf/outer ysep}{#1}% + \fi% +}% +\def\pgf@auto@text{auto}% + +\let\pgf@outer@auto@adjust@hook\relax + + +% Keys for rotating the shape border. +% (may not be supported by all shapes) +% +% /pgf/shape border uses incircle : Calculate the shape border using the incircle +% around the node contents (+inner sep). +% +% /pgf/shape border rotate : Angle of independent border rotation. + +\newif\ifpgfshapeborderusesincircle +\pgfkeys{/pgf/shape border uses incircle/.is if=pgfshapeborderusesincircle}% +\pgfkeys{/pgf/shape border rotate/.initial=0}% + +% +% Rectangle +% + +\pgfdeclareshape{rectangle} +{% + \savedanchor\northeast{% + % Calculate x + % + % First, is width < minimum width? + \pgf@x=\the\wd\pgfnodeparttextbox% + \pgfmathsetlength\pgf@xc{\pgfkeysvalueof{/pgf/inner xsep}}% + \advance\pgf@x by 2\pgf@xc% + \pgfmathsetlength\pgf@xb{\pgfkeysvalueof{/pgf/minimum width}}% + \ifdim\pgf@x<\pgf@xb% + % yes, too small. Enlarge... + \pgf@x=\pgf@xb% + \fi% + % Now, calculate right border: .5\wd\pgfnodeparttextbox + .5 \pgf@x + outer sep + \pgf@x=.5\pgf@x% + \advance\pgf@x by.5\wd\pgfnodeparttextbox% + \pgfmathsetlength\pgf@xa{\pgfkeysvalueof{/pgf/outer xsep}}% + \advance\pgf@x by\pgf@xa% + % Calculate y + % + % First, is height+depth < minimum height? + \pgf@y=\ht\pgfnodeparttextbox% + \advance\pgf@y by\dp\pgfnodeparttextbox% + \pgfmathsetlength\pgf@yc{\pgfkeysvalueof{/pgf/inner ysep}}% + \advance\pgf@y by 2\pgf@yc% + \pgfmathsetlength\pgf@yb{\pgfkeysvalueof{/pgf/minimum height}}% + \ifdim\pgf@y<\pgf@yb% + % yes, too small. Enlarge... + \pgf@y=\pgf@yb% + \fi% + % Now, calculate upper border: .5\ht-.5\dp + .5 \pgf@y + outer sep + \pgf@y=.5\pgf@y% + \advance\pgf@y by-.5\dp\pgfnodeparttextbox% + \advance\pgf@y by.5\ht\pgfnodeparttextbox% + \pgfmathsetlength\pgf@ya{\pgfkeysvalueof{/pgf/outer ysep}}% + \advance\pgf@y by\pgf@ya% + }% + + \savedanchor\southwest{% + % Calculate x + % + % First, is width < minimum width? + \pgf@x=\wd\pgfnodeparttextbox% + \pgfmathsetlength\pgf@xc{\pgfkeysvalueof{/pgf/inner xsep}}% + \advance\pgf@x by 2\pgf@xc% + \pgfmathsetlength\pgf@xb{\pgfkeysvalueof{/pgf/minimum width}}% + \ifdim\pgf@x<\pgf@xb% + % yes, too small. Enlarge... + \pgf@x=\pgf@xb% + \fi% + % Now, calculate left border: .5\wd\pgfnodeparttextbox - .5 \pgf@x - outer sep + \pgf@x=-.5\pgf@x% + \advance\pgf@x by.5\wd\pgfnodeparttextbox% + \pgfmathsetlength\pgf@xa{\pgfkeysvalueof{/pgf/outer xsep}}% + \advance\pgf@x by-\pgf@xa% + % Calculate y + % + % First, is height+depth < minimum height? + \pgf@y=\ht\pgfnodeparttextbox% + \advance\pgf@y by\dp\pgfnodeparttextbox% + \pgfmathsetlength\pgf@yc{\pgfkeysvalueof{/pgf/inner ysep}}% + \advance\pgf@y by 2\pgf@yc% + \pgfmathsetlength\pgf@yb{\pgfkeysvalueof{/pgf/minimum height}}% + \ifdim\pgf@y<\pgf@yb% + % yes, too small. Enlarge... + \pgf@y=\pgf@yb% + \fi% + % Now, calculate upper border: .5\ht-.5\dp - .5 \pgf@y - outer sep + \pgf@y=-.5\pgf@y% + \advance\pgf@y by-.5\dp\pgfnodeparttextbox% + \advance\pgf@y by.5\ht\pgfnodeparttextbox% + \pgfmathsetlength\pgf@ya{\pgfkeysvalueof{/pgf/outer ysep}}% + \advance\pgf@y by-\pgf@ya% + }% + + % + % Anchors + % + \anchor{center}{ + \pgf@process{\northeast}% + \pgf@xa=.5\pgf@x% + \pgf@ya=.5\pgf@y% + \pgf@process{\southwest}% + \pgf@x=.5\pgf@x% + \pgf@y=.5\pgf@y% + \advance\pgf@x by \pgf@xa% + \advance\pgf@y by \pgf@ya% + }% + \anchor{mid}{\pgf@anchor@rectangle@center\pgfmathsetlength\pgf@y{.5ex}}% + \anchor{base}{\pgf@anchor@rectangle@center\pgf@y=0pt}% + \anchor{north}{ + \pgf@process{\southwest}% + \pgf@xa=.5\pgf@x% + \pgf@process{\northeast}% + \pgf@x=.5\pgf@x% + \advance\pgf@x by \pgf@xa% + }% + \anchor{south}{ + \pgf@process{\northeast}% + \pgf@xa=.5\pgf@x% + \pgf@process{\southwest}% + \pgf@x=.5\pgf@x% + \advance\pgf@x by \pgf@xa% + }% + \anchor{west}{ + \pgf@process{\northeast}% + \pgf@ya=.5\pgf@y% + \pgf@process{\southwest}% + \pgf@y=.5\pgf@y% + \advance\pgf@y by \pgf@ya% + }% + \anchor{mid west}{\southwest\pgfmathsetlength\pgf@y{.5ex}}% + \anchor{base west}{\southwest\pgf@y=0pt}% + \anchor{north west}{ + \southwest + \pgf@xa=\pgf@x + \northeast + \pgf@x=\pgf@xa}% + \anchor{south west}{\southwest}% + \anchor{east}{% + \pgf@process{\southwest}% + \pgf@ya=.5\pgf@y% + \pgf@process{\northeast}% + \pgf@y=.5\pgf@y% + \advance\pgf@y by \pgf@ya% + }% + \anchor{mid east}{\northeast\pgfmathsetlength\pgf@y{.5ex}}% + \anchor{base east}{\northeast\pgf@y=0pt}% + \anchor{north east}{\northeast}% + \anchor{south east}{ + \northeast + \pgf@xa=\pgf@x + \southwest + \pgf@x=\pgf@xa + }% + \anchorborder{% + \pgf@xb=\pgf@x% xb/yb is target + \pgf@yb=\pgf@y% + \southwest% + \pgf@xa=\pgf@x% xa/ya is se + \pgf@ya=\pgf@y% + \northeast% + \advance\pgf@x by-\pgf@xa% + \advance\pgf@y by-\pgf@ya% + \pgf@xc=.5\pgf@x% x/y is half width/height + \pgf@yc=.5\pgf@y% + \advance\pgf@xa by\pgf@xc% xa/ya becomes center + \advance\pgf@ya by\pgf@yc% + \edef\pgf@marshal{% + \noexpand\pgfpointborderrectangle + {\noexpand\pgfqpoint{\the\pgf@xb}{\the\pgf@yb}} + {\noexpand\pgfqpoint{\the\pgf@xc}{\the\pgf@yc}}% + }% + \pgf@process{\pgf@marshal}% + \advance\pgf@x by\pgf@xa% + \advance\pgf@y by\pgf@ya% + }% + + % + % Background path + % + \backgroundpath{ + \pgfpathrectanglecorners + {\pgfpointadd{\southwest}{\pgfpoint{\pgfkeysvalueof{/pgf/outer xsep}}{\pgfkeysvalueof{/pgf/outer ysep}}}} + {\pgfpointadd{\northeast}{\pgfpointscale{-1}{\pgfpoint{\pgfkeysvalueof{/pgf/outer xsep}}{\pgfkeysvalueof{/pgf/outer ysep}}}}} + }% +}% + + +% Special current path start coordinate: +\expandafter\def\csname pgf@sh@ns@current subpath start\endcsname{coordinate}% +\expandafter\def\csname pgf@sh@np@current subpath start\endcsname{% + \def\centerpoint{\expandafter\pgfqpoint\pgfsyssoftpath@lastmoveto}% +}% +\expandafter\def\csname pgf@sh@nt@current subpath start\endcsname{{1}{0}{0}{1}{0pt}{0pt}}% +\expandafter\def\csname pgf@sh@pi@current subpath start\endcsname{\pgfpictureid}% + + + +% Special current bounding box rectangle: +\expandafter\def\csname pgf@sh@ns@current bounding box\endcsname{rectangle}% +\expandafter\def\csname pgf@sh@np@current bounding box\endcsname{% + \def\southwest{\pgfqpoint{\pgf@picminx}{\pgf@picminy}}% + \def\northeast{\pgfqpoint{\pgf@picmaxx}{\pgf@picmaxy}}% +}% +\expandafter\def\csname pgf@sh@nt@current bounding box\endcsname{{1}{0}{0}{1}{0pt}{0pt}}% +\expandafter\def\csname pgf@sh@pi@current bounding box\endcsname{\pgfpictureid}% + + +% Special current path bounding box rectangle: +\expandafter\def\csname pgf@sh@ns@current path bounding box\endcsname{rectangle}% +\expandafter\def\csname pgf@sh@np@current path bounding box\endcsname{% + \def\southwest{\pgfqpoint{\pgf@pathminx}{\pgf@pathminy}}% + \def\northeast{\pgfqpoint{\pgf@pathmaxx}{\pgf@pathmaxy}}% +}% +\expandafter\def\csname pgf@sh@nt@current path bounding box\endcsname{{1}{0}{0}{1}{0pt}{0pt}}% +\expandafter\def\csname pgf@sh@pi@current path bounding box\endcsname{\pgfpictureid}% + + +% Special current page bounding box rectangle: +\expandafter\def\csname pgf@sh@ns@current page\endcsname{rectangle}% +\expandafter\def\csname pgf@sh@np@current page\endcsname{% + \def\southwest{\pgfpointorigin}% + \def\northeast{\pgfpoint{\pgfsys@thepagewidth}{\pgfsys@thepageheight}}% +}% +\expandafter\def\csname pgf@sh@nt@current page\endcsname{{1}{0}{0}{1}{0pt}{0pt}}% +\expandafter\def\csname pgf@sh@pi@current page\endcsname{pgfpageorigin}% + + +% Special scope bounding box rectangle: +\pgfkeys{/pgf/local bounding box/.code={% + \expandafter\gdef\csname pgf@sh@ns@#1\endcsname{rectangle} + \expandafter\gdef\csname pgf@sh@np@#1\endcsname{% + \def\southwest{\pgfqpoint{\csname pgf@lbb@minx@#1\endcsname}{\csname pgf@lbb@miny@#1\endcsname}}% + \def\northeast{\pgfqpoint{\csname pgf@lbb@maxx@#1\endcsname}{\csname pgf@lbb@maxy@#1\endcsname}}% + } + \expandafter\gdef\csname pgf@sh@nt@#1\endcsname{{1}{0}{0}{1}{0pt}{0pt}} + \expandafter\gdef\csname pgf@sh@pi@#1\endcsname{\pgfpictureid} + \expandafter\gdef\csname pgf@lbb@maxx@#1\endcsname{-16000pt}% + \expandafter\gdef\csname pgf@lbb@minx@#1\endcsname{16000pt}% + \expandafter\gdef\csname pgf@lbb@maxy@#1\endcsname{-16000pt}% + \expandafter\gdef\csname pgf@lbb@miny@#1\endcsname{16000pt}% + \pgf@size@hookedtrue% + \expandafter\def\expandafter\pgf@path@size@hook\expandafter{\pgf@path@size@hook\pgf@lbb@do{#1}} + }, + /pgf/freeze local bounding box/.code={% + {% + \csname pgf@sh@np@#1\endcsname% + \southwest% + \pgf@xa=\pgf@x% + \pgf@ya=\pgf@y% + \northeast% + \expandafter\xdef\csname pgf@sh@np@#1\endcsname{% + \noexpand\def\noexpand\southwest{\noexpand\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}% + \noexpand\def\noexpand\northeast{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}% + }% + }% + }, +}% +\def\pgf@lbb@do#1{% + \ifdim\pgf@size@hook@x<\csname pgf@lbb@minx@#1\endcsname\expandafter\xdef\csname pgf@lbb@minx@#1\endcsname{\the\pgf@size@hook@x}\fi% + \ifdim\pgf@size@hook@x>\csname pgf@lbb@maxx@#1\endcsname\expandafter\xdef\csname pgf@lbb@maxx@#1\endcsname{\the\pgf@size@hook@x}\fi% + \ifdim\pgf@size@hook@y<\csname pgf@lbb@miny@#1\endcsname\expandafter\xdef\csname pgf@lbb@miny@#1\endcsname{\the\pgf@size@hook@y}\fi% + \ifdim\pgf@size@hook@y>\csname pgf@lbb@maxy@#1\endcsname\expandafter\xdef\csname pgf@lbb@maxy@#1\endcsname{\the\pgf@size@hook@y}\fi% +}% + +% +% Circle +% + +\pgfdeclareshape{circle} +% +% Draws a circle around the text +% +{% + \savedanchor\centerpoint{% + \pgf@x=.5\wd\pgfnodeparttextbox% + \pgf@y=.5\ht\pgfnodeparttextbox% + \advance\pgf@y by-.5\dp\pgfnodeparttextbox% + }% + + \saveddimen\radius{% + % + % Calculate ``height radius'' + % + \pgf@ya=.5\ht\pgfnodeparttextbox% + \advance\pgf@ya by.5\dp\pgfnodeparttextbox% + \pgfmathsetlength\pgf@yb{\pgfkeysvalueof{/pgf/inner ysep}}% + \advance\pgf@ya by\pgf@yb% + % + % Calculate ``width radius'' + % + \pgf@xa=.5\wd\pgfnodeparttextbox% + \pgfmathsetlength\pgf@xb{\pgfkeysvalueof{/pgf/inner xsep}}% + \advance\pgf@xa by\pgf@xb% + % + % Calculate length of radius vector: + % + \pgf@process{\pgfpointnormalised{\pgfqpoint{\pgf@xa}{\pgf@ya}}}% + \ifdim\pgf@x>\pgf@y% + \c@pgf@counta=\pgf@x% + \ifnum\c@pgf@counta=0\relax% + \else% + \divide\c@pgf@counta by 255\relax% + \pgf@xa=16\pgf@xa\relax% + \divide\pgf@xa by\c@pgf@counta% + \pgf@xa=16\pgf@xa\relax% + \fi% + \else% + \c@pgf@counta=\pgf@y% + \ifnum\c@pgf@counta=0\relax% + \else% + \divide\c@pgf@counta by 255\relax% + \pgf@ya=16\pgf@ya\relax% + \divide\pgf@ya by\c@pgf@counta% + \pgf@xa=16\pgf@ya\relax% + \fi% + \fi% + \pgf@x=\pgf@xa% + % + % If necessary, adjust radius so that the size requirements are + % met: + % + \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/minimum width}}% + \pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/minimum height}}% + \ifdim\pgf@x<.5\pgf@xb% + \pgf@x=.5\pgf@xb% + \fi% + \ifdim\pgf@x<.5\pgf@yb% + \pgf@x=.5\pgf@yb% + \fi% + % + % Now, add larger of outer separations. + % + \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}% + \pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}% + \ifdim\pgf@xb<\pgf@yb% + \advance\pgf@x by\pgf@yb% + \else% + \advance\pgf@x by\pgf@xb% + \fi% + }% + + % + % Anchors + % + \anchor{center}{\centerpoint}% + \anchor{mid}{\centerpoint\pgfmathsetlength\pgf@y{.5ex}}% + \anchor{base}{\centerpoint\pgf@y=0pt}% + \anchor{north}{\centerpoint\advance\pgf@y by\radius}% + \anchor{south}{\centerpoint\advance\pgf@y by-\radius}% + \anchor{west}{\centerpoint\advance\pgf@x by-\radius}% + \anchor{east}{\centerpoint\advance\pgf@x by\radius}% + \anchor{mid west}{\centerpoint\advance\pgf@x by-\radius\pgfmathsetlength\pgf@y{.5ex}}% + \anchor{mid east}{\centerpoint\advance\pgf@x by\radius\pgfmathsetlength\pgf@y{.5ex}}% + \anchor{base west}{\centerpoint\advance\pgf@x by-\radius\pgf@y=0pt}% + \anchor{base east}{\centerpoint\advance\pgf@x by\radius\pgf@y=0pt}% + \anchor{north west}{ + \centerpoint + \pgf@xa=\radius + \advance\pgf@x by-0.707107\pgf@xa + \advance\pgf@y by0.707107\pgf@xa + }% + \anchor{south west}{ + \centerpoint + \pgf@xa=\radius + \advance\pgf@x by-0.707107\pgf@xa + \advance\pgf@y by-0.707107\pgf@xa + }% + \anchor{north east}{ + \centerpoint + \pgf@xa=\radius + \advance\pgf@x by0.707107\pgf@xa + \advance\pgf@y by0.707107\pgf@xa + }% + \anchor{south east}{ + \centerpoint + \pgf@xa=\radius + \advance\pgf@x by0.707107\pgf@xa + \advance\pgf@y by-0.707107\pgf@xa + }% + \anchorborder{% + \pgf@xa=\pgf@x% + \pgf@ya=\pgf@y% + \edef\pgf@marshal{% + \noexpand\pgfpointborderellipse + {\noexpand\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}} + {\noexpand\pgfqpoint{\radius}{\radius}}% + }% + \pgf@marshal% + \pgf@xa=\pgf@x% + \pgf@ya=\pgf@y% + \centerpoint% + \advance\pgf@x by\pgf@xa% + \advance\pgf@y by\pgf@ya% + }% + + % + % Background path + % + \backgroundpath{% + \pgfutil@tempdima=\radius% + \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}% + \pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}% + \ifdim\pgf@xb<\pgf@yb% + \advance\pgfutil@tempdima by-\pgf@yb% + \else% + \advance\pgfutil@tempdima by-\pgf@xb% + \fi% + \pgfpathcircle{\centerpoint}{\pgfutil@tempdima}% + }% +}% + + + + + +\endinput diff --git a/graphics/pgf/base/tex/generic/modules/pgfmodulesnakes.code.tex b/graphics/pgf/base/tex/generic/modules/pgfmodulesnakes.code.tex new file mode 100644 index 0000000000..9b692b1340 --- /dev/null +++ b/graphics/pgf/base/tex/generic/modules/pgfmodulesnakes.code.tex @@ -0,0 +1,369 @@ +% Copyright 2019 by Till Tantau +% +% This file may be distributed and/or modified +% +% 1. under the LaTeX Project Public License and/or +% 2. under the GNU Public License. +% +% See the file doc/generic/pgf/licenses/LICENSE for more details. + +\ProvidesFileRCS{pgfmodulesnakes.code.tex} + +\pgfwarning{Snakes have been superseded by decorations. Use the module decorations instead of snakes} + +\usepgfmodule{decorations}% + +\endinput + + + +\newdimen\pgfsnakeremainingdistance +\newdimen\pgfsnakecompleteddistance + + +% Creates a new pgf snake +% +% #1 = snake name +% #1 = initial state +% #3 = states of the snake +% +% +% This command declares a new snake for later use. The second +% parameter specifies the states of the snake, see also the +% description of pgfpathsnake. +% +% Inside the code of #3 the command \state may be used. This command +% will only be defined while #3 is executed. +% +% Example: +% +% \pgfdeclaresnake{zig zag}{one zig zag} +% { +% \state{one zig zag}[width=10pt] +% { +% \pgfpathlineto{\pgfpoint{2.5pt}{2.5pt}} +% \pgfpathlineto{\pgfpoint{7.5pt}{-2.5pt}} +% \pgfpathlineto{\pgfpoint{10pt}{0pt}} +% } +% \state{final} +% { +% \pgfpathlineto{\pgfpoint{\pgfsnakeremainingdistance}{0pt}} +% } +% } + +\long\def\pgfdeclaresnake#1#2#3{% + \def\pgf@snake@name{#1}% + \pgfutil@namedef{pgf@snake@@#1@initial}{#2}% + \let\pgf@orig@state=\state% + \let\state=\pgf@snake@state + #3 + \let\state=\pgf@orig@state% +}% + + +% Sets the additional transformation applied to every segment of a snake +% +% #1 = transformation +% +% Example: +% +% \pgfsetsnakesegmenttransformation{\pgftransformyshift{5pt}} + +\def\pgfsetsnakesegmenttransformation#1{\def\pgf@snakeadditionaltransform{#1}}% +\pgfsetsnakesegmenttransformation{}% + + +% Declares a new state +% +% #1 = state name +% #2 = options +% #3 = path element +% +% Description: +% +% When a snake is drawn and the current state is #1, the following +% happens. First, the options are executed, which will possible change +% the state. If that does not happen, the path element is added to the +% path and the coordinate system is translated by the path element's +% width (which is specified using the width option). +% +% Example: +% +% \state{initial}[width=10pt] +% { +% \pgfpathlineto{\pgfpoint{2.5pt}{2.5pt}} +% \pgfpathlineto{\pgfpoint{7.5pt}{-2.5pt}} +% \pgfpathlineto{\pgfpoint{10pt}{0pt}} +% } + +\def\pgf@snake@state#1{\pgfutil@ifnextchar[{\pgf@@snake@start#1}{\pgf@@snake@start#1[]}}%}% +\def\pgf@@snake@start#1[#2]#3{% + \pgfutil@namedef{pgf@snake@@\pgf@snake@name @#1@options}{#2}% + \pgfutil@namedef{pgf@snake@@\pgf@snake@name @#1@code}{#3}% +}% + + + +% Use multiple snakes +% +% #1 = list of snake names/length pairs +% #2 = point to which the snake leads +% +% This operation uses the snakes in parameter #1 to get to the point +% #4. The parameter #1 should contain pairs consisting of a snake name +% and a length for which this snake should be used. When the length is +% computed, the dimensions \pgfsnakeremainingdistance and +% \pgfsnakecompleteddistance will have been set to the length of the +% total distance to #2 that has already been covered/that still needs +% to be covered. +% +% Example: +% +% \pgfpathsnakesto{{lineto}{1cm},{zig zag}{\pgfsnakeremainingdistance}}{\pgfpoint{2cm}{3cm}} + +\def\pgfpathsnakesto#1#2{% + \begingroup% + % compute target vector + \pgfpointtransformed{#2}% + \advance\pgf@x by-\pgf@path@lastx% + \advance\pgf@y by-\pgf@path@lasty% + % + % MW: Calculate the angle of the snake. + % + \edef\pgf@marshal{% + \noexpand\pgfmathanglebetweenpoints{\noexpand\pgfpointorigin}{% + \noexpand\pgf@x\the\pgf@x\noexpand\pgf@y\the\pgf@y}% + }% + \pgf@marshal% + \let\pgfsnakeangle\pgfmathresult% + % + % MW: Use this instead for length (?). More accurate and only a few operations slower. + % + \pgfmathveclen@{\pgfmath@tonumber{\pgf@x}}{\pgfmath@tonumber{\pgf@y}}% + \pgfsnakeremainingdistance\pgfmathresult pt\relax% + % + % Ok, now normalize the vector... + \pgf@xa=\pgf@x% + \pgf@ya=\pgf@y% + \pgf@process{\pgfpointnormalised{}}% + % ok, now computer length (arghh...) + % In theory that's easy: divide the larger of the values x or y by + % the normalized x or y. Well... + % + % First, make xa and ya positive: + % \ifdim\pgf@xa<0pt% +% \pgf@xa=-\pgf@xa% +% \fi% +% \ifdim\pgf@ya<0pt% +% \pgf@ya=-\pgf@ya% +% \fi% +% % Now do division: +% \ifdim\pgf@xa>\pgf@ya% +% \c@pgf@counta=\pgf@x% +% \ifnum\c@pgf@counta=0\relax% +% \else% +% \divide\c@pgf@counta by 255\relax% +% \pgf@xa=16\pgf@xa\relax% +% \divide\pgf@xa by\c@pgf@counta% +% \pgf@xa=16\pgf@xa\relax% +% \fi% +% \else% +% \c@pgf@counta=\pgf@y% +% \ifnum\c@pgf@counta=0\relax% +% \else% +% \divide\c@pgf@counta by 255\relax% +% \pgf@ya=16\pgf@ya\relax% +% \divide\pgf@ya by\c@pgf@counta% +% \pgf@xa=16\pgf@ya\relax% +% \fi% +% \fi% +% % Make positive: +% \ifdim\pgf@xa<0pt% +% \pgf@xa=-\pgf@xa% +% \fi% +% % Ok, now we draw things... + \edef\pgf@list{#1}% +% \pgfsnakeremainingdistance=\pgf@xa% + \pgfsnakecompleteddistance=0pt% + \pgf@xb=\pgf@x% + \pgf@yb=\pgf@y% + \pgfutil@for\pgf@temp:=\pgf@list\do{% + \ifx\pgf@temp\pgfutil@empty% + \else% + \expandafter\pgf@snake@invoke\pgf@temp% + \fi% + }% + \endgroup% +}% + +\def\pgf@snake@invoke#1#2{% + \pgfmathsetlength\pgf@xa{#2}% + {% + \edef\pgf@marshal{\noexpand\pgfpathsnakealongvector{#1}{\the\pgf@xa}{\noexpand\pgfqpoint{\the\pgf@xb}{\the\pgf@yb}}}% + \pgf@marshal% + }% + \advance\pgfsnakecompleteddistance by\pgf@xa% + \advance\pgfsnakeremainingdistance by-\pgf@xa% +}% + +% Use a snake +% +% #1 = snake name +% #2 = point to which the snake leads +% +% This operation mainly computes \pgfpathsnakealongvector for a vector +% appropriately chosen. See \pgfpathsnakealongvector for details. +% +% Example: +% +% \pgfpathsnaketo{zig zag}{\pgfpoint{2cm}{3cm}} + +\def\pgfpathsnaketo#1#2{\pgfpathsnakesto{{#1}{\pgfsnakeremainingdistance}}{#2}}% + + + +% Use a snake +% +% #1 = snake name +% #2 = length of the snake +% #3 = vector along which the snake grows, should have unit length. +% +% This command draws a snake (more precisely, it adds a snake to the +% path). This works as follows: +% +% First, the coordinate system is transformed such that the vector #3 +% points to the right. +% +% Next, the state `initial' of the snake is entered. Unless the +% options of this state cause it to switch to another state, the path +% element is added to the path. Then, the coordinate system is +% translated by the width of the path element as specified in the +% width option of the path element. The dimensions +% \pgfsnakeremainingdistance and \pgfsnakecompleteddistance are +% updated. +% +% The process ends when the state `final' is entered. The code of the +% final state is executed and the process stops. +% +% Example: +% +% \pgfpathsnakealongvector{zig zag}{100pt}{\pgfpolar{30}{1pt}} + +\def\pgfpathsnakealongvector#1#2#3{% + \pgfutil@ifundefined{pgf@snake@@#1@initial}{\pgferror{Undefined snake ``#1''}} + { + \begingroup% keep things local + \pgftransformreset% + \pgf@pt@x=\pgf@path@lastx% evil trickery to transform to the last point + \pgf@pt@y=\pgf@path@lasty% + \pgf@process{#3}% + \pgf@xa=\pgf@x% + \pgf@ya=\pgf@y% + \pgf@xb=-\pgf@y% + \pgf@yb=\pgf@x% + \pgftransformcm + {\pgf@sys@tonumber{\pgf@xa}}{\pgf@sys@tonumber{\pgf@ya}} + {\pgf@sys@tonumber{\pgf@xb}}{\pgf@sys@tonumber{\pgf@yb}} + {\pgfpointorigin}% + % Now, setup the automaton + \expandafter\let\expandafter\pgf@snake@current@state\expandafter=\csname pgf@snake@@#1@initial\endcsname% + \def\pgf@snake@name{#1}% + \pgfsnakecompleteddistance=0pt% + \pgfmathsetlength\pgfsnakeremainingdistance{#2}% + \pgf@snake@run% + % Last step: + {% + \pgftransformxshift{\pgfsnakecompleteddistance}% + \pgf@snakeadditionaltransform% + \csname pgf@snake@@#1@final@code\endcsname% + }% + \endgroup% + }% +}% + +\def\pgf@final@text{final}% + +\def\pgf@snake@run{% + \let\pgf@snake@next=\pgf@snake@do@state% + \ifx\pgf@snake@current@state\pgf@final@text% + \let\pgf@snake@next=\relax% + \fi% + \pgf@snake@next% +}% + +\def\pgf@snake@do@state{% + \let\pgf@snake@next=\relax% + \let\pgf@snake@next@state=\pgf@snake@current@state% + % execute options + \expandafter\expandafter\expandafter\pgf@snakes@setter + \expandafter\expandafter\expandafter{\csname pgf@snake@@\pgf@snake@name @\pgf@snake@current@state @options\endcsname}% + \ifx\pgf@snake@next\relax% + \let\pgf@snake@next=\pgf@snake@do@code% + \fi% + \pgf@snake@next% +}% +\def\pgf@snakes@setter{% + \pgfqkeys{/pgf/snakes}% +}% + +\def\pgf@snake@do@code{% + % Ok, execute code: + {% + \pgftransformxshift{\pgfsnakecompleteddistance}% + \pgf@snakeadditionaltransform% + \csname pgf@snake@@\pgf@snake@name @\pgf@snake@current@state @code\endcsname% + }% + % next, do transformation and update + \pgfmathsetlength{\pgf@xa}{\pgf@snake@width}% + \advance\pgfsnakeremainingdistance by-\pgf@xa% + \advance\pgfsnakecompleteddistance by\pgf@xa% + % Next iteration: + \let\pgf@snake@current@state=\pgf@snake@next@state% + \pgf@snake@run% +}% + +\pgfkeys{ + /pgf/snakes/width/.code=\def\pgf@snake@width{#1}\pgf@snake@switch@if#1 to final\pgf@stop,% + /pgf/snakes/switch if less than/.code=\pgf@snake@switch@if#1\pgf@stop,% + /pgf/snakes/next state/.store in=\pgf@snake@next@state% +}% + +\def\pgf@snake@switch@if#1to #2\pgf@stop{% + \ifx\pgf@snake@next\relax% + \pgfmathsetlength\pgf@x{#1}% + \ifdim\pgfsnakeremainingdistance<\pgf@x% + \def\pgf@snake@current@state{#2}% + \let\pgf@snake@next=\pgf@snake@run% + \fi% + \fi% +}% + + + +% lineto snake +% +% This snake simply adds a straight line. This snake is mainly useful +% in conjunction with other snakes. + +\pgfdeclaresnake{lineto}{final} +{% + \state{final} + { \pgfpathlineto{\pgfqpoint{\pgfsnakeremainingdistance}{0pt}} } +}% + + + +% moveto snake +% +% This snake simply jumps to the end. This snake, too, is mainly +% useful in conjunction with other snakes. + +\pgfdeclaresnake{moveto}{final} +{% + \state{final} + { \pgfpathmoveto{\pgfqpoint{\pgfsnakeremainingdistance}{0pt}} } +}% + + + +\endinput diff --git a/graphics/pgf/base/tex/generic/modules/pgfmodulesorting.code.tex b/graphics/pgf/base/tex/generic/modules/pgfmodulesorting.code.tex new file mode 100644 index 0000000000..9aa1df9fef --- /dev/null +++ b/graphics/pgf/base/tex/generic/modules/pgfmodulesorting.code.tex @@ -0,0 +1,69 @@ +% Copyright 2019 by Till Tantau +% +% This file may be distributed and/or modified +% +% 1. under the LaTeX Project Public License and/or +% 2. under the GNU Public License. +% +% See the file doc/generic/pgf/licenses/LICENSE for more details. + +\ProvidesFileRCS{pgfmodulesorting.code.tex} + +% +% This file defines commands for sorting data. This is useful for 3d plots, +% but also for drawing things in the correct order. +% + + +\newcount\pgfsortingbuckets + + +% Init the sorting +% +% #1 = number of buckets +% +% Description: +% +% This macro inits a sorting. At any time there can be only one active +% sorting. When you init the sorting, #1 should be a number of +% buckets. You can then call \pgfsortinginsert to insert code into the +% buckets. Once you are done, calling \pgfsortingexecute will execute +% first the code in buckets 0, then the code in bucket 1, and so on to +% bucket number #1-1. +% +\def\pgfsortinginit#1{ + \pgfsortingbuckets#1\relax% + \c@pgf@counta=0\relax + \pgfutil@loop + \ifnum\c@pgf@counta<\c@pgf@countb\relax% + \expandafter\global\expandafter\let\csname pgf@sort@\the\c@pgf@counta\endcsname\pgfutil@empty% + \advance\c@pgf@counta by1\relax% + \pgfutil@repeat +}% + +% Insert code into some bucket +% +% #1 = the bucket +% #2 = the code +% +\def\pgfsortinginsert#1#2{ + \expandafter\let\expandafter\pgf@temp\csname pgf@lib@sort@#1\endcsname% + \expandafter\def\expandafter\pgf@temp\expandafter{\pgf@temp#2}% + \expandafter\global\expandafter\let\csname pgf@lib@sort@\the\c@pgf@counta\endcsname\pgf@temp% +}% + + +% After all code has been inserted into the buckets, you can call +% this method to execute the code in the order of the buckets. +\def\pgfsortingexecute{ + \c@pgf@countb=\pgfoovalueof{buckets}\relax + \c@pgf@counta=0\relax + \pgfutil@loop + \ifnum\c@pgf@counta<\c@pgf@countb\relax% + \csname pgf@lib@sort@\the\c@pgf@counta\endcsname% + \advance\c@pgf@counta by1\relax% + \pgfutil@repeat +}% + + +\endinput |