Current File : //usr/share/texlive/texmf-dist/tex/generic/pgf/modules/pgfmoduledatavisualization.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{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{%
\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