From 56e54baee95f0a27552aa239421d29b4802abc81 Mon Sep 17 00:00:00 2001 From: jkriege2 Date: Sun, 12 May 2019 22:22:48 +0200 Subject: [PATCH] new: rework/extension of the JKQTPDatastore interface (WIP) new: Example simpletest_datastore, which demonstrates the extended interface of JKQTPDatastore --- JKQtPlotterBuildAllExamples.pro | 1 + doc/dox/examples_and_tutorials.dox | 10 + doc/dox/jkqtplotter.dox | 2 + examples/README.md | 13 +- examples/simpletest_datastore/README.md | 282 +++++++ .../jkqtplotter_simpletest_datastore.cpp | 140 ++++ .../jkqtplotter_simpletest_datastore.pro | 26 + ...qtplotter_simpletest_datastore_and_lib.pro | 8 + examples/simpletest_filledgraphs/README.md | 26 +- .../jkqtplotter_simpletest_filledgraphs.cpp | 23 +- lib/jkqtplotter/jkqtpbaseplotter.cpp | 12 +- lib/jkqtplotter/jkqtpcoordinateaxes.cpp | 24 +- lib/jkqtplotter/jkqtpdatastorage.cpp | 154 +++- lib/jkqtplotter/jkqtpdatastorage.h | 715 +++++++++++++++--- lib/jkqtplotter/jkqtpgraphsbarchart.cpp | 18 +- lib/jkqtplotter/jkqtpgraphsbase.cpp | 42 +- lib/jkqtplotter/jkqtpgraphsbase.h | 13 + lib/jkqtplotter/jkqtpgraphsbaseerrors.cpp | 18 +- .../jkqtpgraphsbasestylingmixins.cpp | 4 +- lib/jkqtplotter/jkqtpgraphsboxplot.cpp | 14 +- .../jkqtpgraphsboxplotstylingmixins.cpp | 10 +- lib/jkqtplotter/jkqtpgraphscontour.cpp | 2 +- .../jkqtpgraphsevaluatedfunction.cpp | 10 +- lib/jkqtplotter/jkqtpgraphsfilledcurve.cpp | 4 +- lib/jkqtplotter/jkqtpgraphsimage.cpp | 20 +- lib/jkqtplotter/jkqtpgraphsimageoverlays.cpp | 4 +- lib/jkqtplotter/jkqtpgraphsimagergb.cpp | 18 +- lib/jkqtplotter/jkqtpgraphsimpulses.cpp | 4 +- lib/jkqtplotter/jkqtpgraphspeakstream.cpp | 6 +- lib/jkqtplotter/jkqtpgraphsrange.cpp | 4 +- lib/jkqtplotter/jkqtpgraphsscatter.cpp | 26 +- .../jkqtpgraphssinglecolumnsymbols.cpp | 2 +- lib/jkqtplotter/jkqtpgraphsspecialline.cpp | 4 +- lib/jkqtplottertools/jkqtpdrawingtools.cpp | 4 +- lib/jkqtplottertools/jkqtpdrawingtools.h | 2 +- lib/jkqtplottertools/jkqtpimagetools.cpp | 4 +- .../simpletest_datastore_calccolumns.png | Bin 0 -> 17833 bytes screenshots/simpletest_datastore_image.png | Bin 0 -> 46922 bytes .../simpletest_datastore_linkedcarray.png | Bin 0 -> 17865 bytes screenshots/simpletest_datastore_map.png | Bin 0 -> 14854 bytes screenshots/simpletest_datastore_sine.png | Bin 0 -> 27559 bytes screenshots/simpletest_datastore_sineimg.png | Bin 0 -> 318 bytes screenshots/simpletest_datastore_small.png | Bin 0 -> 19963 bytes 43 files changed, 1409 insertions(+), 260 deletions(-) create mode 100644 examples/simpletest_datastore/README.md create mode 100644 examples/simpletest_datastore/jkqtplotter_simpletest_datastore.cpp create mode 100644 examples/simpletest_datastore/jkqtplotter_simpletest_datastore.pro create mode 100644 examples/simpletest_datastore/jkqtplotter_simpletest_datastore_and_lib.pro create mode 100644 screenshots/simpletest_datastore_calccolumns.png create mode 100644 screenshots/simpletest_datastore_image.png create mode 100644 screenshots/simpletest_datastore_linkedcarray.png create mode 100644 screenshots/simpletest_datastore_map.png create mode 100644 screenshots/simpletest_datastore_sine.png create mode 100644 screenshots/simpletest_datastore_sineimg.png create mode 100644 screenshots/simpletest_datastore_small.png diff --git a/JKQtPlotterBuildAllExamples.pro b/JKQtPlotterBuildAllExamples.pro index 904166e366..0cf25f8440 100644 --- a/JKQtPlotterBuildAllExamples.pro +++ b/JKQtPlotterBuildAllExamples.pro @@ -77,6 +77,7 @@ addSimpleTest(ui) addSimpleTest(boxplot) addSimpleTest(advancedlineandfillstyling) addSimpleTest(imageplot_nodatastore) +addSimpleTest(datastore) #addSimpleTest(rgbimageplot_opencv) #addSimpleTest(imageplot_opencv) diff --git a/doc/dox/examples_and_tutorials.dox b/doc/dox/examples_and_tutorials.dox index 914e18011f..b1b081064a 100644 --- a/doc/dox/examples_and_tutorials.dox +++ b/doc/dox/examples_and_tutorials.dox @@ -143,6 +143,16 @@ All test-projects are Qt-projects that use qmake to build. You can load them int +\subsection jkqtp_extut_datamanagement Data Management + + +
Screenshot Description Notes +
\image html simpletest_datastore_small.png + \subpage JKQTPlotterAdvancedJKQTPDatastore + Advanced Data Management with JKQTPDatastore +
+ + \subsection jkqtp_extut_complexexamples More Complex Examples diff --git a/doc/dox/jkqtplotter.dox b/doc/dox/jkqtplotter.dox index 2208b73bdf..1e2b81fb62 100644 --- a/doc/dox/jkqtplotter.dox +++ b/doc/dox/jkqtplotter.dox @@ -44,6 +44,8 @@ data sources (internal or external memory arrays. Later on it is simply possible using the column number and the not a link to the actual data array, as the link is stored in these classes. +\see \ref JKQTPlotterAdvancedJKQTPDatastore for a detailed description of how to use this class for data management! + \defgroup jkqtpopencvinterface OpenCV Interfaceing Tools \ingroup jkqtpdatastorage diff --git a/examples/README.md b/examples/README.md index 4b06f5f6e6..e9b8419253 100644 --- a/examples/README.md +++ b/examples/README.md @@ -59,12 +59,21 @@ All test-projects are Qt-projects that use qmake to build. You can load them int -## Plot Layout & Styling +## Data Management + +| Screenshot | Description | Notes | +|:-------------:| ------------- | ------------- | +| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/simpletest_datastore_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore) | [Advanced Usage of JKQTPDatastore](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore) | Advanced Data Management with JKQTPDatastore | + + + + +## More Complex Examples | Screenshot | Description | Notes | |:-------------:| ------------- | ------------- | | [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/test_multiplot_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/test_multiplot) | [Layouting Several Plots](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/test_multiplot) | Combining plots in Qt Layouts
linking plot axes
copy data from a `std::map` int the datastore
print plots/print preview | -| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/test_styling_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/test_styling) | [Styling of JKQTPlotter](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/test_styling) | Modifying different Aspects of the Styling of JKQTPlotter | +| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/test_distributionplot_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/test_distributionplot) | [Plotting a Statistical Distribution of Data](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/test_distributionplot) | Combines several different graphs to draw random values, their distribution and some statistical properties | diff --git a/examples/simpletest_datastore/README.md b/examples/simpletest_datastore/README.md new file mode 100644 index 0000000000..c9eacc0a92 --- /dev/null +++ b/examples/simpletest_datastore/README.md @@ -0,0 +1,282 @@ +# Example (JKQTPlotter): Advanced Usage of JKQTPDatastore {#JKQTPlotterAdvancedJKQTPDatastore} +This project (see `./examples/simpletest_datastore/`) explains several advanced options of JKQTPDatastore, which is the class used to centrally store the data for (most) graphs on a JKQTPlotter widget. + + +The source code of the main application can be found in [`jkqtplotter_simpletest_datastore.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore/jkqtplotter_simpletest_datastore.cpp). +This tutorial cites parts of this code to demonstrate different ways of working with data for the graphs. + +In every code-segment below, we will use these two declarations from the code to access the internal datastore of the JKQTPlotter instance: +```.cpp + // 1. create a plotter window and get a pointer to the internal datastore (for convenience) + JKQTPlotter plot; + JKQTPDatastore* datastore=plot.getDatastore(); +``` + +## Copy Data from different data structures into JKQTPDatastore + +### Copy Data from a Vector into a column of the JKQTPDatastore +First we fill data into a QVector for a simple plot (a sine curve) and add a plot using this data: +```.cpp + QVector X, Y; + const int Ndata=100; + for (int i=0; isetXColumn(ds->addCopiedColumn(X, "x")); + linegraph->setYColumn(ds->addCopiedColumn(Y, "y")); + linegraph->setTitle(QObject::tr("sine graph")); +``` +Note that you could also use a `std::vector` instead, as `JKQTPDatastore::addCopiedColumn()` is a template function that only requires the container to support C++ standard iterations (with `begin()` and `end()`), as well as the function `size()` to determine the number of items in the container. Also other datatypes than `double` are possible, as long as they can be converted to `double` (the function `jkqtp_todouble()` is used to perform the conversion, notably all integer and floating-point types, as well as `bool` are supported out of the box). + +The plot from the code above looks like this: + +![simpletest_datastore_sine](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/simpletest_datastore_sine.png) + + +### Copy Data from a C-array into a column of the JKQTPDatastore +Of course if you have your data in a C-array, you can use the same syntax: +```.cpp + #define NDATA 5 + double X[NDATA]= { 1, 2, 3, 4, 5 }; + double Y[NDATA]= { 1, 0, 1, 0, 1 }; + linegraph->setXColumn(datastore->addCopiedColumn(X, NDATA, "x")); + linegraph->setYColumn(datastore->addCopiedColumn(Y, NDATA, "Y")); +``` + + +The plot from the code above looks like this: + +![simpletest_datastore_linkedcarray](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/simpletest_datastore_linkedcarray.png) + + +### Copy Data from a Map into a JKQTPDatastore +Since graphs often display (x,y)-pairs, it may make sense to store them in a map (e.g. for histograms). There there are also functions that copy the contents of a map into a JKQTPDatastore, resulting in two columns beeing added: +```.cpp + std::map datamap; + datamap[1]=1.1; + datamap[2]=1.4; + datamap[4]=1.2; + datamap[5]=1.8; + datamap[7]=0.9; + plot.addGraph(linegraph=new JKQTPXYLineGraph(&plot)); + linegraph->setXYColumns(datastore->addCopiedMap(datamap, "map_x", "map_y")); + linegraph->setTitle(QObject::tr("copied map")); +``` +This code results in a graph like this: + +![simpletest_datastore_map](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/simpletest_datastore_map.png) + + +## Reference External Data in a column of the JKQTPDatastore + +### Referencing without transfer of ownership +As an alternative to the method of copying data (see above), you could also just link the data. For this to work, the data has to reside in a C-array of type `double`, as this is the internal datatype of the `JKQTPDatastore`. You can simply replace the two lines with `JKQTPDatastore::addCopiedColumn()` in the example above by (we exploit the fact that `QVector::data()` returns a pointer to the internal C-array of the vector): +```.cpp + linegraph->setXColumn(datastore->addColumn(X.data(), X.size(), "x")); + linegraph->setYColumn(datastore->addColumn(Y.data(), Y.size(), "Y")); +``` +Of course if you have your data in a C-array, you can use the same syntax: +```.cpp + #define NDATA 5 + double XCA[NDATA]= { 1, 2, 3, 4, 5 }; + double YCA[NDATA]= { 1, 0, 1, 0, 1 }; + linegraph->setXColumn(datastore->addColumn(XCA, NDATA, "x")); + linegraph->setYColumn(datastore->addColumn(YCA, NDATA, "Y")); +``` +This method is especially useful, when you have large datasets (e.g. images) that you don't want to copy around. + +Note however that the ownership of the data is not transfered to the JKQTPDatastore, i.e. you have to ensure that the data behind the pointer exists, as long as the column references the array and you have to ensure that the data is freed, when you don't need it any more. + +The plot from the code above looks like this: + +![simpletest_datastore_linkedcarray](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/simpletest_datastore_linkedcarray.png) + +### Referencing with transfer of ownership +In addition to the variants of `JKQTPDatastore::addColumn()`, that do not transfer ownership of the data to the `JKQTPDatastore`, you can also use `JKQTPDatastore::addInternalColumn()`, which tells the `JKQTPDatastore` to use the external data array and also take over its owner-ship. This implies that the array is freed when the `JKQTPDatastore` is destroyed, by calling `free()` in the array. Therefor data for this method needs to be allocated by using `malloc()` or `calloc()`: +```.cpp + #define NDATA 5 + double* XCA=(double*)malloc(NDATA, sizeof(double)); + ... + linegraph->setXColumn(datastore->addInternalColumn(XCA, NDATA, "x")); +``` + + +## JKQTPDatastore-internal Data Management +It is also possible to leave the data mangement completely to the JKQTPDatastore and just edit the data with access functions from JKQTPDatastore. + +### Generating Columns Non-Initialized Columns and Filling Them +The most basic way to generate data for a plot is to generate two non-initialized columns for the x- and y-coordinates of the graph points +```.cpp + const int Ndata=100; + size_t colX=datastore->addColumn(Ndata, "x"); + size_t colY=datastore->addColumn(Ndata, "y"); +``` +These calls to JKQTPDatastore::addColumn() will generate a column each with with `ndata` uninitialized entries. +Then use JKQTPDatastore::set() to fill them with data: +```.cpp + for (int i=0; iset(colX, i, x); + datastore->set(colY, i, sin(x)); + } +``` + +Plotting these two columns versus each other results in a simple sine graph: + +![simpletest_datastore_sine](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/simpletest_datastore_sine.png) + + + +### Generating Columns Preinitialized Columns +For your convenience there are also function that simply create such a linear vector with one call: +```.cpp + size_t colLinX=datastore->addLinearColumn(count, 0, 20, "x_lin"); +``` +This call is equivalent to +```.cpp + for (size_t i=0; iset(colLinX, i, 0.0+static_cast(i)/static_cast(count-1)*20.0); + } +``` + +In both cases the column `colLinX` will contain these numbers afterwards: +``` + 0, 0.512821, 1.02564, 1.53846, 2.05128, 2.5641, 3.07692, ..., 18.9744, 19.4872, 20 +``` + +Comparable functions exist for logarithmically spaced columns (`JKQTPDatastore::addLogColumn()` and `JKQTPDatastore::addDecadeLogColumn()` which only differ in the way the start and end values are specified): +```.cpp + size_t colLogX=datastore->addLogColumn(30, 1, 1000, "x_log"); +``` +This call results in a column with these 30 values spanning the range between 1 and 1000: +``` + 1, 1.26896, 1.61026, 2.04336, ..., 8.53168, 10.8264, 13.7382, ..., 72.7895, 92.3671, ..., 788.046, 1000 +``` + +### Using Data from one Column to Calculate Another + +After generating columns, as shown above, you can also use the data in these columns to calculate a second column based on the values in the first. You can do this explicitly: +```.cpp + size_t colLinX=datastore->addLinearColumn(40, 0, 20, "x_lin"); + size_t colFunc1=datastore->addColumn(datastore->getRows(colLinX), "cos(x_lin)*x_lin/20.0"); + for (size_t i=0; igetRows(colLinX); i++) { + double x=datastore->get(colLinX, i); + datastore->set(colFunc1, i, cos(x)*x/20.0); + } +``` +In this example the function JKQTPDatastore::get() is used to retrieve a value from a column and then JKQTPDatastore::set() is used to store a new value calculated from the read values into another column. + +Or use a special function that gets a column with values and a functor f: double->double as parameters: +```.cpp + size_t colLinX=datastore->addLinearColumn(40, 0, 20, "x_lin"); + size_t colFunc1=datastore->addColumnCalculatedFromColumn(colLinX, [](double x)->double { return cos(x)*x/20.0; }, "cos(x_lin)*x_lin/20.0"); +``` + +Plotting these two columns versus each other +```.cpp + plot.addGraph(linegraph=new JKQTPXYLineGraph(&plot)); + linegraph->setXColumn(colLinX); + linegraph->setYColumn(colFunc1); + linegraph->setTitle(QObject::tr("calculated column(s)")); +``` + +results in: + +![simpletest_datastore_calccolumns](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/simpletest_datastore_calccolumns.png) + + +### 2D-Datasets and Images +There is also a function `JKQTPDatastore::addLinearGridColumns(size_t width, double startX, double endX, size_t height, double startY, double endY, const QString &nameX, const QString &nameY)` that generate two columns simultaneously that conatin the x- and y-coordinates of the points on a rectangular grid, in a column-major order: +```.cpp + std::pair colLinXY=datastore->addLinearGridColumns(10, 10, 20, 10, 1.5, 3); +``` +This call will generate these columns with x-values scaling between 10 and 20 (in 10 steps) and y-values between 1.5 and 3 (also in 10 steps): +``` + x y + ---------------------- + 10, 1.5 + 11.1111, 1.5 + 12.2222, 1.5 + 13.3333, 1.5 + 14.4444, 1.5 + 15.5556, 1.5 + 16.6667, 1.5 + 17.7778, 1.5 + 18.8889, 1.5 + 20, 1.5 + 10, 1.66667 + 11.1111, 1.66667 + 12.2222, 1.66667 + 13.3333, 1.66667 + 14.4444, 1.66667 + 15.5556, 1.66667 + 16.6667, 1.66667 + 17.7778, 1.66667 + 18.8889, 1.66667 + 20, 1.66667 + 10, 1.83333 + 11.1111, 1.83333 + ... ... + ... ... + 18.8889, 3 + 20, 3 +``` +Such x-y-coordinate columns are especially usefull when calculating data for an image (plotted by `JKQTPColumnMathImage`), or for a parametrized scatter plot (plotted by `JKQTPXYParametrizedScatterGraph`). To demonstrate this, we can can add another column with 10*10=100 entries and fill it with some values calculated from the the x and y-values in colLinXY: +```.cpp + size_t imgColumn=datastore->addImageColumn(10, 10, "image values"); + for (size_t i=0; igetRows(imgColumn); i++) { + double x=datastore->get(colLinXY.first, i); + double y=datastore->get(colLinXY.second, i); + datastore->set(imgColumn, i, cos((x-15.0))/(x-15.0)*cos((y-2.0))/(x-2.0)); + } +``` +Note that we used `JKQTPDatastore::addImageColumn(width,height,name)` here, which generates a column with `width*height` entries that are meant to be interpreted as a 2D image with width columns and height rows. This function therefore also save this image size in the column metadata and graphs can later extract this dimension and use it to set them up internally (e.g. JKQTPColumnMathImage::setImageColumn() does just that). +In the example above we used the simple JKQTPDatastore::set() store values into our image column. This function treats the column as linearized in row-major order. Alternatively you can also use code like this to access the imageColumn as a real image or matrix: +```.cpp + for (int iy=0; iy<10; iy++) { + for (int ix=0; ix<10; ix++) { + datastore->setPixel(imgColumn, ix, iy, sin(ix*iy)); + } + } +``` +Drawing this column as an image (using JKQTPColumnMathImage) will look like this: + +![simpletest_datastore_sineimg](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/simpletest_datastore_sineimg.png) + +In these examples, the iteration and calculation is written out with an explicit for-loop. JKQTPDatastore also offers a shortcut function for this that accepts a functor with the calculation only: +```.cpp + imgColumn=datastore->addColumnCalculatedFromColumn(colLinXY.first, colLinXY.second, + [](double x, double y)->double { + return cos((x-15.0))/(x-15.0)*cos((y-2.0))/(x-2.0); + }, + "image value"); +``` + +Then you can plot these as a parametrized scatter graph (`JKQTPXYParametrizedScatterGraph`) using: +```.cpp + plot.addGraph(paramscattergraph=new JKQTPXYParametrizedScatterGraph(&plot)); + paramscattergraph->setXYColumns(colLinXY); + paramscattergraph->setColorColumn(imgColumn); + paramscattergraph->setTitle(QObject::tr("parametrized scatter")); +``` +... or alternatively you can only use the column imgColumn in a `JKQTPColumnMathImage` +```.cpp + plot.addGraph(imggraph=new JKQTPColumnMathImage(&plot)); + imggraph->setImageColumn(imgColumn); + imggraph->setX(21); + imggraph->setY(1.5); + imggraph->setWidth(10); + imggraph->setHeight(1.5); + imggraph->setTitle(QObject::tr("imgColumn")); +``` +The result will look like this: + +![simpletest_datastore_image](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/simpletest_datastore_image.png) + + diff --git a/examples/simpletest_datastore/jkqtplotter_simpletest_datastore.cpp b/examples/simpletest_datastore/jkqtplotter_simpletest_datastore.cpp new file mode 100644 index 0000000000..9853b49475 --- /dev/null +++ b/examples/simpletest_datastore/jkqtplotter_simpletest_datastore.cpp @@ -0,0 +1,140 @@ +/** \example jkqtplotter_simpletest_datastore.cpp + * A very basic example for the usage of JKQTPlotter + * + * \ref JKQTPlottersimpletest_datastore + */ + +#include +#include "jkqtplotter/jkqtplotter.h" +#include "jkqtplotter/jkqtpgraphsscatter.h" +#include "jkqtplotter/jkqtpgraphsimage.h" + + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + + // 1. create a plotter window and get a pointer to the internal datastore (for convenience) + JKQTPlotter plot; + JKQTPDatastore* datastore=plot.getDatastore(); + + JKQTPXYLineGraph* linegraph; + JKQTPColumnMathImage* imggraph; + JKQTPXYParametrizedScatterGraph* paramscattergraph; + + // 2. first we create data inside a QVector for a simple plot (a sine curve) ... and add the plot + // note that you could use a std::vector equally well + QVector X, Y; + const int Ndata=100; + for (int i=0; isetXColumn(datastore->addCopiedColumn(X, "x")); + linegraph->setYColumn(datastore->addCopiedColumn(Y, "y")); + // alternatively you can also tell JKQTPDatastore to just reference an external array: + //linegraph->setXColumn(datastore->addColumn(X.data(), X.size(), "x")); + //linegraph->setYColumn(datastore->addColumn(Y.data(), Y.size(), "Y")); + linegraph->setTitle(QObject::tr("sine graph")); + + + + // 3. Now we generate a plot from data in a C-array, just reference in the JKQTPDatastore + #define NDATA 5 + double XCA[NDATA]= { 1, 2, 3, 4, 5 }; + double YCA[NDATA]= { 1, 0, 1, 0, 1 }; + plot.addGraph(linegraph=new JKQTPXYLineGraph(&plot)); + linegraph->setXColumn(datastore->addColumn(XCA, NDATA, "xca (C-array)")); + linegraph->setYColumn(datastore->addColumn(YCA, NDATA, "yca (C-array)")); + // of course you could also simply copy the data with a comparable syntax: + //linegraph->setXColumn(datastore->addCopiedColumn(XCA, NDATA, "xca (C-array)")); + //linegraph->setYColumn(datastore->addCopiedColumn(YCA, NDATA, "yca (C-array)")); + linegraph->setTitle(QObject::tr("linked C-array data")); + + + // 4. Since graphs often display (x,y)-pairs, it may make sense to store them in a map (e.g. for histograms) + // There there are also functions that copy the contents of a map into a JKQTPDatastore, resulting in + // two columns beeing added: + std::map datamap; + datamap[1]=1.1; + datamap[2]=1.4; + datamap[4]=1.2; + datamap[5]=1.8; + datamap[7]=0.9; + plot.addGraph(linegraph=new JKQTPXYLineGraph(&plot)); + linegraph->setXYColumns(datastore->addCopiedMap(datamap, "map_x", "map_y")); + linegraph->setTitle(QObject::tr("copied map")); + + + + // 5. It is also possible to leave the data mangement completely to the JKQTPDatastore + // and just edit the data with access functions from JKQTPDatastore: + plot.addGraph(linegraph=new JKQTPXYLineGraph(&plot)); + // 5.1 this adds a column with 40 values, linearly spaced between 0 and 20 (inclusive). + size_t colLinX=datastore->addLinearColumn(40, 0, 20, "x_lin"); + // 5.2 this adds a column with one entry for every entry x in the column colLinX, where + // the entry is calculated by applying a function cos(x)*x/20.0 + size_t colFunc1=datastore->addColumnCalculatedFromColumn(colLinX, [](double x)->double { return cos(x)*x/20.0; }, "cos(x_lin)*x_lin/20.0"); + // the same can be done by this code explicitly: + // 5.2.1 add a column with as many rows as column colLinX + //size_t colFunc1=datastore->addColumn(datastore->getRows(colLinX), "cos(x_lin)*x_lin/20.0"); + // 5.2.2 iterate over the data in column colLinX and set a newly calculated value into a row of column colFunc1 + //for (size_t i=0; igetRows(colLinX); i++) { + // double x=datastore->get(colLinX, i); + // datastore->set(colFunc1, i, cos(x)*x/20.0); + //} + linegraph->setXColumn(colLinX); + linegraph->setYColumn(colFunc1); + linegraph->setTitle(QObject::tr("calculated column(s)")); + + + // 6. The function addLinearGridColumns() generates a rectangular 2D grid of coordinates + // in row-major order. Here we generate a 10x10 grid with x-coordinates between 10 and 20 (inclusive) + // and y-coordinates between 1.5 and 3: + std::pair colLinXY=datastore->addLinearGridColumns(10, 10, 20, 10, 1.5, 3); + // now we can add another column with 10*10=100 entries and fill it with some values + // calculated from the the x and y-values in colLinXY: + size_t imgColumn=datastore->addImageColumn(10, 10, "image values"); + for (size_t i=0; igetRows(imgColumn); i++) { + double x=datastore->get(colLinXY.first, i); + double y=datastore->get(colLinXY.second, i); + datastore->set(imgColumn, i, cos((x-15.0))/(x-15.0)*cos((y-2.0))/(x-2.0)); + } + // alternatively you can access image pixels with setPixel(): + //for (int iy=0; iy<10; iy++) { + // for (int ix=0; ix<10; ix++) { + // datastore->setPixel(imgColumn, ix, iy, sin(ix*iy/30.0)); + // } + //} + // the loop above can be written more compact using addColumnCalculatedFromColumn(): + //imgColumn=datastore->addColumnCalculatedFromColumn(colLinXY.first, colLinXY.second, [](double x, double y)->double { return cos((x-15.0))/(x-15.0)*cos((y-2.0))/(x-2.0); }, "image value"); + // finally we can use a JKQTPXYParametrizedScatterGraph to display the data from our three columns + // by using colLinXY->first and colLinXY->second as positions for symbols that are colored, based + // on the respective value in imgColumn: + plot.addGraph(paramscattergraph=new JKQTPXYParametrizedScatterGraph(&plot)); + paramscattergraph->setXYColumns(colLinXY); + paramscattergraph->setColorColumn(imgColumn); + paramscattergraph->setTitle(QObject::tr("parametrized scatter")); + // alternatively you can only use the column imgColumn in a JKQTPColumnMathImage + plot.addGraph(imggraph=new JKQTPColumnMathImage(&plot)); + imggraph->setImageColumn(imgColumn); + imggraph->setX(21); + imggraph->setY(1.5); + imggraph->setWidth(10); + imggraph->setHeight(1.5); + imggraph->setTitle(QObject::tr("imgColumn")); + + + + // 6. autoscale the plot so the graph is contained + plot.zoomToFit(); + + // show plotter and make it a decent size + plot.show(); + plot.resize(600,400); + + return app.exec(); +} diff --git a/examples/simpletest_datastore/jkqtplotter_simpletest_datastore.pro b/examples/simpletest_datastore/jkqtplotter_simpletest_datastore.pro new file mode 100644 index 0000000000..4e7258517e --- /dev/null +++ b/examples/simpletest_datastore/jkqtplotter_simpletest_datastore.pro @@ -0,0 +1,26 @@ +# source code for this simple demo +SOURCES = jkqtplotter_simpletest_datastore.cpp + +# configure Qt +CONFIG += link_prl qt +QT += core gui xml svg +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport + +# output executable name +TARGET = jkqtplotter_simpletest_datastore + +# include JKQTPlotter source headers and link against library +DEPENDPATH += ../../lib ../../staticlib/jkqtplotterlib +INCLUDEPATH += ../../lib +CONFIG (debug, debug|release) { + LIBS += -L../../staticlib/jkqtplotterlib/debug -ljkqtplotterlib_debug +} else { + LIBS += -L../../staticlib/jkqtplotterlib/release -ljkqtplotterlib +} +message("LIBS = $$LIBS") + +win32-msvc*: DEFINES += _USE_MATH_DEFINES + + + + diff --git a/examples/simpletest_datastore/jkqtplotter_simpletest_datastore_and_lib.pro b/examples/simpletest_datastore/jkqtplotter_simpletest_datastore_and_lib.pro new file mode 100644 index 0000000000..01630b7267 --- /dev/null +++ b/examples/simpletest_datastore/jkqtplotter_simpletest_datastore_and_lib.pro @@ -0,0 +1,8 @@ +TEMPLATE = subdirs + +SUBDIRS += jkqtplotterlib jkqtplotter_simpletest_datastore + +jkqtplotterlib.file = ../../staticlib/jkqtplotterlib/jkqtplotterlib.pro + +jkqtplotter_simpletest_datastore.file=$$PWD/jkqtplotter_simpletest_datastore.pro +jkqtplotter_simpletest_datastore.depends = jkqtplotterlib diff --git a/examples/simpletest_filledgraphs/README.md b/examples/simpletest_filledgraphs/README.md index 338a7a6190..6a23262d2c 100644 --- a/examples/simpletest_filledgraphs/README.md +++ b/examples/simpletest_filledgraphs/README.md @@ -15,20 +15,12 @@ And three columns with 256 entries each, which will be filled with the R-, G- an size_t columnB=ds->addColumn(256, "historam_B"); ``` -In this example we will access the data in the internal datastore directly. This access is possible through objects of type JKQTPColumn, which is a proxy to the data in one of the columns in a `JKQTdatastore`: - -```.cpp - JKQTPColumn cG=ds->getColumn(columnG); - JKQTPColumn cR=ds->getColumn(columnR); - JKQTPColumn cB=ds->getColumn(columnB); -``` - In order to calculate the histograms, first all enries in the columns are set to 0: ```.cpp - cR.setAll(0); - cG.setAll(0); - cB.setAll(0); + ds->setAll(columnG, 0); + ds->setAll(columnR, 0); + ds->setAll(columnB, 0); ``` Finally the histogram is calculated: @@ -38,14 +30,14 @@ Finally the histogram is calculated: for (int y=0; yinc(columnR, qRed(pix), 1); + ds->inc(columnG, qGreen(pix), 1); + ds->inc(columnB, qBlue(pix), 1); } } - cR.scale(100.0/static_cast(image.width()*image.height())); - cG.scale(100.0/static_cast(image.width()*image.height())); - cB.scale(100.0/static_cast(image.width()*image.height())); + ds->scaleColumnValues(columnR, 100.0/static_cast(image.width()*image.height())); + ds->scaleColumnValues(columnG, 100.0/static_cast(image.width()*image.height())); + ds->scaleColumnValues(columnB, 100.0/static_cast(image.width()*image.height())); ``` Finally three `JKQTPFilledCurveXGraph` objects are generated and added to the plot (here we show the code for the R-channel only): diff --git a/examples/simpletest_filledgraphs/jkqtplotter_simpletest_filledgraphs.cpp b/examples/simpletest_filledgraphs/jkqtplotter_simpletest_filledgraphs.cpp index ccf5e005e1..6b8c0d2cf3 100644 --- a/examples/simpletest_filledgraphs/jkqtplotter_simpletest_filledgraphs.cpp +++ b/examples/simpletest_filledgraphs/jkqtplotter_simpletest_filledgraphs.cpp @@ -25,15 +25,10 @@ int main(int argc, char* argv[]) size_t columnR=ds->addColumn(256, "historam_R"); size_t columnG=ds->addColumn(256, "historam_G"); size_t columnB=ds->addColumn(256, "historam_B"); - // - in addition JKQTPColumn objects are generated, which can be used to access - // the data in the columns - JKQTPColumn cG=ds->getColumn(columnG); - JKQTPColumn cR=ds->getColumn(columnR); - JKQTPColumn cB=ds->getColumn(columnB); // - now all columns for RGB are initialized to 0 - cR.setAll(0); - cG.setAll(0); - cB.setAll(0); + ds->setAll(columnG, 0); + ds->setAll(columnR, 0); + ds->setAll(columnB, 0); // 3. now we open a BMP-file and load it into a QImage QImage image(":/example.bmp"); @@ -41,15 +36,15 @@ int main(int argc, char* argv[]) for (int y=0; yinc(columnR, qRed(pix), 1); + ds->inc(columnG, qGreen(pix), 1); + ds->inc(columnB, qBlue(pix), 1); } } // ... and normalize histograms - cR.scale(100.0/static_cast(image.width()*image.height())); - cG.scale(100.0/static_cast(image.width()*image.height())); - cB.scale(100.0/static_cast(image.width()*image.height())); + ds->scaleColumnValues(columnR, 100.0/static_cast(image.width()*image.height())); + ds->scaleColumnValues(columnG, 100.0/static_cast(image.width()*image.height())); + ds->scaleColumnValues(columnB, 100.0/static_cast(image.width()*image.height())); // 4. now we add three semi-transparent, filled curve plots, one for each histogram diff --git a/lib/jkqtplotter/jkqtpbaseplotter.cpp b/lib/jkqtplotter/jkqtpbaseplotter.cpp index 0cb1136132..adccb96f51 100644 --- a/lib/jkqtplotter/jkqtpbaseplotter.cpp +++ b/lib/jkqtplotter/jkqtpbaseplotter.cpp @@ -957,11 +957,11 @@ JKQTBasePlotter::JKQTPPen JKQTBasePlotter::getPlotStyle(int i) const{ p.setFillColor(JKQTPGetDerivedColor(plotterStyle.graphFillColorDerivationMode, p.color())); p.setErrorLineColor(JKQTPGetDerivedColor(plotterStyle.graphErrorColorDerivationMode, p.color())); p.setErrorFillColor(JKQTPGetDerivedColor(plotterStyle.graphErrorFillColorDerivationMode, p.errorColor())); - p.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, plotterStyle.defaultGraphWidth)); - p.setErrorLineWidth(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, plotterStyle.defaultGraphWidth)); - p.setSymbolSize(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, plotterStyle.defaultGraphSymbolSize)); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, plotterStyle.defaultGraphWidth)); + p.setErrorLineWidth(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, plotterStyle.defaultGraphWidth)); + p.setSymbolSize(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, plotterStyle.defaultGraphSymbolSize)); p.setSymbolFillColor(JKQTPGetDerivedColor(plotterStyle.graphFillColorDerivationMode, p.color())); - p.setSymbolLineWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, plotterStyle.defaultGraphSymbolLineWidth)); + p.setSymbolLineWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, plotterStyle.defaultGraphSymbolLineWidth)); p.setStyle(plotterStyle.defaultGraphPenStyles[styleI]); p.setSymbolType(plotterStyle.defaultGraphSymbols[symbolI]); p.setFillStyle(plotterStyle.defaultGraphFillStyles[brushI]); @@ -1088,7 +1088,7 @@ void JKQTBasePlotter::drawKey(JKQTPEnhancedPainter& painter) { painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();}); QPen pf=painter.pen(); pf.setColor(plotterStyle.keyStyle.frameColor); - pf.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, pt2px(painter, plotterStyle.keyStyle.frameWidth*lineWidthMultiplier))); + pf.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, pt2px(painter, plotterStyle.keyStyle.frameWidth*lineWidthMultiplier))); pf.setStyle(Qt::SolidLine); painter.setBrush(plotterStyle.keyStyle.backgroundBrush); @@ -1162,7 +1162,7 @@ void JKQTBasePlotter::drawPlot(JKQTPEnhancedPainter& painter, bool showOverlays) if (plotterStyle.plotFrameVisible) { painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();}); QPen p(plotterStyle.plotFrameColor); - p.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, pt2px(painter, plotterStyle.plotFrameWidth*lineWidthMultiplier))); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, pt2px(painter, plotterStyle.plotFrameWidth*lineWidthMultiplier))); painter.setPen(p); painter.setBrush(plotterStyle.plotBackgroundBrush); if (plotterStyle.plotFrameRounding<=0) { diff --git a/lib/jkqtplotter/jkqtpcoordinateaxes.cpp b/lib/jkqtplotter/jkqtpcoordinateaxes.cpp index 86ca151623..3bf7e70deb 100644 --- a/lib/jkqtplotter/jkqtpcoordinateaxes.cpp +++ b/lib/jkqtplotter/jkqtpcoordinateaxes.cpp @@ -1148,11 +1148,11 @@ void JKQTPVerticalAxis::drawGrids(JKQTPEnhancedPainter& painter) { painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();}); QPen pg=painter.pen(); pg.setColor(axisStyle.gridColor); - pg.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.gridWidth*parent->getLineWidthMultiplier()))); + pg.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.gridWidth*parent->getLineWidthMultiplier()))); pg.setStyle(axisStyle.gridStyle); QPen pmg=painter.pen(); pmg.setColor(axisStyle.minorGridColor); - pmg.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, axisStyle.minorGridWidth*parent->getLineWidthMultiplier()))); + pmg.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, axisStyle.minorGridWidth*parent->getLineWidthMultiplier()))); pmg.setStyle(axisStyle.minorGridStyle); //double top=x2p(axismax); //double bottom=x2p(axismin); @@ -1343,14 +1343,14 @@ void JKQTPVerticalAxis::drawAxes(JKQTPEnhancedPainter& painter) { painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();}); QPen pmain=painter.pen(); pmain.setColor(axisStyle.axisColor); - pmain.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.lineWidth*parent->getLineWidthMultiplier()))); + pmain.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.lineWidth*parent->getLineWidthMultiplier()))); pmain.setStyle(Qt::SolidLine); QPen ptick=pmain; - ptick.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.tickWidth*parent->getLineWidthMultiplier()))); + ptick.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.tickWidth*parent->getLineWidthMultiplier()))); QPen pmtick=ptick; - pmtick.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.minorTickWidth*parent->getLineWidthMultiplier()))); + pmtick.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.minorTickWidth*parent->getLineWidthMultiplier()))); getParentMathText()->setFontSize(this->axisStyle.tickLabelFontSize*parent->getFontSizeMultiplier()); getParentMathText()->setFontRomanOrSpecial(getParent()->getCurrentPlotterStyle().defaultFontName); @@ -1364,7 +1364,7 @@ void JKQTPVerticalAxis::drawAxes(JKQTPEnhancedPainter& painter) { JKQTPAutoOutputTimer jkaat(QString("JKQTPEnhancedPainter[%1]::drawAxes(): 0Axis").arg(objectName())); #endif QPen pmain1=pmain; - pmain1.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.lineWidthZeroAxis*parent->getLineWidthMultiplier()))); + pmain1.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.lineWidthZeroAxis*parent->getLineWidthMultiplier()))); pmain1.setColor(axisStyle.colorZeroAxis); pmain1.setStyle(axisStyle.styleZeroAxis); painter.setPen(pmain1); @@ -1771,11 +1771,11 @@ void JKQTPHorizontalAxis::drawGrids(JKQTPEnhancedPainter& painter) { } QPen pg=painter.pen(); pg.setColor(axisStyle.gridColor); - pg.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.gridWidth*parent->getFontSizeMultiplier()))); + pg.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.gridWidth*parent->getFontSizeMultiplier()))); pg.setStyle(axisStyle.gridStyle); QPen pmg=painter.pen(); pmg.setColor(axisStyle.minorGridColor); - pmg.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.minorGridWidth*parent->getLineWidthMultiplier()))); + pmg.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.minorGridWidth*parent->getLineWidthMultiplier()))); pmg.setStyle(axisStyle.minorGridStyle); double x=tickStart; @@ -1926,14 +1926,14 @@ void JKQTPHorizontalAxis::drawAxes(JKQTPEnhancedPainter& painter) { QPen pmain=painter.pen(); pmain.setColor(axisStyle.axisColor); - pmain.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.lineWidth*parent->getLineWidthMultiplier()))); + pmain.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.lineWidth*parent->getLineWidthMultiplier()))); pmain.setStyle(Qt::SolidLine); QPen ptick=pmain; - ptick.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.tickWidth*parent->getLineWidthMultiplier()))); + ptick.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.tickWidth*parent->getLineWidthMultiplier()))); QPen pmtick=ptick; - pmtick.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.minorTickWidth*parent->getLineWidthMultiplier()))); + pmtick.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.minorTickWidth*parent->getLineWidthMultiplier()))); getParentMathText()->setFontSize(this->axisStyle.tickLabelFontSize*parent->getFontSizeMultiplier()); getParentMathText()->setFontRomanOrSpecial(getParent()->getCurrentPlotterStyle().defaultFontName); @@ -1947,7 +1947,7 @@ void JKQTPHorizontalAxis::drawAxes(JKQTPEnhancedPainter& painter) { JKQTPAutoOutputTimer jkaat(QString("JKQTPHorizontalAxis[%1]::drawAxes(): 0Axis").arg(objectName())); #endif QPen pmain1=pmain; - pmain1.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.lineWidthZeroAxis*parent->getLineWidthMultiplier()))); + pmain1.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, axisStyle.lineWidthZeroAxis*parent->getLineWidthMultiplier()))); pmain1.setColor(axisStyle.colorZeroAxis); pmain1.setStyle(axisStyle.styleZeroAxis); painter.setPen(pmain1); diff --git a/lib/jkqtplotter/jkqtpdatastorage.cpp b/lib/jkqtplotter/jkqtpdatastorage.cpp index 6dfcdc3f74..d6e1486fd7 100644 --- a/lib/jkqtplotter/jkqtpdatastorage.cpp +++ b/lib/jkqtplotter/jkqtpdatastorage.cpp @@ -22,7 +22,7 @@ #include "jkqtplotter/jkqtpdatastorage.h" #include #include - +#include /************************************************************************************************************************** * JKQTPColumn @@ -34,23 +34,39 @@ JKQTPColumn::JKQTPColumn() name=""; datastoreItem=0; datastoreOffset=0; + imageColumns=1; valid=false; } //////////////////////////////////////////////////////////////////////////////////////////////// -JKQTPColumn::JKQTPColumn(JKQTPDatastore *datastore, const QString &name, size_t datastoreItem, size_t datastoreOffset) +JKQTPColumn::JKQTPColumn(JKQTPDatastore *datastore, const QString &name, size_t datastoreItem, size_t datastoreOffset, size_t imageColumns) { this->datastore=datastore; this->datastoreItem=datastoreItem; this->datastoreOffset=datastoreOffset; + this->imageColumns=imageColumns; this->name=name; valid=true; } //////////////////////////////////////////////////////////////////////////////////////////////// -JKQTPColumn::~JKQTPColumn() -= default; +void JKQTPColumn::setName(const QString &__value) +{ + this->name = __value; +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +QString JKQTPColumn::getName() const +{ + return this->name; +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +void JKQTPColumn::setImageColumns(size_t __value) +{ + imageColumns=__value; +} //////////////////////////////////////////////////////////////////////////////////////////////// size_t JKQTPColumn::getRows() const { @@ -261,6 +277,11 @@ JKQTPDatastore::JKQTPDatastore() clear(); } +//////////////////////////////////////////////////////////////////////////////////////////////// +JKQTPDatastore::~JKQTPDatastore() { + clear(); +} + //////////////////////////////////////////////////////////////////////////////////////////////// void JKQTPDatastore::clear(){ maxItemID=0; @@ -310,6 +331,7 @@ void JKQTPDatastore::deleteAllPrefixedColumns(QString prefix, bool removeItems) } + //////////////////////////////////////////////////////////////////////////////////////////////// void JKQTPDatastore::deleteColumn(size_t column, bool removeItems) { if (removeItems) { @@ -475,7 +497,31 @@ size_t JKQTPDatastore::addInternalColumn(double* data, size_t rows, const QStrin //std::cout<<"adding column\n"; size_t it=addItem(new JKQTPDatastoreItem(JKQTPSingleColumn, data, 1, rows,true)); return addColumnForItem(it, 0, name); -}; +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +size_t JKQTPDatastore::addImageColumn(size_t width, size_t height, const QString &name) +{ + size_t col= addColumn(width*height, name); + columns[col].setImageColumns(width); + return col; +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +size_t JKQTPDatastore::addImageColumn(double *data, size_t width, size_t height, const QString &name) +{ + size_t col= addColumn(data, width*height, name); + columns[col].setImageColumns(width); + return col; +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +size_t JKQTPDatastore::addInternalImageColumn(double *data, size_t width, size_t height, const QString &name) +{ + size_t col= addInternalColumn(data, width*height, name); + columns[col].setImageColumns(width); + return col; +} @@ -524,6 +570,102 @@ size_t JKQTPDatastore::addLinearColumn(size_t rows, double start, double end, co return addColumnForItem(itemid, 0, name); } +//////////////////////////////////////////////////////////////////////////////////////////////// +size_t JKQTPDatastore::addLogColumn(size_t rows, double start, double end, const QString &name) +{ + JKQTPDatastoreItem* it=new JKQTPDatastoreItem(1, rows); + const double x0=log(start)/log(10.0); + const double x1=log(end)/log(10.0); + for (size_t i=0; iset(0, i, pow(10.0, x0+static_cast(i)/static_cast(rows-1)*(x1-x0))); + } + size_t itemid= addItem(it); + return addColumnForItem(itemid, 0, name); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +size_t JKQTPDatastore::addDecadeLogColumn(size_t rows, double startDecade, double endDecade, const QString &name) +{ + JKQTPDatastoreItem* it=new JKQTPDatastoreItem(1, rows); + const double x0=startDecade; + const double x1=endDecade; + for (size_t i=0; iset(0, i, pow(10.0, x0+static_cast(i)/static_cast(rows-1)*(x1-x0))); + } + size_t itemid= addItem(it); + return addColumnForItem(itemid, 0, name); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +std::pair JKQTPDatastore::addLinearGridColumns(size_t width, double startX, double endX, size_t height, double startY, double endY, const QString &nameX, const QString &nameY) +{ + const double decX=(endX-startX)/static_cast(width-1); + const double decY=(endY-startY)/static_cast(height-1); + double y=startY; + size_t cx=addColumn(width*height, nameX); + size_t cy=addColumn(width*height, nameY); + size_t i=0; + for (size_t iy=0; iy &f, const QString &name) +{ + JKQTPDatastoreItem* it=new JKQTPDatastoreItem(1, rows); + for (size_t i=0; iset(0, i, f(i, this)); + } + size_t itemid= addItem(it); + return addColumnForItem(itemid, 0, name); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +size_t JKQTPDatastore::addCalculatedColumn(size_t rows, const std::function &f, const QString &name) +{ + JKQTPDatastoreItem* it=new JKQTPDatastoreItem(1, rows); + for (size_t i=0; iset(0, i, f(i)); + } + size_t itemid= addItem(it); + return addColumnForItem(itemid, 0, name); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +size_t JKQTPDatastore::addColumnCalculatedFromColumn(size_t otherColumn, const std::function &f, const QString &name) +{ + const JKQTPColumn& oc=columns.value(otherColumn); + JKQTPDatastoreItem* it=new JKQTPDatastoreItem(1, oc.getRows()); + for (size_t i=0; iset(0, i, f(oc.getValue(i))); + } + size_t itemid= addItem(it); + return addColumnForItem(itemid, 0, name); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +size_t JKQTPDatastore::addColumnCalculatedFromColumn(size_t otherColumnX, size_t otherColumnY, const std::function &f, const QString &name) +{ + const JKQTPColumn& ocx=columns.value(otherColumnX); + const JKQTPColumn& ocy=columns.value(otherColumnY); + const size_t N= qMin(ocx.getRows(), ocy.getRows()); + JKQTPDatastoreItem* it=new JKQTPDatastoreItem(1, N); + for (size_t i=0; iset(0, i, f(ocx.getValue(i), ocy.getValue(i))); + } + size_t itemid= addItem(it); + return addColumnForItem(itemid, 0, name); +} + //////////////////////////////////////////////////////////////////////////////////////////////// int JKQTPDatastore::getNextLowerIndex(size_t column, size_t row, int start, int end) const @@ -938,7 +1080,7 @@ QVariant JKQTPDatastoreModel::data(const QModelIndex &index, int role) const { if (datastore) { if (role==Qt::DisplayRole || role==Qt::EditRole) { int col=static_cast(datastore->getColumnIDs().value(column, -1)); - if (col>-1 && row>=0 && row(datastore->getColumn(col).getRows())) { + if (col>-1 && row>=0 && row(datastore->getRows(col))) { return datastore->get(col, static_cast(row)); } } diff --git a/lib/jkqtplotter/jkqtpdatastorage.h b/lib/jkqtplotter/jkqtpdatastorage.h index 0257344c15..6689ee00ce 100644 --- a/lib/jkqtplotter/jkqtpdatastorage.h +++ b/lib/jkqtplotter/jkqtpdatastorage.h @@ -82,6 +82,8 @@ enum JKQTPDatastoreItemFormat { /** \brief This class manages chunks of memory that are used for column data in JKQTBasePlotter descendents * \ingroup jkqtpdatastorage * + * \see \ref JKQTPlotterAdvancedJKQTPDatastore for a detailed description of how to use this class for data management! + * * This class manages a list if JKQTPDatastoreItem onjects that may each contain a chunk of memory, containig * one or more columns of data. Each item can be accessed with get() by a specific ID which is returned by add(). * JKQTPColumn. You may only clear all chunks of memory/items. If you no longer need some of the data, but still want @@ -135,17 +137,37 @@ class JKQTP_LIB_EXPORT JKQTPDatastore{ /** \brief add a new column to the datastore and return its ID */ size_t addColumn(JKQTPColumn col); + /** \brief add a new item to the datastore and return its ID + * This function retuns an item ID (which can be used with getItem() ), not a column ID! */ + size_t addItem(JKQTPDatastoreItem* item); - public: - /** \brief class constructor, generates an empty datastore */ - JKQTPDatastore(); - /** \brief class destructor, destroys all subordered JKQTPDatastoreItem objects */ - ~JKQTPDatastore() { - clear(); - } - /** \brief deletes all items from the datastore and possibly frees the memory they manage */ - void clear(); + /** \brief add a new columns/item with \a rows rows to the datastore and return its ID. The item uses internal memory management. + * This function retuns an item ID (which can be used with getItem() ), not a column ID! */ + size_t addItem(size_t rows); + + /** \brief add a new item with \a rows rows and \a columns columns to the datastore and return its ID. The item uses internal memory management. + * This function retuns an item ID (which can be used with getItem() ), not a column ID! */ + size_t addItem(size_t columns, size_t rows); + + /** \brief add one external column to the datastore. It contains \a rows rows. + * This function retuns an item ID (which can be used with getItem() ), not a column ID! */ + size_t addItem(double* data, size_t rows); + /** \brief add one internal column to the datastore. It contains \a rows rows. + * This function retuns an item ID (which can be used with getItem() ), not a column ID! */ + size_t addInternalItem(double* data, size_t rows); + + /** \brief add one external column to the datastore. It contains \a rows rows. The data is copied and the copy managed internally + * This function retuns an item ID (which can be used with getItem() ), not a column ID! */ + size_t addCopiedItem(const double *data, size_t rows); + + /** \brief add an external memory block to the datastore. It contains \a rows rows and \a columns columns. \a dataformat determined the memory layout. + * This function retuns an item ID (which can be used with getItem() ), not a column ID! */ + size_t addItem(JKQTPDatastoreItemFormat dataformat, double* data, size_t columns, size_t rows); + + /** \brief add one external data block to the datastore. It contains \a rows rows and \a columns columns. The data is copied and the copy managed internally + * This function retuns an item ID (which can be used with getItem() ), not a column ID! */ + size_t addCopiedItem(JKQTPDatastoreItemFormat dataformat, double* data, size_t columns, size_t rows); /** \brief returns the JKQTPDatastoreItem object for the \a i -th item in the store */ inline JKQTPDatastoreItem* getItem(size_t i) { @@ -157,12 +179,38 @@ class JKQTP_LIB_EXPORT JKQTPDatastore{ return items.value(i, nullptr); } - /** \brief add a new item to the datastore and return its ID */ - size_t addItem(JKQTPDatastoreItem* item); + /** \brief add a new columns which references a specified item and a specified column therein. + * \see \ref JKQTPlotterAdvancedJKQTPDatastore + */ + size_t addColumnForItem(size_t itemID, size_t columnInItem, const QString& name=QString("")); + + /** \brief returns the JKQTPColumn object for the \a i -th column in the store + * + * \warning This function copies the pointers/references to the internal data into a new object. + * Therefore you should delete it as soon as possible and not store the return value over long durations, + * as the data may get moved in the meantime and then the object gets invalid, but is not informed of this fact! + */ + JKQTPColumn getColumn(size_t i) const; - /** \brief add a new columns/item with \a rows rows to the datastore and return its ID. The item uses internal memory management. */ - size_t addItem(size_t rows); + /** \brief returns the JKQTPColumn object for the \a i -th column in the store + * + * \warning This function copies the pointers/references to the internal data into a new object. + * Therefore you should delete it as soon as possible and not store the return value over long durations, + * as the data may get moved in the meantime and then the object gets invalid, but is not informed of this fact! + */ + JKQTPColumn getColumn(int i) const; + public: + /** \brief class constructor, generates an empty datastore */ + JKQTPDatastore(); + /** \brief class destructor, destroys all subordered JKQTPDatastoreItem objects */ + ~JKQTPDatastore(); + + + + /** \brief deletes all items from the datastore and possibly frees the memory they manage */ + void clear(); + /** \brief delete the given column, if no other columns points to the datastore item of the column and \a removeItems is \c true, the item will be removed */ void deleteColumn(size_t column, bool removeItems=true); @@ -173,24 +221,24 @@ class JKQTP_LIB_EXPORT JKQTPDatastore{ /** \brief delete all columns where the name starts with a given prefix, if no other columns points to the datastore item of the column and \a removeItems is \c true, the item will be removed */ void deleteAllPrefixedColumns(QString prefix, bool removeItems=true); - /** \brief add a new item with \a rows rows and \a columns columns to the datastore and return its ID. The item uses internal memory management. */ - size_t addItem(size_t columns, size_t rows); - - /** \brief add one external column to the datastore. It contains \a rows rows.*/ - size_t addItem(double* data, size_t rows); - /** \brief add one internal column to the datastore. It contains \a rows rows.*/ - size_t addInternalItem(double* data, size_t rows); - - /** \brief add an external memory block to the datastore. It contains \a rows rows and \a columns columns. \a dataformat determined the memory layout*/ - size_t addItem(JKQTPDatastoreItemFormat dataformat, double* data, size_t columns, size_t rows); - - /** \brief add one external column to the datastore. It contains \a rows rows. The data is copied and the copy managed internally */ - size_t addCopiedItem(const double *data, size_t rows); - - /** \brief add one external data block to the datastore. It contains \a rows rows and \a columns columns. The data is copied and the copy managed internally */ - size_t addCopiedItem(JKQTPDatastoreItemFormat dataformat, double* data, size_t columns, size_t rows); - + /** \brief returns the number of rows in the column \a column */ + inline size_t getRows(size_t column) const; + /** \brief returns a pointer to the data in column \a column, starting ar row \a row */ + inline const double* getColumnPointer(size_t column, size_t row=0) const; + /** \brief returns a pointer to the data in column \a column, starting ar row \a row */ + inline double* getColumnPointer(size_t column, size_t row=0); + /** \brief returns the number of rows in the column \a column */ + inline size_t getRows(int column) const; + /** \brief returns a pointer to the data in column \a column, starting ar row \a row */ + inline const double* getColumnPointer(int column, size_t row=0) const; + /** \brief returns a pointer to the data in column \a column, starting ar row \a row */ + inline double* getColumnPointer(int column, size_t row=0); + /** \brief returns the width of the image, represented by \a column (in row-major ordering). + * Internally this returns the imageColumns or image width, if set in the column */ + inline size_t getColumnImageWidth(int column); + /** \brief returns the height of the image, represented by \a column (in row-major ordering) */ + inline size_t getColumnImageHeight(int column); /** \brief returns the value at position (\c column, \c row). \c column is the logical column and will be mapped to the according memory block internally!) */ inline double get(size_t column, size_t row) const ; @@ -201,7 +249,7 @@ class JKQTP_LIB_EXPORT JKQTPDatastore{ inline double get(int column, int row) const ; /** \brief returns the value at position (\c column, \c row). \c column is the logical column and will be mapped to the according memory block internally!) */ inline double get(size_t column, int row) const ; - /** \brief gets the index of the datapoint with the nearest, but lower value in the column (in a given inclusive row range [start ... end] values of -1 for the ranges are "wildcards", i.e. start/end of column)*/ + /** \brief gets the index of the datapoint with the nearest, but lower value in the column (in a given inclusive row range [start ... end] values of -1 for the ranges are "wildcards", i.e. start/end of column)*/ int getNextLowerIndex(size_t column, size_t row, int start, int end) const; /** \brief gets the index of the datapoint with the nearest, but lower value in the column */ int getNextLowerIndex(size_t column, size_t row) const; @@ -209,11 +257,6 @@ class JKQTP_LIB_EXPORT JKQTPDatastore{ int getNextHigherIndex(size_t column, size_t row, int start, int end) const; /** \brief gets the index of the datapoint with the nearest, but higher value in the column */ int getNextHigherIndex(size_t column, size_t row) const; - - /** \brief sets the value at position (\c column, \c row). \c column is the logical column and will be mapped to the according memory block internally!) */ - inline void set(size_t column, size_t row, double value); - /** \brief sets the value at position (\c column, \c row). \c column is the logical column and will be mapped to the according memory block internally!) */ - inline void set(int column, size_t row, double value); /** \brief gets the index of the datapoint with the nearest, but lower value in the column (in a given inclusive row range [start ... end] values of -1 for the ranges are "wildcards", i.e. start/end of column)*/ int getNextLowerIndex(int column, size_t row, int start, int end) const; /** \brief gets the index of the datapoint with the nearest, but lower value in the column */ @@ -223,24 +266,153 @@ class JKQTP_LIB_EXPORT JKQTPDatastore{ /** \brief gets the index of the datapoint with the nearest, but higher value in the column */ int getNextHigherIndex(int column, size_t row) const; + /** \brief sets the value at position (\c column, \c row). \c column is the logical column and will be mapped to the according memory block internally!) */ + inline void set(size_t column, size_t row, double value); + /** \brief sets the value at position (\c column, \c row). \c column is the logical column and will be mapped to the according memory block internally!) */ + inline void set(int column, size_t row, double value); + /** \brief returns the value at position (\c x, \c y) in the \a column-th column, which is interpreted with the imageWidth stored in that column */ + inline double getPixel(size_t column, size_t x, size_t y) const ; + /** \brief returns the value at position (\c x, \c y) in the \a column-th column, which is interpreted with the imageWidth stored in that column */ + inline void setPixel(size_t column, size_t x, size_t y, double value) ; + /** \brief sets all entries in column \a column to \a value + * + * \see \ref JKQTPlotterFilledGraphs + */ + inline void setAll(size_t column, double value); + /** \brief scales (multiplies) all entries in column \a column by \a factor + * + * \see \ref JKQTPlotterFilledGraphs + */ + inline void scaleColumnValues(size_t column, double factor); + /** \brief increases entry in row \a row of column \a column by \a increment + * + * \see \ref JKQTPlotterFilledGraphs + */ + inline void inc(size_t column, size_t row, double increment=1); + /** \brief decrements entry in row \a row of column \a column by \a decrement + * + * \see \ref JKQTPlotterFilledGraphs + */ + inline void dec(size_t column, size_t row, double decrement=1); - /** \brief add a new columns which references a specified item and a specified column therein. */ - size_t addColumnForItem(size_t itemID, size_t columnInItem, const QString& name=QString("")); - /** \brief add a new columns with \a rows rows to the datastore and return its column ID. The new item uses internal memory management. */ + /** \brief add a new columns with \a rows rows to the datastore and return its column ID. The new item uses internal memory management. + * \param rows number of rows in the data array + * \param name name for the column + * \return the ID of the newly created column + * \see \ref JKQTPlotterAdvancedJKQTPDatastore + */ size_t addColumn(size_t rows, const QString& name=QString("")); - /** \brief add one external column to the datastore. It contains \a rows rows. This returns its logical column ID.*/ + /** \brief add one external column to the datastore. It contains \a rows rows. This returns its logical column ID. + * Data is not owned by the JKQTPDatastore! + * + * \param data data array to be copied + * \param rows number of rows in the data array + * \param name name for the column + * \return the ID of the newly created column + * + * \code + * #define NDATA 5 + * double XCA[NDATA]= { 1, 2, 3, 4, 5 }; + * double YCA[NDATA]= { 1, 0, 1, 0, 1 }; + * plot.addGraph(linegraph=new JKQTPXYLineGraph(&plot)); + * linegraph->setXColumn(datastore->addColumn(XCA, NDATA, "xca (C-array)")); + * linegraph->setYColumn(datastore->addColumn(YCA, NDATA, "yca (C-array)")); + * \endcode + * \see \ref JKQTPlotterAdvancedJKQTPDatastore + */ size_t addColumn(double* data, size_t rows, const QString& name=QString("")); + /** \brief add a column with \a rows entries from the array \a data, + * ownership of the memory behind \a data is transfered to the datastore + * + * \param data data array to be copied + * \param rows number of rows in the data array + * \param name name for the column + * \return the ID of the newly created column + * + * \code + * #define NDATA 5 + * double* XCA=(double*)malloc(NDATA, sizeof(double)); + * double* YCA=(double*)malloc(NDATA, sizeof(double)); + * ... + * plot.addGraph(linegraph=new JKQTPXYLineGraph(&plot)); + * linegraph->setXColumn(datastore->addInternalColumn(XCA, NDATA, "x")); + * linegraph->setXColumn(datastore->addInternalColumn(YCA, NDATA, "y")); + * \endcode + * \see \ref JKQTPlotterAdvancedJKQTPDatastore + */ + size_t addInternalColumn(double *data, size_t rows, const QString& name); + + + /** \brief add a new columns with \a width * \a height rows to the datastore and return its column ID. The new item uses internal memory management. + * The column is meant to represent an image in row-major order with x-dimention \a width and y-dimension \a height . + * + * \param width width of the image represented by the data array + * \param height height of the image represented by the data array + * \param name name for the column + * \return the ID of the newly created column + * \see addImageColumn(), addInternalImageColumn(), \ref JKQTPlotterAdvancedJKQTPDatastore + */ + size_t addImageColumn(size_t width, size_t height, const QString& name=QString("")); + + /** \brief add one external column to the datastore. It contains \a width * \a height rows. This returns its logical column ID. + * Data is not owned by the JKQTPDatastore! + * The column is meant to represent an image in row-major order with x-dimention \a width and y-dimension \a height . + * + * + * \param data data array to be copied + * \param width width of the image represented by the data array + * \param height height of the image represented by the data array + * \param name name for the column + * \return the ID of the newly created column + * + * \see addColumn(), addImageColumn(), addInternalImageColumn(), \ref JKQTPlotterAdvancedJKQTPDatastore + */ + size_t addImageColumn(double* data, size_t width, size_t height, const QString& name=QString("")); + /** \brief add a column with \a width * \a height entries from the array \a data, + * ownership of the memory behind \a data is transfered to the datastore + * The column is meant to represent an image in row-major order with x-dimention \a width and y-dimension \a height . + * + * + * \param data data array to be copied + * \param width width of the image represented by the data array + * \param height height of the image represented by the data array + * \param name name for the column + * \return the ID of the newly created column + * + * \see addInternalColumn(), addImageColumn(), addInternalImageColumn(), \ref JKQTPlotterAdvancedJKQTPDatastore + */ + size_t addInternalImageColumn(double *data, size_t width, size_t height, const QString& name); - - - /** \brief copies the given \a old_column into a new one, reading the data with the given start column and stride */ + /** \brief copies the given \a old_column into a new one, reading the data with the given start column and stride + * + * \param old_column the column to be duplicated + * \param start for row in column \a old_column to copy + * \param stride stride for iterating through \a old_column when copying + * \param name name for the new column + * \return ID of the newly created column + * + * Pseuo-Code: + * \code + * newColumn=addColumn(rowcount(old_column), name) + * forall ( r: rows(old_column)) { + * set(newColumn, r, get(old_column, r)) + * } + * return newColumn; + * \endcode + */ size_t copyColumn(size_t old_column, size_t start, size_t stride, const QString& name=QString("")); - /** \brief copies the given \a old_column into a new one */ + /** \brief copies the given \a old_column into a new one + * + * \param old_column the column to be duplicated + * \param name name for the new column + * \return ID of the newly created column + * \see \ref JKQTPlotterAdvancedJKQTPDatastore + */ size_t copyColumn(size_t old_column, const QString& name=QString("")); @@ -250,6 +422,22 @@ class JKQTP_LIB_EXPORT JKQTPDatastore{ * \param data data vector to be copied * \param name name for the column * \return the ID of the newly created column + * + * \code + * QVector X, Y; + * const int Ndata=100; + * for (int i=0; isetXColumn(datastore->addCopiedColumn(X, "x")); + * linegraph->setYColumn(datastore->addCopiedColumn(Y, "y")); + * \endcode + * + * \see \ref JKQTPlotterAdvancedJKQTPDatastore */ template size_t addCopiedColumn(const TContainer& data, const QString& name=QString("")) { @@ -270,11 +458,24 @@ class JKQTP_LIB_EXPORT JKQTPDatastore{ /** \brief add one external column to the datastore. It will be filled with the contents of vector \a data. * * \tparam TContainer datatype of the container, which need to support standard C++ iterators and the function \c size(). The contents needs to be convertible to double. + * The iterator of TContainer needs to support \c ++ and \c += * \param data data vector to be copied * \param name name for the column * \param stride strides through the container \a data with the given stride * \param start starts copying from \a data with the element \a start * \return the ID of the newly created column + * + * Pseudocode: + * \code + * it=data.begin(); + * it += start; // shift by start items + * while (it!=data.end()) { + * newColumn.push_back(jkqtp_todouble(*it)); + * it += stride; + * } + * \endcode + * + * \see \ref JKQTPlotterAdvancedJKQTPDatastore */ template size_t addCopiedColumn(const TContainer& data, const QString& name, size_t stride, size_t start=0) { @@ -304,6 +505,7 @@ class JKQTP_LIB_EXPORT JKQTPDatastore{ * \return the ID of the newly created column * * \note This function converts the input array \a data into an array of double! + * \see \ref JKQTPlotterAdvancedJKQTPDatastore */ template size_t addCopiedColumn(const T* data, size_t rows, const QString& name=QString("")){ @@ -328,7 +530,15 @@ class JKQTP_LIB_EXPORT JKQTPDatastore{ * \param name name for the column * \return the ID of the newly created column * + * Pseudocode: + * \code + * for (i=start; i size_t addCopiedColumn(const T* data, size_t rows, size_t stride, int start, const QString& name) { @@ -351,7 +561,16 @@ class JKQTP_LIB_EXPORT JKQTPDatastore{ * \param stride when copying, this function steps throught the data with the given stride, so only eleemnts [0, stride, 2*stride, ... (rows-1)*stride] are copied! * \return the ID of the newly created column * + * + * Pseudocode: + * \code + * for (i=0; i size_t addCopiedColumn(const T* data, size_t rows, size_t stride, const QString& name) { @@ -362,49 +581,60 @@ class JKQTP_LIB_EXPORT JKQTPDatastore{ /** \brief add one external column to the datastore. It contains \a width * \a height rows. The external data is assumed to be organized as a row-major image and is copied as such. The external data is copied to an internal array, so * afterwards you can delete the external arrayThis returns its logical column ID.*/ template - inline size_t addCopiedImageAsColumn(const T* data, size_t width, size_t height, const QString& name=QString(""), size_t stride=1, size_t start=0){ - return addCopiedColumn(data, width*height, stride, start, name); - } + inline size_t addCopiedImageAsColumn(const T* data, size_t width, size_t height, const QString& name=QString(""), size_t stride=1, size_t start=0); /** \brief add one external column to the datastore. It contains \a width * \a height rows. The external data is assumed to be organized as a row-major image and is copied as such. The external data is copied to an internal array, so - * afterwards you can delete the external arrayThis returns its logical column ID.*/ + * afterwards you can delete the external arrayThis returns its logical column ID. + * + * \tparam TContainer datatype of the container, which need to support standard C++ iterators and the function \c size(). The contents needs to be convertible to double. + * \param data data vector to be copied + * \param width width of the image, stored in \a data + * \param name name for the column + * \return the ID of the newly created column + */ template - inline size_t addCopiedImageAsColumn(const TContainer& data, const QString& name=QString("")){ - return addCopiedColumn(data, name); - } + inline size_t addCopiedImageAsColumn(const TContainer& data, size_t width, const QString& name=QString("")); - /** \brief add one external column to the datastore. It contains \a width * \a height rows. The external data is assumed to be organized as a column-major image and is copied as row-major (i.e. is transposed). The external data is copied to an internal array, so - * afterwards you can delete the external arrayThis returns its logical column ID.*/ + /** \brief add a new column to the datastore, which is filled from the transposed column-major array \a data with + * the given \a width and \a height. + * + * The external data is assumed to be organized as a column-major image and is copied as row-major (i.e. is transposed). + * The external data is copied to an internal array, so afterwards you can delete the external arrayThis returns its logical column ID. + * + * \tparam T data type of the array \a data, needs to be convertible to \c double by jkqtp_todouble() + * \param data a column major image + * \param width width of \a data + * \param height height of \a data + * \param name name of the new column + * \param stride stride to use, when reading \a data. Use this to e.g. read one channel from a packed RGB-image (\c stride=3, \c start=0/1/2 ) + * \param start first entry from \a data top copy \a data. Use this to e.g. read one channel from a packed RGB-image (\c stride=3, \c start=0/1/2 ) + * \return ID of the newly added column + */ template - size_t addCopiedImageAsColumnTranspose(const T* data, size_t width, size_t height, const QString& name=QString(""), size_t stride=1, size_t start=0){ - double* temp=static_cast(malloc(width*height*sizeof(T))); - - for (size_t x=0; x - inline size_t addCopiedImageAsColumnTranspose(const QVector& data, size_t width, const QString& name=QString("")){ - return addCopiedImageAsColumnTranspose(data.data(), width, static_cast(data.size())/width, name); - } - + inline size_t addCopiedImageAsColumnTranspose(const QVector& data, size_t width, const QString& name=QString("")); /** \brief add one external column to the datastore. It contains \a rows rows. The external data is copied to an internal array, so * afterwards you can delete the external arrayThis returns its logical column ID. * * \note This function converts the input array \a data into an array of double! + * \see \ref JKQTPlotterAdvancedJKQTPDatastore */ template size_t addCopiedColumnMasked(const T* data, const bool* mask, size_t rows, const QString& name=QString(""), bool useIfMaskEquals=false) { @@ -420,19 +650,31 @@ class JKQTP_LIB_EXPORT JKQTPDatastore{ } - size_t col= addCopiedColumn(d, rrs, name); - free(d); + size_t col= addInternalColumn(d, rrs, name); return col; } /** \brief add one external column to the datastore. It will be filled with the contents of vector \a data. * - * \tparam TContainer datatype of the container, which need to support standard C++ iterators and the function \c size(). The contents needs to be convertible to double. + * \tparam TContainer datatype of the container \a data, which need to support standard C++ iterators and the function \c size(). The contents needs to be convertible to double. + * \tparam TContainerMask datatype of the container \a mask, which need to support standard C++ iterators and the function \c size(). The contents needs to be convertible to bool. * \param data data vector to be copied + * \param mask data vector to be copied * \param name name for the column * \param stride strides through the container \a data with the given stride * \param start starts copying from \a data with the element \a start * \return the ID of the newly created column + * \see \ref JKQTPlotterAdvancedJKQTPDatastore + * + * Pseudocode: + * \code + * for (i=0; i(mask[i])==useIfMaskEquals) { + * newColumn.push_back(jkqtp_todouble(data[i])); + * } + * } + * return newColumn; + * \endcode */ template size_t addCopiedColumnMasked(const TContainer& data, const TContainerMask& mask, const QString& name=QString(""), bool useIfMaskEquals=false) { @@ -443,7 +685,7 @@ class JKQTP_LIB_EXPORT JKQTPDatastore{ auto itmask=mask.begin(); auto itdata=data.begin(); for (size_t r=0; r(*itmask)==useIfMaskEquals) { d[rrs]=jkqtp_todouble(*itdata); rrs++; } @@ -451,42 +693,149 @@ class JKQTP_LIB_EXPORT JKQTPDatastore{ ++itdata; } } - - - size_t col= addCopiedColumn(d, rrs, name); - free(d); + size_t col= addInternalColumn(d, rrs, name); return col; } /** \brief copies the contents of the map-like container \a c into two columns of the datastore, - * returns the two IDs of the items as a std::pair */ + * returns the two IDs of the items as a std::pair + * \see \ref JKQTPlotterAdvancedJKQTPDatastore, jkqtp_todouble() + * + * \tparam TContainer datatype of the map-typed container (e.g. \c std::map or \c QMap ) The requiremen to this container is + * that it supports standard iterators with \c begin() and \c end() . + * \param c the map to copy to the datastore + * \param nameKey name for the column with the map keys + * \param nameValue name for the column with the map values + * \return a pair of IDs to the newly created columns (IDkeyColumn, IDvalueColumn) + * Example of usage: + * \code + * std::map datamap; + * datamap[1]=1.1; + * datamap[2]=1.4; + * datamap[4]=1.2; + * datamap[5]=1.8; + * datamap[7]=0.9; + * plot.addGraph(linegraph=new JKQTPXYLineGraph(&plot)); + * linegraph->setXYColumns(datastore->addCopiedMap(datamap, "map_x", "map_y")); + * linegraph->setTitle(QObject::tr("copied map")); + * \endcode + */ template std::pair addCopiedMap(const TContainer& c, const QString& nameKey=QString("map_key"), const QString& nameValue=QString("map_value")) { - std::vector xvals; - std::vector yvals; + const size_t N=std::distance(c.begin(), c.end()); + double* xvals=static_cast(malloc(N*sizeof(double))); + double* yvals=static_cast(malloc(N*sizeof(double))); + size_t i=0; for (auto it=c.begin(); it!=c.end(); ++it) { - xvals.push_back(jkqtp_todouble(it->first)); - yvals.push_back(jkqtp_todouble(it->second)); + xvals[i]=(jkqtp_todouble(it->first)); + yvals[i]=(jkqtp_todouble(it->second)); + i++; } - return std::make_pair( - addCopiedColumn(xvals, nameKey), - addCopiedColumn(yvals, nameValue) - ); + const size_t cx=addInternalColumn(xvals, N, nameKey); + const size_t cy=addInternalColumn(yvals, N, nameValue); + return std::pair(cx,cy); } - /** \brief add a column to the datastore that contains \a rows rows with increasing value starting at \a start and ending at \a end. - * the values are equidistant between \a start end \a end */ + /** \brief add a column to the datastore that contains \a rows rows with monotonely increasing value starting at \a start and ending at \a end. + * the values are equidistant between \a start end \a end + * \see addLogColumn(), addDecadeLogColumn(), \ref JKQTPlotterAdvancedJKQTPDatastore + */ size_t addLinearColumn(size_t rows, double start, double end, const QString& name=QString("")); + /** \brief add a column to the datastore that contains \a rows rows with monotonely increasing value starting at \a start and ending at \a end. + * the values are logarithmically spaced between \a start end \a end + * \see addLinearColumn(), addDecadeLogColumn(), \ref JKQTPlotterAdvancedJKQTPDatastore + */ + size_t addLogColumn(size_t rows, double start, double end, const QString& name=QString("")); + /** \brief add a column to the datastore that contains \a rows rows with monotonely increasing value starting at 10^start and ending at 10^end. + * the values are logarithmically spaced between 10^start end 10^end + * \see addLinearColumn(), addLogColumn(), \ref JKQTPlotterAdvancedJKQTPDatastore + */ + size_t addDecadeLogColumn(size_t rows, double startDecade, double endDecade, const QString& name=QString("")); + + + /** \brief add two columns to the datastore that contains the x- and y- coordinates of a rectangular grid with \a width points in x- and \a height + * points in y-direction. + * + * \param width number of columns in the mesh grid + * \param startX x-coordinate of the first column of the mesh grid + * \param endX x-coordinate of the last column of the mesh grid + * \param height number of rows in the mesh grid + * \param startY y-coordinate of the first row of the mesh grid + * \param endY y-coordinate of the last row of the mesh grid + * \param nameX name for the x-coordinate column + * \param nameY name for the y-coordinate column + * \return IDs of two column that contain the x- and y- coordinates od the mesh points (in row-major order), where the + * x-coordinates are linearly distributed between \a startX and \a endX and the x-coordinates are linearly + * distributed between \a startY and \a endY . + * + * \see addLogGridColumns(), addDecadeLogGridColumns(), addColumnCalculatedFromColumn(), JKQTPXYParametrizedScatterGraph, \ref JKQTPlotterAdvancedJKQTPDatastore + */ + std::pair addLinearGridColumns(size_t width, double startX, double endX, size_t height, double startY, double endY, const QString& nameX=QString(""), const QString& nameY=QString("")); + + + /** \brief add a column with \a rows entries, that is calculated by calling \a f for each entry + * + * Pseudocode: + * \code + * for (i=0; i& f, const QString& name=QString("")); + /** \brief add a column with \a rows entries, that is calculated by calling \a f for each entry + * + * Pseudocode: + * \code + * for (i=0; i& f, const QString& name=QString("")); + /** \brief add a column with the same number of entries, as in the other column \a otherColumn , that are calculated by calling \a f for each entry in \a otherColumn + * + * Pseudocode: + * \code + * for (i=0; i& f, const QString& name=QString("")); + /** \brief add a column with the same number of entries, as in the other column \a otherColumn , that are calculated by calling \a f for each pair of entries in \a otherColumnX and \a otherColumnY + * + * Pseudocode: + * \code + * for (i=0; i& f, const QString& name=QString("")); /** \brief returns the number of (logical) columns currently managed by the datastore */ inline size_t getColumnCount() { return static_cast(columns.size()); } + /** \brief returns a list with all available column IDs */ inline QList getColumnIDs() { return columns.keys(); } /** \brief return the num of the first column with the given name, or -1 if none was found */ @@ -495,12 +844,6 @@ class JKQTP_LIB_EXPORT JKQTPDatastore{ /** \brief return the num of the first column with the given name, if none was found this creates a new column with no rows and returns its num */ int ensureColumnNum(const QString& name); - /** \brief returns the JKQTPColumn object for the \a i -th column in the store */ - JKQTPColumn getColumn(size_t i) const; - - - /** \brief returns the JKQTPColumn object for the \a i -th column in the store */ - JKQTPColumn getColumn(int i) const; /** \brief returns the maximum number of rows in all columns */ size_t getMaxRows(); @@ -580,9 +923,10 @@ class JKQTP_LIB_EXPORT JKQTPDatastore{ /** \brief return a list with all columns available in the datastore */ QStringList getColumnNames() const; - /** \brief add a column with \a rows entries from the array \a data, - * ownership of the memory behind \a data is transfered to the datastore */ - size_t addInternalColumn(double *data, size_t rows, const QString& name); + + friend class JKQTPColumn; + friend class JKQTPDatastoreItem; + friend class JKQTPDatastoreModel; }; @@ -603,8 +947,10 @@ class JKQTP_LIB_EXPORT JKQTPColumn { QString name; /** \brief pointer to the datastore object used to manage the data of the plot */ JKQTPDatastore* datastore; - + /** \brief is this item valid?/usable? */ bool valid; + /** \brief number of columns, if interpreted as a row-major image */ + size_t imageColumns; protected: inline JKQTPDatastore* getDatastore() { return datastore; } @@ -616,23 +962,22 @@ class JKQTP_LIB_EXPORT JKQTPColumn { * The use of this constructor is mandatory. The default constructor (no arguments) is hidden. Also note * that you cannot change the binding of a column to a datastore object after creation of the column. */ - JKQTPColumn(JKQTPDatastore* datastore, const QString& name=QString(""), size_t datastoreItem=0, size_t datastoreOffset=0); + JKQTPColumn(JKQTPDatastore* datastore, const QString& name=QString(""), size_t datastoreItem=0, size_t datastoreOffset=0, size_t imageColumns=1); inline bool isValid() const { return valid; } /** \brief class destructor */ - ~JKQTPColumn() ; + ~JKQTPColumn() =default; /*! \brief sets the property name ( \copybrief name ) to the specified \a __value. \details Description of the parameter name is:
\copydoc JKQTPColumn::name
\see JKQTPColumn::name for more information */ - inline void setName (const QString& __value) - { - this->name = __value; - } + void setName (const QString& __value); /*! \brief returns the property name ( \copybrief name ). \see name for more information */ - inline QString getName () const - { - return this->name; - } + QString getName () const; + + /*! \copydoc imageColumns */ + void setImageColumns (size_t __value); + /*! \copydoc imageColumns */ + inline size_t getImageColumns () const { return imageColumns; } /** \brief returns the number of rows in this column (accesses the datastore) */ size_t getRows() const; @@ -681,13 +1026,32 @@ class JKQTP_LIB_EXPORT JKQTPColumn { /** \brief sets the element at (x,y) in the column, where the data is interpreted as a row-major ordered Matrix of the given width * - * This method accesses the datastore and returns the double value stored in the \a n'th row of the according + * This method accesses the datastore and returns the double value stored in the \c (y*width+x)'th row of the according * column. */ inline void setPixelValue(size_t x, size_t y, size_t width, double val) { setValue(y*width+x, val); } + + /** \brief sets the element at (x,y) in the column, where the data is interpreted as a row-major ordered Matrix of the width imageWidth + * + * This method accesses the datastore and returns the double value stored in the \c (y*imageColumns+x)'th row of the according + * column. + */ + inline void setPixelValue(size_t x, size_t y, double val) { + setValue(y*imageColumns+x, val); + } + + /** \brief returns the element at (x,y) in the column, where the data is interpreted as a row-major ordered Matrix of the width imageWidth + * + * This method accesses the datastore and returns the double value stored in the \c (y*imageColumns+x)'th row of the according + * column. + */ + inline double getPixelValue(size_t x, size_t y) const { + return getValue(y*imageColumns+x); + } + /** \brief returns a pointer to the datastore item representing this column */ inline JKQTPDatastoreItem* getDatastoreItem() const { return datastore->getItem(datastoreItem); } @@ -893,6 +1257,57 @@ inline double JKQTPColumn::getValue(int n) const { return datastore->getItem(datastoreItem)->get(datastoreOffset, static_cast(n)); } +//////////////////////////////////////////////////////////////////////////////////////////////// +size_t JKQTPDatastore::getRows(size_t column) const { + return columns.value(column).getRows(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +const double *JKQTPDatastore::getColumnPointer(int column, size_t row) const +{ + if (column<0) return nullptr; + return columns.value(static_cast(column)).getPointer(row); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +double *JKQTPDatastore::getColumnPointer(int column, size_t row) +{ + if (column<0) return nullptr; + return columns[static_cast(column)].getPointer(row); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +size_t JKQTPDatastore::getColumnImageWidth(int column) +{ + if (column<0) return 0; + return columns[static_cast(column)].getImageColumns(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +size_t JKQTPDatastore::getColumnImageHeight(int column) +{ + if (column<0) return 0; + return columns[static_cast(column)].getRows()/columns[static_cast(column)].getImageColumns(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +size_t JKQTPDatastore::getRows(int column) const { + if (column<0) return 0; + return columns.value(static_cast(column)).getRows(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +const double *JKQTPDatastore::getColumnPointer(size_t column, size_t row) const +{ + return columns.value(column).getPointer(row); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +double *JKQTPDatastore::getColumnPointer(size_t column, size_t row) +{ + return columns[column].getPointer(row); +} + //////////////////////////////////////////////////////////////////////////////////////////////// inline double JKQTPDatastore::get(size_t column, size_t row) const { return columns[column].getValue(row); @@ -928,6 +1343,80 @@ inline void JKQTPDatastore::set(int column, size_t row, double value) { set(static_cast(column), static_cast(row), value); } +//////////////////////////////////////////////////////////////////////////////////////////////// +inline double JKQTPDatastore::getPixel(size_t column, size_t x, size_t y) const { + return columns.value(column).getPixelValue(x, y); +} +//////////////////////////////////////////////////////////////////////////////////////////////// +inline void JKQTPDatastore::setPixel(size_t column, size_t x, size_t y, double value) { + return columns[column].setPixelValue(x, y, value); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +void JKQTPDatastore::setAll(size_t column, double value) +{ + columns[column].setAll(value); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +void JKQTPDatastore::scaleColumnValues(size_t column, double factor) +{ + columns[column].scale(factor); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +void JKQTPDatastore::inc(size_t column, size_t row, double increment) +{ + columns[column].incValue(row, increment); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +void JKQTPDatastore::dec(size_t column, size_t row, double decrement) +{ + columns[column].decValue(row, decrement); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////// +template +inline size_t JKQTPDatastore::addCopiedImageAsColumn(const T* data, size_t width, size_t height, const QString& name, size_t stride, size_t start){ + size_t col=addCopiedColumn(data, width*height, stride, start, name); + columns[col].setImageColumns(width); + return col; +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +template +inline size_t JKQTPDatastore::addCopiedImageAsColumn(const TContainer& data, size_t width, const QString& name){ + size_t col= addCopiedColumn(data, name); + columns[col].setImageColumns(width); + return col; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////// +template +size_t JKQTPDatastore::addCopiedImageAsColumnTranspose(const T* data, size_t width, size_t height, const QString& name, size_t stride, size_t start){ + double* temp=static_cast(malloc(width*height*sizeof(double))); + + for (size_t x=0; x(data[start+(y*width+x)*stride]); + } + + } + + size_t idx=addInternalColumn(temp, width*height, name); + columns[idx].setImageColumns(height); + return idx; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////// +template +inline size_t JKQTPDatastore::addCopiedImageAsColumnTranspose(const QVector& data, size_t width, const QString& name) { + return addCopiedImageAsColumnTranspose(data.data(), width, static_cast(data.size())/width, name); +} #endif // JKQTPDATASTORAGE_H diff --git a/lib/jkqtplotter/jkqtpgraphsbarchart.cpp b/lib/jkqtplotter/jkqtpgraphsbarchart.cpp index c7080af1f4..ca590d140d 100644 --- a/lib/jkqtplotter/jkqtpgraphsbarchart.cpp +++ b/lib/jkqtplotter/jkqtpgraphsbarchart.cpp @@ -84,7 +84,7 @@ void JKQTPBarVerticalGraph::draw(JKQTPEnhancedPainter& painter) { QBrush b=getFillBrush(painter, parent); - int imax=qMin(datastore->getColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))); int imin=0; if (imaxgetDatastore(); int imin=0; - int imax=qMin(datastore->getColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))); if (imaxgetDatastore(); int imin=0; - int imax=qMin(datastore->getColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))); if (imaxgetColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))); int imin=0; if (imaxgetDatastore(); int imin=0; - int imax=qMin(datastore->getColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(xColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(xColumn))); if (imaxgetDatastore(); int imin=0; - int imax=qMin(datastore->getColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))); if (imaxgetDatastore(); int imin=0; - int imax=qMin(datastore->getColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))); if (imaxgetDatastore(); int imin=0; - int imax=qMin(datastore->getColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))); if (imaxgetDatastore(); int imin=0; - int imax=qMin(datastore->getColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))); if (imaxgetDatastore(); int imin=0; - int imax=static_cast(datastore->getColumn(column).getRows()); + int imax=static_cast(datastore->getRows(column)); if (imin<0) imin=0; if (imax<0) imax=0; @@ -402,6 +402,42 @@ void JKQTPXYGraph::setDataSortOrder(int __value) { sortData=static_cast(__value); } +void JKQTPXYGraph::setXYColumns(size_t xCol, size_t yCol) +{ + setXColumn(xCol); + setYColumn(yCol); +} + +void JKQTPXYGraph::setXYColumns(int xCol, int yCol) +{ + setXColumn(xCol); + setYColumn(yCol); +} + +void JKQTPXYGraph::setXYColumns(std::pair xyColPair) +{ + setXColumn(xyColPair.first); + setYColumn(xyColPair.second); +} + +void JKQTPXYGraph::setXYColumns(std::pair xyColPair) +{ + setXColumn(xyColPair.first); + setYColumn(xyColPair.second); +} + +void JKQTPXYGraph::setXYColumns(QPair xyColPair) +{ + setXColumn(xyColPair.first); + setYColumn(xyColPair.second); +} + +void JKQTPXYGraph::setXYColumns(QPair xyColPair) +{ + setXColumn(xyColPair.first); + setYColumn(xyColPair.second); +} + double JKQTPXYGraph::hitTest(const QPointF &posSystem, QPointF *closestSpotSystem, QString *label, HitTestMode mode) const { @@ -547,7 +583,7 @@ bool JKQTPSingleColumnGraph::getIndexRange(int &imin, int &imax) const JKQTPDatastore* datastore=parent->getDatastore(); imin=0; - imax=static_cast(datastore->getColumn(static_cast(dataColumn)).getRows()); + imax=static_cast(datastore->getRows(static_cast(dataColumn))); if (imaxgetDatastore(); imin=0; - imax=static_cast(qMin(datastore->getColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows())); + imax=static_cast(qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn)))); if (imax\copydoc sortData \see sortData for more information */ void setDataSortOrder(int __value); + /** \brief sets xColumn and yColumn at the same time */ + void setXYColumns(size_t xCol, size_t yCol); + /** \brief sets xColumn and yColumn at the same time */ + void setXYColumns(int xCol, int yCol); + /** \brief sets xColumn and yColumn at the same time */ + void setXYColumns(std::pair xyColPair); + /** \brief sets xColumn and yColumn at the same time */ + void setXYColumns(std::pair xyColPair); + /** \brief sets xColumn and yColumn at the same time */ + void setXYColumns(QPair xyColPair); + /** \brief sets xColumn and yColumn at the same time */ + void setXYColumns(QPair xyColPair); + /** \brief Implmentation of JKQTPPlotElement::hitTest(), which searches through all graph points defined by xColumn and yColumn * and returns a general x/y-label, also taking into account possibly known errors to the graphs (if it is derived diff --git a/lib/jkqtplotter/jkqtpgraphsbaseerrors.cpp b/lib/jkqtplotter/jkqtpgraphsbaseerrors.cpp index 4049aecb48..c96eb7aff9 100644 --- a/lib/jkqtplotter/jkqtpgraphsbaseerrors.cpp +++ b/lib/jkqtplotter/jkqtpgraphsbaseerrors.cpp @@ -82,7 +82,7 @@ void JKQTPGraphErrorStyleMixin::setErrorColorFromGraphColor(QColor graphColor) QPen JKQTPGraphErrorStyleMixin::getErrorLinePen(JKQTPEnhancedPainter &painter, JKQTBasePlotter *parent) const { QPen p=m_errorLinePen; - p.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*m_errorLineWidth))); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*m_errorLineWidth))); return p; } @@ -282,8 +282,8 @@ void JKQTPGraphErrorStyleMixin::intPlotXYErrorIndicators(JKQTPEnhancedPainter& p painter.setPen(p); size_t imaxx=0, imaxy=0; - if (xColumn>=0) imaxx=datastore->getColumn(static_cast(xColumn)).getRows(); - if (yColumn>=0) imaxy=datastore->getColumn(static_cast(yColumn)).getRows(); + if (xColumn>=0) imaxx=datastore->getRows(static_cast(xColumn)); + if (yColumn>=0) imaxy=datastore->getRows(static_cast(yColumn)); size_t imax=qMin(imaxx, imaxy); size_t imin=0; if (imax=0 && i>=0 && i(ds->getColumn(xErrorColumn).getRows())) { + if (ds && xErrorColumn>=0 && i>=0 && i(ds->getRows(xErrorColumn))) { return ds->get(xErrorColumn, static_cast(i)); } return 0.0; @@ -640,9 +640,9 @@ double JKQTPXGraphErrorData::getXErrorL(int i, JKQTPDatastore *ds) const { if (ds) { if (xErrorSymmetric) { - if (xErrorColumn>=0 && i>=0 && i(ds->getColumn(xErrorColumn).getRows())) return ds->get(xErrorColumn, static_cast(i)); + if (xErrorColumn>=0 && i>=0 && i(ds->getRows(xErrorColumn))) return ds->get(xErrorColumn, static_cast(i)); } else { - if (xErrorColumnLower>=0 && i>=0 && i(ds->getColumn(xErrorColumnLower).getRows())) return ds->get(xErrorColumnLower, static_cast(i)); + if (xErrorColumnLower>=0 && i>=0 && i(ds->getRows(xErrorColumnLower))) return ds->get(xErrorColumnLower, static_cast(i)); } } return 0.0; @@ -706,7 +706,7 @@ void JKQTPYGraphErrorData::setYErrorColumnLower(int __value) { double JKQTPYGraphErrorData::getYErrorU(int i, JKQTPDatastore *ds) const { - if (ds && yErrorColumn>=0 && i>=0 && i(ds->getColumn(yErrorColumn).getRows())) { + if (ds && yErrorColumn>=0 && i>=0 && i(ds->getRows(yErrorColumn))) { return ds->get(yErrorColumn, static_cast(i)); } return 0.0; @@ -716,9 +716,9 @@ double JKQTPYGraphErrorData::getYErrorL(int i, JKQTPDatastore *ds) const { if (ds) { if (yErrorSymmetric) { - if (yErrorColumn>=0 && i>=0 && i(ds->getColumn(yErrorColumn).getRows())) return ds->get(yErrorColumn, static_cast(i)); + if (yErrorColumn>=0 && i>=0 && i(ds->getRows(yErrorColumn))) return ds->get(yErrorColumn, static_cast(i)); } else { - if (yErrorColumnLower>=0 && i>=0 && i(ds->getColumn(yErrorColumnLower).getRows())) return ds->get(yErrorColumnLower, static_cast(i)); + if (yErrorColumnLower>=0 && i>=0 && i(ds->getRows(yErrorColumnLower))) return ds->get(yErrorColumnLower, static_cast(i)); } } return 0.0; diff --git a/lib/jkqtplotter/jkqtpgraphsbasestylingmixins.cpp b/lib/jkqtplotter/jkqtpgraphsbasestylingmixins.cpp index ab4c46fec4..cba1bf3d6b 100644 --- a/lib/jkqtplotter/jkqtpgraphsbasestylingmixins.cpp +++ b/lib/jkqtplotter/jkqtpgraphsbasestylingmixins.cpp @@ -135,7 +135,7 @@ QBrush JKQTPGraphLineStyleMixin::getLineBrush() const QPen JKQTPGraphLineStyleMixin::getLinePen(JKQTPEnhancedPainter& painter, JKQTBasePlotter* parent) const { QPen p=m_linePen; - p.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*m_lineWidth))); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*m_lineWidth))); return p; } @@ -239,7 +239,7 @@ double JKQTPGraphSymbolStyleMixin::getSymbolLineWidth() const QPen JKQTPGraphSymbolStyleMixin::getSymbolPen(JKQTPEnhancedPainter& painter, JKQTBasePlotter* parent) const { QPen p; p.setColor(m_symbolColor); - p.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*m_symbolLineWidth))); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*m_symbolLineWidth))); p.setStyle(Qt::SolidLine); p.setJoinStyle(Qt::RoundJoin); p.setCapStyle(Qt::RoundCap); diff --git a/lib/jkqtplotter/jkqtpgraphsboxplot.cpp b/lib/jkqtplotter/jkqtpgraphsboxplot.cpp index e720155900..2602ea4401 100644 --- a/lib/jkqtplotter/jkqtpgraphsboxplot.cpp +++ b/lib/jkqtplotter/jkqtpgraphsboxplot.cpp @@ -71,7 +71,7 @@ void JKQTPBoxplotVerticalGraph::draw(JKQTPEnhancedPainter& painter) { drawErrorsBefore(painter); - int imax=static_cast(datastore->getColumn(static_cast(posColumn)).getRows()); + int imax=static_cast(datastore->getRows(static_cast(posColumn))); int imin=0; if (imaxgetDatastore(); int imin=0; - int imax=static_cast(datastore->getColumn(static_cast(posColumn)).getRows()); + int imax=static_cast(datastore->getRows(static_cast(posColumn))); if (imaxgetDatastore(); int imin=0; - int imax=datastore->getColumn(medianColumn).getRows(); + int imax=datastore->getRows(medianColumn); if (imaxgetDatastore(); int imin=0; - int imax=datastore->getColumn(medianColumn).getRows(); + int imax=datastore->getRows(medianColumn); if (imaxgetDatastore(); int imin=0; - int imax=static_cast(datastore->getColumn(static_cast(posColumn)).getRows()); + int imax=static_cast(datastore->getRows(static_cast(posColumn))); if (imax(datastore->getColumn(static_cast(posColumn)).getRows()); + int imax=static_cast(datastore->getRows(static_cast(posColumn))); int imin=0; if (imaxgetDatastore(); int imin=0; - int imax=static_cast(datastore->getColumn(static_cast(posColumn)).getRows()); + int imax=static_cast(datastore->getRows(static_cast(posColumn))); if (imaxpt2px(painter, parent->getLineWidthMultiplier()*whiskerLineWidth))); + pw.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*whiskerLineWidth))); pw.setJoinStyle(Qt::MiterJoin); pw.setCapStyle(Qt::FlatCap); return pw; @@ -513,7 +513,7 @@ QBrush JKQTPGraphBoxplotStyleMixin::getWhiskerCapLineBrush() const QPen JKQTPGraphBoxplotStyleMixin::getWhiskerCapPen(JKQTPEnhancedPainter &painter, JKQTBasePlotter *parent) const { QPen pw=m_whiskerCapLinePen; - pw.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*whiskerCapLineWidth))); + pw.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*whiskerCapLineWidth))); pw.setJoinStyle(Qt::MiterJoin); return pw; } @@ -529,7 +529,7 @@ QPen JKQTPGraphBoxplotStyleMixin::getWhiskerCapPen(JKQTPEnhancedPainter &painter QPen JKQTPGraphBoxplotStyleMixin::getMedianPen(JKQTPEnhancedPainter &painter, JKQTBasePlotter *parent) const { QPen pw=m_medianLinePen; - pw.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*medianLineWidth))); + pw.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*medianLineWidth))); pw.setJoinStyle(Qt::MiterJoin); pw.setCapStyle(Qt::FlatCap); return pw; @@ -537,7 +537,7 @@ QPen JKQTPGraphBoxplotStyleMixin::getMedianPen(JKQTPEnhancedPainter &painter, JK QPen JKQTPGraphBoxplotStyleMixin::getMeanSymbolPen(JKQTPEnhancedPainter& painter, JKQTBasePlotter* parent) const { QPen p=m_meanSymbolLinePen; - p.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*m_meanSymbolLineWidth))); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*m_meanSymbolLineWidth))); p.setStyle(Qt::SolidLine); p.setJoinStyle(Qt::RoundJoin); p.setCapStyle(Qt::RoundCap); @@ -546,7 +546,7 @@ QPen JKQTPGraphBoxplotStyleMixin::getMeanSymbolPen(JKQTPEnhancedPainter& painter QPen JKQTPGraphBoxplotStyleMixin::getMeanLinePen(JKQTPEnhancedPainter& painter, JKQTBasePlotter* parent) const { QPen p=m_meanSymbolLinePen; - p.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*m_meanSymbolLineWidth))); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*m_meanSymbolLineWidth))); p.setJoinStyle(Qt::MiterJoin); p.setCapStyle(Qt::FlatCap); return p; diff --git a/lib/jkqtplotter/jkqtpgraphscontour.cpp b/lib/jkqtplotter/jkqtpgraphscontour.cpp index 218046dce3..33585e02e3 100644 --- a/lib/jkqtplotter/jkqtpgraphscontour.cpp +++ b/lib/jkqtplotter/jkqtpgraphscontour.cpp @@ -216,7 +216,7 @@ bool JKQTPContour::getRelativeLevels() const void JKQTPContour::setImageColumn(size_t columnID) { datatype=JKQTPMathImageBase::DoubleArray; - data=parent->getDatastore()->getColumn(columnID).getPointer(0); + data=parent->getDatastore()->getColumnPointer(columnID,0); } diff --git a/lib/jkqtplotter/jkqtpgraphsevaluatedfunction.cpp b/lib/jkqtplotter/jkqtpgraphsevaluatedfunction.cpp index f70b543367..56250db3f2 100644 --- a/lib/jkqtplotter/jkqtpgraphsevaluatedfunction.cpp +++ b/lib/jkqtplotter/jkqtpgraphsevaluatedfunction.cpp @@ -263,7 +263,7 @@ void JKQTPXFunctionLineGraph::collectParameters() iparams.clear(); JKQTPDatastore* datastore=parent->getDatastore(); int imin=0; - int imax=datastore->getColumn(parameterColumn).getRows(); + int imax=datastore->getRows(parameterColumn); for (int i=imin; iget(parameterColumn,i); @@ -286,7 +286,7 @@ void JKQTPXFunctionLineGraph::collectParameters() ierrorparams.clear(); JKQTPDatastore* datastore=parent->getDatastore(); int imin=0; - int imax=datastore->getColumn(errorParameterColumn).getRows(); + int imax=datastore->getRows(errorParameterColumn); for (int i=imin; iget(errorParameterColumn,i); @@ -361,7 +361,7 @@ void JKQTPXFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { QPen ep=painter.pen(); ep.setColor(errorColor); - ep.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, errorLineWidth*parent->getLineWidthMultiplier()))); + ep.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, errorLineWidth*parent->getLineWidthMultiplier()))); ep.setStyle(errorStyle); ep.setJoinStyle(Qt::RoundJoin); @@ -540,7 +540,7 @@ void JKQTPYFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { QPen ep=painter.pen(); ep.setColor(errorColor); - ep.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, errorLineWidth*parent->getLineWidthMultiplier()))); + ep.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, errorLineWidth*parent->getLineWidthMultiplier()))); ep.setStyle(errorStyle); ep.setJoinStyle(Qt::RoundJoin); @@ -723,7 +723,7 @@ QBrush JKQTPXFunctionLineGraph::getErrorBrush(JKQTPEnhancedPainter& /*painter*/) QPen JKQTPXFunctionLineGraph::getErrorLinePen(JKQTPEnhancedPainter& painter) const { QPen p; p.setColor(errorColor); - p.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*errorLineWidth))); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*errorLineWidth))); p.setStyle(errorStyle); p.setJoinStyle(Qt::RoundJoin); p.setCapStyle(Qt::RoundCap); diff --git a/lib/jkqtplotter/jkqtpgraphsfilledcurve.cpp b/lib/jkqtplotter/jkqtpgraphsfilledcurve.cpp index 35ded99016..c0f0bff33e 100644 --- a/lib/jkqtplotter/jkqtpgraphsfilledcurve.cpp +++ b/lib/jkqtplotter/jkqtpgraphsfilledcurve.cpp @@ -148,7 +148,7 @@ bool JKQTPFilledVerticalRangeGraph::getYMinMax(double &miny, double &maxy, doubl JKQTPDatastore* datastore=parent->getDatastore(); int imin=0; - int imax=static_cast(qMin(qMin(datastore->getColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()), datastore->getColumn(static_cast(yColumn2)).getRows())); + int imax=static_cast(qMin(qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))), datastore->getRows(static_cast(yColumn2)))); if (imax(qMin(qMin(datastore->getColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()), datastore->getColumn(static_cast(yColumn2)).getRows())); + int imax=static_cast(qMin(qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))), datastore->getRows(static_cast(yColumn2)))); int imin=0; if (imaxgetAxisColor()); - p.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, colorBarRightAxis->getLineWidth()*parent->getLineWidthMultiplier()))); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, colorBarRightAxis->getLineWidth()*parent->getLineWidthMultiplier()))); painter.setPen(p); painter.drawRect(cb); @@ -1171,7 +1171,7 @@ void JKQTPMathImage::drawOutside(JKQTPEnhancedPainter& painter, QRect /*leftSpac painter.drawImage(cb, b.transformed(rm)); QPen p=painter.pen(); p.setColor(colorBarTopAxis->getAxisColor()); - p.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, colorBarTopAxis->getLineWidth()*parent->getLineWidthMultiplier()))); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, colorBarTopAxis->getLineWidth()*parent->getLineWidthMultiplier()))); painter.setPen(p); painter.drawRect(cb); @@ -1647,7 +1647,7 @@ QImage JKQTPMathImage::drawImage() { void JKQTPMathImage::setPalette(int pal) { - palette=(JKQTPMathImageColorPalette)pal; + palette=static_cast(pal); } @@ -1727,6 +1727,10 @@ JKQTPColumnMathImage::JKQTPColumnMathImage(double x, double y, double width, dou void JKQTPColumnMathImage::setImageColumn(int __value) { this->imageColumn = __value; + if (parent && __value>=0 && parent->getDatastore()) { + setNx(parent->getDatastore()->getColumnImageWidth(__value)); + setNy(parent->getDatastore()->getColumnImageHeight(__value)); + } } int JKQTPColumnMathImage::getImageColumn() const @@ -1752,20 +1756,20 @@ bool JKQTPColumnMathImage::usesColumn(int c) const void JKQTPColumnMathImage::ensureImageData() { - if (this->Nx==0 || imageColumn<0 || !parent->getDatastore()->getColumn(imageColumn).getPointer(0)) { + if (this->Nx==0 || imageColumn<0 || !parent->getDatastore()->getColumnPointer(imageColumn,0)) { this->Ny=0; this->data=nullptr; this->datatype=JKQTPMathImageBase::DoubleArray; } else { this->datatype=JKQTPMathImageBase::DoubleArray; - this->data=parent->getDatastore()->getColumn(imageColumn).getPointer(0); - this->Ny=parent->getDatastore()->getColumn(imageColumn).getRows()/this->Nx; + this->data=parent->getDatastore()->getColumnPointer(imageColumn,0); + this->Ny=parent->getDatastore()->getRows(imageColumn)/this->Nx; } - if (this->Nx==0 || modifierColumn<0 || !parent->getDatastore()->getColumn(modifierColumn).getPointer(0)) { + if (this->Nx==0 || modifierColumn<0 || !parent->getDatastore()->getColumnPointer(modifierColumn,0)) { this->dataModifier=nullptr; } else { this->datatypeModifier=JKQTPMathImageBase::DoubleArray; - this->dataModifier=parent->getDatastore()->getColumn(modifierColumn).getPointer(0); + this->dataModifier=parent->getDatastore()->getColumnPointer(modifierColumn,0); } } diff --git a/lib/jkqtplotter/jkqtpgraphsimageoverlays.cpp b/lib/jkqtplotter/jkqtpgraphsimageoverlays.cpp index fc657b5692..9dcef93b81 100644 --- a/lib/jkqtplotter/jkqtpgraphsimageoverlays.cpp +++ b/lib/jkqtplotter/jkqtpgraphsimageoverlays.cpp @@ -352,8 +352,8 @@ int JKQTPColumnOverlayImageEnhanced::getImageColumn() const return this->imageColumn; } void JKQTPColumnOverlayImageEnhanced::draw(JKQTPEnhancedPainter &painter) { - double* d=parent->getDatastore()->getColumn(imageColumn).getPointer(0); - size_t imgSize=parent->getDatastore()->getColumn(imageColumn).getRows(); + double* d=parent->getDatastore()->getColumnPointer(imageColumn,0); + size_t imgSize=parent->getDatastore()->getRows(imageColumn); this->data=(bool*)malloc(imgSize*sizeof(bool)); this->Ny=imgSize/this->Nx; for (size_t i=0; igetAxisColor()); - p.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, l[li].colorBarRightAxis->getLineWidth()*parent->getLineWidthMultiplier()))); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, l[li].colorBarRightAxis->getLineWidth()*parent->getLineWidthMultiplier()))); painter.setPen(p); painter.drawRect(cb); @@ -456,7 +456,7 @@ void JKQTPRGBMathImage::drawOutside(JKQTPEnhancedPainter& painter, QRect /*leftS painter.drawImage(cb, l[li].paletteImage.transformed(mt)); QPen p=painter.pen(); p.setColor(l[li].colorBarTopAxis->getAxisColor()); - p.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, l[li].colorBarTopAxis->getLineWidth()*parent->getLineWidthMultiplier()))); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, l[li].colorBarTopAxis->getLineWidth()*parent->getLineWidthMultiplier()))); painter.setPen(p); painter.drawRect(cb); @@ -1250,19 +1250,19 @@ void JKQTPColumnRGBMathImage::ensureImageData() this->datatype=JKQTPMathImageBase::DoubleArray; this->datatypeG=JKQTPMathImageBase::DoubleArray; this->datatypeB=JKQTPMathImageBase::DoubleArray; - this->data=parent->getDatastore()->getColumn(imageRColumn).getPointer(0); - this->dataG=parent->getDatastore()->getColumn(imageGColumn).getPointer(0); - this->dataB=parent->getDatastore()->getColumn(imageBColumn).getPointer(0); - /*if (Nx*Ny==0 || Nx*Ny>parent->getDatastore()->getColumn(imageRColumn).getRows()) { + this->data=parent->getDatastore()->getColumnPointer(imageRColumn,0); + this->dataG=parent->getDatastore()->getColumnPointer(imageGColumn,0); + this->dataB=parent->getDatastore()->getColumnPointer(imageBColumn,0); + /*if (Nx*Ny==0 || Nx*Ny>parent->getDatastore()->getRows(imageRColumn)) { if (Nx>0) { - Ny=parent->getDatastore()->getColumn(imageRColumn).getRows()/this->Nx; + Ny=parent->getDatastore()->getRows(imageRColumn)/this->Nx; } else { - Nx=parent->getDatastore()->getColumn(imageRColumn).getRows(); + Nx=parent->getDatastore()->getRows(imageRColumn); Ny=1; } }*/ this->datatypeModifier=JKQTPMathImageBase::DoubleArray; - this->dataModifier=parent->getDatastore()->getColumn(modifierColumn).getPointer(0); + this->dataModifier=parent->getDatastore()->getColumnPointer(modifierColumn,0); } diff --git a/lib/jkqtplotter/jkqtpgraphsimpulses.cpp b/lib/jkqtplotter/jkqtpgraphsimpulses.cpp index e6135605c4..eac8dc2c7c 100644 --- a/lib/jkqtplotter/jkqtpgraphsimpulses.cpp +++ b/lib/jkqtplotter/jkqtpgraphsimpulses.cpp @@ -64,7 +64,7 @@ void JKQTPImpulsesHorizontalGraph::draw(JKQTPEnhancedPainter& painter) { QPen p=getLinePen(painter, parent); p.setCapStyle(Qt::FlatCap); - int imax=qMin(datastore->getColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))); int imin=0; if (imaxgetColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))); int imin=0; if (imax(datastore->getColumn(static_cast(dataColumn)).getRows()); + int imax=static_cast(datastore->getRows(static_cast(dataColumn))); int imin=0; if (imaxpt2px(painter, p.widthF()), rect.width()/10.0))); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH,qMin(parent->pt2px(painter, p.widthF()), rect.width()/10.0))); if (drawBaseline) { if (peakHeight>=0) painter.drawLine(rect.bottomLeft(), rect.bottomRight()); else painter.drawLine(rect.topLeft(), rect.topRight()); @@ -157,7 +157,7 @@ void JKQTPPeakStreamGraph::drawKeyMarker(JKQTPEnhancedPainter &painter, QRectF & painter.drawLine(QPointF(rect.left()+rect.width()*0.75, rect.top()), QPointF(rect.left()+rect.width()*0.75, rect.bottom())); painter.drawLine(QPointF(rect.left()+rect.width()*0.9, rect.top()), QPointF(rect.left()+rect.width()*0.9, rect.bottom())); } else { - p.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH,qMin(parent->pt2px(painter, p.widthF()), rect.height()/15.0))); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH,qMin(parent->pt2px(painter, p.widthF()), rect.height()/15.0))); if (drawBaseline) { if (peakHeight>=0) painter.drawLine(rect.bottomLeft(), rect.topLeft()); else painter.drawLine(rect.bottomRight(), rect.topRight()); diff --git a/lib/jkqtplotter/jkqtpgraphsrange.cpp b/lib/jkqtplotter/jkqtpgraphsrange.cpp index e9979e53a5..fdbfb713f6 100644 --- a/lib/jkqtplotter/jkqtpgraphsrange.cpp +++ b/lib/jkqtplotter/jkqtpgraphsrange.cpp @@ -123,7 +123,7 @@ void JKQTPHorizontalRange::draw(JKQTPEnhancedPainter& painter) { QPen p=painter.pen(); p.setColor(centerColor); p.setStyle(centerStyle); - p.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, centerLineWidth*parent->getLineWidthMultiplier()))); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, centerLineWidth*parent->getLineWidthMultiplier()))); painter.setPen(p); painter.drawLine(QLineF(mi, c, ma, c)); } @@ -411,7 +411,7 @@ void JKQTPVerticalRange::draw(JKQTPEnhancedPainter& painter) { QPen p=painter.pen(); p.setColor(centerColor); p.setStyle(centerStyle); - p.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, centerLineWidth*parent->getLineWidthMultiplier()))); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, centerLineWidth*parent->getLineWidthMultiplier()))); painter.setPen(p); painter.drawLine(QLineF(c, mi, c, ma)); } diff --git a/lib/jkqtplotter/jkqtpgraphsscatter.cpp b/lib/jkqtplotter/jkqtpgraphsscatter.cpp index ff8a27d3ee..a5629dffbb 100644 --- a/lib/jkqtplotter/jkqtpgraphsscatter.cpp +++ b/lib/jkqtplotter/jkqtpgraphsscatter.cpp @@ -78,7 +78,7 @@ void JKQTPXYLineGraph::draw(JKQTPEnhancedPainter& painter) { QPen penSelection=getHighlightingLinePen(painter, parent); - int imax=qMin(datastore->getColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))); int imin=0; if (imaxgetDatastore(); int imin=0; - int imax=qMin(datastore->getColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))); if (imaxgetDatastore(); int imin=0; - int imax=qMin(datastore->getColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))); if (imaxgetColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))); int imin=0; if (imaxgetDatastore(); if (datastore==nullptr) return; if (colorColumn<0) return; - int imax=qMin(datastore->getColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))); int imin=0; if (imaxgetDatastore(); if (datastore==nullptr) return getSymbolSize(); if (sizeColumn<0) return getSymbolSize(); - if (i>=(int64_t)datastore->getColumn(sizeColumn).getRows()) return getSymbolSize(); + if (i>=(int64_t)datastore->getRows(sizeColumn)) return getSymbolSize(); return m_toSizePtFunctor(datastore->get(xColumn,i), datastore->get(yColumn,i), datastore->get(sizeColumn,i)); } @@ -834,7 +834,7 @@ double JKQTPXYParametrizedScatterGraph::getLocalLineWidth(int i) JKQTPDatastore* datastore=parent->getDatastore(); if (datastore==nullptr) return getLineWidth(); if (linewidthColumn<0) return getLineWidth(); - if (i>=(int64_t)datastore->getColumn(linewidthColumn).getRows()) return getLineWidth(); + if (i>=(int64_t)datastore->getRows(linewidthColumn)) return getLineWidth(); return m_toWidthPtFunctor(datastore->get(xColumn,i), datastore->get(yColumn,i), datastore->get(linewidthColumn,i)); } @@ -845,18 +845,18 @@ QColor JKQTPXYParametrizedScatterGraph::getLocalColor(int i) const if (datastore==nullptr) return getLineColor(); if (colorColumn<0) return getLineColor(); if (colorColumnContainsRGB) { - if (i<0 || i>=(int64_t)datastore->getColumn(colorColumn).getRows()) return getLineColor(); + if (i<0 || i>=(int64_t)datastore->getRows(colorColumn)) return getLineColor(); //QRgb rgb= return QRgb(round(datastore->get(colorColumn,i))); } else { QImage img; double colorval=0; - if (i>=0 && i<(int64_t)datastore->getColumn(colorColumn).getRows()) colorval=datastore->get(colorColumn,i); + if (i>=0 && i<(int64_t)datastore->getRows(colorColumn)) colorval=datastore->get(colorColumn,i); double colMin=0; double colMax=0; if (intColMin==intColMax) { colMin=0; - colMax=datastore->getColumn(colorColumn).getRows()-1; + colMax=datastore->getRows(colorColumn)-1; } else { colMin=intColMin; colMax=intColMax; @@ -873,7 +873,7 @@ JKQTPGraphSymbols JKQTPXYParametrizedScatterGraph::getLocalSymbolType(int i) JKQTPDatastore* datastore=parent->getDatastore(); if (datastore==nullptr) return getSymbolType(); if (symbolColumn<0) return getSymbolType(); - if (i>=static_cast(datastore->getColumn(symbolColumn).getRows())) return getSymbolType(); + if (i>=static_cast(datastore->getRows(symbolColumn))) return getSymbolType(); return m_toSymbolFunctor(datastore->get(xColumn,i), datastore->get(yColumn,i), datastore->get(symbolColumn,i)); } @@ -906,7 +906,7 @@ bool JKQTPXYParametrizedErrorScatterGraph::getXMinMax(double &minx, double &maxx JKQTPDatastore* datastore=parent->getDatastore(); int imin=0; - int imax=qMin(datastore->getColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))); if (imaxgetDatastore(); int imin=0; - int imax=qMin(datastore->getColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))); if (imax(datastore->getColumn(static_cast(dataColumn)).getRows()); + int imax=static_cast(datastore->getRows(static_cast(dataColumn))); int imin=0; if (imaxgetColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))); int imin=0; if (imaxgetColumn(static_cast(xColumn)).getRows(), datastore->getColumn(static_cast(yColumn)).getRows()); + int imax=qMin(datastore->getRows(static_cast(xColumn)), datastore->getRows(static_cast(yColumn))); int imin=0; if (imax) #include "jkqtplottertools/jkqtpdrawingtools.h" #include "jkqtplottertools/jkqtpenhancedpainter.h" -const double JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH= 0.02; +const double JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH= 0.02; void JKQTPPlotSymbol(QPaintDevice& paintDevice, double x, double y, JKQTPGraphSymbols symbol, double size, double symbolLineWidth, QColor color, QColor fillColor) { JKQTPEnhancedPainter p(&paintDevice); @@ -37,7 +37,7 @@ void JKQTPPlotSymbol(JKQTPEnhancedPainter& painter, double x, double y, JKQTPGra painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();}); QPen p=painter.pen(); p.setColor(color); - p.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, symbolLineWidth)); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, symbolLineWidth)); p.setStyle(Qt::SolidLine); p.setCapStyle(Qt::FlatCap); painter.setPen(p); diff --git a/lib/jkqtplottertools/jkqtpdrawingtools.h b/lib/jkqtplottertools/jkqtpdrawingtools.h index d5cbf3527d..0e0d466168 100644 --- a/lib/jkqtplottertools/jkqtpdrawingtools.h +++ b/lib/jkqtplottertools/jkqtpdrawingtools.h @@ -30,7 +30,7 @@ class JKQTPEnhancedPainter; // forward /*! \brief tool class with static values used by JKQTPlotter/JKQTBasePlotter \ingroup jkqtptools_drawing */ -JKQTP_LIB_EXPORT struct JKQTPlotterDrawinTools { +JKQTP_LIB_EXPORT struct JKQTPlotterDrawingTools { /** \brief smallest linewidth any line in JKQTPlotter/JKQTBasePlotter may have */ static const double ABS_MIN_LINEWIDTH; diff --git a/lib/jkqtplottertools/jkqtpimagetools.cpp b/lib/jkqtplottertools/jkqtpimagetools.cpp index 12c542db89..75457029b8 100644 --- a/lib/jkqtplottertools/jkqtpimagetools.cpp +++ b/lib/jkqtplottertools/jkqtpimagetools.cpp @@ -1942,7 +1942,7 @@ void JKQTPColorPaletteTools::cbDrawOutside(JKQTPEnhancedPainter& painter, QRect painter.drawImage(cb, b.mirrored(true, false)); QPen p=painter.pen(); p.setColor(colorBarRightAxis->getAxisColor()); - p.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, cbParent->pt2px(painter, colorBarRightAxis->getLineWidth()*cbParent->getLineWidthMultiplier()))); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, cbParent->pt2px(painter, colorBarRightAxis->getLineWidth()*cbParent->getLineWidthMultiplier()))); painter.setPen(p); painter.drawRect(cb); @@ -1995,7 +1995,7 @@ void JKQTPColorPaletteTools::cbDrawOutside(JKQTPEnhancedPainter& painter, QRect painter.drawImage(cb, b.transformed(rm)); QPen p=painter.pen(); p.setColor(colorBarTopAxis->getAxisColor()); - p.setWidthF(qMax(JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, cbParent->pt2px(painter, colorBarTopAxis->getLineWidth()*cbParent->getLineWidthMultiplier()))); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, cbParent->pt2px(painter, colorBarTopAxis->getLineWidth()*cbParent->getLineWidthMultiplier()))); painter.setPen(p); painter.drawRect(cb); diff --git a/screenshots/simpletest_datastore_calccolumns.png b/screenshots/simpletest_datastore_calccolumns.png new file mode 100644 index 0000000000000000000000000000000000000000..00ddede9b8d34d6434b23b3b71df634c1d9dc996 GIT binary patch literal 17833 zcmbvSWn7feyEY6fs5D4R_fXQ^9fLzSlu9Ed-5t^`F(5EB3^6F6gh+>gbPggY-Q6Jw zJQw)i_ukL@-uwOXewg1}G1pqxI@emqS;sliS{f>N*p%4!?%l%!t13e8-Mb$Ld=xR! zfGdsjIi0}QeK)9z{Jn}H>J8xJfwi2v+`W6B6L9{TJp|6NoK;`C-MfeX?e=s3yHlwJ zaFNJe>4m$tla;%dnXBbJ6<143M>iWMcVk^l;1MNyu%eufxA9IkMiXh*RChbJd12|O zX`DIu;}e}YAws3X2sTqW-u84%o2P#|FO(FYKPHigF_N`c{2cf~3jv04E0v*j=vJ_W zkgy*~N$g3ut#0p%o&9d~8=7+4yS$NV?H_2YuC892^-0gDE_&~JUQLfFk7oL8>=T$_ zx=i;22Ce^k^&7Xer#HsTMA0z?e`4Ra9IR%3VzSfC0W%y;wq84QEWcy|4UVQe`G$QF{XtvYloGj@44eAhyTHUBGb)TTuooox&tWk@0y!iWT&= zbs!5-^kF`MboSM{+urHdn-%YH@#k(H;)+^g87LaB8Cc;XJRkCJ0&}~JN$a!UBLYRO zx{v>ueCI?(!#|8Ogs2H1HDyeQ%3O#+;kF%}!=ON6pAJOt5^JC`;WgFP)Ifi%ei0RR0yQ?6zZIH15Yd_N`(~QL#%`b4V>)enMyd5&7RE!WH^T zKb{wS=6xM^GT@H=%^>$h;$~QlVBU6D4#t`{xYiWODPHN~CH3Vz3go*8<8U zec4ghIO?}98KC?pL{9g`Q@{IOcJEXM>Z$|_por4Kj`@mi@=bHl$IhEB7s4KNt zZHHDKf5M6r<_|7!In--c@aMP8A7xaFLt*I@;HsXXzV!Ie@cI2&p=B%;guw;9d`bfi z8}UINm-A;guEW~aXFlh{1k%peug{Xf4sU-{DlGo}zQ+Qi6!Xb(@+v%Q_|mXC`@F?e ziU zLGtL!MV~=H1x+XI;*pXiQwbNXmBg18!_)>s7k7Rcr-4^Beo% ziZdtBh8nNEtOae^ZZ>E8m}r-~VHZpN6#OXTc{cXOp&!{U%C?v?gp#kL2=0*nBLn6U z&1-CoZH+iCXUjctzD}S#9O68=e?i5g1_Oj(<(|l4SG+rmRCvlkC2;jY>m^LSBNY!2 zKoUVQ-D3Z!vg+2he#*|tyj$t!n)_opL-KiZD;}~85}Lc2k!*dcV;A^}0CsVNPLMOX z*CMQW+Oqzqk(=T-4d~>kI|ZC%3N0+*Qk|Q*)NXP5s19kZbo*7IumjR)T|4otdr>AE zJA^)`;Je+1e~pHm`S!(1gJ5SS|HggVh_&=>VSe+dKz7v3U3=^LX3KJhJ#irwAzZ%WwwfEjA zy5A=`I<2aDdG=Ti0GyPEb73(MY~lu-oL!HDHd-3v?H?{ZT(j<)n#X@ZLIGwMyL7+b z%FMZBt&h|@M6z1#7rUTKxYM2ozTnH=FKVUmR{2FW>m2fv=0{s2#+M7vGwdVTws9J| za}G@ycb2VnnMFk3H=HnGQ8%g(0|8+s`V`zh%&i%R5o?H7B;d;SI-&lBCiRyxo2EPwG| zrY%P<8M@dOo(*sRD%!zw5|`VaEjz<$x~${LTl;Dd&{#r#>V!9bsp+d-e=IJbLu;HD z^5~KiYig7IBgOsjz+@!$DA6?Ev9srglKpQ*bzIAZLnGcvx|%!muVqXqIoy{XX0+>> z_;5aG`fiI_hUe?6}v>ez)G_tc-MOTkd(0W}u^R zZS#S8Ul>_%|XR zZi0z3nF@UaVUCld)p!{s7gfr1$ng*LE#Z$Qb%8StLUw_F%}xlOfXn~52thW-hg%_Y z0q61M6^%hnndg76xg8ALeHIk#mGno6!=B~(w%c>j`IqGI{}#*gtUnmqDjkS-y5M-4 zD}5dQ>`kB<)0@VUqU;W2-;VJc{&L&VvSCvmBFS*b8=fSCP&b?WR*H@A0PH`^Cu?!0 zY0@H|3y(yF9(=KqG3-&<2}ENk0l7|WE!@~*yrkub;~$9>04K=(ZA=7|1NS}yDB$_3 zO}O4vCr{sQfJI*Ur(q^^LU$(B!nnGALoYAQ-8;mkFbO0tlkhzbxqL8DeHPGsA?aK~ z?Bm+TNo>WVK6h0}WiWGT{)$RbP4+QP|KGgA7KD!U zZ5iZViTSkGX|q+yI=wO#$v}Jal5A)3PTgJj_nv*msSd?p{S#_N8N5rwMnMUw`q$io z_3V}VW=EH$-Qq2dO>ErZ(nl7VD8&MQn~skw1X5Sr-8cu>9tEqL?sv-jbZp|E!IX)r z`p;1YD$7#l&UESodFP@=3-}!$7RIQI3Yw1&>W>Cg*VlV{>=h*ku+nYj%pL83bPfI ztvGtK53RhkMBrt2+#iy&M;APsPL* z16htScrX&SBjh=l<@+&0K}5z0evd-5i~tenyaI&>g8A$%`pwXkq_Q|52|px!k$K=1 zy%M)=l;@f(eg-ElyOZW1bhI~uPUo>2RqjJM77J**jWIapY?|fCdZH z)gFhzBZfoh`Pk(i6NUZX4)T`aD1l)~?GJk8UmSk9O8aa#?zPyMm1ty(=1OkTqWezPSkU^*gcE%@38U5DVbM=V&#*!|Ypx%U!3T1kx{B0|SOS<$!0Z zEc>50)QD%I@*`-l$WaJ&A4(`=f{`TS`4@E zE;#e;IPxfIW2$;h4uhsML$p-81pn1n#~JY~>B$N>ok}zEq_Acb z@Hw;)-23zmf2j$oZ{$JB_|j?w5UOn=n9Hc)xC4i$Q*%fKaz5n%V2RBM0PqC6jbx+&3JKE!ay%;?%yqw8@PbWr=@oi1>w$Q z!29ffBapdRFBBurAme(K=hL{`z&SS4;PDHPiI5=Db7%U1&h+r7Q|r}8#}jo9PtWN$ ztTnvKuo!ZFc5m!8rlqT{^_!nzh|y1G3V&_&Dh$~{x>}tG#$*B*CBFrxbr>_Bc_-#y z&$4{5k#qaQxqvg8T+2fnK$%hg8GFX#BJC4LA+QR(!!m@?=VGJeb=KQhFbf$Ua*-ZH zO2dbw3`y5FwVWy&c;Hb)?~21rIjJg$Umt zikz?tB655n!{z(hJB;Xo16tPPc@;PR3@1MSOsocNiQnrK%JLJCFx$ze-V2nh=9#^) zf`OJm2}E3*`F-+~$durM6?-;25oH|~&5&Ml6@6L_!9O8nOTtMmo{`qXLvx)Ua+hXk z<72*JvkPkYN@zmC{4`^+ZRWvm!F@})PoFMf`%3=NPg$@m0tg?+z4(uah^(O_9}I(W zY*$m-Sy?dq&@w%OpdIl)KdPhO9*5q_ zj6m`CgQA(-_OQVa(+Bj=oQ;?I8{jqs!ZJ6Cn= z^3O5>4?_1^E**aNnD4rW+kd=L^*o;p=hd5fA^GenjN}9r`e;}tx6wCOIydlw-QaCEfTOc2(0dMxDd-_&F(d2j z#7}f*mSnAmDAX62RA@X%C?gJ1;z#rtv631E9mB>Hi5R_ReXNIbq-HRSqWhHhw=nsT zIpi|8pB`NgLjEAW41gKZL4;vx8_?riuk%l{NCxluV2taly`a{^KF-RP zi_PbS%H>{p0&8)_`$1VQVz^sA_WUFRC>5eM zfUqaS3aR2Yhsu+-RVqL@GMHx&Wp5#8=J}5_&fr3JKc1=5TIcu_r|S^j?#U*Q$LW;y zWTiA3dpUPngpmwLbP65H0WztG?3F|>?$c1`^W6|MQc$$EJPVjHf7Ur*5vrai)P&zXDOMPI)`BdI4J$o4fU|+R;IBZo{6L z*>+&c79lZ-PG^9EVzh()_h{As*Aem|?Ew*}KsXr90deKvy%Z%6>tnpb;|KXwJ?@ox zw|fD=qbSJP{~Td7*bT8gGv3ao7A`M2$qtAfX&&2k`mn-IQ>MPqZUO%-bkJ0r?BPD_6tQ!hZiEpH|P7 zX-~&BB?p`&b*p$OD|NVvHrY=4W5Uce_cd5l-e{hQd+jzAn*DO9Q*zF9_%dZ2s(N#M zr6M+h<)*BCj@it$c>2q!j&NFynSmeBs%trZL-AU0Rr%#$3|eo%>i7-p#R3)HnXRd6 z*0tB=V$A&0Zn+0eahgi}+qIvsyB|(+aFY;voHcnoMie4~^CajZR)+jLLhT(?2uTk>z{#3Qk+P`oi%gWA#zVRuS+XIcgTRL>5VgD8 z*WJk#`nI45_%y=wMUxmae(g7 zL+!QGlIXYNNuTfQ{Ui+>wbju~{K`nMnuED|wxEY^+J-H81${7p4#aoBQ*=Hp`rHq{ zluXU20=1+jq_de|#ren(U%MERaa#GtR%8hDm=9?VU>RW3klY9fAG2YMK{kb|Pi$vZ zqrkl#KEl9=qQZs|*WZ7Rkl-USNI+ikCqIl%t^~O?k<6JXfHiOneTp5{(AG&>9nDL^ z1vtsq{;xk@i`xN+h7F7Zz;-2s^LR7fg{%6L5jxTN1py>~(U(cfcc_=~|Fz|_fe5Fd z`}DI9!4xcJ%u0lqUvwusqvzn;w)j!t{~&?TG=gjk=R3GO{?gRmGBnbLNx__dFW)!a z{IwW8NcREE_vY&C?C0x*29K>pi05yY@5=F%scC6Pe^>y7UPsx$T)jTrSaZA?hn5Rsxi=>( z=PpjRHtb=yi2Ye^?l(=37{~^S6Aw1(x}QMwQuO9xt8z4Z;e+T0{PhzbFaP(1p;hWJ;9hV^JnP>V-qd6qhdIkZa= zK+c17l-A5Mr%97QOY#`q)k$4?m~91BgwJm)d|pd6JZwyDdDo%}IAm*xX`B&2uhasOuDS177dc|Zo#dHN z!8T19_1SA?a{mYc`LR>%QTlD3Fl6NYxwD1$z~m zx!!2{OUylzA-o1$LG4?0%z3Nu#}T|b8KpYb0hUZBW|-M?MeCs2WVeB|;%pkR`O!~| zFiM+K-{=41=Z_H;Teeh1Kb+YUfXzUuu;U+sPD=_$306-I-l;dzm!dqB(lFl{_g7!^hSwRIp-lWLa zV5h+ol~zyxu7=BLO~pVZ(^gRPt66 zx`|1AlQ1Noel_DThS>8-&^nZuF(^TKihGM=q#Hsu&82HMm`eKHD*^YROFo_#={i0^ zu;%9|wa)$9(Eafqp<1ANpK-Sj@$Ij%^*)?spKVM2v-EL3QoUBe0VEa~W^~DgB7)yw&>*N%CTs?^s!n~sg`XZPDPJKHs`6+ME~HTTX0+g;*U45TowUE` zKf|9-y-%L-pRT9HcR$P;C6RXRBvi>WBRBTW|7V6v{Kw{>Y4mCZ&q=sA^_OKf^t*>M>?p)OL+6^Gl&Ngbz$z1sW}Bt1+x7)8dUC-hWQNp z@0{ZzRiwE?ZjXBr1xoj69k zPLLv#``YGEH}gYVz^5vpToalo1Z?RVkdUGQ?TmS4B21#JXQ#dTbPjZfkKI&+aPzn4ey?gyD4;7@(B0D+!?TA3MX$v8NVkQJ*dQ7 zng85dxHDo_7;)AWnjxyR&4i#92W4_i$k2H$vMZ87_hX%H|?QE^|8fLBSceLFf@^j!_yE=Ematr z%!Zu`SS0h*G{rg;L0A8kj-UPVP5C0_`|PSXruQZ>VBJT8QS(i&!6X49KQ~_H1)%B_phlX*Jp9vg0`BAcN$mz6$=5VGMIdN&+)nXW}u82>L+X z78WRl8`8K%YJeZKXDa~bhuaKDXZ1(tfu~}h2)n)o6!JYl(}def%=nq8 z*oFpvfQBC$kEGdbNpq+5ybm4E{V-&x&c;PAl-YN z2y=!`1nu)pNI*a4##wRQa=}AGl{lR)MBs>~q1^IQd<4*@wca7m%sgbH{Pjy~zGB zx3!z}7}hX*ypKh#8f@wbvvoQxPm$;x>TSSl6n%DHPY+6!g(U;NAfV2$1dwWwvu%aE zb)U?W+x~eV3etzr1)Zgn3fgkRZ1L1+nd$*t|#D^U1e(6ex zZN1>84H!cftUMrH$LW22{NG{fEsePtPmlcxVrIjQQAu!;j7&K|PV;2XfebqZcDjt`3e+9GXt! z*Yly`I12jXZ^fufp93aq3K1{+D^^cdMZDvc>6AcinZ9Ik+?vWjxBEsRiWjiJYr^BW zp;_Ci$EzeN`*PSA$`coIF0(eJeg3vX+}>bcGf@>fsdLmkJQ#2X)ecMia2mMJeI}19 z?zy$Q-~0H(YgpT>DDCRq$bre`ANrY>kD5zE+c#!wr$-sJ>o;@Zfyyt#kQ6Hu@50_H1= z)U(>sP#c8Affw>nWk~9t)O_Dc+S#K~snKs;E{n~OPV57@CgP|$#E;C+WtY4Wn0H>h z)gXML+3yVS-|?+)@!Wmuhr^8cnHe=!L6~~q>8aH5)vd>GyqyIifwlt}z1HK!M+J^escHQfeqPc0x ztlj#bTdXszA67gX$ZetS{j`#k(`qEJs`nx=bV%HIb8*9U|9JQ*Q&fIIyp}*&%+st` zuw`K<#1?ivo!Jn!=en~Es_hwgJVRxPZ4X7sm*Qba7Nn;H6tBRjP0a<7+9zWbk~z+Rbh z980lCRf0~6#MWCy=YjCqyW2bqvJ9pOlgE&qlZ~zE8{+z9=vAE-IQvoAp#-Ji`~0u> z?6*RBm~a1a1vfQY4LDz48(n(stqKXOBhQMQ#yv8Uz7k~Fs_Q=astYl8@bYnr4JJmDZh4IeOnp=h9!uw5dRjLMyT;ti_{N{u+CMEae|x_78XEuMy+ z(sE(6uMq*U_kBsKR84I>n+tP%r#Q*{L6#;NUbGnLQJ{wy`L@}YriXUW^VZ;ZU$f9@ z&7m8&i@XNh>s~pA$V8QVlQHt%=s=MwuM3O35tDmR(nDDC3u8`n0b%5f=0=dZpSOL7^zMN8H6s*tix z<~c++kSp+Pd*MWb9Z>lBsLEu{od;*VcU`A5yjUM&5A?EdDK+REtO4>U6sy=n>*<4& zJpD#<0i>Vk_8ue+=7QVfv|9I&?f>4H2v){np!-XE+85-p^=!82r{xTv zKaJC(rclcuwxx>mrx;RYR!}BLR}B59-zxVBgO}ej>m~FN5od`fbVKH6K={i7m2#%)|N5nG4^}k?ZCp z++YWQgxaY4jnL46h~mE`o|WIq`}N)%)`^NQ!pLlsMOBBZ0O7uQO**Z;0+El|^Kpm~ z^fUJ=j7>DyG54sPz(?>@+DGc#7AQ`cJv6;~L}>ietFz2YZ}rWmN2F=wTWr_Vd+ z+~_s@K_0PU@BNVEkN<8)yzIxze=hdM0#ptB z0wycMv2_L!dRs~>b~F>CXDiRATXgU5Y*2i6PdINx{%HQmI=ev&kHH2Xi(AG)TU|o* z0DFEf)2(;EbXaw~`SnTe18Lc@z2o408H#K2&vH?;$-26?f#sD%S(vZR-ml+;;z!rw zE>W{O&m79?qIp6oze)Hg^07DiJ61|#?Bggd^0W7Y1VptUJ+ z7IZ>pOn|5E`Vj~h{1IZWNu6&}K*Dth0%(r-E~d?K??29$mK8b1pM1D4v*zRxqB#6y zeOrSI?l|~Td%qNF~9~2@1xqn4qCvqIJAJm{yc4pML&En zgm$vDgw2!;u5WYj8o?0HM0WvDNd@NG%)YK&WC7)AL4ekc6%(*ANt>3$VcjG5W@tm7 z63sY7gs~CO=@Mu z&pFvAujy~r;h}HC(Dcem&{Lk9>I%FmV)yAy(%C{Ztp$xN$iX|YY)((rsu7uLIZ z+3tBh)KR&10}Rp&vc}%M)GLAbC^kSrcg%RtHsRBV=S}|hKN|c!!K&GP16@W3vg3IV zGVXH0Vf2_nF+a);U<8kEIS>|5z&-r1llRLccI=mZ_23Yoxf>Oj@T>*@yb~;&D81G= z(N4;y+<9TAQsbO9-6;4oU#@V5QmAZ))aGk|LJy~QfH*CT8qd6Uke`9-HBNIm z{%--EL<&93l|5bvnSBgL;fMt_>T8-f*yr-M#6zvqSy`^MO=Q%?)moMMtn#meak8W4 z;VA(eyA#HaK(dW4@Z1bAI(Gf-IR=zkIN!R`|ALb=3sbl{7RTleuJ%>RxVEu;5n^vJ zbgaW|{f^lKPf1TvIDYmRa3D9(PE$P}H&d+K3y8$P;{Ni?m+axXXX|&56+QJ6Ifqjn z?KOP0=0(zkc0`+N5gA6~I8}}W-A3RD& z(|^`^+;9)EgDP&vfe(dr9lhD4bR6-3K4{b&mrLwIjOfo89R1#)l}swz%;O2QZ8u8* z0FzJZnG(*MduV2DsyrQT9u0@xD)nzQNpnL9e2*mDu`rsR#V68q)Qh&8P|$2~;dkN& zdg-+8{aB;~LXFbxbQBP?qS48+nY}S5-tLdf+(r05V$}#X7l=HAP1=%;Cp${lOvDd@ zd`(XXuEHQGC9U&D2RJAZ=DiQ)Ydp%%5A3_{;l06a(Gs)0mKEuRqwI>4P1sKqa@1q+ zAFwu~4D+IT*{h@(@Q?b!wP00F7vZc#2XLK?z#fw5-Kwx@F*(k)UM}4SZvJI+UUn|= zh=vTEs$#!muT{3C!dJWj_x2DNZj+TK4qoZ)*=L_PSyx5d)-uNNrzdfBGQF*)>@Fr7!5S76bnbG0e5C zCYILG8g+^|j+}jY)#>Mr)j;sR26mk@WW~4L(S{N2p^+OlDmXsQB0IeET?vPs=4IpV!t8}(2?WJXXHVBR zPhC_EPC~KwOCZw*NLL4FE3N@7L}WBTAYEYEhyx|x{LI}DCSO@du0YclO!6FpEuh!% z9TR4PV~C#g6@q<3{JRsJAiS!r=7os&vNtr6sRCc=O=c}E*Cqd_Cs24R>Sn@Z7B&K& zmlMX>ng7`Gt%TRE-yE9WIT|#Ih)jKDi;@fa_EksSKVY$xT*W8piHy=D;%xx*{3$-~ zb4W}?VB)1sa}%Jn+!W7V5`Io6fvcM=bqCn-G2CSdOOH9rt9}P(fuHyf{=muw(;#)w zwIlrFu^+dDA9){Sptg!ByjXiASQ?uN%pudH!bSooTXMy22dsCLLO zHyj-QEL9y_z~q(XFQ%)7TpuAMOM~k=9kppeW=g=HBVy{(h1=w(Vn9)9BSEKaCjGk4$V}C;L|#dRy)n!k2SXCDbx!M-q-FX( zfP=?9t|T~41TbWAK}vIN@|>cNdCZ|ezB#HLv0o=KPWTk7S}e2q`P`8b+1uznw+0-V zN{OEohLI|xK>XVHsR9+RdUai^?i6C(`1j6VlbV$oM55AFqT8T8F32Off;d-mzPs%O z5Tb(hDeJ)Gg9?q4V%O2om^CKOw&;*)uBz_(-?KFy7@-+8n}Qy0|DupykzIjcO9WQd z_V^RL9Y;h>sL>4Pf}71X+Up_Ak6^Zlt$}alU18}dPqVBG$8gr<$k>Fw6DmWcvYuLL z`fYT&at}FC_bRo}K4HoSJ!C~JZK`y7FSj^f2aj)KlTJ34gn)$Rs5^4dqP{vCJ8s@K z#uAgRBNyF{@IU{7ST`S{KQ-526rJhkl-R)NG5xx*Nl`tJkgLixrbXVL#COr5gBhy0 zIiZ;u_2ILjpNzy+2p0n9MetGtsY!v8#5VCo>*UH1g(+cEbT|f@M zzFe(la!5TZr|)!v3T9T{tc=}!t3{XJuN$h~<^3R!Fa@PXA2d*mSW@?d3&JQJY}wj8 z5+XOzrxM_bx#d)qMZyjkdjt3Rk$Ym_f7@`lJ8>0ry1p(5gV=8UD9;pj*c>au@m0^2 z4j37%A1(o$Ab>@1vfH?~NNw11b!s5~3qjZl{_~=Oj@EpTE%ur;@W_?{Vq3f$w(gzi zq1)F`@Oi>~wl~=d`ea%w&2uh3i}k&z0@a71;`{op7T?CHavFNGgRMa*#C1XF`HSCk z_|ox|Z=kvUhgc^z{UHx={!jRV%kZo7{(QT2mdm4|g)m}`CxSL3-SJ7esY!H)j?g6b zFtAj~C4(=`G3$8m=r4A2rAdRPa<7eY6ru0Nnxd|!=~VY?5hy$t5qHp!CGO`v4@&hH z=&xzz#Narv7}A0joT*+qa#bKfwXQ3DKw$oQLo-Oea^vS`rUtQYy5BoAw*=84L=(`kyK9l$mA;FTh80#;I6hh2Z`|pe7){@tz-_|2{fy!_ODi=N95sAl%kv-r zQ2Vw}mPr3beIOwwX$V78*>sGqxMsq5GKt`=TF)oucCRHrmc(dcRkrZH^r00qt}{F9 zGx4JkMAomy`9HHahXT>H_QZ{x$YI2Gp*M!h+g?*BQivVS@xyagT$=>eFjf`f*n2Zil&)y}xBTV#tvxXiYbM}aQ?{$RQozTdz%^TmmB!T%(*OzDvcJLwSKz^~qBneGk}lS!KNnf&v42mt1ZKXdDT7;42&B zp9bH3=ycwPyEV8yxv;!&^#Byg2urP>j_b~EYv?I=2B&DEJlRke0y`o-MP&qWuX*87 z6+s1{D^AVYyXn%Q7kX@%|~uQxW~xA!Usf3f=Jy9 zzGdcyHAlLW@s3>Dt2?Hw2aM#sCw!Q&mqAUFCNhuPG$?JooI=&>yhy6 z=h0SB_<>P4$RlD_%Ddc$7Fg_e%&CrpjFjekQl&c+Z&fguM-h5%I3n4t$T3jugyaLU zn-8n95ROMM`B2OuiTy`-k+?ttBM}ouiQtJ7?~@!Kng|@yeoSNuou}uivb^C+6-Fwp zZXdSV;scy4c*>Tv94odmKrQ;5NCKOF&iZBDOWFUUPEgUc3R1@Vbv(dOR@xRKIP*?t zQVa(|P$e%Cb=~-olJFEBoE-1P=x4YXbpE{9)NTWODLX$uF9E#2n!aF-bOCsW%5=?} zQ00QmDNF|zTl)uv1pMrM6+5l()%)ru)pNiuRug4=`u=+!+xb(rk9=Z2`n-wvIu=6q zH0*Bm*!}qHg%@d4>1(@FXeUPZ>S)j&`a$L6s_G{QUhMJST&_6(`Jhu^sIlAj=kKo9 z$C=E5+gt$b-W&s;2Yp)^93%K!{}sm@c!3<)ppY-d+MjfrLy`$^Y;S-cwRg2_kq1dZDX886+PKD2NV>XKSaP^Wad?@j2N z6dB{bs<8<+c01@Iche90djN)klJ5NCMG^Lzw|RmOgELPFe>2+wDJyDNfwg@{sOs7# zdE3vAqF@>AT19{`hoWF$1;K?efUE?He2g32;l$w5p$JO#1nVvq@eGXyG~Fz{vp) z+_M7FypOQ}--zTp!ar}#x6#CHQW&0=_nd_&HYPHv+0(#X4h=o^+!cH z{Nh?N?#GV9c<>D|C6eBY~!{Lnq8M0Z}vOs zT4`3l|00>-7Hb)A8J%Oi$lTOvCBKoRrqOReQ1!m8R8io3JbNbspsn1tqxKCU%PF8| z`^H$&(4@8a2~7G@{!h0rS`^yr6!>ctIBfu+dH!QLRu0*~)LgP%7(0L2O-=IXmp0eq z-v=U5Bz(xtxM75`GbEBb^a6}Ts(J9X6T6d5zeGBM)Gv(F$OZqy0MqD&oJEL%PW-Nv zu@+wt`=_DLPGD)eRNVvo224;mL9^T1P$XTT=+JUrDE=l2ISv|FkZ0G}|Bi{)j{~WEE`0do*l7)Q-0q%XA40W7==8 zdDQNf*YCaqkcDIZoB1@}4EO+L;`yNP8*UM{i7s#AH&j>MuYpY>ew!dn3P0M6b6WTf zC*d3U&56F#!B_kVr0e=0K|wrWXaXtX#xj5M_%NREAwTxi{iE1|cik(a*VHnvzr5*o z5ok(N%tESfj@8tudKa{(0EEyn!CMMHF)D&)t$)Sp;UDaooE3|AV#qzmb6fR|e3{5A&<;M$8V-TK}8> z#|SWe{#VxOfAd&boS+t($8Nj7>z8l=4?yXYW&itiD*+)acVLRvfw3W&ffD!iQB>^x z@@1fQBDL}2s|%2o`(f1vWJx%#Rk0JnqX3uu>f*$2DtIXOR#$0A9w-WK!|f_Q13XsM zFagSFXJzei#wQM1|Apy5Os-+)%P)K(!0>*nVG0zAh@H=eCWtO#X6;jBi2DrLVAD-m z=LU>)0P)SAfX+*PjJz#Q5%t*Ap~^2;9A%>O-@gyXsH{se%~({naU3f*5TU|)&+dqD zS?ab~V9oV62Q-ON93)4QV(^#Su+(U5bOat1V->*9cGH(R9swGEe-|Fqc>t3q#u}o6 zxqdDz6Ly#$$zV+kIGZ8oYP#6`AeJlHg8%}k-~WWkfjs)yOPr;B3|+cNZOoE+!l&#i zn@2hTc>z^LiC`MfpJrK=fpfdTPt47?+k$eR{pXGmbKY(g&3q}V@ zQ+}#r2Z^1NF3C9G6~9~nMJqgw+x-6udH@FhcUr$Vfh0m$8FjVQpnUjw@1Gu^Zb-`S zRB}BpRFoLwbEeMG>R_YiPG83D4NFVQQfE}@^SdIVlTKpITfqvrJZ=PM1MC&PX`2&1 zBz-z^5%NH;^nH!v47}0z-fcOUDYSNp>2!Lr{4sM=ILmW}l12K{)!?u?-Z@g}7x8ow z@6`-zXMSK|toU01+1Ui_1K3eg{)X{wSruId!1xw=GZrS`qE+W;RPz`{4^-czzkb(# z{9Yi5LfoAV?lt|U1SnVn-c%v{TLj*czlF^dL;r6?l_W6TCtH(1HPndyfZOT-P+as1 zAOW`o_zfvq{lBZP0HrpVBX#-rX!!eo3WX%So!5r4#cqXw0`O<9H7ICE5vU+~Rb_z{ z*hS2$vFv|aF(oqV&7_QT5BL$lPM@uFG8s}-hLqh(>oZV~)%9!}L@EJ@aT~aB0KEdj zEid?xY;A~r3ZSfJq$wJjVNP*1+eT3RqV{%rRj>?itc; z1tJoDN6E^E22}QDWPo#-8UcPOnu7cLl)2m??0Vn{J_}ZzZpN5W=Oi;uBoVq$ufvNm z$~Oi!`QB{6NYvU8hE=)7!7fy=(unZnHZrS=d&FZX$e=2U^$iuQjUW-z0rH;g0?3M% zf)}VUhp0l57#D>>&+%|rg1H8c?}HG?40uwOCjSB*eQAgFdNqCN17&FM-(xv*Dl4!z zj(`vkgF09KYOw9N{J;0VSEA@F&F5U5Jjshw$CJc$TpKfuC2C^t&z1k>{t!8-V@x|a z!ro<}_=&$9rV9@t!PQq)#FbEYPV{K)1auutl7*Uan88H(=Hzq7Qw8c%?O)ZQwl-UjZD z-hMU^hcjvr2M0H=hwk21MkK&3^&{%aiiZAH>v_cf%wMPKJ&To|T)9KS^=#+PR`HeV zq&Kcm-(^Q@@}9keV>wTRyd?o@q(@eJLW6K^`?Em}4&`Q+8F8>}Fzz8;k}B?@lb5zJ-4G+zJMJXgxI`?zd5H7!WwD>vW%9|c87m#`E_)$swCYu`Ra zojVJ7S?&N1r~Z0RFinX8Ow%X(zb0Al3c|?Sn7+xd>yP`O`u(%5S zL<2-Pht{%7LKmr^A8U<{!slwX@@l3y&kN>wkWO<)4gAFQ0n+h7C;x*VzSpa`Ws;lJ zp6t>*={pWjFnH2!hZVv=a)WFs>uqU_w+J_pk{7Y=z>!0LPR(@=O!s}}Ug7pwADvAX z`aK(Q>qnw+gfObHqehd|dWodIE<@1MiZ2tao}~kP11GovL#zX@Lu$*_yiY`I^YZUi z4Tuk{SX1YQyr?x9KJ!f*;hBv51#;e5r%dPKGM6Z&p6MRMD}OY?lE|m8%_O-D`A9vf zlAUAg^+Q~0Sa7-8JemM5b4BpHBqb~olGTK-=}OH}_8SBXJv{(X(@(w*=^SnGQ0vfB z>}lXXsTw7)u@W(lcokerz88h2+}LM3Ci0KyQ}-{i=_BZVy%>0M`R*KALHNYJj$D%U z{Mgxq4XW7(0nV5s0#z96(@+d5rjZ&hj2*W!jUEsloZ{4luniLK2x!@@2#kj~9`QXG zVuX*Z20ct_FiV_Wl6^Q#Kp$d*^DG4&VwSJA@W6#5T!Qk=mq~Yo!WauSW(M~Q&1U!N z;3-<`8e7%D>$>mc1;Zk&mB!fEUNbCviRJb6o2pVo1Pdh%;fU(8q{D6f$`Bl%XZsyJ zghTsk%sH9+?@2U>vLsf=%*D`Q6po12_%s5(`g0T#1Y5sFAMu_nM9UR~c`3}9gL3Ay zH_3{9$1J(`-scg2 zd2}JT{N@INU#S8dkw~~yTVMi-!7!}A*<8svYh)> zHFUPI2m+_uF^AUdG>u1+h zKR_~-HgqS>${gq4ane;R!aB(XQ9gD%o9}i8V~GV_Q-~@nO2oK+T#^t#)zg(7^1Z&1 z=^-Xq_#;G+t7iF@EV*u-q3=pLM)|qBu3>d%{A-ZPhUOGiocPqy^qd-fJIplHgxv=` zFy!njXm8k&%q2fD2+d!>ptNiyo9}#&GOV`+xr~t;9$)htvRA;J&UB^5u#lTMF~$!y zrdafgv1pPUeQCoEB7vFW9`rRjsi@ig!X$nlAr60rE=`{68M*f3{G5A&t(6)xnxH@r zxgGfv%QTZq&{26}eX8;|>u<@*v}BA#$*T0EXOO@n?kad-r#q{HzU%LQ_LkZBn$al~ zRDD=;Vi$r}DA4vrw5r*_+4s4RtZAAHoTQ$ljxJ;ld>$EHr;rO%xK&>R@&BcXV>p~DHvD}$(P~6VHi}Xe-=w! zj3Cq|b)@ZP)U=;RfeUc;4_v%1VrG7XG|M*Q%7H})P39+itMsQ91Y+i%ec#DsV4XU$ z*648Ky-fDwf;>vcO#Tyyxq#9JD1d4RUe2mdhvy9OWZ52#17}_0Ioz`M{Td;#mkfEIDA0#<}+9(;(nxZ4$ z;Xe@E-+yq`EnoJb7;>tcnoz0qdWli3KyJpiWPER!{=#68tA}^g+Wz)Bus^*m&gk07 zRA9se+XAvdbF=iMx%VmFS9X3*sk{;lF`XqyF^07*UJB4&RBR=H2SZo-VQZdCA%a`@ zd)_Nn%x3DdpOQzHgRZt&Ka!`$HMb>oWGXPTZ5r*|tP+h6TFM%Ks`Spf9T!+FqyS>G zP?`T)L&o%}vGwK$bN1Qq6GHKbnX^6c*|!`U-w`n?Bd-Fai$`1B*i#-RY06jf6^%?@ zpf&0Ia*1}*7(vtYM=>`Z!%uI=U+GxJu=6fW3i9k-NpiC>jsKe6g&{KmlgekAce@qZ z>09z*O?!#%&@?@lGs(zmw%ht!U#SFNE=wz8cKXIKd8KLYxkFH(s%3}$ZZm@pdbt1a zZSPa5dnZb+3f{|J`!9Fl`vS%{oi+om=GdkO9mtMM?Lv1iyaRVf;?fqA3RbBp7tPZS z!w_K`m1@z0GX$yGLvD0v3uyED{UeftkM3_3ZP7yyMn4 z51~r}VRf;c<#xn$G6_#DWL7L2uBg8sXcMXL7jh6dx>K)gqPqA{zy1zQ-No#*cVi9u zr+eMNxz=6bh-X3UH7NGUvS`Sw1a3&e(+Z+mEsC5MxA<(F9*~CiJvRcWj4NBEbEZWY zqRzScOj6%0K5A+2aj?F}RSAu<3g)r48^3mve8Ah9=y2@~uaX)F$`s)w1tS9|;6|Iz zfN^g%EgXnE%tT-AGW%W3zmR`#@>uA09qt>@OYdC5ZG=wkg8_NJ=Tl)yYoRb>$Snpw zEaPh@!HHK?nV%clbxeHO|EPXSC!vWDPZdQc8BB-95DQ=Q8lwuC5m!#KSe7`WTX%#l zIQsBSgspTHNpMshm{cstxk1U6#A<}cN!$U+3Te(hbkijC|Y&%C^$ z9DjN*w;glffIktQyLGr`n8vjg)$D7zYE}PAn5hcvHMEh5bf|eH8*i! zQ}gZ@pAUB$J*(PY8HXHAUf~DjOqc9jKUbR>qz8H`+wEvT(^TMHB*Qa56ytAG$2O3w z(KePPq%(a!eG1m0S{YTq&^&JH$&hP^y=rW}u(~TZJ@ruC0ojLZrJjrq zR_(Z)11x=~G55O6N z;%g%3^K!+j)|M`by!v>nbv~D$R@2^jN;Bsi_*(@W375s38O+)Zi(BVh2}5M%rkOLqs+^^DgWICVOl&If?4(;+gkOexQ2kt1`92 zgRAnZjDA8}NE5sUk6E_e&|Tk0=C5QSrnX|Qf}nM%oUL@SgG#%Y_+IpdCc_LS?NzG` z8!63~Y1_S^sx26Tu>&xGBG<^u_})*zhTD-`jL-a)d-31)BD&iiKp|S)rp{O;%*$0y zj{UmSjF|Vma#82@UlB7E%T6h-D#xsG;-9+K+V{3!tlFlvEeIE23*lR5Y6MBk zRVB@P;T0$sxP4hqx+kMUUNcKSeQ=%CYa~0f=S{XMh17Oj+W>ZGnmN5sp>XcYJcZ=V}4R58FUq{1haNlj&b{+c`4(WvwT^RqcV@~qx zYZ+nKkRH5OWOaMp$%agA+;Z&2=v|-8O9iePJ6vQfv?VfLFq0W32&u($uY^N8q?dWq zid_P8N@m?$knI)O$KTTJSrr}M=5$Ry9QHg^^$oL27$%Zt#o1kje+&1~yz5&dUH$S>UWkX*PeWfi}h-PB&OC2#U`XjT!&3>V&hiyZXBw=q_ zAuztUBs0A#J5hr9$*w!3;w#h}y?-K=4*4si?rqriRhb~#rv>jCEwGW~2d_xrUoI_|3-(#LF0w+`<^~?z%t`$; zBIvt@=ilUx;?k_Nc8{fa(SO{L6C|H{xPMD?oGfD%gpq=w#37&i11r? zhWXXV20z=yU7u`#1sM>?P0o83!l^Y+unu9 zpe_-#YqhTj&2E{#9$NUoQjXI%40Yw0yLB-3N4ZA2FN;w;8)?zY<%FOUlM-Iz)Jnfnj} zmL!oZbHgT0l$*!X0vlX-%D%A&)ycK?jZH-9>aH5MljW14#QC=LR@v+a6fKD&5xQbL z9@#GUSD1bGjq#;?N4nL|SYmr|6eit5S@(@qH)mt#p5Yv?abFh*AF;TxRnsexQSk}G z@6U9W;oayR$@V0?Ebj?^t%+;4`Eg8xg89hDr z30UzxQRvz%PgUOP^|?u94a7_O+Mauzo`ZZ6X5v&$htqtno+TwWW0pKzUku)~LMNAL zNtzEX&%8d58YSJMktj7na=A%bb70iwUZGfHf+@o61*%=BlKT@a%obU$hoM#zhL!!U z9X1Y(9~fx2s!C7hj<&IvuA(7?FTE~?xepr(a46n6T|tB8{**iIu=y2L(&>-{0&90x=GyPA-c;_dj3I5AO zC&1}>I^X?X1OD8LcX6uS89PzD*699n8-P7o`d8WM>yk$rGM~>5_ABT5 zj)*mAN?R9RNcA9Nx$9C%Fi>#xV}KT=xoGzVx^~&Sc3#8uG7l=h&5*V{H*7@|yu&y4 zUUn>mw>rS2QFK`e-uC{g=?9Y4IjYq!7p@^_5#4Cp7f?_jEU$dp#Ez8CbFRwPATRcO z@E5GOy*6=j)FVW~0#bmcNlk(?^>An_q_e3#gm3@wQhVk>Fwb;NQk6}Rap>8#9$&%U zPVINM_7PW@?MYK(+ywt(qyNqaJN3wb<+mD68mf(+JUnvltEzOcWI+W5Xod2Os^4(1 zIN@!sZP^f&Faq0b2k{)RbP%L-U~DyKU2Lx(jJuwjHdxOryh6W8SR0%iayU8&KDoGG zR|Z~HkZg}m_I<#uDs7?%epcg8MYAEJI6CarH00)n4_zst>1yMFKxY!D5`U1-c20o4Q8ylZ z$TzKF>a`fJIqK`bJTV7{U%yW^_D6! zRM+MTd`h*w%HPrb%j@5h-oNY6Q>XTH2~U*^_3>jY_jS#exR+GQ5DajQ|N+1a6)MinA zG8xMNgO^|@{E2lV$aA6@X`6Y1FLU)bNSL-2TsI^)aC7M7A-~3)h^%Ll(vaYYz$Ewj zT9Nu0U5O=%l3w?~EBXD_TCcu-XK6j9L zKeke&|9liM?>Bd@U620mLR^1VI&*1*qMh$E1$4bkZ%B~i=Lv7OSDwfe{g4buOJB}K zK|O*kLtyI6+bL!hcV!~D{zXlE_G5h2-F9$G@+QoWd~d_jdpVzNxN=ZI13!GD`H>D^ zzS5B2gB3Rs_}$`;i}|gz1ms%2 z>cOTgjK_oI57jygaREhGrF^f$!rLJfm6;V@^zmGLYzD~PCnjuCoIYTV10KQ!5EV3Yl>**nP99WwizCmjcA)Lffy7Zh~t8*K|-JwXt4l?4|u6b7u#c+ zQM(R&QYvh>UArc(bKA6=e>5?SNLr4-sy0KjxcX)=+Ok(6XIIm)H|g$7(y(kn#N@34 zZKc=$<9wU1a^(E6$~82=p)v%$p_$*3Y+Zx8#`N_vURceo?JdcSE|D5f&W5JM8|KRX zWNIykx*&zVlUxpO@H@VJ7ru-rz(wauUCijgn&+0O`qZcm7HM%W+#J3m0AdJjZS(@) z!x4ARjDFXzx^R(@5cC9he393PC@wq-o#b$44$S61FJbvT^kf*?##|{x5r*yK!^Tv) zE|*pd6p_x13ThQ8m~R|zRO!K}w&Iuv+M@!}Ro4`EO0KDFFdz?YID;w$eEvvBixj{g z>F?KGSj*$qlDSZ0Pbd3S#CEjEQLHRz3O4do>{8hg=%1nxU|j%!9oera1Xv9gGHYp1 zqYyz348;+(S%IGauJvaIe6)29b0G40hYh7#OZKoh#YtjT?=;olyXz%1-28_KVd-{Oz#(t@ zEazT_Zb!O|SK>bmsr|)D>Yi>X1U9)uv#wP5w|3TsEsO3wtQfF&D|5<~VQ}Z*sEiS+ zO1}9QbNJKm_!UI};R6sb+_9Y+5-N)>#rvqveGMD(a>!a|2e-)Sd{8geaxy52k&8}f z96A7N!4SL}Xt@{@{3HPCpi0}NVU05oX8lx#AFbbnmf6NH1nfE z#y|%KuZ(scC;Sq=Oru{;8(*vVk;Ftsvp6HgUDm` zQvI(S=N?k(Kqjxq+r8Omy;i9q7mXHdgAj!6n z^s})*Y`H-`Mg1$p{1sdN*=7Jnr~r--HC!Q7T_tS|(RQv1tC=qV$DmDKX*E7 zrwu3OEcisAgxq2ML$Fk}7?MA~ss{Wsk&R@fQ{GWA7NIvX+a52!Fa(a#Obu5#jk8EV zhRolLPRkEIB*c_p$+}ULbMLOTxd3P-NpedCe}`Q({!kXPvQCBa^80r6nKpfeGR?3F z7wfZN?1|PTHGS@C_@^xG_W*&St2@u`G!eyO;NEHI%QkBWT!W-DW{gtC($Q0SY6l+x zG{t%_>=thHGP+ANsL2I;-muNrhd6t?`Pn8PzqYzM** z5gy=K;Gpla{OE|qz3uhM-k!1Uq&i^sWBDW+^V$m5p`F^ON;GiLBc+3cFDtmBl8*>S z?ndFn>;)gKgK5S*U$#Wt3P7L4!jWPz#GY^yDZ|ilOU0e&H(-?pjjcpgvahn~>0++^ zwzB!XsCzu)D#_3jVqpv=Lu5x>dGoHLpzCLzP z+;_oKfuL(4aBj{x^ehm@z)m zH3r!HB1=E%(cuACx?V}vZmh-OAHzZcZV06<=pae3eCV)=a|H|ur%DVcqF%8rY4L7M z<4mXB5ow=bQ=sv`!m}w;5JaW3WaszIL%Q#I=#BiYbKb@dSo9U+nzGPb99KONzE^!d zTQ=OCkd@$`Cb~R_F%`C(G1#ffxZMO7lny)5>vO{8<$Sx7G`49u@04j`fnzm*@x9a? zBW7uQM{f8^O=@Ia=8Wa7YrzH)QSu;F`Y7d* zqsf*gQy1K>9M3h`IJ#^iH)JoI$>+4MS0Tb_X;hQsQ1>+@0eMr3|yuqSIIXhR|2}-VJga_4LnTd^BPb{kWTL z2&UaR1tto`H^kP>i`T#feIP?fzmx7y4xJ9u;gC$sG%3=b%44!2oKpH|vUhf|FShIv zIyu-FD^ll%P@&gB3Jzcy zGr^%lUQ2PgdlKE!dTs(($E6*K9Ax6iUCTI*9Nm z0i7WY=c;BYTaTqSK6gCnIKG7UJwKlWMlmVOgRTTYrXHs@p=3SS;8WY?)rrN!d*88i?HU)eJ4)k+Ut=F(h+;9yv^%6OGJ6KiT!|Z&-??SIqOSGI+YTqE%wY*t zaJ_9GJJt09HOwhw@VRoyMa$7d7Zj3uuYaYQYS3L+yGL z;;LJJ6}Y?_GBq8VXc~*pQ$by6V-6JR-pz*v@#U;^)Jy8+bPu_9O{8gYHLx3Mw({fs`B1`)Xu5ex z&anVmp+Yi&<`7C8hDv-Sa&i4OOm{ge$#CoJDy(veL*rd9AVPpHX>XbRXt}yT&|ia+ z5kwrf<{qbcEmpAyd7t;a*TI=b6CB?Xz^T-`vgo5C7`#2R|tAwtt{NXD7%leTtSHbnMt5 zV^J=ThkZK2UA_BAUFY2|jb;nYQKtED>z*y9+$8Md5gaGo*!VqLlr4{=-Nhevqze5q z@tu0E%PrNinz91r%*Q|Sy0=H^m%50PmJ%dmg#)%8<;s0i=(>&$2#}KYzeQ#pOm&e` z?#XKt>%OuYJH61O#>4ntFUHdAsl6kPS=rGxBjQ*{M`!9$EsPI4O)IE(+_=1lWNK>^ za$LGXc4_osrs!tF!u5VTK7~H5OMcFSj!qm*<%34_kC69WrXJi>c3(-a_l%?E^2lqa zJGIh(XP&>fA#e)lw2Lh$4$7xfhHX)lc;n`!@vT><$|&xR)Rh}4`Vu;i{25v{eZC)u zA(T(TwQ=%@q@26~eNQ%~Re5aPlD1;xnCABs@*&B=hJ!_utuVLR&un$OX=`IkNy9P? zM0_JY>zo@mN?!5h%u>E7SFISP)U!@_^WrgZuckWb*y}#h+z{XVp06hKi!Rmz-RUND zFLm-PPI7T~+na%*Xl<3asFUY`=_dEAP&|(cK?@9a?A=FM1#s1V|Ejcy&Rjwzvtv`2 zw526%#arCj?lt0UuLs`7ZYz+;o7K-MIlZ5+xA-n61Qm1`N--lkiRbgr=ni>0^N#*a ze}@hK$%wpIESDhWrs>n#nXFefBv#(bGfc5=hQY+8df%R!AewSIJMMsXHE(1I5@?z% z75c&uO`+rlaBwebYk!I*Fn<~=Ea;@2rYUqv)&G}KmnxR{3arc%8Zuns0KRB`C&cLb z9Ncb8Hef{0Ss*{xv8R!(qv%T*jHrU4h4g7IR{5D7^2wnm)7~O6GgXy$W~?}a7|xHs z$ZAjbbG#T>>02Bs@Z`j`+5UBlmMbv*m2BhKMp!tvzc|>c6_+_t>e-8qea?FAONpk| zwKoi%v{ZRGcAZ{wE|^-krfO?g)X5(Og#@pe4|qPe#yU8R@LJvoJsJS3 z9&jd|Ns`}VKod?R$vU=K?DKr+VrMUfNjAdx{8Yb-cz(PTduudH6FdB+?qaY#g@d4E zeAaW}k?o}+lGwwf7g}7E^K{?{BSB)SjG9*8exjqu6!)PJ1L_r*!BvZ*+0^edJ&dVC z9rAawy}TBU{1XZ>I)+<(87cm?1#8!OA>vt<(dRK+_BHj&SH8K=*E7Q^;mW?;y83D{ zsxs^1k=_$7=SrViDV@64-ydnS@78gj+W42R+4C1CMzin8Op7btGv%O3(lQ*+UwW(P z%8^tzlmLL1BR3M&*DM80Yg!g)i@ociYFPX2*`$vP3}rz>XawrkiFy~|MU{WArHreT zEq_~NaOC3a_*Q^|L|O+52y9Cq^++_qpKD7GJY$&(?ilPaV4Q<_!3u16#6=KP!E&5< zo&veV?a|Smko|O{l;(oDqvbS%u7RXU2mL%`lH{WZJEww#l#Zj~lrP7*KW1>rMx_)w zJKs(*Qn_>>ohRJB&Z_JXDmyU58_ED=&Yq8QWYCl$AoG6+_<#+{-~G6~uQXGJ(k~((uXqDaxujbL)*Y8&BvaxzTE9Z0N>$=9X2C zv=u{9Xq(W0bS-9(YFUoV2+#v@G55sHZ=0R#UcMpgP3dVZhK{zRX=XbRJkdpR9v<-9 za7ubZmu|^RSyk#_s50sVt|xR+_!7EHbA2=vCLY0jFg8=4`dDow>FSt{cst0$%*}58 zPCopLgL4|P0P9LbH+7aN3iZ<1RX-D={Cdd3jv+_=U`Oo8DGtpndK|k{J}|ZONTezj z$lnmpp{(iKsZ_s>4(f(bS3TE{)#UdHz)w%YK0a{tiu)!~1yhvR)o&6#2tmkgJ{xSl zN88jNc4Cm&?BgZuX^U6t5^)}F+R>l?qy_|dP^GloS2E=u$iKO6Oi zgVI(+)njeX3t%bo)iLINuYmjhu)UeDgg;>7_VPyNI_Lv9qjq>&u*aq@!BSP)rn0$b zGgQKsS`C*}bDo(%8VqeK$z_DsQqwO?+mFt6_^srg<0G@jBNIv|}<6lb9%b76l|h&;yzh_LKURDkA^zp;9Br8_^bBV#R7 zbV)phxF)rV1CZ_{GPt+?UA5Eu&lI8)aN2)TLP2PZ1DHaFRuAL}5J9LK8YU9d z__01;-KTK)caAF9ZP51RtG3xK84rgl-@J54GR*K3(*P|JW{^IHYzIiWi%It`+f6lo z=a_u)WOAyFVZD+ouzjaE7zu%^8!akYcxkiehcQ4BGAUU(NCVX3q;$TDi(Lk-;Ry(p4om)P=rm0@5v>~0^xqine+ zJgNXO-WBZfx92Ed z@1)0SShgc9)ph`m{()o$zyFP6KefN|Nv?3%t>>DD*!C8ZIefpT(IO)xD>r^GX!k83 z082)avxPASm{=AA1Ta?<)U~;cbYmASU=d}AJcVoqZqr#9ipY-_G_VSwnv`NqKv!l# zn9bh0oMVJou;>YW#&Q?1x$_8Z=%-Kwv(3Ad*nAjO2OzWR!^kBpJp`YU@zb2uFDcG{ zYWN~U1QoO0B@c>WK<~&giQ8K!5Q|=_HBISKeF%0_1Z94;x5`qK>b3N_7Uq*lkyYzw zs@TyB1t&75Z_Y%2nZ7GGbdx^xGxU{`?yJya z3#6ZQ8NU~}VK$KZGSf1LeA{>S`Rh#u`LdHut?N5%gg3Fs(V@Ac{(#VLd+Puj<~gsH z7u!n9WC%T+>T`l^8z}j$c0LY1w{J@Z$7S$S0{~nfPk+)9KT}x^O?qX26~yVUjSBTO z__}qZBAol4rywV(6K?v%DpAc2C zgBV;5Vb75RR6cYDCL+#YMx`ZQX>B81m){ican#2G?j6y)YPb14g$elxj;Lj!SMLd> zwY^5Hm2bw-H9q>NE2~n8OOCI(n5OcSGn=cEKDIFT&#^9vj}{kqCP>mr@eCdCRBdi| zo_Dw!#5x;=tCNbZi8DrSrz?4J^@;lk26;);un%jBqP&ugA+liUr?`wa=KyqnWV0X9%%xZ zokJGep2fim0*}sbg^*d~461ThADj=HOP&L-fUcU?O8;O=95`^xmaY3_M0=>SBI?Vq zAf)WHQuo8O&PkU6)O2H%DtL_Kq_Rb4DS~{<<8VDvie=#0Lk9+i!KNS1D67zbd1Z-G z!UnN@*aG>8(+*n2NEZ=DmnC^Z^BrsVekpXuN_~}zqS|mrg*ee?db<>{;spNa>CP$G zcV4hSscBujMt7lnkjsP6FQ_5q%MOe2Y(w~qEn3UauQm0*wFMf&VepByOW1G7RDZ;6 zfTw~~x5hAGQKmd!S_l{`X!oaPx+0A;)CvDN%stg*{EV+M?zXsgIwq=3duok+q>e zut@%ipaLa8G0LFFsnTc%qGnG2WVXDbs@{cz^l&3EXzf=G<-WsM=&2wDJ~Y{qVlC!+9oD`sygho`k@T(g?#=j77gGK z_t_3FyTB@ZLJn}{nEXL|Ni{Tyel9feWv;YPW@EyVuNiE;K5;GvvrR~W>vb{ zGy3tKg^sUsNSK z4w8y)k(ziEg(%IvOs4h(qSp91$LvwZ?UChu$&H%KsR*ohL8>VXKZ&(Xl_h_N*im?D zgLxV6q~^fIgko;zj2mSC(e+eUdV4r*GcI7nVq1i1ChWwE8J>~eI7+4?)+Ea(G%zG= zmn*t}Ruwk8%y|6}Ztc6A2bSkKGDCkY&CP4h!(6%S5a7IyyshfiCX%RK0BE;68V2cN zjYeJ)C_f0KP7mH~linXZml!~k_f|aRDTZSZ?fLQ6p!6r5N+(>uUwu4Ii953PE@-Wb z%q^$JpXNNARZU~6j=846w=zl>h4ByjFBynGPer_}R-Uk+6EuBtu-&7(2OVA!dWFg# zUXl5e0c|R6-2We+@t2A(*gr-X|6nK=BDq9PtIN3m=6L?gdi5I`DcZznotC1-2>q9f z{ycRW=DC3WQkd4WmHuCohMM;OJG3(8cx=#LbrTVz_aeggqWmXNM=2mF;_j$Zm)3c} z3fbTm*_5=O*B$JSlS!N%t7U}0)J(~~r1@Yq zyi2;uHrUf^<#VIAf-^(gw+{=qD&JBi!yT)hUrRi6epWY(^1_l+ zuIIj)!7tPEm5vwlRtEFGW%*&toAd75DNmY+>5z>tJf%LQUo*p7n)1sJoEvJTkIFgm z-bX}bct*CC7;Ps!Q_~whS>>#`5!ij=!H$eMLK?Cc*P+yt+e4^&uqHhl0tuFm363pm zGw>{(7hgFnc3oas43XGLg0#pdhPdX^s4FV+ogCvH>s(YI92qsvN3b-mzMeS>6&O(n z;v13oqiXh+7R)Ro=FUdKb#goRRtg4kWqhi4kX|*rt=rYxN82@#b3!sJ%)^hz&IIL1 zsx?`7usIbONF}l4Q+~QMvq9D3!CBTf^rF+vj?(+KfLNcFTc2sE%dVB?5-PVa6xiR+<#l)uNm)m0P`zf40yLi z+P-z_0Z9p}Y_UggM3YR=>UV&QN1mJ$eF2lMjtytxy!lo9N9Mi{%cgJLxyX12yperm z!I#xG7jT!1`5an!H{g65LSA1^u6n!s;p6RgU3zCX_iRdy;9HeW57TUWJrwXSmL& zhFki$;6<(iZD+yUMMNNNiz?go$nJQxjjah4q@N9i=2u79t(mO25l#1uKRPxfMcqYP=gpQgihJz*AY=Qf&-E?vvj^eMcr}3B<^B8|?8UGdur;e7qx#%E9W-zs3l+jFoFmnx7Aw96sWWk@2rnjQDX(!Rr z;Z*pg1wcFY3v(m9C*H$R*QIUw@!WG`lYCp;Mt#`gjiicUpYRkFZ0&;nh@VK@$AwO0f=7dNbt@&(;-#*V_k4UB!G}o zt$*m#)&%h#Ez%1&goa>TraoJW1l#w5Y%^th(+yJs9TGjO!g`&-Pt7GJ;!B;w)M_a0 zO2>tKgvO^n91-^+MH2baO!Uk3yXYZRKY;QvuyrZlexRD~vF*CM5pr)=ufnNmQ*%a> zXsu5O96ci51V%u^DuT%#QW3D$1N&$E&K@)&l9< z!MpObbIYGCjb53QGVRmXX6gA;VEoq;YSzW1rC?MnHb z{kr4?CgL*Ftri`YB5Oztzc-lJ{=z8L z$q?s4De*`#=6+$8sI8FRGprMfaUpG_PrNzEm8=n2&?I!fy0DmZCMoy?l+amnjl!j1 zxjiUuX*U$}2FTXdJ+InihF2f4Nr;GJ{abq|?lNT@zLkvM^y+N#>Y`zu9tEOy>)(b6 zSmbGV{R_JPR&e}8Mnz_iBPdLte|S4AZtUU89<_p=#V+W>RB=(RX{-jym8m_;3%A-~ zTy|3GW%Dg4I!hGeK*75=Uy2vhAX`~K&PiBg4~pA=0j0~{eEw-MWdCvBMS535l}gW# z;>B-F+}WRBhI>qUa4SOQ4*qa4^_&fOJ17oh*5bD)eQk;P?NKP{gZQ@ZukhD(YT~A* z7}lc_;`?z6R#~OYv6PGIf_@ZBQ6E)(zo9&itgtFT(cX-3q}C~v;P5V6J>+`~93dY| zwZzb285iUJT+n~fp;IOK?nM)~}6mJ2&Q3UzKLL|L!lZ#a9^`@#Hn@M>zD@W8-i3uzDaS36B7JwW; zf95W!jZiWMjlBNkw^;?NLpBJMDy3&`~3<5>Jnp$&h@63Fc(g( z+R&#BH;FLFxR~?>!ZdB?t``)!h#bs0hZ+X1``;cyY=A9u4c1e$h+4Obl`WIa!)>EG=;-{ zxX*kr?3lU#;n9Mj;inIPvZf>hCOuscTA%-NSM7~;7%Qj+^M-%(`Up6T{A9Q4UHSy4 zG71|OJY-~ILx`jYAd9W6+7Ft__U6xl9GePs0Tw?KxtdpjsweA_x1*2rewEYBHModz zHLIrkUtux(PVj&@z5(jes@|W3 zJg;AIBzM2kJo?5TOQ&!RV2A$&Auha2qPpRfo}S*NdBcVc{KT-71NP7Yz=qtlc@BGN zEO=LWsQ)BOKN)`I5xqc$y!{jC#pS$ zbN}-GtobA0R6}K-cCon1Xz@~8W--hybff_1KRRFjB;S3h#!(*(Yj=a(l2USBcYNu zXy3?e?iEi2TkDl+PM%i0Nk3Fod~X1h(82VIEvWsjuANGMz$Z14wX;PU7D}MF)KMfc zrH&t9#cj8rOXk}r=ai~X7jPi0*H ziLnA*a_anV#?K@4D?Nl>8jEKIL6x>x_~vX}jcwII*|HHrxA*xusx;gsk{fEzwv1ZJ z*z44v-QulSob<^$9Ll8GxO>S7jw`;ax-choTl=BNeqA&q=ZTY^s%PZKG`@jc_;mXb zIv7w=g4+GrWY5Rtxhx%%{uq`I+jTBI=k#38{A8?;UN*BQPs)Gnlr;`}i70-&@5`bY zOg)JhrdTF^7;#BM%yp|VchFLG2@peFD!E!rWjfFSaxHTCOmg0gYQ-4Z*iY2nzg?jV zIj}$sPHow3(mB?&KIGWQUP_+wD43Jg4k9nK$C;!GcsUB zHP>%e!y?2d1~~WmY;ufivyY}vW@Ya6Ce3=eon%o)uumWD+c8e&jw8r$A!5Jvu(HJ- zKiqBjUQKgltDZV(@EBDa>|OM_v|l*j6N#HmR;OdyIQnxjXxN_gh^NCvlFRH+ji3*LB`>bkj&~2qu%TR%QK7%igPxRyq~|&l^RfJ&t-P-6?ZlRmwDF($U(W zlR{Azi*#ZUy6ET-tEWV!gu}VQ)*HUi!FVr`?cCwpJffc-MXgGD3dA6YYcHj32uwsP zgXiyKmj~~IJk8%Qzs_8nCL~_;&&>wKuZg2Dguanzixcqhk9qo*_%CzlmdC>(`EY$| zU5V;LL8ne3W%u^DJfq%rdN|*1!+~ULr_9wJHRH4YCC>f3;(_`Ry)=S{=F8yVkwI)Q znVQm{zmtKIj!{-!`ql{KA~Oht=ORb+Oz#d4j&TQw%dR)v?M*mploEKSdp;fNnwhQX zZAlth9D*d1`0(s~VTzGQy2XvUi9#Y5-ysE66?6+mphqI!#baRK%5b(Q9^q`+$C=wx zp00(G?zMHgOI2Yr7{mGPQR}r~0U?7C81pvAG0|nZ*FG-NWx{yzx2Y}47dp@jQF$=I z$u^u6AsExPac5=NKg%Ulr!5fi^$itFH;Q1|#YWXSEO*)x9}J`^Czn=;XLQI31L3t| zrTH|#M0IriEpGp6Yy8Mu_x(zfk;`UDYP{ZMc%Xz)kJ&a!xqi#l)jqS5Z5CC&3jtZ1 z593&&-09^~ASR~QxkYMF>is`JqP(&g9yxz|yyz@>x%vClR=?EboC=4lkyN_6H*J*0-r5J!) zgc_SQi;XrtKXw9}M=`mXMozm;KQVqsF+bl$bBYAIe(WN4`-k4AKWw{pZTo2B$6#auuydB zQ8LXkN-$9R0yxcXJ&yz&Wc@z3s2%->-@&C&xfWi74FxU&scfeRgZ>eV>i-6uxKDrf z=dWdA|LjhkRu})`3I8Nel06kDfluMoWB&_%e<@-6uaEfK&l>@1qMv1E|1dNEc3uJA zRMGUS!fahvAN#)(B6}7Bs+cSu_@`r2389{$E;O z4<&V>v%NS6yYgO@^3NatF)PY-kxA4|PUv8#0V_Wj#U0`FqA7TnIJx=2q*r&(*M<@^ zAM+n@ByVzF$63A_wzW8)5ybwS-|eH z#H%M|(;-wT+!wYnb+q&aNE|7S+FR-Qbu=NV-Z#iQ5mjo6qJb4Fm(sLg-_F%daz%f+!`{f%NVU`J96F^ zUtWw%{_IYKLcP_=3+UM;M>JG?;`+VCHl~)z>kG4&6SrmUT+YIfD4vgfWGf?IIOp6F zLJ`TMO9I7@lyGlPPzh`KC)y{xu5>+0H>y#{Z3}6~-zhlde5c&LH&Zk!V+g zqCInbo8{$0k?_u%Nehl959fq2B(<9e=`rldCbgYes|(rJ3hHpD-s9jo%q2Th$W^bB z^bgFl!@6v(C5vJ6$04|z)XHcPqCWR&Bi|PmZ%IX1Mo&cRj&#bC>v=8pO$;8`oJdJ0B=H1bG1yVTfsf!1#6eTm+0q2-A5S_YF>hhjE;y9 z;S%1Q2{EQp3<@{DG@;N>I`sCw-~IhYTFbOjE#I(ZG>k`tGCjYFps`n+a!faVfK(u& z9-q>5Mh*7z&CJN0R|3z8|4}jP*Q%(oEdTKRQKc)L#a;sDx{7&~4Ux zB%Nh%6ijT7mX;6^nAQw@jZJK!YJ|Dx?Ee=5BX)@h?i)WA$mWId5&9K9Q$qEet`a2` z=PY0cE&}{T8PVOw!ui?N>v_OkhD1f%?DHf}#b9+eEjT-ii2VSeM|(=+YP%OmNh>{) zfbynoeB9iegSS7Z>BMxe7`Fs6Uzs}FK9V&Mvt;gjI1of~WUW2Hmlh<}Km%XKeQTj~ z@~P}=yU$SSslN${7Yl1KiI-y2s1q8LNu;4Na`q=oo zZ0&ON0(p}Qi`6SuHwd$sC<~2?;*Zloul04QCg;qT!@LeULb{Vy(!g~yYk!N{eRb5G zz>({=0P}?E+ft=GmZF7~3O)NSzVP5!&uWbh@Flk9Q7hlz+}sc86StAs?2ph|Q+d)r zc*Kv6s&tK{ZPXi!N6qp$9N!G)rrTVOh^)~%rq?nbX}fnU5Nt?L4?dcn-UA|C7oEv! zr@G(!F4?O9i9gw0*ZTwK<5AO%F8&dfMLiBC9g&z=CcAZd7554I-GJ)S}oLBQrkU^?hG=Bx6 zkbuJ-37IcB-4W=CCwPQ(*vp5yp=YYY*}6non8H4HkV3TYM4EecAS!>oc+{ z;F?#LLEjgJ@AO!0nv_sfG224KBlN&a-%Xhc=@~0Q;0fQUm_{F=i64t|STpL;tS&Ke z(9b2*e-)Kuq0-49T&qbLZhj1;6SpWi^(-CaH)9`%YcgN`B1wu=CFIz)sQqT{&aSQz z3y6U;&5#U@f9UtCbIzMw%&qdGZGHWh?KurWD3$GqiUE%uN1gXQ)-|5AAcJh!$)?Bd z=*C_c?cT|{nK)}9mb*mi`5uH|MJO#!ThhzJoEyt=EqM&(F#JSqs&xrjJ)q!7BX<+B z>I>|?4zct{;L7q=3cCU(ETGPp&|N`D#Td2Q=BmAaM4wr^NR+HKe3`3pR!`_uEFd z57@_Jv{E&aTv(O`Sd@T5Xke3Z2r3w$(io6h+@H?~j{Wjre6a6{ZGKe%?-gM-iQ^9+ z3;juT!LRbzenlrbV5K!DNJ6VZ=PUro$k#flXB*~KVy6}g)d3-bUOpqd>|1Ji`Q}@( zmX8225mY(%-}bNJvuBqzW1G-mplMP)HS8B6@uMCS`lrnV-28V&_=^)^yL)WF51=3; z8XU@UgODx^KZDJg3vHF2=}npwePEpi7>0Nv!a^08et_hP^TYeM(6l5*rx;5rWGXJn zD{AK0k}I&dS!qa=9vQMlTC@}XMHhI$oWDM7)U!cc2M86PBDA@alhgSJ8BN`YN`1`n z^SBP?g&@z4v$02ZI;opm61#opQw(Ux1fU0FjjN(hZV*axM0q_I#!tMW8C26ijkkB${ZCnyzQH%8baev#4c6O>bN zZbIRyE#2^9sDgoM0N6?G^S1IbldXAKK?Y_VBA*AobQntgl?ee_^8Zz4W<{4+t-aX7 z$gC5CL>N3N=dW}&zX6qAlg+n9TzP^A83G|dnGq|9_!a2=gO)dG;#5I`Z%d88SQuS_ ze+%!p;!)>9Mu2Ffifq3E^`cT)l$2=H7WmB0MOskoMSL21oT(%;qTwWXqpt0vMmdIU z!TE4z=0kGcZREllYANZYHaJE%3fR{MEvGyeAW;+kDtGYS@7~OXZfgP4T)q8l;Odk4 z;=2JV+#=aL#N#U?8VXbQU8dV~DbB^WbJ(HZ2*kh2$9?!A2-VAzZ%EoX{cbAhvpH#0$nQXu{uWBOkD@E4;n(sy2M(Lr`2 zL_*Z38+I}sIUlsC5H+vqsm=kT_TvlXLym42`>!8mNSvLPcS#ER$L#&lmuXaw(@#h* zyNZqXAq}IL9yr`7LXheaKf8!D$d?c~pIGJM1m4QjUA%mBeOD(EiQvm9rr)UDj`zxq zTp9Ll4?f{GmLh)UuZ}X~=~K~ff)U4tv4v>VTbb4Xo>aB3N>Re7Aa{Gnc8T}EJ*ZHp z7^TIupU$Q~yU(JjM{EI2mi|wB=kLY|kYvdxwpp#Se;Gwi_(=F}g&2aoQ+~PZFR5im znhjZ;7bY#}cQ2r4lUYnTGx&I&c$;DTs?aV<9Yw+mHpA!TI4%{_eqeEpFXLDn%Wd0v zKHG?TW!k9#m(PAjL^;x$+}m&AkBO<2_dOTG81sxNt4gUkcD7Z?aX=+1Sqar6Y)iVS zW*OB%bYyorNvM*L>x2kqw_sq1bKM)?FEtWlGqVs^tkyuJ&V?STEe|@)$GbCDhC}9h z$hY(Q28s3*Vi^kt=ORvSoX$(d=5nJcx1zx&uE|)KSFDF?$sSbi5%) z$XKe0Bc@u=@iwtE_g{oue}!@ZY^KE6+69fel@piz1^7Hrct6}7olV>zyd!ha2FHTW z>(cO!kAQC7ow~Wbv&PoiuClWLl~bI(QKzg>-`OHxZ7p4-iSnU)+ung#rf&to+$lcA zvlmC`eT{}JSB6AS^7!yp%|UHvmJg(qbLCt&2p?GYmN>!HDkGCb^@4|u^x@BT4%6@Z z)kh<{I3;6>?xC6fU~rZ?^h1a&3mLU7!$f5!Xy@B06-@UcEwc`UlVA z%d3TDa^XVFGzt*y2f1r~H`e6wE(xXR+uRG;mCIcw4a6)kiQ>idj<3(No&=0vg0y4w z;N=!Zp5%FxEv*?B9EIzLBgmI0Eb}5(CM+0*$=|s*-F4f+fjIYMKYr&NaGws+K+th3 z-|6<*;T7u!)kZn*7y*=NfpFN1E=LvcRMjVJ3tNdi)s*FYyQXdP&6xNnePm~t zXKksP9=R#um_1ldjMMm%!f#2p(m}LGF7UtNj(X1joKUT-3o?ifRInLDtz2skZ2!xT z_+PugV%vVYz_xJ2KX4a2>V-W&`3I31!sVBx46eXBM4DA}jp#*R!1x1xDf+Q7)WpBH za(_Lwe-YHb@y(FIo4`4cmW}=A$yQZqyCy~eb?eGoQRMsylId$;aJ z&G)>@wuHGZ&qiC$57I_bm>rtkd9F=Hi>BMF--uzsXx-oMuYYs(zdO=D&?Nj0nHe`F zd*4xsA`7q|wO(WFj)=Y!hVP(>o@(i3u@k5blB{l<7doa|{VJR9+UrRueQL#GSIH;sfwki$@I3$G;fYeICwD(hI?Vu53qN(w{e5r#)Pu1T^xD z8s&MLKeHBoNoaom@k3CPVPfE4<`H80B>Y^Qs!W~|RyAxY$q_+PT;9M;i7uOjxd zW*uIG+fsbBtYuD=Ey^ORaxJc_#r<;CeJEkk(Y}YfoAyj%j&d=~M>rpuMY%le(5cAA z`!$T7xZQd%Z>%+EWDmuA-qW*w*ikOZs|Pc_c(mp)ORwm|#roZx$4~k0g_Xhk`fs3} z?DrJa+!3$EE}^O}jNG?ViC6n27j8i)ZSo1 z3W_(0V`BW+%8w2_Uag+>=u9MjR;MF!=j?J~dFG`&eQNCI>D^7f`ssUiuyMm<#i#dn zA9QcZ!q3<&Vi1ZFuf~_Z=5mg4MHO)2hl~c@>d}d;YHgPmyotTyf31`M<<1J4W`HgQ z14-gHa}GHnhj&o}{%G~`OIo>Pp2~MQd*i#qZmFpjZW4PgOdI7F5hsq?i&t)yozAw^ zy-ts=fO?;2wK1G(>$S;ue{K*D9g>@$d~tzHOI`w=uZSZeNbYlMo3Vq3N`}fixawWaspZSUZuFnjJI5>mN zXCQydC|DS@we$g+!~;bvV9W+?C@Dg;J71y(PQ+8ceAIZ2NLKU?#OijS*RdqG6)5hL!lZvtfwV5zjL-knBh9ssqKK-jMbXkizzpfUyc_J!C zC#n*G^fuD3-Z$MJ3lRqL*UzaZ*FcUEt6n63kz9o5Le+-n?Aa!v`}78RJwY<6r7Z5v z9v%oif=q@yqwVUm$)(O8q6(j^=f>R1(792Wnu8X%`+SnY*D`#oQS}AY?Mdk{ThnmI z2p9Cn_RGM*Wo3-0 zJYd&=hG*kQs1`xLipHvia88hAG(k(w{jifE;Div!t#E$L=ptd3)H_Q^{XuvcUzn}V zYs-FVCdS>m>w9;!}^+X29veWJ|l)Q^Av%j)K6Y9dc*d;^0GA?p!ZJc z;jQ4e73$TGZ?JMFPX?--GnSH_ogI1c#UCRlyTPeG0Id+WGq0R6SruoSUIA_Kr~vBR z7X^jhVIY16`&5g}d_8*p@0K)ZRsGIcu-LH(WEfHw(dC;(WdKv#1-WSr&uuBZk4cr{ z0R@huOdsV%U(;2u05!s?;dF!Q^Vh;_?pT^+RPcU~X})3w&) zgjS$@T;QVXa@(qALYpfEpLHOI;fIuax9;vK026fW&dNtR!ZcjD^=-<)vkG&@a%~Ct z81RU3U-wB?D`~Xks?w{=cCpaH#CW)h!n-|^q1O^+yfMzv7#Kz!$dP=w$k@$1Xan^= zqB9`C;1Gpu;o;y8UcVa&YH)>EwVxp)|L*AjCFCm(REgATaxvG5^tet=rP;|-<@g1g z2V^mOFQ)i%w^rne%N&n!g902OnHf#bo%2=?rJK;^C*;8$L5mTegCfT+i~h+U3WPq( zaW8dp_NReiXyJ{LaB)BUHWs!FpWRXuhcp{D#$RkdiE{9zY}9)JFamc(XyuVT&{fd8 z`zCgsERcD)$_M@_?n{_xInup+Cp!_uM|*@b>~X$7#XIjHTi9pav-b|@lN44tI!%rX zTa(eeyXtIA*zlkLJ=Ix9icRB}OMoiriT`Iq$6r{fQ+pX;C4$!J1fUK>7I#?+8)y&WX1!ofE4RExM0qk z7hq%MI9#K6XTT<@XRA7(+$VM2@vUy$-3zZfd>CqZX$X+8Bo$7pp1|%uzR!52V&l2F z!yA^n;f5o8rmgBd@Zq~O(pO{0CY?%Y1^fzc<#GSOO!B7+0+&q+UKodPM|hP2z-G#H zQz%S|ZqIe^*Jb_dG5y=p4BImNYb!%g*ECsXV6f~UgV{ge!ktv}9w7W_-#aJzm0d7@ z&`PO$EaX*0)MB*rcRH(ftJ9AkKKr%V;86)t-n+&*QPe}-03x!7BPkoio7aEf%9Z?; zdc{OZU^nBl8^I5w=*E&VX2HqTwnqS-oh*NC%m}2xMm?o-ho<+D0QHgb)Qv{d#mntE z$!85X;QwuRJR#2Fc8nS|4n;v=&os&Bezx^qH!1NOPaqNP+$u)+zTGJ)RD(u6!A4Htnw`5rx$O=Wy66IlWG z7WchoJa0_a=ud4^XYMd33#eE;p^nz_n??*?n$;EI%@e`RE242eaqC(bJArQ|Km|R} z_x8Sywt*q~VI=Xo_~%gaX*Fs6!+P9BiW%jD%g`2YuMVGq&l@u_US+;bAE?%TzGziy zLW=JTYdCK|n#kPWr`{7Cemk|h^Ui_&YFa=yM+PLuOAILYhOK2bcV}w}6TMGQH2N&~ z@&Qu95&2wF@#+TyE++Yh@AP*@`U^8*#grbF8msH?ST(Sm8K#kw>pse-8vgEFY|>N< zB!E>ChM7p|tx2Ij8AfGFWm)sFT2oJr%mQ~<+p23|{vSN2t3WCP4Xe`9CJ)DY;YtSE z?;Pl%DtoT@-YM~PW?UY$X$}?%w&p2NJ~^z}*eTd+S{isLqJKYd?y837X5Xll83}6< zFyH#E49N_4ax~^NNP^4R`K2-Sd*o7FMhB|bAhyZPx}E1ntrSy9&k5;@?m+`}g2M~3 zo?O>J)BU7J`4Wg;)ZCY9gXOGtR0s%EZUNk6TxM`~Ljduuu}+H&P?>Lr6aiAJ(7QLl znQhMeGeHU5y8a_e73JBE?fP@rubC^#%uZ!lk`W4Np@o0i_wQ^H-u`C8;lotyL_a(z zOZ4ngYhPqek}G-fyM z?wYAH#Cs7Hamu-eHb|k;j!+DZ3*CYX6^KR^`pQApTfnibZ!%GRf_W1|eAm6b15TaX zIc-@?XDb=b1!jFribFF%1IaGKL*h?jyA~WE4Ki=3)V5v@-8Jim#>N?MOztr^FZU#@ zhcxb!HglY{Ce*Zj9#{+~aq}lEUdd4~)a~{GDa`E)--{Rt<&dXVLT>BrI#+KVpR>=5 z^tiOGYizp2_XMmhWn$@m#&Q26G7YQE_*DS?Y_Rn+@??A43V5+^aBCMa@Ahag{mfuc zz3ED-))CRe;4p1#*tjF!&hNhax`x$w?xFdtKAO^QqtTb~5wnFS$)3vR7tSnMiQRMW zM_Jv-ztSG!HN)-%#$E-eDI`g^{JFmA-P7)tYR-P4XTtQmc_j6sRzQ(0(Zx=thGyj6 z8yD=Mt|z3s`dVkXBzbfNeYkt>feL4XGbug+FVFrUem{H?lTn)}d~;I?_pM0l6oXT3umK*^ON1xdy=Xgb z?%)99uuh{=;UAZAY!q4OfRhF1a&Y zQzthK)7s7P`xTcO1Oy8aITmv~g2-JlN{PD?Gke1jQk|?+_7F4khs<+K5Mkf)8FS7^Ag_0jhZ; ze9AP|GeoMF@0#t12zyKX`IyxV1FyH~nnhk)XQzwJ`KC+@3;EW)PviHsKG;Tvd~& zO89NX26H;MhqHU(nsrR34W`~!+cXSYux%1JGJF6QW<-%7jr*tb9{U40)PL8~f*wg} zc<3O)7xSI% z6(5B9p(GPWJ_CU(bUV&q!RPLWNZv@1MwPfi;_gJwrcTb1QHK%|R_%1~g|YZ6Vd1f=yCtWt>eu|~;o7+D#60miHo2T0JxW$RHM9n@ z#yd}w%^~&|`zcK9Ho^yK(l1MTo*0?&WdmQ)-&)yUHO#+cQ2#qA2zWe#hr?gIiO?6K z#*1mVH+aZQbbomi|95dk#g0M;V}hYPk>Ju& z$>>g3XbUqZs}$zN1)+|fKVeUvgjENu?s&s8r!Q|sbf!L;@`+%J9i_h4ppJ=@=2W?= z_`*4}iQ9bZB$^^6Y`t3{HrK8;SXoW~$uj|%3^-%4UHE??OFt!4*e*3V0ydP$OLnxO z4T*~zdJI0q)CVKh3+^|W>z@f|6BgcthfPPeI44X0xM%-gG6FGE(Ar}eIGw=92g z(1u(!MY+0O0=9a~vUU^ao3Wc-X2etimQG`DE^iZo&t-7@$wS}fsz-H)nCCssvt2ZCy9qjP9*!~{L})~foONpSFavd z%qu$@=S|85_e-egxuCAvnBip7GMd7(M|h_Nf3}qTIe+!fL#fYqI(*!zA8p&8LN+H^ zG_z8ug94Mi}P@U~;hGG!JPAs*{;%byHC@{MpW<8L!EepbNOrafT0!-%JktLdeqB7!Yrt0xH*pCl;y9Fx#G;@$lqu(R;0| zZKSx_kZw0aQx^`rNN2aEmypg)aB_>7jxLG|yMT!3}=A2PPC z)>P!*p(^im#dPZhpZ!@s`Qq#G)8iw0M5c~qwinKYjVm`dYF3+sYeQnZC7^j;W*Apw zU{#knnS*MPK$o{5??lU*#o-YidSiIaD-`)HU)QUTTEYVhf=-s``EuYc=m z`WekeUst!bm_r0K@*gh5kZC_APS}8?j~+MieiH9dPv}xa^adzpC5rLxtdip~cbZka z==F?KYU&GWTWRlm=F@S+al4A##waGV^@R^&;Bwsz7xNHgi&s~d*wX#m zXh+-lRMyNxtJ=CZLc%Q03mB>K%Jrz)+|MQLqOWpDe64^0+sfQxi5FL!X)k?cXxgu& zS9bl`4p&HwXG()hf6e_1rAG~9Nm9E-jEP>`0OwuA(fkNO-Hcg?Cc~>JZ!NwOo!DYd z_bD+?9_~c#+wuq#iEp4Zmt#c_G88$Xb!{qU8UC#arcd?^V~;IY(fMY3O(HvX7vt~l zYOi*zeO3Sr*i+ei#~WlBW1}4h?~?L@bR$MrP8pBH-kK!EYrYb8Oxf=0 zt5ikV6Y3z=hsPN0O!)^SZ~Rr`+{IQ0p1D;XH&u}KtT6Z5_L>NfE%K_Fg2^3^KPZVT8Wx4y}{9#FS25@TT)V zw02uO_oi4<;}%i8ZPGk1P8w%jrE|`Ru&V=<%8O8G{i40A$|?~$izmF8my!D1!Cmp# zwmW+W=C9O3AA-k~9J-td2FSJ^nzjqOIhj8jl{!A+EArBNd9KH$^wsLOyI2`q`xaKQ z^^b8T;JNWzIg0gFGp2CZm5N&oya&7II%68~Ss25?zA=0ZcHeV_-c7=DpSXDQ0tdMi zeNsJs*%=GLT*9*CUeq(D!aZ-{d&dhk(91Bo6d)}(BaOGUGyO{Q0(X@8vae8uRA8p{ zm$&?^809n&vVE(cO2kd8Cd4QLC76{lbpbw-U33GhJqldOY@pSxz#1J^r$@kD`$1q1 z$XYUoeuT9ZZMwPffazeJ=~~MvT4_4y`)xjxzOycs{lG#J4uT!ho#O4aLE9Yp^<$R^ zXW(+9to?aznb>#i43~d`kbi3d{(esU&?+f$xU!Qv5hp5u zC=>lr?{=QiJFgf7Ln0}gNgf0P7!mvhmD9#>xpr1>u8_EJ&va^044aNKq@Vl6^?4dI z@P1j{FoLS4VY6!L*+uXOr7$F90upBjOu3~sK_CnT*|S*lbZn!XoO*M(E=^=EKS=a7 zoT^66cjqCf&3I#|Wyz6pXzGA>2BzmpsRV$fPK;{l3Gk7T+tpu137YUC7>s0RaD^0y zKseGLSCgYK4VDrjRM7Wg_#*pc;K0y3_k=(8jQ$xk!`R96-{XCuLzS*8KZmQCMlq18 zW4?kqmp-I7#v%(7zdZ_ES@|Mc9#<&ka-!+%z-%=`WEbg3(OLTY6@=XcS5?|eOVO+l z(dS!Y#(yry4x`GXN9|XJ3)3dl_(Os2WI4{@l%?vPRBtYm^$Ev9DrdSIz*PiDLj|&Y;?o}dUoTJ`G~0E}mz-rTXKCua@Mz%*+pL zk5{l6JdIS@P7#Ui9>eU?H`bi~x;p*_j(^u*zr<|Vp<(FUAY!HltRF1gdm^P#y#gFX zJG4lrcO`V>uZe0rhp~!!{(C-PgkJ6zYmxU!qWUmrJpWo$FkZrD25`W2>>30=RhmQB z1YZR6%~H8MlQh-Kg;O!uOd^e4;UQ$8+DCm~|B_z&c14lsMY`el`sE@t-j2$dJC`FH zw@zED44mq%+C+e$XT93k;s_fTi&lIAF zM?PtR*GoUs9M69*#_l4Ck<^!j9j#!jnk2z==ey%J{@t&6#^365}Ma)8n=4`tXClXp}0$3yNoMxtV%JH zt;zNSFoUIsm2}zoiw+vVDrYVt(|vUq^0$qed3s#er$MB|F2#57LP<=gXitF02cY9Q zm>2Xa?C*HziPc6?!u;JQlV|g%m0yZG)4Ck3^~f85V{Ztg$^NahSl8Gr*Bkt?+gN<1 zw@uI81nwj*_2-T+tUL_D1zorj#iv*frda_yxOQ?IoCnvMdH80$u24d@eJ-a2B;gkT z@tW#q%9q&{E}&8RmuL9jW2}T%+EgYg=zD*H_#@P84Z}0%(Ty-g4wwo?LndGSdEm%e zMMA;##>^*jr+-i#!|68vFn^Oc?)swNF_|PNsgj@CLAZa&!YJM_ZdwcDO@&q8In*#% zeeNtsiw#|v<@MbKT3i{z_CuRmWw`_cz|A*P(pCHbEMq4+{*e4{JtfceXQJqE^Gf&0c z)vsBdS5k#W-zoV*su7ydRYN6bD-dq%dK%8$yv@R5bLa!m7L^y}$lj}9+X*vy4!Phh zWR-US_yi^49Y&@p_KX?R_If|h=qjp&vuMyBpko?vZBg_6rc!PtnE5!bn?Yni)6Pb1!LNi1f!~ z61hzYxLrYi+b@jFJ0u!DrIB@Bx5|;+e^f^? zizGR{<=ExxuoR!{)D`U^P+27|aapTuh;N$DEs?#UvfMy5m@`1OkTiftDHu>!%KE08 zUiPg_vhb~rU@DiqDp3xmi5lYqnqAJN?K&V9^%Jv|%p_J6lHD^-Y0A0pwCwd#5-37|{^W?x>Qq5JtqAH>V(OsTd0}43| zG-zx(@kJM+SN=qIHh9Te<`g$SC(c(K-quR=5yBItSoPX>4+cll1uo4UxIM{)kemE3Xq@pg`nKYq1Zv4vX}@$3q))`Mf8r&bsdhD%%);oWQOzMQ&sH?OSxZ& zaypy%XXUd4@cZ7_k@I(e3Zre>6AAPA_9ez!i{iz! zce01XrQT@@Ij8U9NrplAak@4nzRwGapN~7*T)+8_b47k;j|01y18wTZ5{Wew*t)hy zc*Tz*9e>3}1zSqmxSGMEp1*S~1J_4adYJ`sBIkUSG9RzyYD;+?JJg{k(pw3y248N4oeERqesOm- z=al~?L8lk}cIVznyIRsjrFZ`69Se>IL@Ms26K=oYQ)nu=Z~v%_^1F#l)AFoE3l2}B z+G94}fy|b%_-*uU)xuniqqrY}=rDQp<#-2Cjn*@WOI@&Gr!+nBx9@XpdIVLSqZa9jE31V*>H&A%{g7Vr4?YqWo^=DrBJvVslw`+I4l>A(_YiI#>=w3L9dL zP}m*$Q%H0EmyrZ45&r7<%Ml>gO*J9b3)Z*?;=7F-eRO8!b9QY35$CA7+YJYAROp2! z@O+A9Wsy!DzHl$0EAYme(7t9*-;g+r-BC3~MEO-)w*(t~`aZip50`nHtjo%$^XYO9 zGS8k>RE58a^t#*Xw$_mKPO2zPUvMF!2DSTD7@9cDKGK#~nq)SKk8;~BpN{DrSlF0U zr`qdSvN;;q7taKnT}Ra_nqDcQOsoV9@|+Rhl8G-m{Y zC4X8tz;C$%>tBjfQNrb#vFYVdEA`agb#k_M(s z;@2EB^8caw_&=EJ+OdP%Hves6-fw>dt0(w(W840-pRP1KDLk>|2_JO(V(19p#KFX& z@?!<&-5C1l6daZ zBR00@e3r^pCn?Fnz}kpI@>qN?#Naem|H4ta|C~H#jKiwSL59Vcp%zICM!&RjShW%A zjgWtYcl}AEgmA^8E`VcyJB)u5@LwfxX$Y$R$t8hlsqc9|=Om&zbT+e-x{rj=Q)CZW zo(j||sl&P!nz&NE9|UKgmxF1Qou{oNY4b_-m7NJIQhvmX+xxl(+|Muas`LU~5KWG}D%JCBthx*ByzJ*5!x% z$+!y@=Ntl=NnoqImkym1#)*;4U0kBZnRntsZwkF+$M6z|*QMys_dJ@(%O3Jr$!0hewh5}^c!X6zCMKCjZC#;^7VLWww#K#!aj7O!5-H1DONKkp40E%5Uv<2ts#|mTzDoM5bkxzO=|<`x zUsU0ptYS${2G({{o*qiS(HM0gItJ6211jep@a&3^cPd12UiBIMTZ$Q{i z9KEH?L^Y6iwA9QWEA>#g-QG35*#G{Ua_rc(g&wCfbN7{-ik@`7a6w>tuWPS4 zi)3rYXy%tVW0VGwX2!YlcEIc8>A^FWESsvSq*XAwn;yU z4D|G%W213Q_^WP_wvODudV(@ihA;l=7W~V32_8v5)IO0x+=nF!kyVXrpJ4(~Sk8rZ z&pV(Z@?NF)i1*%=VL7rrK68bCVeMt6efOGvn(Fv-q_48Of5_cz?l~Whz^{AG6(a^r zN2H$yhtKCsS{mP;1XIt z+-myQmwkd7&JTVZCL=TaPPDt)j+K=A@LIlo3f4`{!EBPonT1K$rk%fOZrM0_yQL~J zbXfZcH@m;Ic<=vc&-qPV-{4~m$UZ0+Q6;i zjRl$L%@s3vVqo#*&Tf-qjXmCkt<|35haIU6vXyakJ9U~Lcr<|XuQvq6>0a1G*0e$K zCWWmIG&x-b!-67SW};Ru>>JuGu(7q?zSelP$HW~3aci5A;LNDI2{%kuYGY5WV(G+;glSG@)jD&8>f zDR1!~aZW%Unoj?UJ#ZQ*pY-I-qwB0XNU%{P$z$^K;k}kR-JDQ-i1Oskb&)_!qb0pk zhSu^qtu)Y+CysbgRY8W^>gel(bp5D>ky-urga^Q|Wy%uSu&GH>0j7O%9N~p0$}|0y zUuw*Y6S;|0_62u9z4#BI6nNzQ?bpD{fI*S|t6;-kVy5k;jSQqk*o0}fqJuXUJl8QB zSzWb8PMmv->ARPqtwK3%JZIkTqf3Zyr)CC{m&rx+&JYe>2$rp44WdDgjXn9EcXwks zLiO`4c%fU-Hi>}5{(TAUZ(QlSyg*)Z1vKYee)yGW4~`~hH9H=GR3x5fsi$4M*pcRR zlSmXBBi%YzXv1|knm{l)ErmvC5p_is{1&HUooM~Z4FTT5pmF3K?17%A@GAm=^doKE zk8x%d%nLKXM7fB*D%4?FS_q9XRs;J#fh=s?x%9FIQMeN-)G>^_!KBA8naNj{_L9a+lFaiu?x-%h%vDFj2Yo{@=e<6+Dyir7fr_#rO#&0= z`b%qiHgZIOK)9~#wn!1G{LHOd-!3aKx%t zc+ra!!67Qcra-O1NcXaJ&PV|;uB?lBg@(3~N80I*9gTbluM5Ci78R(#h z3sBim+h&fvNNF;p?099(#zy1WCFI$>Aa>9lTAY-arCo%AZV$%Yak6=9ZXz05R~0ZP z9s@-XuQb4OSkb3AKfc=-cQ5#E#g@(g>#rs)U6UO43P3XXz}v*rJzqHFI9M(00_Q@0 z##P4vK~^S7kKPt`e7h4qqy*+GHnorS!|@XJnmnWmoO3se1qcYa*`lUvBf$U^Y7_#i zulFX6(O^%@j{6AWGBY3CGAn)v14*pmf4ov;iLaGk7_@GWa zw(y26-CoJ_wkxjK;GWf~Du*yjEz>LjdLTVlT-k*P2z`_HE#1g7HTL8_fLCWDrjytu zNa~$0>__$V-;41QulfuUfmRn*s+%A2;IV@34G*owop!RiSP+B?PkJDj5{5QCHT;pS z{5ul=MK`bmD}OT4pIa_0&=96eUj8_o#hFuo^nIK2aFYl1x=oiei{+hG6##nL$}l|f zD$RDT+~~n)+Fyk2!p?F#QA#~4BuAddXYB6Nj?8IPXo|gui<$~E6mm$5R;SPkDlstA zb8e)_)WGq+$Y36&<@ee)NIYh*Zdgt7sxX_ehCwKej#1D#672YZ8cKnt=d>sHvXrJf z)B`3^seYSR=BU>+Dl`YDZ`EMyL_&rzFHl7N%j!Mp0oaAH+nBo2XO6){FWo1C^5Ub-7|+R}gmSZJ+4_g%P}_ zDcGCe5sgb#)(Ye4=jeJnMe8CiN0`tw&y~0w?|~aN5)TO)Vd@~~f08H?)XkNqU>FCw zq0d0Vobb%&b0^SR5R637l^E#ex?(ECx`mgf;6qN{wBRDNXJBss4Go5M$T8E#``GhT zL7Guj4>LKU@@_iotGEJtC1lc6g=N^0$#qXiXj!mc6k{0|P+5kKz}U_RAd#vZi6Q*i71S-81xe2t4JIF*eoYwJD@Lg}_T(bk?f#dU z(}_v*!+#1;@US4HzVk>TdIvB4n7_kQYc}~bxPC64@WN;qi@34GE?b0~NZ!!%p#$2td=u(w5K0du z>;$H`WC)XZ@lvljz)Eb_?9Py5hH!$2N`4?h!C|JGDK!_spF9Z9dXJ)LN*BY`b~~OM z_!OnBF*TSvmc|X%oi%0_7_)6`uym(+jGB!yzWwD8ZJ}2ZMg5T0K)R$$xKW1 z?#bC--zIwW4p-RGn_Y0Y-9_3r)XVDv!_d{_K7w^KcPhzHbCF?p!r>i}jxq+3cg!S( z1_RdP33>~r(J>d)PpILt^t?}I3&89o|J$oPv_Up+JGtb+J-=RwYS9R@W0`OgVYnFqU7IRRy7LswVu7rtv=_~)!%@!2 z8gz-}%IhWs8^k|H(_MtOFIoKQ8AUc8UPPrG`L1$ua5}3_8ruJ4O?Qs#mu2T|faNYgJwiDHfdK13;`GI}ieVfOPnHPoHdlXF6-oCx? zK$PO%Rrkbig^|1oC4~d=A}|rg+1s&OHU4!H-f_{03m^~1FnsRNd?Z- z>Aj|?mEX-$8QE-S8X3u_cEhK7eJtjMGX5rmS>qV~#Mwxf+e%?ybxr(1Srl%JH`fYMLycr)o25iY6f6#`|XkA$&|eMkwlb29IIUP05p+r$5~ z4E(7fvmqneS7wvebotY)REE{X`3K4{oOvUm67Oy`;N_>OKCp=oJ7b6WGSt=K9iTQX zu5?D?zrB32NXE+YbXn$eq|$+}*r@?oo9DHA_!5LwDNupx5H6x#kr;P2n%0Xq&&4BE z8O!y8IlNQjG0wBS{#S4v$6cXE!!Qu;c^jlv-&!78TR-+5Hsmez@tQ#})5P=Qn$;Z{ zW=r}$suK@L_7T7t{J3x|Iv1-SQO0;oJ@}FiQP*xblVRFfh8f=^`t~k!dz)$h7Vi62 z>5}jUpBNUPUlggz^v$}pZZ0SZgMC?ga2Lkz!>DBK;6|NdVd48?ec|b~7TeXgigUh* z@u07(^9&#MXWJ~=mmAl1-t$f#U#@;>!e-Tfy{&9rr098X@sj*6qWi1o4~mJFpSn=& zk6$lh<7WeD;y2TEGlbdEgLtMteS=|p5gX;X zJ94s%d3wd>@B=?o0rY8=!8*rwJJ{ik?~Amp!IU$zO-my9v>=M`EY0Ys=p$E@VT&H> zkhlFoZAZz-q89AHu=-K7=ZsNiTjCa{QB#d~T!CjagWgDo`w*hwUfAacZZBu4n`viv z$zLEw6ud7b3YqqP=8(=x5%b4SRq#F6nTmQTgjMJKT0{R@Q2r`t{;pYnW(?R5_C=Zb z_t;gyiy=Ys<7yO?9UReKgaVul+-lb}S{OHA`6oxKRHfhLYj|EQe%6_`CaQb9eXG^v za8>T!dI(VE9)=E{p!sq7>oo7|_m5Z^gLxHm0{K;3$-cH1CMo*7m&vHD*U2%hF|ZtrwT8em22SA)QG?^u+{hfOz*(8vz+Em6;)Ns8`IF4H#)+BQ<&WymctGE3i%6BM zw9x}D^%1XzMKh-fzJHy`EN(3?)bMj)5&WTfGOyw=pi`KOx9N|S-R?EWetLp?`jlNS zK=UJ!mw&r%m{lL8obDsjc5k;LxoQ5`#K%Q)BVBn6_)@}dl}fE-KOa56CPMwE;7w(= zen+1+SqtIBn}8+^P#d}~6i*Y1b=)5?+m8bAJqB=(0VitXI{`LgB{19!9~}g;rRYfa z-Aey;sD@4jn;aoLmqL@^aD3``H4B;nT`1Bg5eTEmo3i%4$ZAi~2P|v^qkD}nFFlmY z0O8ex?=Ii~uuY%almKFh=q?C7n-nqi2W9K1{B#Gt3EpVtfH`JCXwMtBG>xLsol56H zR-Jh!jZ7YZJiOH8`&Z?@`VgIBoNW5gVR8XMgX{b=H?x6h_+K8?scQs9HF; zZf8ODyDj#ZBmd=M`a8Atx9Mj+mXa(p=Q`wwkuCS(#^&S@e_BQh?-Y>cT>VgC>MO|e zwuslf>?v3bg*R!eXpWqH0mbUDe<$A`@aziKpcE&M^R`^iyK6YMKiIj(M)EE-&Qng_ z>Q=0JZcR6A^Z!+L-ce0uiyEI%#|jKpkg6acMG#ORB4I{BdJ)lp(urW9hh7B)q{h%< zq$yR35I}(tdJ9b=iUQIE61oHkEdt@~IL@0pYwmjQUjE1*N!B@QpK!|l_V@k1LD$o~ zN|t0W56>I(;J$*0|Hl|)!IaKshgv2@E66r5c04=EJ zh>U^NPV-8CPl~ll%hS*e468+E)~{{K+CX)nz8r_nPJ^+IO>6`n7~(S7fzl~Xa`52192b3*Oa@^mjf7C{>= z2{2$w?(-Hhh~Z{?214ojiX>j?%IF()VjK6bHn|qRGlp8EbOfnd`Hm3*6i5-;jdM}n zbOfolRuB295=N&fJwBwtBH>)r7RxS4g(ml47{k4}t z3lKl#(rZ$5$v{L>D(5)QTgpH%@!?tDE2r7Nid6!+0R#{1sE<<9k!8TCQz zP%-)f<07|YJ@o3x5BK(W?vlJMI{HkuD{$k<7sN>?^0~Jf8QjP(GoGH%$ZQOgXxui4 z{Bm9q_aG{zJX&|8e5pio`Q+*tJn}hPIGwFfn$6OC&YW*K4^dBGn;Ydw)l+KYaA>X~4h(oXQ zw{;-Mpm~d(HlljS@Fp8BGvNF*qM>JiZw}nwME#`W*2uTgsPUqndn@mLU@=>YnA4Rdwn&u$++ltJRz*bkj!~=67yE zPB6?&4bEA5WVD;z83w@^^p^jJ|018^^3$$8{YSbBvZe$vnokHfv9x==GkWmrCk&7F zVct?ccI5JhU0wF|NhZ?7GO3*C$FES{#%o7hs{t7QlA85_%ob+9wesj0qqAkx=Wf~@ zSR%;lNC~9Aj4iYsZ}|#2B5QLn75AQp&$ly6)wPbYQH4s*{1i zVkxS^SK(lWe+hya58{DQfohm{&wniR3OI?1-`TI$&I+W$^?uZAGfDiOb%2^*GQyha(_vlLk|CxL%jx{xK)$ zN$__5(}gsxDvVkiFUHN87wS>siI%XxP~d>|FMA{q8REm_Z?^A0dwK%QCbPQha>Rn9 z-E%!`G(kn9(139?vXerB*bsjz;iawB;T0r_&H9c>nbXQ!s_%Pfm!QWQJg;v0`e2Am zYMolioa;qA$wIr5u8W3Tkqd~-g6b00-S%h>O8jneu)z!bZ;O!DSMo=Z*lenBviv(- zMxbm}oSrKF=`-GtLxVjHhOY?03TZnFa9-`ik(z}Ak^oYw3?7ZZ2{%x#xOI#O~+Oqb1ayVPGs#DLo zNC0$u`Ip5UO^sto3WmL&sz(M+S8Kh;<&t?izb0*_4mKD1JOs;qPmZ{xuB)n-5g~UKAwdu{jM&n^aNB{HiU6H9LF=S7^L@fbh=dxh0v1(z6>og zb?e)0tg3eAx)+&EwwE7gY3C?L>yC=DGaA?dAM5XP)W4nVjHmrdNQXTP8XSy#YnqeL z1sctFdWGFLYGZruB=Dkqf3cbvoXIrt`>0SqrPJx`E&2JF6nx1Dt%&D3BS;i)9sV7X zIK>}TVs8fu5&WfArJdz)1+i@w&GHI{U)~Jd{1Riu*_4UfA>+pdVl3+_U*zO`Yv~4Y z*g8luI`V@4GwUr-`QscJq(r^Z6N>xJ48n-dc9oeW3*tP!W zyJxQ~I{V5Zap-TmPyN;N-fN$KUzRLkua;w9$F;}owH!fxLvnVD(sb5&9evBCt!r=A zfiX3bN_nn!6t%@1CCs?9|41C&MxOm4*wn7~Gv4%B$hiQ#IP47OPE?P8no}E7!w3n-o86DHb@BuFAJ@21476n<@8&<=;5ox+PcX)UDy{ zyp_#sL!z5?bze(Eg^V~2r_J4aAV%&VcZ;7^xYjC5!-*L;s+Nt<1x2ks5ECC1)wWZ* zfTXl!iR8CI`kale9ETcGO0E6zFb3N4|CoRP8iO$S3I7CLFQZ*WsWe?Zcf)A}CMk(? zajb55w^Q*S3;us)G5+|7p_l(RQMCU?!}@QK)fdnt8`|g0KVSZzF1Yv5p^i58;&wto z=Ed*vMjbcKaGs7giXxQf5BVtPCmT|3>HA)EEfg}^;q^VP+!rBRYY=p8#Xl@<5iRV&}$$*CS^15@nl~CrPEI>`g0(a(Y9G%yOZ0Wb7EZ-j2 zveX_|_O&1%o3G8mp9WL`nK*e;KzgYC)shf&`5`WHlG`wWtc5 zYWjUv7AxBcTX}tFCl2e|XY!Ht(5n)J@MITZP%PW?e1se!cW5e2cx`RYeZ-$~oY914 zwbdL1s*xp1?g)uC-+-kzhI2b#O}|L(D}pVPE`RJww?$w3#^|M62t8)x-o^z=(t_G0 z=(XWUU5)X9tZ=;1B8siXWLBHgMsU*|{@Lus!*o^L41Om_jLbnOC(pkE}`ASbts>&@+ zYZSRtA{%pGrM{0tPB1iS4*p@qziZcCan?fBxHOhOgV;FgcrLhOTJ963TG>{zHgPpC zC)%o1dR^x{nHpr!WYwr)yLZM~VvoM!`0&?NmD2t?xNC7u0_DzWy1>9;`bU^|woOZ$ zhog;X!8UH~y@}?sfj#NBN zCs8#*5OZ1YN4HOCI@~=`Rc*3Z62Mj3zZ+{^wIJx#O)?5BM)9>j9wf8wup#?*JJMEN zuw5*0wAd$7`E~h6-aV%{pV_uLc6g;=iF zEN%20VGlL_zM1Qm8r}fTq|2)aN%m9`Q(-SI!a`T|H5swDoUGhv5+DJL2YkjL@`og9haz1 zW;I8L+K%zz@WZ>Nu>)mS!Ilh0WaUHEcv=X$^6$~9?H<lmoCOzZ<4$h?HEm500`J`475k7@9F_|x4&RmhrDgxK%n*_ua_>W7{8$)EHzjZ{P#Usno+KqaaAtQE zEr-nB5!rvR8q6*~n*M&O(l!cG(lvq{IbL~ey*`$gCAF=?6`(19M*)qgf^}|7DFs^? zp=MhVvyjqN7j9<{bTRiZ41EXREQY+GE?JXyJ|86hj(1P$)f)@{IAwn5;eUH@`=Pus zZvIi^!+iMYXdf`8yISagIVS;H&2xLMd;nPFtcTg&X4C3}EvaO2QwZd~8eP_cB&k1C znDKVP%Eu;dqSqAJRM-*5gUqK<4lp<*qn=XfL@>oXbV_`WWZdx`Dgus;urzHXVd&?|Oa%1AaKO$Ap`)Y2HCQK4G0$nZQrX6p z;}sTCSLoHEz<4EtXpSe<%T)c#KDRs`PZg_&)&hnMT z@rH)3DNErppGy-hs~Y{DcCT}>l{WyME43`F`}K>5E$K<8s}TBa149x%P2$Ve?tRU3 ztVxfc6_~eo>1bNS5)T37uPKlVcqGSu zXjv)Vn)@=Kjdz=di;c+Ih-|t9v+cS*wy*}mQl_=ygAM?}>Pzfe${fM(9TJYd?@ z`{8a10dPsD_bk~{@Fb3SO@Blhxvo<6@l=j%M0r6GPgI1yA_1nkg5Wf%sZ`~`@yiH! z1LAY6IbDq7BWFW)_lRMWtfWk8_RVH{srHJ_pwPJG`=I6w{;8(V=h5e*!TPHYf4&E_ z7q*g`LQ^*18zk1CI=^F2p7GLGTkSsNK?bV016E=RJ9gm~eg%jG{fYKCpux=?P8Jm> zF4{X_TgBbt_78T?C{65bgP|MpRu!e~(&fAgg*O#4MvKSoWTwkJ!u8p@YdZoj-82Cm zXa@A{Lc~RjEvFk@XV0ajkBbHf=#zrt%N>~sx~4I5OT3`s8MCJ=hr*}vh>NUfn3`z= z;<@rXYDN&=BA}}?c(QYdBELT!NJS8z(GzbnA5S>iiIYzxZHIFWU_su@eWxs%W7%Nc z8^FKW={Go*naImOlk46@178$oRUQ=w?l)0-*3`EyB_8(KiPvyIR)Sf00)vTk`-9Rs zP33*!WdK4rf7_+aJ-)rWe&-JG1Q}Sl{c=1V!J;@G+<cPyuNe<=^ z)wuU1x81bOSB7f_W9ZEWjW{lJbK78tZn?4|U#CHr`Rgy^g~%^euId+3D0fb&Tqe8q z+^qC`z(%g;;kK-9!o(SHY;(a#Uq%q<2~ZnFxSXFUCve7l8nj58deF$Go^;xo$-QTT zR3J6!5>>go>sOeQNMxLDgrVG~pW%E7_Z^R?cB|}}{o&ao(YxVKN_|QrZ3LzW zd{--XB@=*OQe@APz%R)Lq&`;Khe<6i< z5{Ih0(pJ^rRPP3aDqc^+p9#5KmU;cB0pp#~t)9W$Z8IJF>tpDI7zx_#Gt>r{xiD-^ zMVZ>8!ZBLgDc&Bez=rX37Ur9f^NF1*jvvtxQ7+`0N#jyWv^WVV3KCV3x|0N}WoruF zU66vvTdDU^JjCAMc2=bwZq^)=86=rceQVgm{}LYFM9IF0(>35%e#oV^b87liQRFNB zHX+=WE!@)mh%s$Xj4mCyG(g{|QVx`^mxvVA*h#>V?g-M#K$(!lkkep~(;N1wA^il* z9I5A9Ku{YWE}>pRQF)-IzHz+^ejyNt1jL-H(rw-qQX4#)Un8ej{ZGH=I?=e^VYd8L z!V03NhV&0Q_z7}of^4*-VuA>;Eqy#vVV#TEnN(ZU>4*)yg3HZYP1lEOmMLj2$YOKq z9-Xf*h2J-1^tBg!bVrlQLeiTL>g8o8w@~7`hn2s>uws&~#nJ9Ydh9&QYG^<4R;bMo z72`b=u;iJUY~2Ovy42Jc+_Wh{J;-$#&9Ovs4Y+43kffypu^(wVs}QDN)1x4J8GY{_ z8o?=1qQF?{sk+XqTOAW{{OzE5)TXufg0X7( zD@Wdq=a22|Ft&7ANxz7Q5awNzV8H@S_;@Qyik7?8}QZeUtSdc kv59}~XQn~Q`^(U+C!*f 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 QnI-~QTIofZq*37i0+kfZi2wiq literal 0 HcmV?d00001 diff --git a/screenshots/simpletest_datastore_sine.png b/screenshots/simpletest_datastore_sine.png new file mode 100644 index 0000000000000000000000000000000000000000..00a99b656ec1cc87ac0d518475a8fc1f28e5da09 GIT binary patch literal 27559 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 E0EzK66#xJL literal 0 HcmV?d00001 diff --git a/screenshots/simpletest_datastore_sineimg.png b/screenshots/simpletest_datastore_sineimg.png new file mode 100644 index 0000000000000000000000000000000000000000..9916f82b63aefbf70f4f8d3c62f4ab96c8f918c1 GIT binary patch literal 318 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4aTa()7BevL9RXp+soH$fK*0~5 zE{-7*Q(Mn$^m1a9V11x3%q`T})>beft3!a(XvYcG_YXpNdvk5Q9Qy-V#cGLJ|9&+j0&2tYQKQNf%8slR8$RS4P_z_OQVx_zzX)&&RHkT(~P;!)9 zpTWQ3_}917INx~PJjz#k<}u$}@g;Q+lx1h0tC?K8O8fqK*E^3&4w&4WSd%tIqU$dC8rexHK8LPjiS_Q>+-qT-`AS#-6>i*aBON=W2*vDU0@>hK9q&x-a OcLq;aKbLh*2~7aSY=CV5 literal 0 HcmV?d00001 diff --git a/screenshots/simpletest_datastore_small.png b/screenshots/simpletest_datastore_small.png new file mode 100644 index 0000000000000000000000000000000000000000..4b48b66b04b001a472c43b75ebc9b16002d7329a GIT binary patch literal 19963 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 KMNUMnLSTY_g!~Nv literal 0 HcmV?d00001