diff options
Diffstat (limited to 'Master/texmf-dist/tex/generic/pgf/modules/pgfmoduledatavisualization.code.tex')
-rw-r--r-- | Master/texmf-dist/tex/generic/pgf/modules/pgfmoduledatavisualization.code.tex | 3035 |
1 files changed, 3035 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/generic/pgf/modules/pgfmoduledatavisualization.code.tex b/Master/texmf-dist/tex/generic/pgf/modules/pgfmoduledatavisualization.code.tex new file mode 100644 index 00000000000..1c78bb221e6 --- /dev/null +++ b/Master/texmf-dist/tex/generic/pgf/modules/pgfmoduledatavisualization.code.tex @@ -0,0 +1,3035 @@ +% Copyright 2006 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 $Header: /cvsroot/pgf/pgf/generic/pgf/modules/pgfmoduledatavisualization.code.tex,v 1.37 2010/08/24 21:28:58 tantau Exp $ + +\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} + + % 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) { + \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 pointing from some point to another. +% +% #1 = code for setting the attributes of the first point +% #2 = code for setting the attributes of the second point +% +% Using #1 and #2 you specify two data points. These are then +% localized on the canvas and a vector pointing from the first data +% 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 "path" signal with the \pgfdvpathdirectionfromtoken set and +% then sets up the second point and emits the corresponding +% ...totoken. +% + +\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}{ + \edef\pgf@dv@dir@xa{\the\pgf@x} + \edef\pgf@dv@dir@ya{\the\pgf@y} + \pgf@process{ + \pgfpointnormalised{% + \pgfpointdiff{\pgf@dv@from}{\pgfqpoint{\pgf@dv@dir@xa}{\pgf@dv@dir@ya}} + } + } + } + } + } +} + + + + + +% +% +% Data parsing and formatting +% +% + + + +% Run the rendering pipeline on a 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 setting of the following keys is important: +% +% /pgf/data visualization/obj contains a handle to the dv-object +% /pgf/data/format stores the format (see below) +% /pgf/data/source determines where the data is. +% +% If source is empty, the data is stored in the argument that +% follows. Otherwise, the file whose name is stored in source is read. +% 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[source=data,format=comma separated columns] +% +% \dv.survey() +% \dv.visualize() + +\def\pgfdata{\pgfutil@ifnextchar[{\pgf@dataset@data@opt}{\pgf@dataset@data@opt[]}}%} +\def\pgf@dataset@data@opt[#1]{% + % Ok, add one data thing... + \pgfkeysvalueof{/pgf/data visualization/obj}.add data(\pgf@do@data{#1})% + \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/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 + \PackageError{pgf}{Unknown data format '\pgf@dv@format'}{}% + \else% + \pgfkeysgetvalue{/pgf/data/source}\pgf@dv@source% + \ifx\pgf@dv@source\pgfutil@empty% + \let\pgf@next\pgf@dataset@grab@inline% + \else% + \let\pgf@next\pgf@dataset@grab@external% + \fi% + \pgf@next% +} +\def\pgf@dataset@grab@inline{% + \pgfutil@ifnextchar\bgroup{% + \begingroup% + \catcode`\^^M=\active% + \pgf@dv@format@catcodes% + \pgf@dataset@grab@@inline}% + {\PackageError{pgf}{Opening brace expected}{}}% +} +\def\pgf@dataset@grab@external{% + \fi% + \endgroup% + \pgfkeysvalueof{/pgf/data visualization/obj}.add data({{{\pgf@dataset@do@external}}})% + \pgf@dv@cont@code% +} + +{\catcode`\^^M=\active% + \gdef\pgf@dataset@grab@@inline#1{% + \endgroup% + \pgfkeysvalueof{/pgf/data visualization/obj}.add data({{{\pgf@dataset@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@dataset@do@external{% + \csname pgfdv@format@\pgfkeysvalueof{/pgf/data/format}@catcodes\endcsname% + \immediate\openin\r@pgf@reada=\pgfkeysvalueof{/pgf/data/source} % + \ifeof\r@pgf@reada\relax + \PackageError{pgf}{Data source '\pgfkeysvalueof{/pgf/data/source}' not found}{}% + \else + \pgf@dataset@readline% + \fi + \immediate\closein\r@pgf@reada% +} + +\def\pgf@partext{\par}% +\def\pgf@dataset@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@dataset@readline\fi% +} + + +% +% Read inline data +% + +\def\pgf@dataset@do@inline{% + \pgf@dv@handle@line% +} + +{\catcode`\^^M=\active% +\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/source/.initial=, + /pgf/data/continue code/.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 \dataset 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=key value pairs] { +% x=5, y=6, hi=9 +% x=7, y=6, lo=10 +% } + +\pgfdeclaredataformat{key value pairs}{}{}{#1}{{\pgfkeys{/data point/.cd,#1}\pgfdatapoint}}{}{} + + + + + +% 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... + {\PackageError{pgf}{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... + {\PackageError{pgf}{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 +% parantheses. From a "mathematical" point of view these +% parantheses 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 +% parantheses 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% +} + + + + +% 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. + +% \attribute clip; +% % The code for clipping the interval. Will set \pgf@dvcliptrue, if a +% % clip is necessary. + + \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 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(d)-f(c))/(b-a). + + + % 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 +% \method set clip interval(#1,#2) { +% \def\pgfdvmin{#1}% +% \def\pgfdvmax{#2}% +% \ifx\pgfdvmin\pgfutil@empty% +% \else% +% \pgfdvmathenter{\pgf@dv@temp}{\pgfdvmin}% +% \pgfdvmathexitbyserializing{\pgfdvmin}{\pgf@dv@temp}% +% \fi +% \ifx\pgfdvmax\pgfutil@empty% +% \else% +% \pgfdvmathenter{\pgf@dv@temp}{\pgfdvmax}% +% \pgfdvmathexitbyserializing{\pgfdvmax}{\pgf@dv@temp}% +% \fi +% \pgfooeset{clip}{% +% \noexpand\pgf@dvclipfalse% +% \ifx\pgfdvmin\pgfutil@empty% +% \else% +% \noexpand\pgfdvmathenter{\noexpand\pgf@dv@temp}{\pgfdvmin}% +% \noexpand\pgfdvmathifless{\noexpand\pgfvalue}{\noexpand\pgf@dv@temp}{\noexpand\pgf@dvcliptrue}{}% +% \fi% +% \ifx\pgfdvmax\pgfutil@empty% +% \else% +% \noexpand\pgfdvmathenter{\noexpand\pgf@dv@temp}{\pgfdvmax}% +% \noexpand\pgfdvmathifless{\noexpand\pgf@dv@temp}{\noexpand\pgfvalue}{\noexpand\pgf@dvcliptrue}{}% +% \fi% +% } +% } + + % 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} + \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}% + }% + } + + % Slot + \method map() { + \pgfooget{scale}\pgf@temp + \ifx\pgf@temp\pgfutil@empty% + % not yet setup + \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% + \else% + \ifx\pgf@dv@external@value\relax% + \else% +% % Clip? + \pgfdvmathenter{\pgfvalue}{\pgf@dv@external@value}% +% \pgfoovalueof{clip}% +% \ifpgf@dvclip% +% \pgfkeyslet{/data point/\pgfoovalueof{out}}\pgfutil@empty% +% \else% + \pgf@dv@mapper@trans{} +% \fi% + \fi% + \fi% + \pgfkeysgetvalue{/data point/\pgfoovalueof{in}/min}\pgf@dv@external@value% + \ifx\pgf@dv@external@value\relax\else% + \ifx\pgf@dv@external@value\pgfutil@empty\else% + \pgfdvmathenter{\pgfvalue}{\pgf@dv@external@value}% + \pgf@dv@mapper@trans{/min} + \fi + \fi + \pgfkeysgetvalue{/data point/\pgfoovalueof{in}/max}\pgf@dv@external@value% + \ifx\pgf@dv@external@value\relax\else% + \ifx\pgf@dv@external@value\pgfutil@empty\else% + \pgfdvmathenter{\pgfvalue}{\pgf@dv@external@value}% + \pgf@dv@mapper@trans{/max} + \fi + \fi + \fi% + } + + \def\pgf@dv@mapper@trans#1{ + \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}#1/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}#1}\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 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) + } + + +% % Method +% % +% % #1 = a min clip -- everything below this value will be clipped +% % #2 = a max value -- everything above this value will be clipped +% % +% % Sets a clipping for the underlying interval mapper. +% % +% \method set clip interval(#1,#2) { +% \pgfoovalueof{interval mapper}.set clip interval({#1},{#2})% +% } + + % 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 + \else + \ifx\pgf@temp\pgf@max@text + \pgf@dv@lib@set@mm% + \let\pgf@dv@value\pgfdvmax + \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 + \else + \pgfdvmathenter{\pgf@lib@dv@min@mv}{\pgf@lib@dv@min} + \fi + \ifx\pgf@lib@dv@max\pgf@max@text% + \let\pgf@lib@dv@max@mv\pgfdvmax + \else + \pgfdvmathenter{\pgf@lib@dv@max@mv}{\pgf@lib@dv@max} + \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 detemined later on. + % + % In case the attribute has the sub-attributes "min" and "max", they + % are also taken into account. + + \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) { + \def\pgf@dv@val{#1} + \pgfoovalueof{interval obj}.adjust(\pgf@dv@range@surv) + } + + % 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() { + \pgfkeysgetvalue{/data point/\pgfoovalueof{attribute}}\pgf@dv@val% + \pgfoovalueof{interval obj}.adjust(\pgf@dv@range@surv) + \pgfkeysgetvalue{/data point/\pgfoovalueof{attribute}/min}\pgf@dv@val% + \pgfoovalueof{interval obj}.adjust(\pgf@dv@range@surv) + \pgfkeysgetvalue{/data point/\pgfoovalueof{attribute}/max}\pgf@dv@val% + \pgfoovalueof{interval obj}.adjust(\pgf@dv@range@surv) + } + + \def\pgf@dv@range@surv{% + \ifx\pgf@dv@val\relax% + \else% + \edef\pgf@dv@val{\pgf@dv@val}% + \ifx\pgf@dv@val\pgfutil@empty% + \else% + \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% + \fi% + \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. + % + % 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; + % Filter code + + \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. + + + % 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} + } + + % 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 + \edef\pgf@dv@add{\noexpand\pgf@dv@ph{\pgfkeysvalueof{/data point/canvas x}}{\pgfkeysvalueof{/data point/canvas y}}}% + \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 + \pgfplotstreamstart + \pgfoovalueof{positions} + \pgfoovalueof{cache} + \pgfplotstreamend + \pgfkeysvalueof{/data point/\pgfoovalueof{name}/execute at end} + \fi + } + \endpgfscope + \pgfoolet{positions}\pgfutil@empty + \pgfoolet{cache}\pgfutil@empty + } + + \def\pgf@dv@ph#1#2{\pgfplotstreampoint{\pgfqpoint{#1}{#2}}} +} + +\newcount\pgf@lib@dv@cache@count + + +% A possible filter for unnamed visualizers +% +% #1 = Text to be compared with key /data point/visualizer +% +% 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 accoring 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/visualizer being equal to #1. + +\def\pgfdvvisualizerfilter#1{% + \pgfkeysgetvalue{/data point/visualizer}\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/visualizer is tested against this attribute. + +\def\pgfdvnamedvisualizerfilter{% + \pgfkeysgetvalue{/data point/visualizer}\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 + % two attributes a1 and a2, each of which must have min and max + % subattributes. Then, it draws a rectangle between (a1/min,a2/min) + % and (a1/max,a2/max). + + \attribute attribute 1; + \attribute attribute 2; + % The attributes + + \attribute use path attribute; + % The attribute that stores the to-be-taken action + + + % Constructor + % + % #1 = first attribute + % #2 = second attribute + % + \method rectangle visualizer(#1,#2,#3) { + \pgfooset{attribute 1}{#1} + \pgfooset{attribute 2}{#2} + \pgfooset{use path attribute}{#3} + } + + % Method + \method default connects() { + \pgfoothis.get handle(\pgf@dv@me) + \pgfkeysvalueof{/pgf/data visualization/obj}.connect(\pgf@dv@me,visualize,visualize datapoint signal) + } + + % Slot + \method visualize() { + { + \pgfkeysgetvalue{/data point/\pgfoovalueof{attribute 1}/min}\pgf@dv@amin + \pgfkeysgetvalue{/data point/\pgfoovalueof{attribute 1}/max}\pgf@dv@amax + \pgfkeysgetvalue{/data point/\pgfoovalueof{attribute 2}/min}\pgf@dv@bmin + \pgfkeysgetvalue{/data point/\pgfoovalueof{attribute 2}/max}\pgf@dv@bmax + \ifx\pgf@dv@amin\relax\else\ifx\pgf@dv@amin\pgfutil@empty\else + \ifx\pgf@dv@bmin\relax\else\ifx\pgf@dv@bmin\pgfutil@empty\else + \ifx\pgf@dv@amax\relax\else\ifx\pgf@dv@amax\pgfutil@empty\else + \ifx\pgf@dv@bmax\relax\else\ifx\pgf@dv@bmax\pgfutil@empty\else + \pgfkeyssetvalue{/data point/\pgfoovalueof{attribute 1}}{\pgf@dv@amin} + \pgfkeyssetvalue{/data point/\pgfoovalueof{attribute 2}}{\pgf@dv@bmin} + \pgfpathdvmoveto + \pgfkeyssetvalue{/data point/\pgfoovalueof{attribute 2}}{\pgf@dv@bmax} + \pgfpathdvlineto + \pgfkeyssetvalue{/data point/\pgfoovalueof{attribute 1}}{\pgf@dv@amax} + \pgfpathdvlineto + \pgfkeyssetvalue{/data point/\pgfoovalueof{attribute 2}}{\pgf@dv@bmin} + \pgfpathdvlineto + \pgfkeyssetvalue{/data point/\pgfoovalueof{attribute 1}}{\pgf@dv@amin} + \pgfpathdvlineto + \pgfpathclose + \pgfkeysvalueof{/data point/\pgfoovalueof{use path attribute}} + \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} + \ifx#2\pgfutil@empty + \else + \pgfdvmathenter{\pgf@temp}{#2} + \fi + \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 arrangment(\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; + % Collectes 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 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} + } + + % 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 mimum 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 and some action that should be taken when the counter + % reaches zero. You can also specify that certain data points should + % not have an effect on the counter. + % + % Counters can serve different purposes. + % + % 1. For plotting points in a table or sequence, the counter can be + % used to provide the position along one axis. + % 2. By using the counter as a repetitive count down one can for + % instance have only every 10th data point be marked. + + \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 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) + } + + % Setter + \method set value(#1) { + \pgfooset{val}{#1} + \pgfkeyssetvalue{/data point/\pgfoovalueof{attribute}}{#1}% + } + + % Setter + \method set start value(#1) { + \pgfooset{start val}{#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% + \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 + + + + + + |