JezK
Edit File: pgfmoduleanimations.code.tex
% 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