% 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//execute at begin and /data % point//execute at end, where 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//attribute 1 and /data % point//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//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// and executes % this key, provided it is defined. If it not defined, the key % /pgf/data visualization/style sheets//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