Added gin module
This commit is contained in:
parent
8bcae6f613
commit
7ae5824887
90 changed files with 26503 additions and 0 deletions
26
Modules/gin/3rdparty/avir/LICENSE
vendored
Executable file
26
Modules/gin/3rdparty/avir/LICENSE
vendored
Executable file
|
@ -0,0 +1,26 @@
|
||||||
|
AVIR License Agreement
|
||||||
|
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
AVIR Copyright (c) 2015-2019 Aleksey Vaneev
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Please credit the author of this library in your documentation in the
|
||||||
|
following way: "AVIR image resizing algorithm designed by Aleksey Vaneev"
|
330
Modules/gin/3rdparty/avir/README.md
vendored
Executable file
330
Modules/gin/3rdparty/avir/README.md
vendored
Executable file
|
@ -0,0 +1,330 @@
|
||||||
|
# AVIR #
|
||||||
|
## Introduction ##
|
||||||
|
Me, Aleksey Vaneev, is happy to offer you an open source image resizing /
|
||||||
|
scaling library which has reached a production level of quality, and is
|
||||||
|
ready to be incorporated into any project. This library features routines
|
||||||
|
for both down- and upsizing of 8- and 16-bit, 1 to 4-channel images. Image
|
||||||
|
resizing routines were implemented in multi-platform C++ code, and have a
|
||||||
|
high level of optimality. Beside resizing, this library offers a sub-pixel
|
||||||
|
shift operation. Built-in sRGB gamma correction is available.
|
||||||
|
|
||||||
|
The resizing algorithm at first produces 2X upsized image (relative to the
|
||||||
|
source image size, or relative to the destination image size if downsizing is
|
||||||
|
performed) and then performs interpolation using a bank of sinc function-based
|
||||||
|
fractional delay filters. At the last stage a correction filter is applied
|
||||||
|
which fixes smoothing introduced at previous steps.
|
||||||
|
|
||||||
|
The resizing algorithm was designed to provide the best visual quality. The
|
||||||
|
author even believes this algorithm provides the "ultimate" level of
|
||||||
|
quality (for an orthogonal resizing) which cannot be increased further: no
|
||||||
|
math exists to provide a better frequency response, better anti-aliasing
|
||||||
|
quality and at the same time having less ringing artifacts: these are 3
|
||||||
|
elements that define any resizing algorithm's quality; in AVIR practice these
|
||||||
|
elements have a high correlation to each other, so they can be represented by
|
||||||
|
a single parameter (AVIR offers several parameter sets with varying quality).
|
||||||
|
Algorithm's time performance turned out to be very good as well (for the
|
||||||
|
"ultimate" image quality).
|
||||||
|
|
||||||
|
An important element utilized by this algorithm is the so called Peaked Cosine
|
||||||
|
window function, which is applied over sinc function in all filters. Please
|
||||||
|
consult the documentation for more details.
|
||||||
|
|
||||||
|
Note that since AVIR implements orthogonal resizing, it may exhibit diagonal
|
||||||
|
aliasing artifacts. These artifacts are usually suppressed by EWA or radial
|
||||||
|
filtering techniques. EWA-like technique is not implemented in AVIR, because
|
||||||
|
it requires considerably more computing resources and may produce a blurred
|
||||||
|
image.
|
||||||
|
|
||||||
|
AVIR does not offer affine and non-linear image transformations "out of the
|
||||||
|
box". Since upsizing is a relatively fast operation in AVIR (required time
|
||||||
|
scales linearly with the output image area), affine and non-linear
|
||||||
|
transformations can be implemented in steps: 4- to 8-times upsizing,
|
||||||
|
transformation via bilinear interpolation, downsizing (linear proportional
|
||||||
|
affine transformations can probably skip the downsizing step). This should not
|
||||||
|
compromise the transformation quality much as bilinear interpolation's
|
||||||
|
problems will mostly reside in spectral area without useful signal, with a
|
||||||
|
maximum of 0.7 dB high-frequency attenuation for 4-times upsizing, and 0.17 dB
|
||||||
|
attenuation for 8-times upsizing. This approach is probably as time efficient
|
||||||
|
as performing a high-quality transform over the input image directly (the only
|
||||||
|
serious drawback is the increased memory requirement). Note that affine
|
||||||
|
transformations that change image proportions should first apply proportion
|
||||||
|
change during upsizing.
|
||||||
|
|
||||||
|
*AVIR is devoted to women. Your digital photos can look good at any size!*
|
||||||
|
|
||||||
|
## Requirements ##
|
||||||
|
C++ compiler and system with efficient "float" floating point (24-bit
|
||||||
|
mantissa) type support. This library can also internally use the "double" and
|
||||||
|
SIMD floating point types during resizing if needed. This library does not
|
||||||
|
have dependencies beside the standard C library.
|
||||||
|
|
||||||
|
## Links ##
|
||||||
|
* [Documentation](https://www.voxengo.com/public/avir/Documentation/)
|
||||||
|
|
||||||
|
## Usage Information ##
|
||||||
|
The image resizer is represented by the `avir::CImageResizer<>` class, which
|
||||||
|
is a single front-end class for the whole library. Basically, you do not need
|
||||||
|
to use nor understand any other classes beside this class.
|
||||||
|
|
||||||
|
The code of the library resides in the "avir" C++ namespace, effectively
|
||||||
|
isolating it from all other code. The code is thread-safe. You need just
|
||||||
|
a single resizer object per running application, at any time, even when
|
||||||
|
resizing images concurrently.
|
||||||
|
|
||||||
|
To resize images in your application, simply add 3 lines of code:
|
||||||
|
|
||||||
|
#include "avir.h"
|
||||||
|
avir :: CImageResizer<> ImageResizer( 8 );
|
||||||
|
ImageResizer.resizeImage( InBuf, 640, 480, 0, OutBuf, 1024, 768, 3, 0 );
|
||||||
|
(multi-threaded operation requires additional coding, see the documentation)
|
||||||
|
|
||||||
|
For low-ringing performance:
|
||||||
|
|
||||||
|
avir :: CImageResizer<> ImageResizer( 8, 0, avir :: CImageResizerParamsLR() );
|
||||||
|
|
||||||
|
To use the built-in gamma correction, an object of the
|
||||||
|
`avir::CImageResizerVars` class with its variable `UseSRGBGamma` set to "true"
|
||||||
|
should be supplied to the `resizeImage()` function. Note that the gamma
|
||||||
|
correction is applied to all channels (e.g. alpha-channel) in the current
|
||||||
|
implementation.
|
||||||
|
|
||||||
|
avir :: CImageResizerVars Vars;
|
||||||
|
Vars.UseSRGBGamma = true;
|
||||||
|
|
||||||
|
Dithering (error-diffusion dither which is perceptually good) can be enabled
|
||||||
|
this way:
|
||||||
|
|
||||||
|
typedef avir :: fpclass_def< float, float,
|
||||||
|
avir :: CImageResizerDithererErrdINL< float > > fpclass_dith;
|
||||||
|
avir :: CImageResizer< fpclass_dith > ImageResizer( 8 );
|
||||||
|
|
||||||
|
The library is able to process images of any bit depth: this includes 8-bit,
|
||||||
|
16-bit, float and double types. Larger integer and signed integer types are
|
||||||
|
not supported. Supported source and destination image sizes are only limited
|
||||||
|
by the available system memory.
|
||||||
|
|
||||||
|
The code of this library was commented in the [Doxygen](http://www.doxygen.org/)
|
||||||
|
style. To generate the documentation locally you may run the
|
||||||
|
`doxygen ./other/avirdoxy.txt` command from the library's directory. Note that
|
||||||
|
the code was suitably documented allowing you to make modifications, and to
|
||||||
|
gain full understanding of the algorithm.
|
||||||
|
|
||||||
|
Preliminary tests show that this library (compiled with Intel C++ Compiler
|
||||||
|
18.2 with AVX2 instructions enabled, without explicit SIMD resizing code) can
|
||||||
|
resize 8-bit RGB 5184x3456 (17.9 Mpixel) 3-channel image down to 1920x1280
|
||||||
|
(2.5 Mpixel) image in 245 milliseconds, utilizing a single thread, on Intel
|
||||||
|
Core i7-7700K processor-based system without overclocking. This scales down to
|
||||||
|
74 milliseconds if 8 threads are utilized.
|
||||||
|
|
||||||
|
Multi-threaded operation is not provided by this library "out of the box".
|
||||||
|
The multi-threaded (horizontally-threaded) infrastructure is available, but
|
||||||
|
requires additional system-specific interfacing code for engagement.
|
||||||
|
|
||||||
|
## SIMD Usage Information ##
|
||||||
|
This library is capable of using SIMD floating point types for internal
|
||||||
|
variables. This means that up to 4 color channels can be processed in
|
||||||
|
parallel. Since the default interleaved processing algorithm itself remains
|
||||||
|
non-SIMD, the use of SIMD internal types is not practical for 1- and 2-channel
|
||||||
|
image resizing (due to overhead). SIMD internal type can be used this way:
|
||||||
|
|
||||||
|
#include "avir_float4_sse.h"
|
||||||
|
avir :: CImageResizer< avir :: fpclass_float4 > ImageResizer( 8 );
|
||||||
|
|
||||||
|
For 1-channel and 2-channel image resizing when AVX instructions are allowed
|
||||||
|
it may be reasonable to utilize de-interleaved SIMD processing algorithm.
|
||||||
|
While it gives no performance benefit if the "float4" SSE processing type is
|
||||||
|
used, it offers some performance boost if the "float8" AVX processing type is
|
||||||
|
used (given dithering is not performed, or otherwise performance is reduced at
|
||||||
|
the dithering stage since recursive dithering cannot be parallelized). The
|
||||||
|
internal type remains non-SIMD "float". De-interleaved algorithm can be used
|
||||||
|
this way:
|
||||||
|
|
||||||
|
#include "avir_float8_avx.h"
|
||||||
|
avir :: CImageResizer< avir :: fpclass_float8_dil > ImageResizer( 8 );
|
||||||
|
|
||||||
|
It's important to note that on the latest Intel processors (i7-7700K and
|
||||||
|
probably later) the use of the aforementioned SIMD-specific resizing code may
|
||||||
|
not be justifiable, or may be even counter-productive due to many factors:
|
||||||
|
memory bandwidth bottleneck, increased efficiency of processor's circuitry
|
||||||
|
utilization and out-of-order execution, automatic SIMD optimizations performed
|
||||||
|
by the compiler. This is at least true when compiling 64-bit code with Intel
|
||||||
|
C++ Compiler 18.2 with /QxSSE4.2, or especially with the /QxCORE-AVX2 option.
|
||||||
|
SSE-specific resizing code may still be a little bit more efficient for
|
||||||
|
4-channel image resizing.
|
||||||
|
|
||||||
|
## Notes ##
|
||||||
|
This library was tested for compatibility with [GNU C++](http://gcc.gnu.org/),
|
||||||
|
[Microsoft Visual C++](http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-products)
|
||||||
|
and [Intel C++](http://software.intel.com/en-us/c-compilers) compilers, on 32-
|
||||||
|
and 64-bit Windows, macOS and CentOS Linux. The code was also tested with
|
||||||
|
Dr.Memory/Win32 for the absence of uninitialized or unaddressable memory
|
||||||
|
accesses.
|
||||||
|
|
||||||
|
All code is fully "inline", without the need to compile any source files. The
|
||||||
|
memory footprint of the library itself is very modest, except that the size of
|
||||||
|
the temporary image buffers depends on the input and output image sizes, and
|
||||||
|
is proportionally large.
|
||||||
|
|
||||||
|
The "heart" of resizing algorithm's quality resides in the parameters defined
|
||||||
|
via the `avir::CImageResizerParams` structure. While the default set of
|
||||||
|
parameters that offers a good quality was already provided, there is
|
||||||
|
(probably) still a place for improvement exists, and the default parameters
|
||||||
|
may change in a future update. If you need to recall an exact set of
|
||||||
|
parameters, simply save them locally for a later use.
|
||||||
|
|
||||||
|
When the algorithm is run with no resizing applied (k=1), the result of
|
||||||
|
resizing will not be an exact, but a very close copy of the source image. The
|
||||||
|
reason for such inexactness is that the image is always low-pass filtered at
|
||||||
|
first to reduce aliasing during subsequent resizing, and at last filtered by a
|
||||||
|
correction filter. Such approach allows algorithm to maintain a stable level
|
||||||
|
of quality regardless of the resizing "k" factor used.
|
||||||
|
|
||||||
|
This library includes a binary command line tool "imageresize" for major
|
||||||
|
desktop platforms. This tool was designed to be used as a demonstration of
|
||||||
|
library's performance, and as a reference, it is multi-threaded (the `-t`
|
||||||
|
switch can be used to control the number of threads utilized). This tool uses
|
||||||
|
plain "float" processing (no explicit SIMD) and relies on automatic compiler
|
||||||
|
optimization (with Win64 binary being the "main" binary as it was compiled
|
||||||
|
with the best ICC optimization options for the time being). This tool uses the
|
||||||
|
following libraries:
|
||||||
|
* turbojpeg Copyright (c) 2009-2013 D. R. Commander
|
||||||
|
* libpng Copyright (c) 1998-2013 Glenn Randers-Pehrson
|
||||||
|
* zlib Copyright (c) 1995-2013 Jean-loup Gailly and Mark Adler
|
||||||
|
|
||||||
|
Note that you can enable gamma-correction with the `-g` switch. However,
|
||||||
|
sometimes gamma-correction produces "greenish/reddish/bluish haze" since
|
||||||
|
low-amplitude oscillations produced by resizing at object boundaries are
|
||||||
|
amplified by gamma correction. This can also have an effect of reduced
|
||||||
|
contrast.
|
||||||
|
|
||||||
|
## Interpolation Discussion ##
|
||||||
|
The use of certain low-pass filters and 2X upsampling in this library is
|
||||||
|
hardly debatable, because they are needed to attain a certain anti-aliasing
|
||||||
|
effect and keep ringing artifacts low. But the use of sinc function-based
|
||||||
|
interpolation filter that is 18 taps-long (may be higher, up to 36 taps in
|
||||||
|
practice) can be questioned, because even in 0th order case such
|
||||||
|
interpolation filter requires 18 multiply-add operations. Comparatively, an
|
||||||
|
optimal Hermite or cubic interpolation spline requires 8 multiply and 11 add
|
||||||
|
operations.
|
||||||
|
|
||||||
|
One of the reasons 18-tap filter is preferred, is because due to memory
|
||||||
|
bandwidth limitations using a lower-order filter does not provide any
|
||||||
|
significant performance increase (e.g. 14-tap filter is less than 5% more
|
||||||
|
efficient overall). At the same time, in comparison to cubic spline, 18-tap
|
||||||
|
filter embeds a low-pass filter that rejects signal above 0.5\*pi (provides
|
||||||
|
additional anti-aliasing filtering), and this filter has a consistent shape at
|
||||||
|
all fractional offsets. Splines have a varying low-pass filter shape at
|
||||||
|
different fractional offsets (e.g. no low-pass filtering at 0.0 offset,
|
||||||
|
and maximal low-pass filtering at 0.5 offset). 18-tap filter also offers a
|
||||||
|
superior stop-band attenuation which almost guarantees absence of artifacts if
|
||||||
|
the image is considerably sharpened afterwards.
|
||||||
|
|
||||||
|
## Why 2X upsizing in AVIR? ##
|
||||||
|
Classic approaches to image resizing do not perform an additional 2X upsizing.
|
||||||
|
So, why such upsizing is needed at all in AVIR? Indeed, image resizing can be
|
||||||
|
implemented using a single interpolation filter which is applied to the source
|
||||||
|
image directly. However, such approach has limitations:
|
||||||
|
|
||||||
|
First of all, speaking about non-2X-upsized resizing, during upsizing the
|
||||||
|
interpolation filter has to be tuned to a frequency close to pi (Nyquist) in
|
||||||
|
order to reduce high-frequency smoothing: this reduces the space left for
|
||||||
|
filter optimization. Beside that, during downsizing, a filter that performs
|
||||||
|
well and predictable when tuned to frequencies close to the Nyquist frequency,
|
||||||
|
may become distorted in its spectral shape when it is tuned to lower
|
||||||
|
frequencies. That is why it is usually a good idea to have filter's stop-band
|
||||||
|
begin below Nyquist so that the transition band's shape remains stable at any
|
||||||
|
lower-frequency setting. At the same time, this requirement complicates a
|
||||||
|
further corrective filtering, because correction filter may become too steep
|
||||||
|
at the point where the stop-band begins.
|
||||||
|
|
||||||
|
Secondly, speaking about non-2X-upsized resizing, filter has to be very short
|
||||||
|
(with a base length of 5-7 taps, further multiplied by the resizing factor) or
|
||||||
|
otherwise the ringing artifacts will be very strong: it is a general rule that
|
||||||
|
the steeper the filter is around signal frequencies being removed the higher
|
||||||
|
the ringing artifacts are. That is why it is preferred to move steep
|
||||||
|
transitions into the spectral area with a quieter signal. A short filter also
|
||||||
|
means it cannot provide a strong "beyond-Nyquist" stop-band attenuation, so an
|
||||||
|
interpolated image will look a bit edgy or not very clean due to stop-band
|
||||||
|
artifacts.
|
||||||
|
|
||||||
|
To sum up, only additional controlled 2X upsizing provides enough spectral
|
||||||
|
space to design interpolation filter without visible ringing artifacts yet
|
||||||
|
providing a strong stop-band attenuation and stable spectral characteristics
|
||||||
|
(good at any resizing "k" factor). Moreover, 2X upsizing becomes very
|
||||||
|
important in maintaining a good resizing quality when downsizing and upsizing
|
||||||
|
by small "k" factors, in the range 0.5 to 2: resizing approaches that do not
|
||||||
|
perform 2X upsizing usually cannot design a good interpolation filter for such
|
||||||
|
factors just because there is not enough spectral space available.
|
||||||
|
|
||||||
|
## Why Peaked Cosine in AVIR? ##
|
||||||
|
First of all, AVIR is a general solution to image resizing problem. That is
|
||||||
|
why it should not be directly compared to "spline interpolation" or "Lanczos
|
||||||
|
resampling", because the latter two are only means to design interpolation
|
||||||
|
filters, and they can be implemented in a variety of ways, even in sub-optimal
|
||||||
|
ways. Secondly, with only a minimal effort AVIR can be changed to use any
|
||||||
|
existing interpolation formula and any window function, but this is just not
|
||||||
|
needed.
|
||||||
|
|
||||||
|
An effort was made to compare Peaked Cosine to Lanczos window function, and
|
||||||
|
here is the author's opinion. Peaked Cosine has two degrees of freedom whereas
|
||||||
|
Lanczos has one degree of freedom. While both functions can be used with
|
||||||
|
acceptable results, Peaked Cosine window function used in automatic parameter
|
||||||
|
optimization really pushes the limits of frequency response linearity,
|
||||||
|
anti-aliasing strength (stop-band attenuation) and low-ringing performance
|
||||||
|
which Lanczos cannot usually achieve. This is true at least when using a
|
||||||
|
general-purpose downhill simplex optimization method. Lanczos window has good
|
||||||
|
(but not better) characteristics in several special cases (certain "k"
|
||||||
|
factors) which makes it of limited use in a general solution such as AVIR.
|
||||||
|
|
||||||
|
Among other window functions (Kaiser, Gaussian, Cauchy, Poisson, generalized
|
||||||
|
cosine windows) there are no better candidates as well. It looks like Peaked
|
||||||
|
Cosine function's scalability (it retains stable, almost continously-variable
|
||||||
|
spectral characteristics at any window parameter values), and its ability to
|
||||||
|
create "desirable" pass-band ripple in the frequency response near the cutoff
|
||||||
|
point contribute to its better overall quality. Somehow Peaked Cosine window
|
||||||
|
function optimization manages to converge to reasonable states in most cases
|
||||||
|
(that is why AVIR library comes with a set of equally robust, but distinctive
|
||||||
|
parameter sets) whereas all other window functions tend to produce
|
||||||
|
unpredictable optimization results.
|
||||||
|
|
||||||
|
The only disadvantage of Peaked Cosine window function is that usable filters
|
||||||
|
windowed by this function tend to be longer than "usual" (with Kaiser window
|
||||||
|
being the "golden standard" for filter length per decibel of stop-band
|
||||||
|
attenuation). This is a price that should be paid for stable spectral
|
||||||
|
characteristics.
|
||||||
|
|
||||||
|
## Change log ##
|
||||||
|
Version 2.2:
|
||||||
|
|
||||||
|
* Released AVIR under a permissive MIT license agreement.
|
||||||
|
|
||||||
|
Version 2.1:
|
||||||
|
|
||||||
|
* Fixed error-diffusion dither problems introduced in the previous version.
|
||||||
|
* Added the `-1` switch to the `imageresize` to enable 1-bit output for
|
||||||
|
dither's quality evaluation (use together with the `-d` switch).
|
||||||
|
* Added the `--algparams=` switch to the `imageresize` to control resizing
|
||||||
|
quality (replaces the `--low-ring` switch).
|
||||||
|
* Added `avir :: CImageResizerParamsULR` parameter set for lowest-ringing
|
||||||
|
performance possible (not considerably different to
|
||||||
|
`avir :: CImageResizerParamsLR`, but a bit lower ringing).
|
||||||
|
|
||||||
|
Version 2.0:
|
||||||
|
|
||||||
|
* Minor inner loop optimizations.
|
||||||
|
* Lifted the supported image size constraint by switching buffer addressing to
|
||||||
|
`size_t` from `int`, now image size is limited by the available system memory.
|
||||||
|
* Added several useful switches to the `imageresize` utility.
|
||||||
|
* Now `imageresize` does not apply gamma-correction by default.
|
||||||
|
* Fixed scaling of bit depth-reduction operation.
|
||||||
|
* Improved error-diffusion dither's signal-to-noise ratio.
|
||||||
|
* Compiled binaries with AVX2 instruction set (SSE4 for macOS).
|
||||||
|
|
||||||
|
## Users ##
|
||||||
|
This library is used by:
|
||||||
|
|
||||||
|
* [Contaware.com](http://www.contaware.com/)
|
||||||
|
|
||||||
|
Please drop me a note at aleksey.vaneev@gmail.com and I will include a link to
|
||||||
|
your software product to the list of users. This list is important at
|
||||||
|
maintaining confidence in this library among the interested parties.
|
6198
Modules/gin/3rdparty/avir/avir.h
vendored
Executable file
6198
Modules/gin/3rdparty/avir/avir.h
vendored
Executable file
File diff suppressed because it is too large
Load diff
1012
Modules/gin/3rdparty/avir/avir_dil.h
vendored
Executable file
1012
Modules/gin/3rdparty/avir/avir_dil.h
vendored
Executable file
File diff suppressed because it is too large
Load diff
319
Modules/gin/3rdparty/avir/avir_float4_sse.h
vendored
Executable file
319
Modules/gin/3rdparty/avir/avir_float4_sse.h
vendored
Executable file
|
@ -0,0 +1,319 @@
|
||||||
|
//$ nobt
|
||||||
|
//$ nocpp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file avir_float4_sse.h
|
||||||
|
*
|
||||||
|
* @brief Inclusion file for the "float4" type.
|
||||||
|
*
|
||||||
|
* This file includes the "float4" SSE-based type used for SIMD variable
|
||||||
|
* storage and processing.
|
||||||
|
*
|
||||||
|
* AVIR Copyright (c) 2015-2019 Aleksey Vaneev
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AVIR_FLOAT4_SSE_INCLUDED
|
||||||
|
#define AVIR_FLOAT4_SSE_INCLUDED
|
||||||
|
|
||||||
|
#include <xmmintrin.h>
|
||||||
|
#include <emmintrin.h>
|
||||||
|
|
||||||
|
namespace avir {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SIMD packed 4-float type.
|
||||||
|
*
|
||||||
|
* This class implements a packed 4-float type that can be used to perform
|
||||||
|
* parallel computation using SIMD instructions on SSE-enabled processors.
|
||||||
|
* This class can be used as the "fptype" argument of the avir::fpclass_def
|
||||||
|
* class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class float4
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
float4()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
float4( const float4& s )
|
||||||
|
: value( s.value )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
float4( const __m128 s )
|
||||||
|
: value( s )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
float4( const float s )
|
||||||
|
: value( _mm_set1_ps( s ))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
float4& operator = ( const float4& s )
|
||||||
|
{
|
||||||
|
value = s.value;
|
||||||
|
return( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
float4& operator = ( const __m128 s )
|
||||||
|
{
|
||||||
|
value = s;
|
||||||
|
return( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
float4& operator = ( const float s )
|
||||||
|
{
|
||||||
|
value = _mm_set1_ps( s );
|
||||||
|
return( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
operator float () const
|
||||||
|
{
|
||||||
|
return( _mm_cvtss_f32( value ));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param p Pointer to memory from where the value should be loaded,
|
||||||
|
* should be 16-byte aligned.
|
||||||
|
* @return float4 value loaded from the specified memory location.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static float4 load( const float* const p )
|
||||||
|
{
|
||||||
|
return( _mm_load_ps( p ));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param p Pointer to memory from where the value should be loaded,
|
||||||
|
* may have any alignment.
|
||||||
|
* @return float4 value loaded from the specified memory location.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static float4 loadu( const float* const p )
|
||||||
|
{
|
||||||
|
return( _mm_loadu_ps( p ));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param p Pointer to memory from where the value should be loaded,
|
||||||
|
* may have any alignment.
|
||||||
|
* @param lim The maximum number of elements to load, >0.
|
||||||
|
* @return float4 value loaded from the specified memory location, with
|
||||||
|
* elements beyond "lim" set to 0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static float4 loadu( const float* const p, int lim )
|
||||||
|
{
|
||||||
|
if( lim > 2 )
|
||||||
|
{
|
||||||
|
if( lim > 3 )
|
||||||
|
{
|
||||||
|
return( _mm_loadu_ps( p ));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return( _mm_set_ps( 0.0f, p[ 2 ], p[ 1 ], p[ 0 ]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( lim == 2 )
|
||||||
|
{
|
||||||
|
return( _mm_set_ps( 0.0f, 0.0f, p[ 1 ], p[ 0 ]));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return( _mm_load_ss( p ));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function stores *this value to the specified memory location.
|
||||||
|
*
|
||||||
|
* @param[out] p Output memory location, should be 16-byte aligned.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void store( float* const p ) const
|
||||||
|
{
|
||||||
|
_mm_store_ps( p, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function stores *this value to the specified memory location.
|
||||||
|
*
|
||||||
|
* @param[out] p Output memory location, may have any alignment.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void storeu( float* const p ) const
|
||||||
|
{
|
||||||
|
_mm_storeu_ps( p, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function stores "lim" lower elements of *this value to the specified
|
||||||
|
* memory location.
|
||||||
|
*
|
||||||
|
* @param[out] p Output memory location, may have any alignment.
|
||||||
|
* @param lim The number of lower elements to store, >0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void storeu( float* const p, int lim ) const
|
||||||
|
{
|
||||||
|
if( lim > 2 )
|
||||||
|
{
|
||||||
|
if( lim > 3 )
|
||||||
|
{
|
||||||
|
_mm_storeu_ps( p, value );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_mm_storel_pi( (__m64*) (void*) p, value );
|
||||||
|
_mm_store_ss( p + 2, _mm_movehl_ps( value, value ));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( lim == 2 )
|
||||||
|
{
|
||||||
|
_mm_storel_pi( (__m64*) (void*) p, value );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_mm_store_ss( p, value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float4& operator += ( const float4& s )
|
||||||
|
{
|
||||||
|
value = _mm_add_ps( value, s.value );
|
||||||
|
return( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
float4& operator -= ( const float4& s )
|
||||||
|
{
|
||||||
|
value = _mm_sub_ps( value, s.value );
|
||||||
|
return( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
float4& operator *= ( const float4& s )
|
||||||
|
{
|
||||||
|
value = _mm_mul_ps( value, s.value );
|
||||||
|
return( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
float4& operator /= ( const float4& s )
|
||||||
|
{
|
||||||
|
value = _mm_div_ps( value, s.value );
|
||||||
|
return( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 operator + ( const float4& s ) const
|
||||||
|
{
|
||||||
|
return( _mm_add_ps( value, s.value ));
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 operator - ( const float4& s ) const
|
||||||
|
{
|
||||||
|
return( _mm_sub_ps( value, s.value ));
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 operator * ( const float4& s ) const
|
||||||
|
{
|
||||||
|
return( _mm_mul_ps( value, s.value ));
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 operator / ( const float4& s ) const
|
||||||
|
{
|
||||||
|
return( _mm_div_ps( value, s.value ));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Horizontal sum of elements.
|
||||||
|
*/
|
||||||
|
|
||||||
|
float hadd() const
|
||||||
|
{
|
||||||
|
const __m128 v = _mm_add_ps( value, _mm_movehl_ps( value, value ));
|
||||||
|
const __m128 res = _mm_add_ss( v, _mm_shuffle_ps( v, v, 1 ));
|
||||||
|
return( _mm_cvtss_f32( res ));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function performs in-place addition of a value located in memory and
|
||||||
|
* the specified value.
|
||||||
|
*
|
||||||
|
* @param p Pointer to value where addition happens. May be unaligned.
|
||||||
|
* @param v Value to add.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void addu( float* const p, const float4& v )
|
||||||
|
{
|
||||||
|
( loadu( p ) + v ).storeu( p );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function performs in-place addition of a value located in memory and
|
||||||
|
* the specified value. Limited to the specfied number of elements.
|
||||||
|
*
|
||||||
|
* @param p Pointer to value where addition happens. May be unaligned.
|
||||||
|
* @param v Value to add.
|
||||||
|
* @param lim The element number limit, >0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void addu( float* const p, const float4& v, const int lim )
|
||||||
|
{
|
||||||
|
( loadu( p, lim ) + v ).storeu( p, lim );
|
||||||
|
}
|
||||||
|
|
||||||
|
__m128 value; ///< Packed value of 4 floats.
|
||||||
|
///<
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SIMD rounding function, exact result.
|
||||||
|
*
|
||||||
|
* @param v Value to round.
|
||||||
|
* @return Rounded SIMD value.
|
||||||
|
*/
|
||||||
|
|
||||||
|
inline float4 round( const float4& v )
|
||||||
|
{
|
||||||
|
unsigned int prevrm = _MM_GET_ROUNDING_MODE();
|
||||||
|
_MM_SET_ROUNDING_MODE( _MM_ROUND_NEAREST );
|
||||||
|
|
||||||
|
const __m128 res = _mm_cvtepi32_ps( _mm_cvtps_epi32( v.value ));
|
||||||
|
|
||||||
|
_MM_SET_ROUNDING_MODE( prevrm );
|
||||||
|
|
||||||
|
return( res );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SIMD function "clamps" (clips) the specified packed values so that they are
|
||||||
|
* not lesser than "minv", and not greater than "maxv".
|
||||||
|
*
|
||||||
|
* @param Value Value to clamp.
|
||||||
|
* @param minv Minimal allowed value.
|
||||||
|
* @param maxv Maximal allowed value.
|
||||||
|
* @return The clamped value.
|
||||||
|
*/
|
||||||
|
|
||||||
|
inline float4 clamp( const float4& Value, const float4& minv,
|
||||||
|
const float4& maxv )
|
||||||
|
{
|
||||||
|
return( _mm_min_ps( _mm_max_ps( Value.value, minv.value ), maxv.value ));
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef fpclass_def< avir :: float4, float > fpclass_float4; ///<
|
||||||
|
///< Class that can be used as the "fpclass" template parameter of the
|
||||||
|
///< avir::CImageResizer class to perform calculation using default
|
||||||
|
///< interleaved algorithm, using SIMD float4 type.
|
||||||
|
///<
|
||||||
|
|
||||||
|
} // namespace avir
|
||||||
|
|
||||||
|
#endif // AVIR_FLOAT4_SSE_INCLUDED
|
359
Modules/gin/3rdparty/avir/avir_float8_avx.h
vendored
Executable file
359
Modules/gin/3rdparty/avir/avir_float8_avx.h
vendored
Executable file
|
@ -0,0 +1,359 @@
|
||||||
|
//$ nobt
|
||||||
|
//$ nocpp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file avir_float8_avx.h
|
||||||
|
*
|
||||||
|
* @brief Inclusion file for the "float8" type.
|
||||||
|
*
|
||||||
|
* This file includes the "float8" AVX-based type used for SIMD variable
|
||||||
|
* storage and processing.
|
||||||
|
*
|
||||||
|
* AVIR Copyright (c) 2015-2019 Aleksey Vaneev
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AVIR_FLOAT8_AVX_INCLUDED
|
||||||
|
#define AVIR_FLOAT8_AVX_INCLUDED
|
||||||
|
|
||||||
|
#include <immintrin.h>
|
||||||
|
#include "avir_dil.h"
|
||||||
|
|
||||||
|
namespace avir {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SIMD packed 8-float type.
|
||||||
|
*
|
||||||
|
* This class implements a packed 8-float type that can be used to perform
|
||||||
|
* parallel computation using SIMD instructions on AVX-enabled processors.
|
||||||
|
* This class can be used as the "fptype" argument of the avir::fpclass_def
|
||||||
|
* or avir::fpclass_def_dil class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class float8
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
float8()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
float8( const float8& s )
|
||||||
|
: value( s.value )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
float8( const __m256 s )
|
||||||
|
: value( s )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
float8( const float s )
|
||||||
|
: value( _mm256_set1_ps( s ))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
float8& operator = ( const float8& s )
|
||||||
|
{
|
||||||
|
value = s.value;
|
||||||
|
return( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
float8& operator = ( const __m256 s )
|
||||||
|
{
|
||||||
|
value = s;
|
||||||
|
return( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
float8& operator = ( const float s )
|
||||||
|
{
|
||||||
|
value = _mm256_set1_ps( s );
|
||||||
|
return( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
operator float () const
|
||||||
|
{
|
||||||
|
return( _mm_cvtss_f32( _mm256_extractf128_ps( value, 0 )));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param p Pointer to memory from where the value should be loaded,
|
||||||
|
* should be 32-byte aligned.
|
||||||
|
* @return float8 value loaded from the specified memory location.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static float8 load( const float* const p )
|
||||||
|
{
|
||||||
|
return( _mm256_load_ps( p ));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param p Pointer to memory from where the value should be loaded,
|
||||||
|
* may have any alignment.
|
||||||
|
* @return float8 value loaded from the specified memory location.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static float8 loadu( const float* const p )
|
||||||
|
{
|
||||||
|
return( _mm256_loadu_ps( p ));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param p Pointer to memory from where the value should be loaded,
|
||||||
|
* may have any alignment.
|
||||||
|
* @param lim The maximum number of elements to load, >0.
|
||||||
|
* @return float8 value loaded from the specified memory location, with
|
||||||
|
* elements beyond "lim" set to 0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static float8 loadu( const float* const p, const int lim )
|
||||||
|
{
|
||||||
|
__m128 lo;
|
||||||
|
__m128 hi;
|
||||||
|
|
||||||
|
if( lim > 4 )
|
||||||
|
{
|
||||||
|
lo = _mm_loadu_ps( p );
|
||||||
|
hi = loadu4( p + 4, lim - 4 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lo = loadu4( p, lim );
|
||||||
|
hi = _mm_setzero_ps();
|
||||||
|
}
|
||||||
|
|
||||||
|
return( _mm256_insertf128_ps( _mm256_castps128_ps256( lo ), hi, 1 ));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function stores *this value to the specified memory location.
|
||||||
|
*
|
||||||
|
* @param[out] p Output memory location, should be 32-byte aligned.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void store( float* const p ) const
|
||||||
|
{
|
||||||
|
_mm256_store_ps( p, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function stores *this value to the specified memory location.
|
||||||
|
*
|
||||||
|
* @param[out] p Output memory location, may have any alignment.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void storeu( float* const p ) const
|
||||||
|
{
|
||||||
|
_mm256_storeu_ps( p, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function stores "lim" lower elements of *this value to the specified
|
||||||
|
* memory location.
|
||||||
|
*
|
||||||
|
* @param[out] p Output memory location, may have any alignment.
|
||||||
|
* @param lim The number of lower elements to store, >0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void storeu( float* p, int lim ) const
|
||||||
|
{
|
||||||
|
__m128 v;
|
||||||
|
|
||||||
|
if( lim > 4 )
|
||||||
|
{
|
||||||
|
_mm_storeu_ps( p, _mm256_extractf128_ps( value, 0 ));
|
||||||
|
v = _mm256_extractf128_ps( value, 1 );
|
||||||
|
p += 4;
|
||||||
|
lim -= 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
v = _mm256_extractf128_ps( value, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( lim > 2 )
|
||||||
|
{
|
||||||
|
if( lim > 3 )
|
||||||
|
{
|
||||||
|
_mm_storeu_ps( p, v );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_mm_storel_pi( (__m64*) p, v );
|
||||||
|
_mm_store_ss( p + 2, _mm_movehl_ps( v, v ));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( lim == 2 )
|
||||||
|
{
|
||||||
|
_mm_storel_pi( (__m64*) p, v );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_mm_store_ss( p, v );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float8& operator += ( const float8& s )
|
||||||
|
{
|
||||||
|
value = _mm256_add_ps( value, s.value );
|
||||||
|
return( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
float8& operator -= ( const float8& s )
|
||||||
|
{
|
||||||
|
value = _mm256_sub_ps( value, s.value );
|
||||||
|
return( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
float8& operator *= ( const float8& s )
|
||||||
|
{
|
||||||
|
value = _mm256_mul_ps( value, s.value );
|
||||||
|
return( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
float8& operator /= ( const float8& s )
|
||||||
|
{
|
||||||
|
value = _mm256_div_ps( value, s.value );
|
||||||
|
return( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
float8 operator + ( const float8& s ) const
|
||||||
|
{
|
||||||
|
return( _mm256_add_ps( value, s.value ));
|
||||||
|
}
|
||||||
|
|
||||||
|
float8 operator - ( const float8& s ) const
|
||||||
|
{
|
||||||
|
return( _mm256_sub_ps( value, s.value ));
|
||||||
|
}
|
||||||
|
|
||||||
|
float8 operator * ( const float8& s ) const
|
||||||
|
{
|
||||||
|
return( _mm256_mul_ps( value, s.value ));
|
||||||
|
}
|
||||||
|
|
||||||
|
float8 operator / ( const float8& s ) const
|
||||||
|
{
|
||||||
|
return( _mm256_div_ps( value, s.value ));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Horizontal sum of elements.
|
||||||
|
*/
|
||||||
|
|
||||||
|
float hadd() const
|
||||||
|
{
|
||||||
|
__m128 v = _mm_add_ps( _mm256_extractf128_ps( value, 0 ),
|
||||||
|
_mm256_extractf128_ps( value, 1 ));
|
||||||
|
|
||||||
|
v = _mm_hadd_ps( v, v );
|
||||||
|
v = _mm_hadd_ps( v, v );
|
||||||
|
return( _mm_cvtss_f32( v ));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function performs in-place addition of a value located in memory and
|
||||||
|
* the specified value.
|
||||||
|
*
|
||||||
|
* @param p Pointer to value where addition happens. May be unaligned.
|
||||||
|
* @param v Value to add.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void addu( float* const p, const float8& v )
|
||||||
|
{
|
||||||
|
( loadu( p ) + v ).storeu( p );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function performs in-place addition of a value located in memory and
|
||||||
|
* the specified value. Limited to the specfied number of elements.
|
||||||
|
*
|
||||||
|
* @param p Pointer to value where addition happens. May be unaligned.
|
||||||
|
* @param v Value to add.
|
||||||
|
* @param lim The element number limit, >0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void addu( float* const p, const float8& v, const int lim )
|
||||||
|
{
|
||||||
|
( loadu( p, lim ) + v ).storeu( p, lim );
|
||||||
|
}
|
||||||
|
|
||||||
|
__m256 value; ///< Packed value of 8 floats.
|
||||||
|
///<
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @param p Pointer to memory from where the value should be loaded,
|
||||||
|
* may have any alignment.
|
||||||
|
* @param lim The maximum number of elements to load, >0.
|
||||||
|
* @return __m128 value loaded from the specified memory location, with
|
||||||
|
* elements beyond "lim" set to 0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static __m128 loadu4( const float* const p, const int lim )
|
||||||
|
{
|
||||||
|
if( lim > 2 )
|
||||||
|
{
|
||||||
|
if( lim > 3 )
|
||||||
|
{
|
||||||
|
return( _mm_loadu_ps( p ));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return( _mm_set_ps( 0.0f, p[ 2 ], p[ 1 ], p[ 0 ]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( lim == 2 )
|
||||||
|
{
|
||||||
|
return( _mm_set_ps( 0.0f, 0.0f, p[ 1 ], p[ 0 ]));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return( _mm_load_ss( p ));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SIMD rounding function, exact result.
|
||||||
|
*
|
||||||
|
* @param v Value to round.
|
||||||
|
* @return Rounded SIMD value.
|
||||||
|
*/
|
||||||
|
|
||||||
|
inline float8 round( const float8& v )
|
||||||
|
{
|
||||||
|
return( _mm256_round_ps( v.value,
|
||||||
|
( _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC )));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SIMD function "clamps" (clips) the specified packed values so that they are
|
||||||
|
* not lesser than "minv", and not greater than "maxv".
|
||||||
|
*
|
||||||
|
* @param Value Value to clamp.
|
||||||
|
* @param minv Minimal allowed value.
|
||||||
|
* @param maxv Maximal allowed value.
|
||||||
|
* @return The clamped value.
|
||||||
|
*/
|
||||||
|
|
||||||
|
inline float8 clamp( const float8& Value, const float8& minv,
|
||||||
|
const float8& maxv )
|
||||||
|
{
|
||||||
|
return( _mm256_min_ps( _mm256_max_ps( Value.value, minv.value ),
|
||||||
|
maxv.value ));
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef fpclass_def_dil< float, avir :: float8 > fpclass_float8_dil; ///<
|
||||||
|
///< Class that can be used as the "fpclass" template parameter of the
|
||||||
|
///< avir::CImageResizer class to perform calculation using
|
||||||
|
///< de-interleaved SIMD algorithm, using SIMD float8 type.
|
||||||
|
///<
|
||||||
|
|
||||||
|
} // namespace avir
|
||||||
|
|
||||||
|
#endif // AVIR_FLOAT8_AVX_INCLUDED
|
397
Modules/gin/3rdparty/muParser/muParser.cpp
vendored
Executable file
397
Modules/gin/3rdparty/muParser/muParser.cpp
vendored
Executable file
|
@ -0,0 +1,397 @@
|
||||||
|
/*
|
||||||
|
__________
|
||||||
|
_____ __ __\______ \_____ _______ ______ ____ _______
|
||||||
|
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
||||||
|
| Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/
|
||||||
|
|__|_| /|____/ |____| (____ /|__| /____ > \___ >|__|
|
||||||
|
\/ \/ \/ \/
|
||||||
|
|
||||||
|
Copyright (C) 2013 Ingo Berg
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
without restriction, including without limitation the rights to use, copy, modify,
|
||||||
|
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||||
|
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "muParser.h"
|
||||||
|
#include "muParserTemplateMagic.h"
|
||||||
|
|
||||||
|
//--- Standard includes ------------------------------------------------------------------------
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
|
/** \brief Pi (what else?). */
|
||||||
|
#define PARSER_CONST_PI 3.141592653589793238462643
|
||||||
|
|
||||||
|
/** \brief The Eulerian number. */
|
||||||
|
#define PARSER_CONST_E 2.718281828459045235360287
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
\brief Implementation of the standard floating point parser.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** \brief Namespace for mathematical applications. */
|
||||||
|
namespace mu
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
// Trigonometric function
|
||||||
|
value_type Parser::Sin(SParam, value_type v) { return MathImpl<value_type>::Sin(v); }
|
||||||
|
value_type Parser::Cos(SParam, value_type v) { return MathImpl<value_type>::Cos(v); }
|
||||||
|
value_type Parser::Tan(SParam, value_type v) { return MathImpl<value_type>::Tan(v); }
|
||||||
|
value_type Parser::ASin(SParam, value_type v) { return MathImpl<value_type>::ASin(v); }
|
||||||
|
value_type Parser::ACos(SParam, value_type v) { return MathImpl<value_type>::ACos(v); }
|
||||||
|
value_type Parser::ATan(SParam, value_type v) { return MathImpl<value_type>::ATan(v); }
|
||||||
|
value_type Parser::ATan2(SParam, value_type v1, value_type v2) { return MathImpl<value_type>::ATan2(v1, v2); }
|
||||||
|
value_type Parser::Sinh(SParam, value_type v) { return MathImpl<value_type>::Sinh(v); }
|
||||||
|
value_type Parser::Cosh(SParam, value_type v) { return MathImpl<value_type>::Cosh(v); }
|
||||||
|
value_type Parser::Tanh(SParam, value_type v) { return MathImpl<value_type>::Tanh(v); }
|
||||||
|
value_type Parser::ASinh(SParam, value_type v) { return MathImpl<value_type>::ASinh(v); }
|
||||||
|
value_type Parser::ACosh(SParam, value_type v) { return MathImpl<value_type>::ACosh(v); }
|
||||||
|
value_type Parser::ATanh(SParam, value_type v) { return MathImpl<value_type>::ATanh(v); }
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
// Logarithm functions
|
||||||
|
|
||||||
|
// Logarithm base 2
|
||||||
|
value_type Parser::Log2(SParam, value_type v)
|
||||||
|
{
|
||||||
|
#ifdef MUP_MATH_EXCEPTIONS
|
||||||
|
if (v<=0)
|
||||||
|
throw ParserError(ecDOMAIN_ERROR, _T("Log2"));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return MathImpl<value_type>::Log2(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logarithm base 10
|
||||||
|
value_type Parser::Log10(SParam, value_type v)
|
||||||
|
{
|
||||||
|
#ifdef MUP_MATH_EXCEPTIONS
|
||||||
|
if (v<=0)
|
||||||
|
throw ParserError(ecDOMAIN_ERROR, _T("Log10"));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return MathImpl<value_type>::Log10(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logarithm base e (natural logarithm)
|
||||||
|
value_type Parser::Ln(SParam, value_type v)
|
||||||
|
{
|
||||||
|
#ifdef MUP_MATH_EXCEPTIONS
|
||||||
|
if (v<=0)
|
||||||
|
throw ParserError(ecDOMAIN_ERROR, _T("Ln"));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return MathImpl<value_type>::Log(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
// misc
|
||||||
|
value_type Parser::Exp(SParam, value_type v) { return MathImpl<value_type>::Exp(v); }
|
||||||
|
value_type Parser::Abs(SParam, value_type v) { return MathImpl<value_type>::Abs(v); }
|
||||||
|
value_type Parser::Sqrt(SParam, value_type v)
|
||||||
|
{
|
||||||
|
#ifdef MUP_MATH_EXCEPTIONS
|
||||||
|
if (v<0)
|
||||||
|
throw ParserError(ecDOMAIN_ERROR, _T("sqrt"));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return MathImpl<value_type>::Sqrt(v);
|
||||||
|
}
|
||||||
|
value_type Parser::Rint(SParam, value_type v) { return MathImpl<value_type>::Rint(v); }
|
||||||
|
value_type Parser::Sign(SParam, value_type v) { return MathImpl<value_type>::Sign(v); }
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Callback for the unary minus operator.
|
||||||
|
\param v The value to negate
|
||||||
|
\return -v
|
||||||
|
*/
|
||||||
|
value_type Parser::UnaryMinus(SParam, value_type v)
|
||||||
|
{
|
||||||
|
return -v;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Callback for the unary minus operator.
|
||||||
|
\param v The value to negate
|
||||||
|
\return -v
|
||||||
|
*/
|
||||||
|
value_type Parser::UnaryPlus(SParam, value_type v)
|
||||||
|
{
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Callback for adding multiple values.
|
||||||
|
\param [in] a_afArg Vector with the function arguments
|
||||||
|
\param [in] a_iArgc The size of a_afArg
|
||||||
|
*/
|
||||||
|
value_type Parser::Sum(SParam, const value_type *a_afArg, int a_iArgc)
|
||||||
|
{
|
||||||
|
if (!a_iArgc)
|
||||||
|
throw exception_type(_T("too few arguments for function sum."));
|
||||||
|
|
||||||
|
value_type fRes=0;
|
||||||
|
for (int i=0; i<a_iArgc; ++i) fRes += a_afArg[i];
|
||||||
|
return fRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Callback for averaging multiple values.
|
||||||
|
\param [in] a_afArg Vector with the function arguments
|
||||||
|
\param [in] a_iArgc The size of a_afArg
|
||||||
|
*/
|
||||||
|
value_type Parser::Avg(SParam, const value_type *a_afArg, int a_iArgc)
|
||||||
|
{
|
||||||
|
if (!a_iArgc)
|
||||||
|
throw exception_type(_T("too few arguments for function sum."));
|
||||||
|
|
||||||
|
value_type fRes=0;
|
||||||
|
for (int i=0; i<a_iArgc; ++i) fRes += a_afArg[i];
|
||||||
|
return fRes/(value_type)a_iArgc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Callback for determining the minimum value out of a vector.
|
||||||
|
\param [in] a_afArg Vector with the function arguments
|
||||||
|
\param [in] a_iArgc The size of a_afArg
|
||||||
|
*/
|
||||||
|
value_type Parser::Min(SParam, const value_type *a_afArg, int a_iArgc)
|
||||||
|
{
|
||||||
|
if (!a_iArgc)
|
||||||
|
throw exception_type(_T("too few arguments for function min."));
|
||||||
|
|
||||||
|
value_type fRes=a_afArg[0];
|
||||||
|
for (int i=0; i<a_iArgc; ++i)
|
||||||
|
fRes = std::min(fRes, a_afArg[i]);
|
||||||
|
|
||||||
|
return fRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Callback for determining the maximum value out of a vector.
|
||||||
|
\param [in] a_afArg Vector with the function arguments
|
||||||
|
\param [in] a_iArgc The size of a_afArg
|
||||||
|
*/
|
||||||
|
value_type Parser::Max(SParam, const value_type *a_afArg, int a_iArgc)
|
||||||
|
{
|
||||||
|
if (!a_iArgc)
|
||||||
|
throw exception_type(_T("too few arguments for function min."));
|
||||||
|
|
||||||
|
value_type fRes=a_afArg[0];
|
||||||
|
for (int i=0; i<a_iArgc; ++i) fRes = std::max(fRes, a_afArg[i]);
|
||||||
|
|
||||||
|
return fRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Default value recognition callback.
|
||||||
|
\param [in] a_szExpr Pointer to the expression
|
||||||
|
\param [in, out] a_iPos Pointer to an index storing the current position within the expression
|
||||||
|
\param [out] a_fVal Pointer where the value should be stored in case one is found.
|
||||||
|
\return 1 if a value was found 0 otherwise.
|
||||||
|
*/
|
||||||
|
int Parser::IsVal(SParam, const char_type* a_szExpr, int *a_iPos, value_type *a_fVal)
|
||||||
|
{
|
||||||
|
value_type fVal(0);
|
||||||
|
|
||||||
|
stringstream_type stream(a_szExpr);
|
||||||
|
stream.seekg(0); // todo: check if this really is necessary
|
||||||
|
stream.imbue(Parser::s_locale);
|
||||||
|
stream >> fVal;
|
||||||
|
stringstream_type::pos_type iEnd = stream.tellg(); // Position after reading
|
||||||
|
|
||||||
|
if (iEnd==(stringstream_type::pos_type)-1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
*a_iPos += (int)iEnd;
|
||||||
|
*a_fVal = fVal;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Constructor.
|
||||||
|
|
||||||
|
Call ParserBase class constructor and trigger Function, Operator and Constant initialization.
|
||||||
|
*/
|
||||||
|
Parser::Parser()
|
||||||
|
:ParserBase()
|
||||||
|
{
|
||||||
|
AddValIdent(IsVal);
|
||||||
|
|
||||||
|
InitCharSets();
|
||||||
|
InitFun();
|
||||||
|
InitConst();
|
||||||
|
InitOprt();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Define the character sets.
|
||||||
|
\sa DefineNameChars, DefineOprtChars, DefineInfixOprtChars
|
||||||
|
|
||||||
|
This function is used for initializing the default character sets that define
|
||||||
|
the characters to be useable in function and variable names and operators.
|
||||||
|
*/
|
||||||
|
void Parser::InitCharSets()
|
||||||
|
{
|
||||||
|
DefineNameChars( _T("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") );
|
||||||
|
DefineOprtChars( _T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-*^/?<>=#!$%&|~'_{}") );
|
||||||
|
DefineInfixOprtChars( _T("/+-*^?<>=#!$%&|~'_") );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Initialize the default functions. */
|
||||||
|
void Parser::InitFun()
|
||||||
|
{
|
||||||
|
if (mu::TypeInfo<mu::value_type>::IsInteger())
|
||||||
|
{
|
||||||
|
// When setting MUP_BASETYPE to an integer type
|
||||||
|
// Place functions for dealing with integer values here
|
||||||
|
// ...
|
||||||
|
// ...
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// trigonometric functions
|
||||||
|
DefineFun(_T("sin"), Sin);
|
||||||
|
DefineFun(_T("cos"), Cos);
|
||||||
|
DefineFun(_T("tan"), Tan);
|
||||||
|
// arcus functions
|
||||||
|
DefineFun(_T("asin"), ASin);
|
||||||
|
DefineFun(_T("acos"), ACos);
|
||||||
|
DefineFun(_T("atan"), ATan);
|
||||||
|
DefineFun(_T("atan2"), ATan2);
|
||||||
|
// hyperbolic functions
|
||||||
|
DefineFun(_T("sinh"), Sinh);
|
||||||
|
DefineFun(_T("cosh"), Cosh);
|
||||||
|
DefineFun(_T("tanh"), Tanh);
|
||||||
|
// arcus hyperbolic functions
|
||||||
|
DefineFun(_T("asinh"), ASinh);
|
||||||
|
DefineFun(_T("acosh"), ACosh);
|
||||||
|
DefineFun(_T("atanh"), ATanh);
|
||||||
|
// Logarithm functions
|
||||||
|
DefineFun(_T("log2"), Log2);
|
||||||
|
DefineFun(_T("log10"), Log10);
|
||||||
|
DefineFun(_T("log"), Ln);
|
||||||
|
DefineFun(_T("ln"), Ln);
|
||||||
|
// misc
|
||||||
|
DefineFun(_T("exp"), Exp);
|
||||||
|
DefineFun(_T("sqrt"), Sqrt);
|
||||||
|
DefineFun(_T("sign"), Sign);
|
||||||
|
DefineFun(_T("rint"), Rint);
|
||||||
|
DefineFun(_T("abs"), Abs);
|
||||||
|
// Functions with variable number of arguments
|
||||||
|
DefineFun(_T("sum"), Sum);
|
||||||
|
DefineFun(_T("avg"), Avg);
|
||||||
|
DefineFun(_T("min"), Min);
|
||||||
|
DefineFun(_T("max"), Max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Initialize constants.
|
||||||
|
|
||||||
|
By default the parser recognizes two constants. Pi ("pi") and the Eulerian
|
||||||
|
number ("_e").
|
||||||
|
*/
|
||||||
|
void Parser::InitConst()
|
||||||
|
{
|
||||||
|
DefineConst(_T("_pi"), (value_type)PARSER_CONST_PI);
|
||||||
|
DefineConst(_T("_e"), (value_type)PARSER_CONST_E);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Initialize operators.
|
||||||
|
|
||||||
|
By default only the unary minus operator is added.
|
||||||
|
*/
|
||||||
|
void Parser::InitOprt()
|
||||||
|
{
|
||||||
|
DefineInfixOprt(_T("-"), UnaryMinus);
|
||||||
|
DefineInfixOprt(_T("+"), UnaryPlus);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
void Parser::OnDetectVar(string_type * /*pExpr*/, int & /*nStart*/, int & /*nEnd*/)
|
||||||
|
{
|
||||||
|
// this is just sample code to illustrate modifying variable names on the fly.
|
||||||
|
// I'm not sure anyone really needs such a feature...
|
||||||
|
/*
|
||||||
|
|
||||||
|
|
||||||
|
string sVar(pExpr->begin()+nStart, pExpr->begin()+nEnd);
|
||||||
|
string sRepl = std::string("_") + sVar + "_";
|
||||||
|
|
||||||
|
int nOrigVarEnd = nEnd;
|
||||||
|
cout << "variable detected!\n";
|
||||||
|
cout << " Expr: " << *pExpr << "\n";
|
||||||
|
cout << " Start: " << nStart << "\n";
|
||||||
|
cout << " End: " << nEnd << "\n";
|
||||||
|
cout << " Var: \"" << sVar << "\"\n";
|
||||||
|
cout << " Repl: \"" << sRepl << "\"\n";
|
||||||
|
nEnd = nStart + sRepl.length();
|
||||||
|
cout << " End: " << nEnd << "\n";
|
||||||
|
pExpr->replace(pExpr->begin()+nStart, pExpr->begin()+nOrigVarEnd, sRepl);
|
||||||
|
cout << " New expr: " << *pExpr << "\n";
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Numerically differentiate with regard to a variable.
|
||||||
|
\param [in] a_Var Pointer to the differentiation variable.
|
||||||
|
\param [in] a_fPos Position at which the differentiation should take place.
|
||||||
|
\param [in] a_fEpsilon Epsilon used for the numerical differentiation.
|
||||||
|
|
||||||
|
Numerical differentiation uses a 5 point operator yielding a 4th order
|
||||||
|
formula. The default value for epsilon is 0.00074 which is
|
||||||
|
numeric_limits<double>::epsilon() ^ (1/5) as suggested in the muparser
|
||||||
|
forum:
|
||||||
|
|
||||||
|
http://sourceforge.net/forum/forum.php?thread_id=1994611&forum_id=462843
|
||||||
|
*/
|
||||||
|
value_type Parser::Diff(value_type *a_Var,
|
||||||
|
value_type a_fPos,
|
||||||
|
value_type a_fEpsilon) const
|
||||||
|
{
|
||||||
|
value_type fRes(0),
|
||||||
|
fBuf(*a_Var),
|
||||||
|
f[4] = {0,0,0,0},
|
||||||
|
fEpsilon(a_fEpsilon);
|
||||||
|
|
||||||
|
// Backwards compatible calculation of epsilon inc case the user doesn't provide
|
||||||
|
// his own epsilon
|
||||||
|
if (fEpsilon==0)
|
||||||
|
fEpsilon = (a_fPos==0) ? (value_type)1e-10 : (value_type)1e-7 * a_fPos;
|
||||||
|
|
||||||
|
*a_Var = a_fPos+2 * fEpsilon; f[0] = Eval();
|
||||||
|
*a_Var = a_fPos+1 * fEpsilon; f[1] = Eval();
|
||||||
|
*a_Var = a_fPos-1 * fEpsilon; f[2] = Eval();
|
||||||
|
*a_Var = a_fPos-2 * fEpsilon; f[3] = Eval();
|
||||||
|
*a_Var = fBuf; // restore variable
|
||||||
|
|
||||||
|
fRes = (-f[0] + 8*f[1] - 8*f[2] + f[3]) / (12*fEpsilon);
|
||||||
|
return fRes;
|
||||||
|
}
|
||||||
|
} // namespace mu
|
114
Modules/gin/3rdparty/muParser/muParser.h
vendored
Executable file
114
Modules/gin/3rdparty/muParser/muParser.h
vendored
Executable file
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
__________
|
||||||
|
_____ __ __\______ \_____ _______ ______ ____ _______
|
||||||
|
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
||||||
|
| Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/
|
||||||
|
|__|_| /|____/ |____| (____ /|__| /____ > \___ >|__|
|
||||||
|
\/ \/ \/ \/
|
||||||
|
Copyright (C) 2013 Ingo Berg
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
without restriction, including without limitation the rights to use, copy, modify,
|
||||||
|
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||||
|
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef MU_PARSER_H
|
||||||
|
#define MU_PARSER_H
|
||||||
|
|
||||||
|
//--- Standard includes ------------------------------------------------------------------------
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
//--- Parser includes --------------------------------------------------------------------------
|
||||||
|
#include "muParserBase.h"
|
||||||
|
#include "muParserTemplateMagic.h"
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
\brief Definition of the standard floating point parser.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace mu
|
||||||
|
{
|
||||||
|
/** \brief Mathematical expressions parser.
|
||||||
|
|
||||||
|
Standard implementation of the mathematical expressions parser.
|
||||||
|
Can be used as a reference implementation for subclassing the parser.
|
||||||
|
|
||||||
|
<small>
|
||||||
|
(C) 2011 Ingo Berg<br>
|
||||||
|
muparser(at)beltoforion.de
|
||||||
|
</small>
|
||||||
|
*/
|
||||||
|
/* final */ class Parser : public ParserBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
Parser();
|
||||||
|
|
||||||
|
virtual void InitCharSets();
|
||||||
|
virtual void InitFun();
|
||||||
|
virtual void InitConst();
|
||||||
|
virtual void InitOprt();
|
||||||
|
virtual void OnDetectVar(string_type *pExpr, int &nStart, int &nEnd);
|
||||||
|
|
||||||
|
value_type Diff(value_type *a_Var,
|
||||||
|
value_type a_fPos,
|
||||||
|
value_type a_fEpsilon = 0) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
// Trigonometric functions
|
||||||
|
static value_type Sin(SParam, value_type);
|
||||||
|
static value_type Cos(SParam, value_type);
|
||||||
|
static value_type Tan(SParam, value_type);
|
||||||
|
static value_type Tan2(SParam, value_type, value_type);
|
||||||
|
// arcus functions
|
||||||
|
static value_type ASin(SParam, value_type);
|
||||||
|
static value_type ACos(SParam, value_type);
|
||||||
|
static value_type ATan(SParam, value_type);
|
||||||
|
static value_type ATan2(SParam, value_type, value_type);
|
||||||
|
|
||||||
|
// hyperbolic functions
|
||||||
|
static value_type Sinh(SParam, value_type);
|
||||||
|
static value_type Cosh(SParam, value_type);
|
||||||
|
static value_type Tanh(SParam, value_type);
|
||||||
|
// arcus hyperbolic functions
|
||||||
|
static value_type ASinh(SParam, value_type);
|
||||||
|
static value_type ACosh(SParam, value_type);
|
||||||
|
static value_type ATanh(SParam, value_type);
|
||||||
|
// Logarithm functions
|
||||||
|
static value_type Log2(SParam, value_type); // Logarithm Base 2
|
||||||
|
static value_type Log10(SParam, value_type); // Logarithm Base 10
|
||||||
|
static value_type Ln(SParam, value_type); // Logarithm Base e (natural logarithm)
|
||||||
|
// misc
|
||||||
|
static value_type Exp(SParam, value_type);
|
||||||
|
static value_type Abs(SParam, value_type);
|
||||||
|
static value_type Sqrt(SParam, value_type);
|
||||||
|
static value_type Rint(SParam, value_type);
|
||||||
|
static value_type Sign(SParam, value_type);
|
||||||
|
|
||||||
|
// Prefix operators
|
||||||
|
// !!! Unary Minus is a MUST if you want to use negative signs !!!
|
||||||
|
static value_type UnaryMinus(SParam, value_type);
|
||||||
|
static value_type UnaryPlus(SParam, value_type);
|
||||||
|
|
||||||
|
// Functions with variable number of arguments
|
||||||
|
static value_type Sum(SParam, const value_type*, int); // sum
|
||||||
|
static value_type Avg(SParam, const value_type*, int); // mean value
|
||||||
|
static value_type Min(SParam, const value_type*, int); // minimum
|
||||||
|
static value_type Max(SParam, const value_type*, int); // maximum
|
||||||
|
|
||||||
|
static int IsVal(SParam, const char_type* a_szExpr, int *a_iPos, value_type *a_fVal);
|
||||||
|
};
|
||||||
|
} // namespace mu
|
||||||
|
|
||||||
|
#endif
|
1805
Modules/gin/3rdparty/muParser/muParserBase.cpp
vendored
Executable file
1805
Modules/gin/3rdparty/muParser/muParserBase.cpp
vendored
Executable file
File diff suppressed because it is too large
Load diff
324
Modules/gin/3rdparty/muParser/muParserBase.h
vendored
Executable file
324
Modules/gin/3rdparty/muParser/muParserBase.h
vendored
Executable file
|
@ -0,0 +1,324 @@
|
||||||
|
/*
|
||||||
|
__________
|
||||||
|
_____ __ __\______ \_____ _______ ______ ____ _______
|
||||||
|
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
||||||
|
| Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/
|
||||||
|
|__|_| /|____/ |____| (____ /|__| /____ > \___ >|__|
|
||||||
|
\/ \/ \/ \/
|
||||||
|
Copyright (C) 2013 Ingo Berg
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
without restriction, including without limitation the rights to use, copy, modify,
|
||||||
|
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||||
|
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef MU_PARSER_BASE_H
|
||||||
|
#define MU_PARSER_BASE_H
|
||||||
|
|
||||||
|
//--- Standard includes ------------------------------------------------------------------------
|
||||||
|
#include <cmath>
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <locale>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
//--- Parser includes --------------------------------------------------------------------------
|
||||||
|
#include "muParserDef.h"
|
||||||
|
#include "muParserStack.h"
|
||||||
|
#include "muParserTokenReader.h"
|
||||||
|
#include "muParserBytecode.h"
|
||||||
|
#include "muParserError.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4310)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace mu
|
||||||
|
{
|
||||||
|
/** \file
|
||||||
|
\brief This file contains the class definition of the muparser engine.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
/** \brief Mathematical expressions parser (base parser engine).
|
||||||
|
\author (C) 2013 Ingo Berg
|
||||||
|
|
||||||
|
This is the implementation of a bytecode based mathematical expressions parser.
|
||||||
|
The formula will be parsed from string and converted into a bytecode.
|
||||||
|
Future calculations will be done with the bytecode instead the formula string
|
||||||
|
resulting in a significant performance increase.
|
||||||
|
Complementary to a set of internally implemented functions the parser is able to handle
|
||||||
|
user defined functions and variables.
|
||||||
|
*/
|
||||||
|
class ParserBase
|
||||||
|
{
|
||||||
|
friend class ParserTokenReader;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** \brief Typedef for the parse functions.
|
||||||
|
|
||||||
|
The parse function do the actual work. The parser exchanges
|
||||||
|
the function pointer to the parser function depending on
|
||||||
|
which state it is in. (i.e. bytecode parser vs. string parser)
|
||||||
|
*/
|
||||||
|
typedef value_type (ParserBase::*ParseFunction)() const;
|
||||||
|
|
||||||
|
/** \brief Type used for storing an array of values. */
|
||||||
|
typedef std::vector<value_type> valbuf_type;
|
||||||
|
|
||||||
|
/** \brief Type for a vector of strings. */
|
||||||
|
typedef std::vector<string_type> stringbuf_type;
|
||||||
|
|
||||||
|
/** \brief Typedef for the token reader. */
|
||||||
|
typedef ParserTokenReader token_reader_type;
|
||||||
|
|
||||||
|
/** \brief Type used for parser tokens. */
|
||||||
|
typedef ParserToken<value_type, string_type> token_type;
|
||||||
|
|
||||||
|
/** \brief Maximum number of threads spawned by OpenMP when using the bulk mode. */
|
||||||
|
static const int s_MaxNumOpenMPThreads;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** \brief Type of the error class.
|
||||||
|
|
||||||
|
Included for backwards compatibility.
|
||||||
|
*/
|
||||||
|
typedef ParserError exception_type;
|
||||||
|
|
||||||
|
static void EnableDebugDump(bool bDumpCmd, bool bDumpStack);
|
||||||
|
|
||||||
|
ParserBase();
|
||||||
|
ParserBase(const ParserBase &a_Parser);
|
||||||
|
ParserBase& operator=(const ParserBase &a_Parser);
|
||||||
|
|
||||||
|
virtual ~ParserBase();
|
||||||
|
|
||||||
|
value_type Eval() const;
|
||||||
|
value_type* Eval(int &nStackSize) const;
|
||||||
|
void Eval(value_type *results, int nBulkSize);
|
||||||
|
|
||||||
|
int GetNumResults() const;
|
||||||
|
|
||||||
|
void SetExpr(const string_type &a_sExpr);
|
||||||
|
void SetVarFactory(facfun_type a_pFactory, void *pUserData = nullptr);
|
||||||
|
|
||||||
|
void SetDecSep(char_type cDecSep);
|
||||||
|
void SetThousandsSep(char_type cThousandsSep = 0);
|
||||||
|
void ResetLocale();
|
||||||
|
|
||||||
|
void EnableOptimizer(bool a_bIsOn=true);
|
||||||
|
void EnableBuiltInOprt(bool a_bIsOn=true);
|
||||||
|
|
||||||
|
bool HasBuiltInOprt() const;
|
||||||
|
void AddValIdent(identfun_type a_pCallback);
|
||||||
|
|
||||||
|
/** \fn void mu::ParserBase::DefineFun(const string_type &a_strName, fun_type0 a_pFun, bool a_bAllowOpt = true)
|
||||||
|
\brief Define a parser function without arguments.
|
||||||
|
\param a_strName Name of the function
|
||||||
|
\param a_pFun Pointer to the callback function
|
||||||
|
\param a_bAllowOpt A flag indicating this function may be optimized
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
void DefineFun(const string_type &a_strName, T a_pFun, void* param = nullptr, bool a_bAllowOpt = true)
|
||||||
|
{
|
||||||
|
AddCallback( a_strName, ParserCallback(a_pFun, param, a_bAllowOpt), m_FunDef, ValidNameChars() );
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefineOprt(const string_type &a_strName,
|
||||||
|
fun_type2 a_pFun,
|
||||||
|
unsigned a_iPri=0,
|
||||||
|
EOprtAssociativity a_eAssociativity = oaLEFT,
|
||||||
|
bool a_bAllowOpt = false);
|
||||||
|
void DefineConst(const string_type &a_sName, value_type a_fVal);
|
||||||
|
void DefineStrConst(const string_type &a_sName, const string_type &a_strVal);
|
||||||
|
void DefineVar(const string_type &a_sName, value_type *a_fVar);
|
||||||
|
void DefinePostfixOprt(const string_type &a_strFun, fun_type1 a_pOprt, bool a_bAllowOpt=true);
|
||||||
|
void DefineInfixOprt(const string_type &a_strName, fun_type1 a_pOprt, int a_iPrec=prINFIX, bool a_bAllowOpt=true);
|
||||||
|
|
||||||
|
// Clear user defined variables, constants or functions
|
||||||
|
void ClearVar();
|
||||||
|
void ClearFun();
|
||||||
|
void ClearConst();
|
||||||
|
void ClearInfixOprt();
|
||||||
|
void ClearPostfixOprt();
|
||||||
|
void ClearOprt();
|
||||||
|
|
||||||
|
void RemoveVar(const string_type &a_strVarName);
|
||||||
|
const varmap_type& GetUsedVar() const;
|
||||||
|
const varmap_type& GetVar() const;
|
||||||
|
const valmap_type& GetConst() const;
|
||||||
|
const string_type& GetExpr() const;
|
||||||
|
const funmap_type& GetFunDef() const;
|
||||||
|
string_type GetVersion(EParserVersionInfo eInfo = pviFULL) const;
|
||||||
|
|
||||||
|
const char_type ** GetOprtDef() const;
|
||||||
|
void DefineNameChars(const char_type *a_szCharset);
|
||||||
|
void DefineOprtChars(const char_type *a_szCharset);
|
||||||
|
void DefineInfixOprtChars(const char_type *a_szCharset);
|
||||||
|
|
||||||
|
const char_type* ValidNameChars() const;
|
||||||
|
const char_type* ValidOprtChars() const;
|
||||||
|
const char_type* ValidInfixOprtChars() const;
|
||||||
|
|
||||||
|
void SetArgSep(char_type cArgSep);
|
||||||
|
char_type GetArgSep() const;
|
||||||
|
|
||||||
|
void Error(EErrorCodes a_iErrc,
|
||||||
|
int a_iPos = (int)mu::string_type::npos,
|
||||||
|
const string_type &a_strTok = string_type() ) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
|
||||||
|
virtual void InitCharSets() = 0;
|
||||||
|
virtual void InitFun() = 0;
|
||||||
|
virtual void InitConst() = 0;
|
||||||
|
virtual void InitOprt() = 0;
|
||||||
|
|
||||||
|
virtual void OnDetectVar(string_type *pExpr, int &nStart, int &nEnd);
|
||||||
|
|
||||||
|
static const char_type *c_DefaultOprt[];
|
||||||
|
static std::locale s_locale; ///< The locale used by the parser
|
||||||
|
static bool g_DbgDumpCmdCode;
|
||||||
|
static bool g_DbgDumpStack;
|
||||||
|
|
||||||
|
/** \brief A facet class used to change decimal and thousands separator. */
|
||||||
|
template<class TChar>
|
||||||
|
class change_dec_sep : public std::numpunct<TChar>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit change_dec_sep(char_type cDecSep, char_type cThousandsSep = 0, int nGroup = 3)
|
||||||
|
:std::numpunct<TChar>()
|
||||||
|
,m_nGroup(nGroup)
|
||||||
|
,m_cDecPoint(cDecSep)
|
||||||
|
,m_cThousandsSep(cThousandsSep)
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
virtual char_type do_decimal_point() const
|
||||||
|
{
|
||||||
|
return m_cDecPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual char_type do_thousands_sep() const
|
||||||
|
{
|
||||||
|
return m_cThousandsSep;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual std::string do_grouping() const
|
||||||
|
{
|
||||||
|
// fix for issue 4: https://code.google.com/p/muparser/issues/detail?id=4
|
||||||
|
// courtesy of Jens Bartsch
|
||||||
|
// original code:
|
||||||
|
// return std::string(1, (char)m_nGroup);
|
||||||
|
// new code:
|
||||||
|
return std::string(1, (char)(m_cThousandsSep > 0 ? m_nGroup : CHAR_MAX));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
int m_nGroup;
|
||||||
|
char_type m_cDecPoint;
|
||||||
|
char_type m_cThousandsSep;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void Assign(const ParserBase &a_Parser);
|
||||||
|
void InitTokenReader();
|
||||||
|
void ReInit() const;
|
||||||
|
|
||||||
|
void AddCallback( const string_type &a_strName,
|
||||||
|
const ParserCallback &a_Callback,
|
||||||
|
funmap_type &a_Storage,
|
||||||
|
const char_type *a_szCharSet );
|
||||||
|
|
||||||
|
void ApplyRemainingOprt(ParserStack<token_type> &a_stOpt,
|
||||||
|
ParserStack<token_type> &a_stVal) const;
|
||||||
|
void ApplyBinOprt(ParserStack<token_type> &a_stOpt,
|
||||||
|
ParserStack<token_type> &a_stVal) const;
|
||||||
|
|
||||||
|
void ApplyIfElse(ParserStack<token_type> &a_stOpt,
|
||||||
|
ParserStack<token_type> &a_stVal) const;
|
||||||
|
|
||||||
|
void ApplyFunc(ParserStack<token_type> &a_stOpt,
|
||||||
|
ParserStack<token_type> &a_stVal,
|
||||||
|
int iArgCount) const;
|
||||||
|
|
||||||
|
token_type ApplyStrFunc(const token_type &a_FunTok,
|
||||||
|
const std::vector<token_type> &a_vArg) const;
|
||||||
|
|
||||||
|
int GetOprtPrecedence(const token_type &a_Tok) const;
|
||||||
|
EOprtAssociativity GetOprtAssociativity(const token_type &a_Tok) const;
|
||||||
|
|
||||||
|
void CreateRPN() const;
|
||||||
|
|
||||||
|
value_type ParseString() const;
|
||||||
|
value_type ParseCmdCode() const;
|
||||||
|
value_type ParseCmdCodeBulk(int nOffset, int nThreadID) const;
|
||||||
|
|
||||||
|
void CheckName(const string_type &a_strName, const string_type &a_CharSet) const;
|
||||||
|
void CheckOprt(const string_type &a_sName,
|
||||||
|
const ParserCallback &a_Callback,
|
||||||
|
const string_type &a_szCharSet) const;
|
||||||
|
|
||||||
|
void StackDump(const ParserStack<token_type > &a_stVal,
|
||||||
|
const ParserStack<token_type > &a_stOprt) const;
|
||||||
|
|
||||||
|
/** \brief Pointer to the parser function.
|
||||||
|
|
||||||
|
Eval() calls the function whose address is stored there.
|
||||||
|
*/
|
||||||
|
mutable ParseFunction m_pParseFormula;
|
||||||
|
mutable ParserByteCode m_vRPN; ///< The Bytecode class.
|
||||||
|
mutable stringbuf_type m_vStringBuf; ///< String buffer, used for storing string function arguments
|
||||||
|
stringbuf_type m_vStringVarBuf;
|
||||||
|
|
||||||
|
std::unique_ptr<token_reader_type> m_pTokenReader; ///< Managed pointer to the token reader object.
|
||||||
|
|
||||||
|
funmap_type m_FunDef; ///< Map of function names and pointers.
|
||||||
|
funmap_type m_PostOprtDef; ///< Postfix operator callbacks
|
||||||
|
funmap_type m_InfixOprtDef; ///< unary infix operator.
|
||||||
|
funmap_type m_OprtDef; ///< Binary operator callbacks
|
||||||
|
valmap_type m_ConstDef; ///< user constants.
|
||||||
|
strmap_type m_StrVarDef; ///< user defined string constants
|
||||||
|
varmap_type m_VarDef; ///< user defind variables.
|
||||||
|
|
||||||
|
bool m_bBuiltInOp; ///< Flag that can be used for switching built in operators on and off
|
||||||
|
|
||||||
|
string_type m_sNameChars; ///< Charset for names
|
||||||
|
string_type m_sOprtChars; ///< Charset for postfix/ binary operator tokens
|
||||||
|
string_type m_sInfixOprtChars; ///< Charset for infix operator tokens
|
||||||
|
|
||||||
|
mutable int m_nIfElseCounter; ///< Internal counter for keeping track of nested if-then-else clauses
|
||||||
|
|
||||||
|
// items merely used for caching state information
|
||||||
|
mutable valbuf_type m_vStackBuffer; ///< This is merely a buffer used for the stack in the cmd parsing routine
|
||||||
|
mutable int m_nFinalResultIdx;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mu
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
604
Modules/gin/3rdparty/muParser/muParserBytecode.cpp
vendored
Executable file
604
Modules/gin/3rdparty/muParser/muParserBytecode.cpp
vendored
Executable file
|
@ -0,0 +1,604 @@
|
||||||
|
/*
|
||||||
|
__________
|
||||||
|
_____ __ __\______ \_____ _______ ______ ____ _______
|
||||||
|
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
||||||
|
| Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/
|
||||||
|
|__|_| /|____/ |____| (____ /|__| /____ > \___ >|__|
|
||||||
|
\/ \/ \/ \/
|
||||||
|
Copyright (C) 2011 Ingo Berg
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
without restriction, including without limitation the rights to use, copy, modify,
|
||||||
|
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||||
|
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "muParserBytecode.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <string>
|
||||||
|
#include <stack>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "muParserDef.h"
|
||||||
|
#include "muParserError.h"
|
||||||
|
#include "muParserToken.h"
|
||||||
|
#include "muParserStack.h"
|
||||||
|
#include "muParserTemplateMagic.h"
|
||||||
|
|
||||||
|
#if __clang__
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wconversion"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace mu
|
||||||
|
{
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Bytecode default constructor. */
|
||||||
|
ParserByteCode::ParserByteCode()
|
||||||
|
:m_iStackPos(0)
|
||||||
|
,m_iMaxStackSize(0)
|
||||||
|
,m_vRPN()
|
||||||
|
,m_bEnableOptimizer(true)
|
||||||
|
{
|
||||||
|
m_vRPN.reserve(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Copy constructor.
|
||||||
|
|
||||||
|
Implemented in Terms of Assign(const ParserByteCode &a_ByteCode)
|
||||||
|
*/
|
||||||
|
ParserByteCode::ParserByteCode(const ParserByteCode &a_ByteCode)
|
||||||
|
{
|
||||||
|
Assign(a_ByteCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Assignment operator.
|
||||||
|
|
||||||
|
Implemented in Terms of Assign(const ParserByteCode &a_ByteCode)
|
||||||
|
*/
|
||||||
|
ParserByteCode& ParserByteCode::operator=(const ParserByteCode &a_ByteCode)
|
||||||
|
{
|
||||||
|
Assign(a_ByteCode);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
void ParserByteCode::EnableOptimizer(bool bStat)
|
||||||
|
{
|
||||||
|
m_bEnableOptimizer = bStat;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Copy state of another object to this.
|
||||||
|
|
||||||
|
\throw nowthrow
|
||||||
|
*/
|
||||||
|
void ParserByteCode::Assign(const ParserByteCode &a_ByteCode)
|
||||||
|
{
|
||||||
|
if (this==&a_ByteCode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_iStackPos = a_ByteCode.m_iStackPos;
|
||||||
|
m_vRPN = a_ByteCode.m_vRPN;
|
||||||
|
m_iMaxStackSize = a_ByteCode.m_iMaxStackSize;
|
||||||
|
m_bEnableOptimizer = a_ByteCode.m_bEnableOptimizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Add a Variable pointer to bytecode.
|
||||||
|
\param a_pVar Pointer to be added.
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
void ParserByteCode::AddVar(value_type *a_pVar)
|
||||||
|
{
|
||||||
|
++m_iStackPos;
|
||||||
|
m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
|
||||||
|
|
||||||
|
// optimization does not apply
|
||||||
|
SToken tok;
|
||||||
|
tok.Cmd = cmVAR;
|
||||||
|
tok.Val.ptr = a_pVar;
|
||||||
|
tok.Val.data = 1;
|
||||||
|
tok.Val.data2 = 0;
|
||||||
|
m_vRPN.push_back(tok);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Add a Variable pointer to bytecode.
|
||||||
|
|
||||||
|
Value entries in byte code consist of:
|
||||||
|
<ul>
|
||||||
|
<li>value array position of the value</li>
|
||||||
|
<li>the operator code according to ParserToken::cmVAL</li>
|
||||||
|
<li>the value stored in #mc_iSizeVal number of bytecode entries.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
\param a_pVal Value to be added.
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
void ParserByteCode::AddVal(value_type a_fVal)
|
||||||
|
{
|
||||||
|
++m_iStackPos;
|
||||||
|
m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
|
||||||
|
|
||||||
|
// If optimization does not apply
|
||||||
|
SToken tok;
|
||||||
|
tok.Cmd = cmVAL;
|
||||||
|
tok.Val.ptr = nullptr;
|
||||||
|
tok.Val.data = 0;
|
||||||
|
tok.Val.data2 = a_fVal;
|
||||||
|
m_vRPN.push_back(tok);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
void ParserByteCode::ConstantFolding(ECmdCode a_Oprt)
|
||||||
|
{
|
||||||
|
std::size_t sz = m_vRPN.size();
|
||||||
|
value_type &x = m_vRPN[sz-2].Val.data2,
|
||||||
|
&y = m_vRPN[sz-1].Val.data2;
|
||||||
|
switch (a_Oprt)
|
||||||
|
{
|
||||||
|
case cmLAND: x = (int)x && (int)y; m_vRPN.pop_back(); break;
|
||||||
|
case cmLOR: x = (int)x || (int)y; m_vRPN.pop_back(); break;
|
||||||
|
case cmLT: x = x < y; m_vRPN.pop_back(); break;
|
||||||
|
case cmGT: x = x > y; m_vRPN.pop_back(); break;
|
||||||
|
case cmLE: x = x <= y; m_vRPN.pop_back(); break;
|
||||||
|
case cmGE: x = x >= y; m_vRPN.pop_back(); break;
|
||||||
|
case cmNEQ: x = x != y; m_vRPN.pop_back(); break;
|
||||||
|
case cmEQ: x = x == y; m_vRPN.pop_back(); break;
|
||||||
|
case cmADD: x = x + y; m_vRPN.pop_back(); break;
|
||||||
|
case cmSUB: x = x - y; m_vRPN.pop_back(); break;
|
||||||
|
case cmMUL: x = x * y; m_vRPN.pop_back(); break;
|
||||||
|
case cmDIV:
|
||||||
|
|
||||||
|
#if defined(MUP_MATH_EXCEPTIONS)
|
||||||
|
if (y==0)
|
||||||
|
throw ParserError(ecDIV_BY_ZERO, _T("0"));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
x = x / y;
|
||||||
|
m_vRPN.pop_back();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmPOW: x = MathImpl<value_type>::Pow(x, y);
|
||||||
|
m_vRPN.pop_back();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
} // switch opcode
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Add an operator identifier to bytecode.
|
||||||
|
|
||||||
|
Operator entries in byte code consist of:
|
||||||
|
<ul>
|
||||||
|
<li>value array position of the result</li>
|
||||||
|
<li>the operator code according to ParserToken::ECmdCode</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
\sa ParserToken::ECmdCode
|
||||||
|
*/
|
||||||
|
void ParserByteCode::AddOp(ECmdCode a_Oprt)
|
||||||
|
{
|
||||||
|
bool bOptimized = false;
|
||||||
|
|
||||||
|
if (m_bEnableOptimizer)
|
||||||
|
{
|
||||||
|
std::size_t sz = m_vRPN.size();
|
||||||
|
|
||||||
|
// Check for foldable constants like:
|
||||||
|
// cmVAL cmVAL cmADD
|
||||||
|
// where cmADD can stand fopr any binary operator applied to
|
||||||
|
// two constant values.
|
||||||
|
if (sz>=2 && m_vRPN[sz-2].Cmd == cmVAL && m_vRPN[sz-1].Cmd == cmVAL)
|
||||||
|
{
|
||||||
|
ConstantFolding(a_Oprt);
|
||||||
|
bOptimized = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch(a_Oprt)
|
||||||
|
{
|
||||||
|
case cmPOW:
|
||||||
|
// Optimization for polynomials of low order
|
||||||
|
if (m_vRPN[sz-2].Cmd == cmVAR && m_vRPN[sz-1].Cmd == cmVAL)
|
||||||
|
{
|
||||||
|
if (m_vRPN[sz-1].Val.data2==2)
|
||||||
|
m_vRPN[sz-2].Cmd = cmVARPOW2;
|
||||||
|
else if (m_vRPN[sz-1].Val.data2==3)
|
||||||
|
m_vRPN[sz-2].Cmd = cmVARPOW3;
|
||||||
|
else if (m_vRPN[sz-1].Val.data2==4)
|
||||||
|
m_vRPN[sz-2].Cmd = cmVARPOW4;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
|
||||||
|
m_vRPN.pop_back();
|
||||||
|
bOptimized = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmSUB:
|
||||||
|
case cmADD:
|
||||||
|
// Simple optimization based on pattern recognition for a shitload of different
|
||||||
|
// bytecode combinations of addition/subtraction
|
||||||
|
if ( (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVAL) ||
|
||||||
|
(m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVAR) ||
|
||||||
|
(m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVARMUL) ||
|
||||||
|
(m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVAL) ||
|
||||||
|
(m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVAR && m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) ||
|
||||||
|
(m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVARMUL && m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) ||
|
||||||
|
(m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVAR && m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) ||
|
||||||
|
(m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVARMUL && m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) )
|
||||||
|
{
|
||||||
|
assert( (m_vRPN[sz-2].Val.ptr==NULL && m_vRPN[sz-1].Val.ptr!=NULL) ||
|
||||||
|
(m_vRPN[sz-2].Val.ptr!=NULL && m_vRPN[sz-1].Val.ptr==NULL) ||
|
||||||
|
(m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) );
|
||||||
|
|
||||||
|
m_vRPN[sz-2].Cmd = cmVARMUL;
|
||||||
|
m_vRPN[sz-2].Val.ptr = (value_type*)((long long)(m_vRPN[sz-2].Val.ptr) | (long long)(m_vRPN[sz-1].Val.ptr)); // variable
|
||||||
|
m_vRPN[sz-2].Val.data2 += ((a_Oprt==cmSUB) ? -1 : 1) * m_vRPN[sz-1].Val.data2; // offset
|
||||||
|
m_vRPN[sz-2].Val.data += ((a_Oprt==cmSUB) ? -1 : 1) * m_vRPN[sz-1].Val.data; // multiplicand
|
||||||
|
m_vRPN.pop_back();
|
||||||
|
bOptimized = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmMUL:
|
||||||
|
if ( (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVAL) ||
|
||||||
|
(m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVAR) )
|
||||||
|
{
|
||||||
|
m_vRPN[sz-2].Cmd = cmVARMUL;
|
||||||
|
m_vRPN[sz-2].Val.ptr = (value_type*)((long long)(m_vRPN[sz-2].Val.ptr) | (long long)(m_vRPN[sz-1].Val.ptr));
|
||||||
|
m_vRPN[sz-2].Val.data = m_vRPN[sz-2].Val.data2 + m_vRPN[sz-1].Val.data2;
|
||||||
|
m_vRPN[sz-2].Val.data2 = 0;
|
||||||
|
m_vRPN.pop_back();
|
||||||
|
bOptimized = true;
|
||||||
|
}
|
||||||
|
else if ( (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVARMUL) ||
|
||||||
|
(m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVAL) )
|
||||||
|
{
|
||||||
|
// Optimization: 2*(3*b+1) or (3*b+1)*2 -> 6*b+2
|
||||||
|
m_vRPN[sz-2].Cmd = cmVARMUL;
|
||||||
|
m_vRPN[sz-2].Val.ptr = (value_type*)((long long)(m_vRPN[sz-2].Val.ptr) | (long long)(m_vRPN[sz-1].Val.ptr));
|
||||||
|
if (m_vRPN[sz-1].Cmd == cmVAL)
|
||||||
|
{
|
||||||
|
m_vRPN[sz-2].Val.data *= m_vRPN[sz-1].Val.data2;
|
||||||
|
m_vRPN[sz-2].Val.data2 *= m_vRPN[sz-1].Val.data2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_vRPN[sz-2].Val.data = m_vRPN[sz-1].Val.data * m_vRPN[sz-2].Val.data2;
|
||||||
|
m_vRPN[sz-2].Val.data2 = m_vRPN[sz-1].Val.data2 * m_vRPN[sz-2].Val.data2;
|
||||||
|
}
|
||||||
|
m_vRPN.pop_back();
|
||||||
|
bOptimized = true;
|
||||||
|
}
|
||||||
|
else if (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVAR &&
|
||||||
|
m_vRPN[sz-1].Val.ptr == m_vRPN[sz-2].Val.ptr)
|
||||||
|
{
|
||||||
|
// Optimization: a*a -> a^2
|
||||||
|
m_vRPN[sz-2].Cmd = cmVARPOW2;
|
||||||
|
m_vRPN.pop_back();
|
||||||
|
bOptimized = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmDIV:
|
||||||
|
if (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVARMUL && m_vRPN[sz-1].Val.data2!=0)
|
||||||
|
{
|
||||||
|
// Optimization: 4*a/2 -> 2*a
|
||||||
|
m_vRPN[sz-2].Val.data /= m_vRPN[sz-1].Val.data2;
|
||||||
|
m_vRPN[sz-2].Val.data2 /= m_vRPN[sz-1].Val.data2;
|
||||||
|
m_vRPN.pop_back();
|
||||||
|
bOptimized = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// no optimization for other opcodes
|
||||||
|
break;
|
||||||
|
} // switch a_Oprt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If optimization can't be applied just write the value
|
||||||
|
if (!bOptimized)
|
||||||
|
{
|
||||||
|
--m_iStackPos;
|
||||||
|
SToken tok;
|
||||||
|
tok.Cmd = a_Oprt;
|
||||||
|
m_vRPN.push_back(tok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
void ParserByteCode::AddIfElse(ECmdCode a_Oprt)
|
||||||
|
{
|
||||||
|
SToken tok;
|
||||||
|
tok.Cmd = a_Oprt;
|
||||||
|
m_vRPN.push_back(tok);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Add an assignment operator
|
||||||
|
|
||||||
|
Operator entries in byte code consist of:
|
||||||
|
<ul>
|
||||||
|
<li>cmASSIGN code</li>
|
||||||
|
<li>the pointer of the destination variable</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
\sa ParserToken::ECmdCode
|
||||||
|
*/
|
||||||
|
void ParserByteCode::AddAssignOp(value_type *a_pVar)
|
||||||
|
{
|
||||||
|
--m_iStackPos;
|
||||||
|
|
||||||
|
SToken tok;
|
||||||
|
tok.Cmd = cmASSIGN;
|
||||||
|
tok.Oprt.ptr = a_pVar;
|
||||||
|
m_vRPN.push_back(tok);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Add function to bytecode.
|
||||||
|
|
||||||
|
\param a_iArgc Number of arguments, negative numbers indicate multiarg functions.
|
||||||
|
\param a_pFun Pointer to function callback.
|
||||||
|
*/
|
||||||
|
void ParserByteCode::AddFun(generic_fun_type a_pFun, void* p, int a_iArgc)
|
||||||
|
{
|
||||||
|
if (a_iArgc>=0)
|
||||||
|
{
|
||||||
|
m_iStackPos = m_iStackPos - a_iArgc + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// function with unlimited number of arguments
|
||||||
|
m_iStackPos = m_iStackPos + a_iArgc + 1;
|
||||||
|
}
|
||||||
|
m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
|
||||||
|
|
||||||
|
SToken tok;
|
||||||
|
tok.Cmd = cmFUNC;
|
||||||
|
tok.Fun.argc = a_iArgc;
|
||||||
|
tok.Fun.ptr = a_pFun;
|
||||||
|
tok.Fun.param = p;
|
||||||
|
tok.Fun.id = nextId++;
|
||||||
|
m_vRPN.push_back(tok);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Add a bulk function to bytecode.
|
||||||
|
|
||||||
|
\param a_iArgc Number of arguments, negative numbers indicate multiarg functions.
|
||||||
|
\param a_pFun Pointer to function callback.
|
||||||
|
*/
|
||||||
|
void ParserByteCode::AddBulkFun(generic_fun_type a_pFun, void* p, int a_iArgc)
|
||||||
|
{
|
||||||
|
m_iStackPos = m_iStackPos - a_iArgc + 1;
|
||||||
|
m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
|
||||||
|
|
||||||
|
SToken tok;
|
||||||
|
tok.Cmd = cmFUNC_BULK;
|
||||||
|
tok.Fun.argc = a_iArgc;
|
||||||
|
tok.Fun.ptr = a_pFun;
|
||||||
|
tok.Fun.param = p;
|
||||||
|
tok.Fun.id = nextId++;
|
||||||
|
m_vRPN.push_back(tok);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Add Strung function entry to the parser bytecode.
|
||||||
|
\throw nothrow
|
||||||
|
|
||||||
|
A string function entry consists of the stack position of the return value,
|
||||||
|
followed by a cmSTRFUNC code, the function pointer and an index into the
|
||||||
|
string buffer maintained by the parser.
|
||||||
|
*/
|
||||||
|
void ParserByteCode::AddStrFun(generic_fun_type a_pFun, void* p, int a_iArgc, int a_iIdx)
|
||||||
|
{
|
||||||
|
m_iStackPos = m_iStackPos - a_iArgc + 1;
|
||||||
|
|
||||||
|
SToken tok;
|
||||||
|
tok.Cmd = cmFUNC_STR;
|
||||||
|
tok.Fun.argc = a_iArgc;
|
||||||
|
tok.Fun.idx = a_iIdx;
|
||||||
|
tok.Fun.ptr = a_pFun;
|
||||||
|
tok.Fun.param = p;
|
||||||
|
tok.Fun.id = nextId++;
|
||||||
|
m_vRPN.push_back(tok);
|
||||||
|
|
||||||
|
m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Add end marker to bytecode.
|
||||||
|
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
void ParserByteCode::Finalize()
|
||||||
|
{
|
||||||
|
SToken tok;
|
||||||
|
tok.Cmd = cmEND;
|
||||||
|
m_vRPN.push_back(tok);
|
||||||
|
rpn_type(m_vRPN).swap(m_vRPN); // shrink bytecode vector to fit
|
||||||
|
|
||||||
|
// Determine the if-then-else jump offsets
|
||||||
|
ParserStack<int> stIf, stElse;
|
||||||
|
int idx;
|
||||||
|
for (int i=0; i<(int)m_vRPN.size(); ++i)
|
||||||
|
{
|
||||||
|
switch(m_vRPN[i].Cmd)
|
||||||
|
{
|
||||||
|
case cmIF:
|
||||||
|
stIf.push(i);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmELSE:
|
||||||
|
stElse.push(i);
|
||||||
|
idx = stIf.pop();
|
||||||
|
m_vRPN[idx].Oprt.offset = i - idx;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmENDIF:
|
||||||
|
idx = stElse.pop();
|
||||||
|
m_vRPN[idx].Oprt.offset = i - idx;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
const SToken* ParserByteCode::GetBase() const
|
||||||
|
{
|
||||||
|
if (m_vRPN.size()==0)
|
||||||
|
throw ParserError(ecINTERNAL_ERROR);
|
||||||
|
else
|
||||||
|
return &m_vRPN[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
std::size_t ParserByteCode::GetMaxStackSize() const
|
||||||
|
{
|
||||||
|
return m_iMaxStackSize+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Returns the number of entries in the bytecode. */
|
||||||
|
std::size_t ParserByteCode::GetSize() const
|
||||||
|
{
|
||||||
|
return m_vRPN.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Delete the bytecode.
|
||||||
|
|
||||||
|
\throw nothrow
|
||||||
|
|
||||||
|
The name of this function is a violation of my own coding guidelines
|
||||||
|
but this way it's more in line with the STL functions thus more
|
||||||
|
intuitive.
|
||||||
|
*/
|
||||||
|
void ParserByteCode::clear()
|
||||||
|
{
|
||||||
|
m_vRPN.clear();
|
||||||
|
m_iStackPos = 0;
|
||||||
|
m_iMaxStackSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Dump bytecode (for debugging only!). */
|
||||||
|
void ParserByteCode::AsciiDump()
|
||||||
|
{
|
||||||
|
if (!m_vRPN.size())
|
||||||
|
{
|
||||||
|
mu::console() << _T("No bytecode available\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mu::console() << _T("Number of RPN tokens:") << (int)m_vRPN.size() << _T("\n");
|
||||||
|
for (std::size_t i=0; i<m_vRPN.size() && m_vRPN[i].Cmd!=cmEND; ++i)
|
||||||
|
{
|
||||||
|
mu::console() << std::dec << i << _T(" : \t");
|
||||||
|
switch (m_vRPN[i].Cmd)
|
||||||
|
{
|
||||||
|
case cmVAL: mu::console() << _T("VAL \t");
|
||||||
|
mu::console() << _T("[") << m_vRPN[i].Val.data2 << _T("]\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmVAR: mu::console() << _T("VAR \t");
|
||||||
|
mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmVARPOW2: mu::console() << _T("VARPOW2 \t");
|
||||||
|
mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmVARPOW3: mu::console() << _T("VARPOW3 \t");
|
||||||
|
mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmVARPOW4: mu::console() << _T("VARPOW4 \t");
|
||||||
|
mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmVARMUL: mu::console() << _T("VARMUL \t");
|
||||||
|
mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]");
|
||||||
|
mu::console() << _T(" * [") << m_vRPN[i].Val.data << _T("]");
|
||||||
|
mu::console() << _T(" + [") << m_vRPN[i].Val.data2 << _T("]\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmFUNC: mu::console() << _T("CALL\t");
|
||||||
|
mu::console() << _T("[ARG:") << std::dec << m_vRPN[i].Fun.argc << _T("]");
|
||||||
|
mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Fun.ptr << _T("]");
|
||||||
|
mu::console() << _T("\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmFUNC_STR:
|
||||||
|
mu::console() << _T("CALL STRFUNC\t");
|
||||||
|
mu::console() << _T("[ARG:") << std::dec << m_vRPN[i].Fun.argc << _T("]");
|
||||||
|
mu::console() << _T("[IDX:") << std::dec << m_vRPN[i].Fun.idx << _T("]");
|
||||||
|
mu::console() << _T("[ADDR: 0x") << m_vRPN[i].Fun.ptr << _T("]\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmLT: mu::console() << _T("LT\n"); break;
|
||||||
|
case cmGT: mu::console() << _T("GT\n"); break;
|
||||||
|
case cmLE: mu::console() << _T("LE\n"); break;
|
||||||
|
case cmGE: mu::console() << _T("GE\n"); break;
|
||||||
|
case cmEQ: mu::console() << _T("EQ\n"); break;
|
||||||
|
case cmNEQ: mu::console() << _T("NEQ\n"); break;
|
||||||
|
case cmADD: mu::console() << _T("ADD\n"); break;
|
||||||
|
case cmLAND: mu::console() << _T("&&\n"); break;
|
||||||
|
case cmLOR: mu::console() << _T("||\n"); break;
|
||||||
|
case cmSUB: mu::console() << _T("SUB\n"); break;
|
||||||
|
case cmMUL: mu::console() << _T("MUL\n"); break;
|
||||||
|
case cmDIV: mu::console() << _T("DIV\n"); break;
|
||||||
|
case cmPOW: mu::console() << _T("POW\n"); break;
|
||||||
|
|
||||||
|
case cmIF: mu::console() << _T("IF\t");
|
||||||
|
mu::console() << _T("[OFFSET:") << std::dec << m_vRPN[i].Oprt.offset << _T("]\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmELSE: mu::console() << _T("ELSE\t");
|
||||||
|
mu::console() << _T("[OFFSET:") << std::dec << m_vRPN[i].Oprt.offset << _T("]\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmENDIF: mu::console() << _T("ENDIF\n"); break;
|
||||||
|
|
||||||
|
case cmASSIGN:
|
||||||
|
mu::console() << _T("ASSIGN\t");
|
||||||
|
mu::console() << _T("[ADDR: 0x") << m_vRPN[i].Oprt.ptr << _T("]\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: mu::console() << _T("(unknown code: ") << m_vRPN[i].Cmd << _T(")\n");
|
||||||
|
break;
|
||||||
|
} // switch cmdCode
|
||||||
|
} // while bytecode
|
||||||
|
|
||||||
|
mu::console() << _T("END") << std::endl;
|
||||||
|
}
|
||||||
|
} // namespace mu
|
||||||
|
|
||||||
|
#if __clang__
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
143
Modules/gin/3rdparty/muParser/muParserBytecode.h
vendored
Executable file
143
Modules/gin/3rdparty/muParser/muParserBytecode.h
vendored
Executable file
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
__________
|
||||||
|
_____ __ __\______ \_____ _______ ______ ____ _______
|
||||||
|
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
||||||
|
| Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/
|
||||||
|
|__|_| /|____/ |____| (____ /|__| /____ > \___ >|__|
|
||||||
|
\/ \/ \/ \/
|
||||||
|
Copyright (C) 2004-2013 Ingo Berg
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
without restriction, including without limitation the rights to use, copy, modify,
|
||||||
|
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||||
|
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef MU_PARSER_BYTECODE_H
|
||||||
|
#define MU_PARSER_BYTECODE_H
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <string>
|
||||||
|
#include <stack>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "muParserDef.h"
|
||||||
|
#include "muParserError.h"
|
||||||
|
#include "muParserToken.h"
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
\brief Definition of the parser bytecode class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
namespace mu
|
||||||
|
{
|
||||||
|
struct SToken
|
||||||
|
{
|
||||||
|
ECmdCode Cmd;
|
||||||
|
int StackPos;
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct //SValData
|
||||||
|
{
|
||||||
|
value_type *ptr;
|
||||||
|
value_type data;
|
||||||
|
value_type data2;
|
||||||
|
} Val;
|
||||||
|
|
||||||
|
struct //SFunData
|
||||||
|
{
|
||||||
|
// Note: generic_fun_type is merely a placeholder. The real type could be
|
||||||
|
// anything between gun_type1 and fun_type9. I can't use a void
|
||||||
|
// pointer due to constraints in the ANSI standard which allows
|
||||||
|
// data pointers and function pointers to differ in size.
|
||||||
|
generic_fun_type ptr;
|
||||||
|
int argc;
|
||||||
|
int idx;
|
||||||
|
int id;
|
||||||
|
void* param;
|
||||||
|
} Fun;
|
||||||
|
|
||||||
|
struct //SOprtData
|
||||||
|
{
|
||||||
|
value_type *ptr;
|
||||||
|
int offset;
|
||||||
|
} Oprt;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** \brief Bytecode implementation of the Math Parser.
|
||||||
|
|
||||||
|
The bytecode contains the formula converted to revers polish notation stored in a continious
|
||||||
|
memory area. Associated with this data are operator codes, variable pointers, constant
|
||||||
|
values and function pointers. Those are necessary in order to calculate the result.
|
||||||
|
All those data items will be casted to the underlying datatype of the bytecode.
|
||||||
|
|
||||||
|
\author (C) 2004-2013 Ingo Berg
|
||||||
|
*/
|
||||||
|
class ParserByteCode
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** \brief Token type for internal use only. */
|
||||||
|
typedef ParserToken<value_type, string_type> token_type;
|
||||||
|
|
||||||
|
/** \brief Token vector for storing the RPN. */
|
||||||
|
typedef std::vector<SToken> rpn_type;
|
||||||
|
|
||||||
|
/** \brief Position in the Calculation array. */
|
||||||
|
unsigned m_iStackPos;
|
||||||
|
|
||||||
|
/** \brief Maximum size needed for the stack. */
|
||||||
|
std::size_t m_iMaxStackSize;
|
||||||
|
|
||||||
|
/** \brief The actual rpn storage. */
|
||||||
|
rpn_type m_vRPN;
|
||||||
|
|
||||||
|
bool m_bEnableOptimizer;
|
||||||
|
|
||||||
|
void ConstantFolding(ECmdCode a_Oprt);
|
||||||
|
|
||||||
|
int nextId = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
ParserByteCode();
|
||||||
|
ParserByteCode(const ParserByteCode &a_ByteCode);
|
||||||
|
ParserByteCode& operator=(const ParserByteCode &a_ByteCode);
|
||||||
|
void Assign(const ParserByteCode &a_ByteCode);
|
||||||
|
|
||||||
|
void AddVar(value_type *a_pVar);
|
||||||
|
void AddVal(value_type a_fVal);
|
||||||
|
void AddOp(ECmdCode a_Oprt);
|
||||||
|
void AddIfElse(ECmdCode a_Oprt);
|
||||||
|
void AddAssignOp(value_type *a_pVar);
|
||||||
|
void AddFun(generic_fun_type a_pFun, void* p, int a_iArgc);
|
||||||
|
void AddBulkFun(generic_fun_type a_pFun, void* p, int a_iArgc);
|
||||||
|
void AddStrFun(generic_fun_type a_pFun, void* p, int a_iArgc, int a_iIdx);
|
||||||
|
|
||||||
|
void EnableOptimizer(bool bStat);
|
||||||
|
|
||||||
|
void Finalize();
|
||||||
|
void clear();
|
||||||
|
std::size_t GetMaxStackSize() const;
|
||||||
|
std::size_t GetSize() const;
|
||||||
|
|
||||||
|
const SToken* GetBase() const;
|
||||||
|
void AsciiDump();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mu
|
||||||
|
|
||||||
|
#endif
|
497
Modules/gin/3rdparty/muParser/muParserCallback.cpp
vendored
Executable file
497
Modules/gin/3rdparty/muParser/muParserCallback.cpp
vendored
Executable file
|
@ -0,0 +1,497 @@
|
||||||
|
/*
|
||||||
|
__________
|
||||||
|
_____ __ __\______ \_____ _______ ______ ____ _______
|
||||||
|
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
||||||
|
| Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/
|
||||||
|
|__|_| /|____/ |____| (____ /|__| /____ > \___ >|__|
|
||||||
|
\/ \/ \/ \/
|
||||||
|
Copyright (C) 2004-2011 Ingo Berg
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
without restriction, including without limitation the rights to use, copy, modify,
|
||||||
|
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||||
|
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "muParserCallback.h"
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
\brief Implementation of the parser callback class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
namespace mu
|
||||||
|
{
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(fun_type0 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(0)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(fun_type1 a_pFun, void* p, bool a_bAllowOpti, int a_iPrec, ECmdCode a_iCode)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(1)
|
||||||
|
,m_iPri(a_iPrec)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(a_iCode)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Constructor for constructing function callbacks taking two arguments.
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
ParserCallback::ParserCallback(fun_type2 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(2)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Constructor for constructing binary operator callbacks.
|
||||||
|
\param a_pFun Pointer to a static function taking two arguments
|
||||||
|
\param a_bAllowOpti A flag indicating this function can be optimized
|
||||||
|
\param a_iPrec The operator precedence
|
||||||
|
\param a_eOprtAsct The operators associativity
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
ParserCallback::ParserCallback(fun_type2 a_pFun,
|
||||||
|
void* p, bool a_bAllowOpti,
|
||||||
|
int a_iPrec,
|
||||||
|
EOprtAssociativity a_eOprtAsct)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(2)
|
||||||
|
,m_iPri(a_iPrec)
|
||||||
|
,m_eOprtAsct(a_eOprtAsct)
|
||||||
|
,m_iCode(cmOPRT_BIN)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(fun_type3 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(3)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(fun_type4 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(4)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(fun_type5 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(5)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(fun_type6 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(6)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(fun_type7 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(7)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(fun_type8 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(8)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(fun_type9 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(9)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(fun_type10 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(10)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(bulkfun_type0 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(0)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC_BULK)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(bulkfun_type1 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(1)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC_BULK)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Constructor for constructing function callbacks taking two arguments.
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
ParserCallback::ParserCallback(bulkfun_type2 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(2)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC_BULK)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(bulkfun_type3 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(3)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC_BULK)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(bulkfun_type4 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(4)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC_BULK)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(bulkfun_type5 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(5)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC_BULK)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(bulkfun_type6 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(6)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC_BULK)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(bulkfun_type7 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(7)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC_BULK)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(bulkfun_type8 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(8)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC_BULK)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(bulkfun_type9 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(9)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC_BULK)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(bulkfun_type10 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(10)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC_BULK)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(multfun_type a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(-1)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC)
|
||||||
|
,m_iType(tpDBL)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(strfun_type1 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(0)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC_STR)
|
||||||
|
,m_iType(tpSTR)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(strfun_type2 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(1)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC_STR)
|
||||||
|
,m_iType(tpSTR)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserCallback::ParserCallback(strfun_type3 a_pFun, void* p, bool a_bAllowOpti)
|
||||||
|
:m_pFun((void*)a_pFun)
|
||||||
|
,m_iArgc(2)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmFUNC_STR)
|
||||||
|
,m_iType(tpSTR)
|
||||||
|
,m_bAllowOpti(a_bAllowOpti)
|
||||||
|
,m_param(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Default constructor.
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
ParserCallback::ParserCallback()
|
||||||
|
:m_pFun(nullptr)
|
||||||
|
,m_iArgc(0)
|
||||||
|
,m_iPri(-1)
|
||||||
|
,m_eOprtAsct(oaNONE)
|
||||||
|
,m_iCode(cmUNKNOWN)
|
||||||
|
,m_iType(tpVOID)
|
||||||
|
,m_bAllowOpti(0)
|
||||||
|
,m_param(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Copy constructor.
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
ParserCallback::ParserCallback(const ParserCallback &ref)
|
||||||
|
{
|
||||||
|
m_pFun = ref.m_pFun;
|
||||||
|
m_iArgc = ref.m_iArgc;
|
||||||
|
m_bAllowOpti = ref.m_bAllowOpti;
|
||||||
|
m_iCode = ref.m_iCode;
|
||||||
|
m_iType = ref.m_iType;
|
||||||
|
m_iPri = ref.m_iPri;
|
||||||
|
m_eOprtAsct = ref.m_eOprtAsct;
|
||||||
|
m_param = ref.m_param;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Clone this instance and return a pointer to the new instance. */
|
||||||
|
ParserCallback* ParserCallback::Clone() const
|
||||||
|
{
|
||||||
|
return new ParserCallback(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Return tru if the function is conservative.
|
||||||
|
|
||||||
|
Conservative functions return always the same result for the same argument.
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
bool ParserCallback::IsOptimizable() const
|
||||||
|
{
|
||||||
|
return m_bAllowOpti;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Get the callback address for the parser function.
|
||||||
|
|
||||||
|
The type of the address is void. It needs to be recasted according to the
|
||||||
|
argument number to the right type.
|
||||||
|
|
||||||
|
\throw nothrow
|
||||||
|
\return #pFun
|
||||||
|
*/
|
||||||
|
void* ParserCallback::GetAddr() const
|
||||||
|
{
|
||||||
|
return m_pFun;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ParserCallback::GetParam() const
|
||||||
|
{
|
||||||
|
return m_param;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Return the callback code. */
|
||||||
|
ECmdCode ParserCallback::GetCode() const
|
||||||
|
{
|
||||||
|
return m_iCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ETypeCode ParserCallback::GetType() const
|
||||||
|
{
|
||||||
|
return m_iType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Return the operator precedence.
|
||||||
|
\throw nothrown
|
||||||
|
|
||||||
|
Only valid if the callback token is an operator token (binary or infix).
|
||||||
|
*/
|
||||||
|
int ParserCallback::GetPri() const
|
||||||
|
{
|
||||||
|
return m_iPri;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Return the operators associativity.
|
||||||
|
\throw nothrown
|
||||||
|
|
||||||
|
Only valid if the callback token is a binary operator token.
|
||||||
|
*/
|
||||||
|
EOprtAssociativity ParserCallback::GetAssociativity() const
|
||||||
|
{
|
||||||
|
return m_eOprtAsct;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Returns the number of function Arguments. */
|
||||||
|
int ParserCallback::GetArgc() const
|
||||||
|
{
|
||||||
|
return m_iArgc;
|
||||||
|
}
|
||||||
|
} // namespace mu
|
119
Modules/gin/3rdparty/muParser/muParserCallback.h
vendored
Executable file
119
Modules/gin/3rdparty/muParser/muParserCallback.h
vendored
Executable file
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
__________
|
||||||
|
_____ __ __\______ \_____ _______ ______ ____ _______
|
||||||
|
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
||||||
|
| Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/
|
||||||
|
|__|_| /|____/ |____| (____ /|__| /____ > \___ >|__|
|
||||||
|
\/ \/ \/ \/
|
||||||
|
Copyright (C) 2004-2011 Ingo Berg
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
without restriction, including without limitation the rights to use, copy, modify,
|
||||||
|
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||||
|
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MU_PARSER_CALLBACK_H
|
||||||
|
#define MU_PARSER_CALLBACK_H
|
||||||
|
|
||||||
|
#include "muParserDef.h"
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
\brief Definition of the parser callback class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace mu
|
||||||
|
{
|
||||||
|
|
||||||
|
/** \brief Encapsulation of prototypes for a numerical parser function.
|
||||||
|
|
||||||
|
Encapsulates the prototyp for numerical parser functions. The class
|
||||||
|
stores the number of arguments for parser functions as well
|
||||||
|
as additional flags indication the function is non optimizeable.
|
||||||
|
The pointer to the callback function pointer is stored as void*
|
||||||
|
and needs to be casted according to the argument count.
|
||||||
|
Negative argument counts indicate a parser function with a variable number
|
||||||
|
of arguments.
|
||||||
|
|
||||||
|
\author (C) 2004-2011 Ingo Berg
|
||||||
|
*/
|
||||||
|
class ParserCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ParserCallback(fun_type0 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(fun_type1 a_pFun, void* p, bool a_bAllowOpti, int a_iPrec = -1, ECmdCode a_iCode=cmFUNC);
|
||||||
|
ParserCallback(fun_type2 a_pFun, void* p, bool a_bAllowOpti, int a_iPrec, EOprtAssociativity a_eAssociativity);
|
||||||
|
ParserCallback(fun_type2 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(fun_type3 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(fun_type4 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(fun_type5 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(fun_type6 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(fun_type7 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(fun_type8 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(fun_type9 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(fun_type10 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
|
||||||
|
ParserCallback(bulkfun_type0 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(bulkfun_type1 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(bulkfun_type2 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(bulkfun_type3 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(bulkfun_type4 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(bulkfun_type5 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(bulkfun_type6 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(bulkfun_type7 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(bulkfun_type8 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(bulkfun_type9 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(bulkfun_type10 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
|
||||||
|
ParserCallback(multfun_type a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(strfun_type1 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(strfun_type2 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback(strfun_type3 a_pFun, void* p, bool a_bAllowOpti);
|
||||||
|
ParserCallback();
|
||||||
|
ParserCallback(const ParserCallback &a_Fun);
|
||||||
|
|
||||||
|
ParserCallback* Clone() const;
|
||||||
|
|
||||||
|
bool IsOptimizable() const;
|
||||||
|
void* GetAddr() const;
|
||||||
|
void* GetParam() const;
|
||||||
|
ECmdCode GetCode() const;
|
||||||
|
ETypeCode GetType() const;
|
||||||
|
int GetPri() const;
|
||||||
|
EOprtAssociativity GetAssociativity() const;
|
||||||
|
int GetArgc() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void *m_pFun; ///< Pointer to the callback function, casted to void
|
||||||
|
|
||||||
|
/** \brief Number of numeric function arguments
|
||||||
|
|
||||||
|
This number is negative for functions with variable number of arguments. in this cases
|
||||||
|
they represent the actual number of arguments found.
|
||||||
|
*/
|
||||||
|
int m_iArgc;
|
||||||
|
int m_iPri; ///< Valid only for binary and infix operators; Operator precedence.
|
||||||
|
EOprtAssociativity m_eOprtAsct; ///< Operator associativity; Valid only for binary operators
|
||||||
|
ECmdCode m_iCode;
|
||||||
|
ETypeCode m_iType;
|
||||||
|
bool m_bAllowOpti; ///< Flag indication optimizeability
|
||||||
|
void* m_param = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Container for Callback objects. */
|
||||||
|
typedef std::map<string_type, ParserCallback> funmap_type;
|
||||||
|
|
||||||
|
} // namespace mu
|
||||||
|
|
||||||
|
#endif
|
372
Modules/gin/3rdparty/muParser/muParserDef.h
vendored
Executable file
372
Modules/gin/3rdparty/muParser/muParserDef.h
vendored
Executable file
|
@ -0,0 +1,372 @@
|
||||||
|
/*
|
||||||
|
__________
|
||||||
|
_____ __ __\______ \_____ _______ ______ ____ _______
|
||||||
|
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
||||||
|
| Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/
|
||||||
|
|__|_| /|____/ |____| (____ /|__| /____ > \___ >|__|
|
||||||
|
\/ \/ \/ \/
|
||||||
|
Copyright (C) 2014 Ingo Berg
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
without restriction, including without limitation the rights to use, copy, modify,
|
||||||
|
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||||
|
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef MUP_DEF_H
|
||||||
|
#define MUP_DEF_H
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "muParserFixes.h"
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
\brief This file contains standard definitions used by the parser.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MUP_VERSION _T("2.2.5")
|
||||||
|
#define MUP_VERSION_DATE _T("20150427; GC")
|
||||||
|
|
||||||
|
#define MUP_CHARS _T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||||
|
|
||||||
|
/** \brief If this macro is defined mathematical exceptions (div by zero) will be thrown as exceptions. */
|
||||||
|
//#define MUP_MATH_EXCEPTIONS
|
||||||
|
|
||||||
|
/** \brief Define the base datatype for values.
|
||||||
|
|
||||||
|
This datatype must be a built in value type. You can not use custom classes.
|
||||||
|
It should be working with all types except "int"!
|
||||||
|
*/
|
||||||
|
#define MUP_BASETYPE double
|
||||||
|
|
||||||
|
/** \brief Activate this option in order to compile with OpenMP support.
|
||||||
|
|
||||||
|
OpenMP is used only in the bulk mode it may increase the performance a bit.
|
||||||
|
*/
|
||||||
|
//#define MUP_USE_OPENMP
|
||||||
|
|
||||||
|
#if defined(_UNICODE)
|
||||||
|
/** \brief Definition of the basic parser string type. */
|
||||||
|
#define MUP_STRING_TYPE std::wstring
|
||||||
|
|
||||||
|
#if !defined(_T)
|
||||||
|
#define _T(x) L##x
|
||||||
|
#endif // not defined _T
|
||||||
|
#else
|
||||||
|
#ifndef _T
|
||||||
|
#define _T(x) x
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** \brief Definition of the basic parser string type. */
|
||||||
|
#define MUP_STRING_TYPE std::string
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_DEBUG)
|
||||||
|
/** \brief Debug macro to force an abortion of the programm with a certain message.
|
||||||
|
*/
|
||||||
|
#define MUP_FAIL(MSG) \
|
||||||
|
{ \
|
||||||
|
bool MSG=false; \
|
||||||
|
assert(MSG); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief An assertion that does not kill the program.
|
||||||
|
|
||||||
|
This macro is neutralised in UNICODE builds. It's
|
||||||
|
too difficult to translate.
|
||||||
|
*/
|
||||||
|
#define MUP_ASSERT(COND) \
|
||||||
|
if (!(COND)) \
|
||||||
|
{ \
|
||||||
|
stringstream_type ss; \
|
||||||
|
ss << _T("Assertion \"") _T(#COND) _T("\" failed: ") \
|
||||||
|
<< __FILE__ << _T(" line ") \
|
||||||
|
<< __LINE__ << _T("."); \
|
||||||
|
throw ParserError( ss.str() ); \
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define MUP_FAIL(MSG)
|
||||||
|
#define MUP_ASSERT(COND)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace mu
|
||||||
|
{
|
||||||
|
#if defined(_UNICODE)
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Encapsulate wcout. */
|
||||||
|
inline std::wostream& console()
|
||||||
|
{
|
||||||
|
return std::wcout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief Encapsulate cin. */
|
||||||
|
inline std::wistream& console_in()
|
||||||
|
{
|
||||||
|
return std::wcin;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/** \brief Encapsulate cout.
|
||||||
|
|
||||||
|
Used for supporting UNICODE more easily.
|
||||||
|
*/
|
||||||
|
inline std::ostream& console()
|
||||||
|
{
|
||||||
|
return std::cout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief Encapsulate cin.
|
||||||
|
|
||||||
|
Used for supporting UNICODE more easily.
|
||||||
|
*/
|
||||||
|
inline std::istream& console_in()
|
||||||
|
{
|
||||||
|
return std::cin;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Bytecode values.
|
||||||
|
|
||||||
|
\attention The order of the operator entries must match the order in ParserBase::c_DefaultOprt!
|
||||||
|
*/
|
||||||
|
enum ECmdCode
|
||||||
|
{
|
||||||
|
// The following are codes for built in binary operators
|
||||||
|
// apart from built in operators the user has the opportunity to
|
||||||
|
// add user defined operators.
|
||||||
|
cmLE = 0, ///< Operator item: less or equal
|
||||||
|
cmGE = 1, ///< Operator item: greater or equal
|
||||||
|
cmNEQ = 2, ///< Operator item: not equal
|
||||||
|
cmEQ = 3, ///< Operator item: equals
|
||||||
|
cmLT = 4, ///< Operator item: less than
|
||||||
|
cmGT = 5, ///< Operator item: greater than
|
||||||
|
cmADD = 6, ///< Operator item: add
|
||||||
|
cmSUB = 7, ///< Operator item: subtract
|
||||||
|
cmMUL = 8, ///< Operator item: multiply
|
||||||
|
cmDIV = 9, ///< Operator item: division
|
||||||
|
cmPOW = 10, ///< Operator item: y to the power of ...
|
||||||
|
cmLAND = 11,
|
||||||
|
cmLOR = 12,
|
||||||
|
cmASSIGN = 13, ///< Operator item: Assignment operator
|
||||||
|
cmBO = 14, ///< Operator item: opening bracket
|
||||||
|
cmBC = 15, ///< Operator item: closing bracket
|
||||||
|
cmIF = 16, ///< For use in the ternary if-then-else operator
|
||||||
|
cmELSE = 17, ///< For use in the ternary if-then-else operator
|
||||||
|
cmENDIF = 18, ///< For use in the ternary if-then-else operator
|
||||||
|
cmARG_SEP = 19, ///< function argument separator
|
||||||
|
cmVAR = 20, ///< variable item
|
||||||
|
cmVAL = 21, ///< value item
|
||||||
|
|
||||||
|
// For optimization purposes
|
||||||
|
cmVARPOW2,
|
||||||
|
cmVARPOW3,
|
||||||
|
cmVARPOW4,
|
||||||
|
cmVARMUL,
|
||||||
|
cmPOW2,
|
||||||
|
|
||||||
|
// operators and functions
|
||||||
|
cmFUNC, ///< Code for a generic function item
|
||||||
|
cmFUNC_STR, ///< Code for a function with a string parameter
|
||||||
|
cmFUNC_BULK, ///< Special callbacks for Bulk mode with an additional parameter for the bulk index
|
||||||
|
cmSTRING, ///< Code for a string token
|
||||||
|
cmOPRT_BIN, ///< user defined binary operator
|
||||||
|
cmOPRT_POSTFIX, ///< code for postfix operators
|
||||||
|
cmOPRT_INFIX, ///< code for infix operators
|
||||||
|
cmEND, ///< end of formula
|
||||||
|
cmUNKNOWN ///< uninitialized item
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Types internally used by the parser.
|
||||||
|
*/
|
||||||
|
enum ETypeCode
|
||||||
|
{
|
||||||
|
tpSTR = 0, ///< String type (Function arguments and constants only, no string variables)
|
||||||
|
tpDBL = 1, ///< Floating point variables
|
||||||
|
tpVOID = 2 ///< Undefined type.
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
enum EParserVersionInfo
|
||||||
|
{
|
||||||
|
pviBRIEF,
|
||||||
|
pviFULL
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Parser operator precedence values. */
|
||||||
|
enum EOprtAssociativity
|
||||||
|
{
|
||||||
|
oaLEFT = 0,
|
||||||
|
oaRIGHT = 1,
|
||||||
|
oaNONE = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Parser operator precedence values. */
|
||||||
|
enum EOprtPrecedence
|
||||||
|
{
|
||||||
|
// binary operators
|
||||||
|
prLOR = 1,
|
||||||
|
prLAND = 2,
|
||||||
|
prLOGIC = 3, ///< logic operators
|
||||||
|
prCMP = 4, ///< comparsion operators
|
||||||
|
prADD_SUB = 5, ///< addition
|
||||||
|
prMUL_DIV = 6, ///< multiplication/division
|
||||||
|
prPOW = 7, ///< power operator priority (highest)
|
||||||
|
|
||||||
|
// infix operators
|
||||||
|
prINFIX = 6, ///< Signs have a higher priority than ADD_SUB, but lower than power operator
|
||||||
|
prPOSTFIX = 6 ///< Postfix operator priority (currently unused)
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// basic types
|
||||||
|
|
||||||
|
/** \brief The numeric datatype used by the parser.
|
||||||
|
|
||||||
|
Normally this is a floating point type either single or double precision.
|
||||||
|
*/
|
||||||
|
typedef MUP_BASETYPE value_type;
|
||||||
|
|
||||||
|
/** \brief The stringtype used by the parser.
|
||||||
|
|
||||||
|
Depends on whether UNICODE is used or not.
|
||||||
|
*/
|
||||||
|
typedef MUP_STRING_TYPE string_type;
|
||||||
|
|
||||||
|
/** \brief The character type used by the parser.
|
||||||
|
|
||||||
|
Depends on whether UNICODE is used or not.
|
||||||
|
*/
|
||||||
|
typedef string_type::value_type char_type;
|
||||||
|
|
||||||
|
/** \brief Typedef for easily using stringstream that respect the parser stringtype. */
|
||||||
|
typedef std::basic_stringstream<char_type,
|
||||||
|
std::char_traits<char_type>,
|
||||||
|
std::allocator<char_type> > stringstream_type;
|
||||||
|
|
||||||
|
// Data container types
|
||||||
|
|
||||||
|
/** \brief Type used for storing variables. */
|
||||||
|
typedef std::map<string_type, value_type*> varmap_type;
|
||||||
|
|
||||||
|
/** \brief Type used for storing constants. */
|
||||||
|
typedef std::map<string_type, value_type> valmap_type;
|
||||||
|
|
||||||
|
/** \brief Type for assigning a string name to an index in the internal string table. */
|
||||||
|
typedef std::map<string_type, std::size_t> strmap_type;
|
||||||
|
|
||||||
|
// Parser callbacks
|
||||||
|
struct SParam
|
||||||
|
{
|
||||||
|
void* param = nullptr;
|
||||||
|
int id = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions without arguments. */
|
||||||
|
typedef value_type (*generic_fun_type)(SParam);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions without arguments. */
|
||||||
|
typedef value_type (*fun_type0)(SParam);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with a single arguments. */
|
||||||
|
typedef value_type (*fun_type1)(SParam, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with two arguments. */
|
||||||
|
typedef value_type (*fun_type2)(SParam, value_type, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with three arguments. */
|
||||||
|
typedef value_type (*fun_type3)(SParam, value_type, value_type, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with four arguments. */
|
||||||
|
typedef value_type (*fun_type4)(SParam, value_type, value_type, value_type, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with five arguments. */
|
||||||
|
typedef value_type (*fun_type5)(SParam, value_type, value_type, value_type, value_type, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with six arguments. */
|
||||||
|
typedef value_type (*fun_type6)(SParam, value_type, value_type, value_type, value_type, value_type, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with seven arguments. */
|
||||||
|
typedef value_type (*fun_type7)(SParam, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with eight arguments. */
|
||||||
|
typedef value_type (*fun_type8)(SParam, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with nine arguments. */
|
||||||
|
typedef value_type (*fun_type9)(SParam, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with ten arguments. */
|
||||||
|
typedef value_type (*fun_type10)(SParam, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions without arguments. */
|
||||||
|
typedef value_type (*bulkfun_type0)(SParam, int, int);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with a single arguments. */
|
||||||
|
typedef value_type (*bulkfun_type1)(SParam, int, int, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with two arguments. */
|
||||||
|
typedef value_type (*bulkfun_type2)(SParam, int, int, value_type, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with three arguments. */
|
||||||
|
typedef value_type (*bulkfun_type3)(SParam, int, int, value_type, value_type, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with four arguments. */
|
||||||
|
typedef value_type (*bulkfun_type4)(SParam, int, int, value_type, value_type, value_type, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with five arguments. */
|
||||||
|
typedef value_type (*bulkfun_type5)(SParam, int, int, value_type, value_type, value_type, value_type, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with six arguments. */
|
||||||
|
typedef value_type (*bulkfun_type6)(SParam, int, int, value_type, value_type, value_type, value_type, value_type, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with seven arguments. */
|
||||||
|
typedef value_type (*bulkfun_type7)(SParam, int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with eight arguments. */
|
||||||
|
typedef value_type (*bulkfun_type8)(SParam, int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with nine arguments. */
|
||||||
|
typedef value_type (*bulkfun_type9)(SParam, int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with ten arguments. */
|
||||||
|
typedef value_type (*bulkfun_type10)(SParam, int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions with a variable argument list. */
|
||||||
|
typedef value_type (*multfun_type)(SParam, const value_type*, int);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions taking a string as an argument. */
|
||||||
|
typedef value_type (*strfun_type1)(SParam, const char_type*);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions taking a string and a value as arguments. */
|
||||||
|
typedef value_type (*strfun_type2)(SParam, const char_type*, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback type used for functions taking a string and two values as arguments. */
|
||||||
|
typedef value_type (*strfun_type3)(SParam, const char_type*, value_type, value_type);
|
||||||
|
|
||||||
|
/** \brief Callback used for functions that identify values in a string. */
|
||||||
|
typedef int (*identfun_type)(SParam, const char_type *sExpr, int *nPos, value_type *fVal);
|
||||||
|
|
||||||
|
/** \brief Callback used for variable creation factory functions. */
|
||||||
|
typedef value_type* (*facfun_type)(SParam, const char_type*, void*);
|
||||||
|
} // end of namespace
|
||||||
|
|
||||||
|
#endif
|
345
Modules/gin/3rdparty/muParser/muParserError.cpp
vendored
Executable file
345
Modules/gin/3rdparty/muParser/muParserError.cpp
vendored
Executable file
|
@ -0,0 +1,345 @@
|
||||||
|
/*
|
||||||
|
__________
|
||||||
|
_____ __ __\______ \_____ _______ ______ ____ _______
|
||||||
|
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
||||||
|
| Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/
|
||||||
|
|__|_| /|____/ |____| (____ /|__| /____ > \___ >|__|
|
||||||
|
\/ \/ \/ \/
|
||||||
|
Copyright (C) 2011 Ingo Berg
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
without restriction, including without limitation the rights to use, copy, modify,
|
||||||
|
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||||
|
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "muParserError.h"
|
||||||
|
|
||||||
|
#if __clang__
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wconversion"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace mu
|
||||||
|
{
|
||||||
|
const ParserErrorMsg ParserErrorMsg::m_Instance;
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
const ParserErrorMsg& ParserErrorMsg::Instance()
|
||||||
|
{
|
||||||
|
return m_Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
string_type ParserErrorMsg::operator[](unsigned a_iIdx) const
|
||||||
|
{
|
||||||
|
return (a_iIdx<m_vErrMsg.size()) ? m_vErrMsg[a_iIdx] : string_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserErrorMsg::~ParserErrorMsg()
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Assignment operator is deactivated.
|
||||||
|
*/
|
||||||
|
ParserErrorMsg& ParserErrorMsg::operator=(const ParserErrorMsg& )
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserErrorMsg::ParserErrorMsg(const ParserErrorMsg&)
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserErrorMsg::ParserErrorMsg()
|
||||||
|
:m_vErrMsg(0)
|
||||||
|
{
|
||||||
|
m_vErrMsg.resize(ecCOUNT);
|
||||||
|
|
||||||
|
m_vErrMsg[ecUNASSIGNABLE_TOKEN] = _T("Unexpected token \"$TOK$\" found at position $POS$.");
|
||||||
|
m_vErrMsg[ecINTERNAL_ERROR] = _T("Internal error");
|
||||||
|
m_vErrMsg[ecINVALID_NAME] = _T("Invalid function-, variable- or constant name: \"$TOK$\".");
|
||||||
|
m_vErrMsg[ecINVALID_BINOP_IDENT] = _T("Invalid binary operator identifier: \"$TOK$\".");
|
||||||
|
m_vErrMsg[ecINVALID_INFIX_IDENT] = _T("Invalid infix operator identifier: \"$TOK$\".");
|
||||||
|
m_vErrMsg[ecINVALID_POSTFIX_IDENT] = _T("Invalid postfix operator identifier: \"$TOK$\".");
|
||||||
|
m_vErrMsg[ecINVALID_FUN_PTR] = _T("Invalid pointer to callback function.");
|
||||||
|
m_vErrMsg[ecEMPTY_EXPRESSION] = _T("Expression is empty.");
|
||||||
|
m_vErrMsg[ecINVALID_VAR_PTR] = _T("Invalid pointer to variable.");
|
||||||
|
m_vErrMsg[ecUNEXPECTED_OPERATOR] = _T("Unexpected operator \"$TOK$\" found at position $POS$");
|
||||||
|
m_vErrMsg[ecUNEXPECTED_EOF] = _T("Unexpected end of expression at position $POS$");
|
||||||
|
m_vErrMsg[ecUNEXPECTED_ARG_SEP] = _T("Unexpected argument separator at position $POS$");
|
||||||
|
m_vErrMsg[ecUNEXPECTED_PARENS] = _T("Unexpected parenthesis \"$TOK$\" at position $POS$");
|
||||||
|
m_vErrMsg[ecUNEXPECTED_FUN] = _T("Unexpected function \"$TOK$\" at position $POS$");
|
||||||
|
m_vErrMsg[ecUNEXPECTED_VAL] = _T("Unexpected value \"$TOK$\" found at position $POS$");
|
||||||
|
m_vErrMsg[ecUNEXPECTED_VAR] = _T("Unexpected variable \"$TOK$\" found at position $POS$");
|
||||||
|
m_vErrMsg[ecUNEXPECTED_ARG] = _T("Function arguments used without a function (position: $POS$)");
|
||||||
|
m_vErrMsg[ecMISSING_PARENS] = _T("Missing parenthesis");
|
||||||
|
m_vErrMsg[ecTOO_MANY_PARAMS] = _T("Too many parameters for function \"$TOK$\" at expression position $POS$");
|
||||||
|
m_vErrMsg[ecTOO_FEW_PARAMS] = _T("Too few parameters for function \"$TOK$\" at expression position $POS$");
|
||||||
|
m_vErrMsg[ecDIV_BY_ZERO] = _T("Divide by zero");
|
||||||
|
m_vErrMsg[ecDOMAIN_ERROR] = _T("Domain error");
|
||||||
|
m_vErrMsg[ecNAME_CONFLICT] = _T("Name conflict");
|
||||||
|
m_vErrMsg[ecOPT_PRI] = _T("Invalid value for operator priority (must be greater or equal to zero).");
|
||||||
|
m_vErrMsg[ecBUILTIN_OVERLOAD] = _T("user defined binary operator \"$TOK$\" conflicts with a built in operator.");
|
||||||
|
m_vErrMsg[ecUNEXPECTED_STR] = _T("Unexpected string token found at position $POS$.");
|
||||||
|
m_vErrMsg[ecUNTERMINATED_STRING] = _T("Unterminated string starting at position $POS$.");
|
||||||
|
m_vErrMsg[ecSTRING_EXPECTED] = _T("String function called with a non string type of argument.");
|
||||||
|
m_vErrMsg[ecVAL_EXPECTED] = _T("String value used where a numerical argument is expected.");
|
||||||
|
m_vErrMsg[ecOPRT_TYPE_CONFLICT] = _T("No suitable overload for operator \"$TOK$\" at position $POS$.");
|
||||||
|
m_vErrMsg[ecSTR_RESULT] = _T("Function result is a string.");
|
||||||
|
m_vErrMsg[ecGENERIC] = _T("Parser error.");
|
||||||
|
m_vErrMsg[ecLOCALE] = _T("Decimal separator is identic to function argument separator.");
|
||||||
|
m_vErrMsg[ecUNEXPECTED_CONDITIONAL] = _T("The \"$TOK$\" operator must be preceded by a closing bracket.");
|
||||||
|
m_vErrMsg[ecMISSING_ELSE_CLAUSE] = _T("If-then-else operator is missing an else clause");
|
||||||
|
m_vErrMsg[ecMISPLACED_COLON] = _T("Misplaced colon at position $POS$");
|
||||||
|
m_vErrMsg[ecUNREASONABLE_NUMBER_OF_COMPUTATIONS] = _T("Number of computations to small for bulk mode. (Vectorisation overhead too costly)");
|
||||||
|
|
||||||
|
#if defined(_DEBUG)
|
||||||
|
for (int i=0; i<ecCOUNT; ++i)
|
||||||
|
if (!m_vErrMsg[i].length())
|
||||||
|
assert(false);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// ParserError class
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** \brief Default constructor. */
|
||||||
|
ParserError::ParserError()
|
||||||
|
:m_strMsg()
|
||||||
|
,m_strFormula()
|
||||||
|
,m_strTok()
|
||||||
|
,m_iPos(-1)
|
||||||
|
,m_iErrc(ecUNDEFINED)
|
||||||
|
,m_ErrMsg(ParserErrorMsg::Instance())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief This Constructor is used for internal exceptions only.
|
||||||
|
|
||||||
|
It does not contain any information but the error code.
|
||||||
|
*/
|
||||||
|
ParserError::ParserError(EErrorCodes a_iErrc)
|
||||||
|
:m_strMsg()
|
||||||
|
,m_strFormula()
|
||||||
|
,m_strTok()
|
||||||
|
,m_iPos(-1)
|
||||||
|
,m_iErrc(a_iErrc)
|
||||||
|
,m_ErrMsg(ParserErrorMsg::Instance())
|
||||||
|
{
|
||||||
|
m_strMsg = m_ErrMsg[m_iErrc];
|
||||||
|
stringstream_type stream;
|
||||||
|
stream << (int)m_iPos;
|
||||||
|
ReplaceSubString(m_strMsg, _T("$POS$"), stream.str());
|
||||||
|
ReplaceSubString(m_strMsg, _T("$TOK$"), m_strTok);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Construct an error from a message text. */
|
||||||
|
ParserError::ParserError(const string_type &sMsg)
|
||||||
|
:m_ErrMsg(ParserErrorMsg::Instance())
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
m_strMsg = sMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Construct an error object.
|
||||||
|
\param [in] a_iErrc the error code.
|
||||||
|
\param [in] sTok The token string related to this error.
|
||||||
|
\param [in] sExpr The expression related to the error.
|
||||||
|
\param [in] a_iPos the position in the expression where the error occurred.
|
||||||
|
*/
|
||||||
|
ParserError::ParserError( EErrorCodes iErrc,
|
||||||
|
const string_type &sTok,
|
||||||
|
const string_type &sExpr,
|
||||||
|
int iPos )
|
||||||
|
:m_strMsg()
|
||||||
|
,m_strFormula(sExpr)
|
||||||
|
,m_strTok(sTok)
|
||||||
|
,m_iPos(iPos)
|
||||||
|
,m_iErrc(iErrc)
|
||||||
|
,m_ErrMsg(ParserErrorMsg::Instance())
|
||||||
|
{
|
||||||
|
m_strMsg = m_ErrMsg[m_iErrc];
|
||||||
|
stringstream_type stream;
|
||||||
|
stream << (int)m_iPos;
|
||||||
|
ReplaceSubString(m_strMsg, _T("$POS$"), stream.str());
|
||||||
|
ReplaceSubString(m_strMsg, _T("$TOK$"), m_strTok);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Construct an error object.
|
||||||
|
\param [in] iErrc the error code.
|
||||||
|
\param [in] iPos the position in the expression where the error occurred.
|
||||||
|
\param [in] sTok The token string related to this error.
|
||||||
|
*/
|
||||||
|
ParserError::ParserError(EErrorCodes iErrc, int iPos, const string_type &sTok)
|
||||||
|
:m_strMsg()
|
||||||
|
,m_strFormula()
|
||||||
|
,m_strTok(sTok)
|
||||||
|
,m_iPos(iPos)
|
||||||
|
,m_iErrc(iErrc)
|
||||||
|
,m_ErrMsg(ParserErrorMsg::Instance())
|
||||||
|
{
|
||||||
|
m_strMsg = m_ErrMsg[m_iErrc];
|
||||||
|
stringstream_type stream;
|
||||||
|
stream << (int)m_iPos;
|
||||||
|
ReplaceSubString(m_strMsg, _T("$POS$"), stream.str());
|
||||||
|
ReplaceSubString(m_strMsg, _T("$TOK$"), m_strTok);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Construct an error object.
|
||||||
|
\param [in] szMsg The error message text.
|
||||||
|
\param [in] iPos the position related to the error.
|
||||||
|
\param [in] sTok The token string related to this error.
|
||||||
|
*/
|
||||||
|
ParserError::ParserError(const char_type *szMsg, int iPos, const string_type &sTok)
|
||||||
|
:m_strMsg(szMsg)
|
||||||
|
,m_strFormula()
|
||||||
|
,m_strTok(sTok)
|
||||||
|
,m_iPos(iPos)
|
||||||
|
,m_iErrc(ecGENERIC)
|
||||||
|
,m_ErrMsg(ParserErrorMsg::Instance())
|
||||||
|
{
|
||||||
|
stringstream_type stream;
|
||||||
|
stream << (int)m_iPos;
|
||||||
|
ReplaceSubString(m_strMsg, _T("$POS$"), stream.str());
|
||||||
|
ReplaceSubString(m_strMsg, _T("$TOK$"), m_strTok);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Copy constructor. */
|
||||||
|
ParserError::ParserError(const ParserError &a_Obj)
|
||||||
|
:m_strMsg(a_Obj.m_strMsg)
|
||||||
|
,m_strFormula(a_Obj.m_strFormula)
|
||||||
|
,m_strTok(a_Obj.m_strTok)
|
||||||
|
,m_iPos(a_Obj.m_iPos)
|
||||||
|
,m_iErrc(a_Obj.m_iErrc)
|
||||||
|
,m_ErrMsg(ParserErrorMsg::Instance())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Assignment operator. */
|
||||||
|
ParserError& ParserError::operator=(const ParserError &a_Obj)
|
||||||
|
{
|
||||||
|
if (this==&a_Obj)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
m_strMsg = a_Obj.m_strMsg;
|
||||||
|
m_strFormula = a_Obj.m_strFormula;
|
||||||
|
m_strTok = a_Obj.m_strTok;
|
||||||
|
m_iPos = a_Obj.m_iPos;
|
||||||
|
m_iErrc = a_Obj.m_iErrc;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
ParserError::~ParserError()
|
||||||
|
{}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Replace all occurrences of a substring with another string.
|
||||||
|
\param strFind The string that shall be replaced.
|
||||||
|
\param strReplaceWith The string that should be inserted instead of strFind
|
||||||
|
*/
|
||||||
|
void ParserError::ReplaceSubString( string_type &strSource,
|
||||||
|
const string_type &strFind,
|
||||||
|
const string_type &strReplaceWith)
|
||||||
|
{
|
||||||
|
string_type strResult;
|
||||||
|
string_type::size_type iPos(0), iNext(0);
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
iNext = strSource.find(strFind, iPos);
|
||||||
|
strResult.append(strSource, iPos, iNext-iPos);
|
||||||
|
|
||||||
|
if( iNext==string_type::npos )
|
||||||
|
break;
|
||||||
|
|
||||||
|
strResult.append(strReplaceWith);
|
||||||
|
iPos = iNext + strFind.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
strSource.swap(strResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Reset the erro object. */
|
||||||
|
void ParserError::Reset()
|
||||||
|
{
|
||||||
|
m_strMsg = _T("");
|
||||||
|
m_strFormula = _T("");
|
||||||
|
m_strTok = _T("");
|
||||||
|
m_iPos = -1;
|
||||||
|
m_iErrc = ecUNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Set the expression related to this error. */
|
||||||
|
void ParserError::SetFormula(const string_type &a_strFormula)
|
||||||
|
{
|
||||||
|
m_strFormula = a_strFormula;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief gets the expression related tp this error.*/
|
||||||
|
const string_type& ParserError::GetExpr() const
|
||||||
|
{
|
||||||
|
return m_strFormula;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Returns the message string for this error. */
|
||||||
|
const string_type& ParserError::GetMsg() const
|
||||||
|
{
|
||||||
|
return m_strMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Return the formula position related to the error.
|
||||||
|
|
||||||
|
If the error is not related to a distinct position this will return -1
|
||||||
|
*/
|
||||||
|
int ParserError::GetPos() const
|
||||||
|
{
|
||||||
|
return m_iPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Return string related with this token (if available). */
|
||||||
|
const string_type& ParserError::GetToken() const
|
||||||
|
{
|
||||||
|
return m_strTok;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Return the error code. */
|
||||||
|
EErrorCodes ParserError::GetCode() const
|
||||||
|
{
|
||||||
|
return m_iErrc;
|
||||||
|
}
|
||||||
|
} // namespace mu
|
||||||
|
|
||||||
|
#if __clang__
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
175
Modules/gin/3rdparty/muParser/muParserError.h
vendored
Executable file
175
Modules/gin/3rdparty/muParser/muParserError.h
vendored
Executable file
|
@ -0,0 +1,175 @@
|
||||||
|
/*
|
||||||
|
__________
|
||||||
|
_____ __ __\______ \_____ _______ ______ ____ _______
|
||||||
|
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
||||||
|
| Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/
|
||||||
|
|__|_| /|____/ |____| (____ /|__| /____ > \___ >|__|
|
||||||
|
\/ \/ \/ \/
|
||||||
|
Copyright (C) 2004-2011 Ingo Berg
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
without restriction, including without limitation the rights to use, copy, modify,
|
||||||
|
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||||
|
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MU_PARSER_ERROR_H
|
||||||
|
#define MU_PARSER_ERROR_H
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "muParserDef.h"
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
\brief This file defines the error class used by the parser.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace mu
|
||||||
|
{
|
||||||
|
|
||||||
|
/** \brief Error codes. */
|
||||||
|
enum EErrorCodes
|
||||||
|
{
|
||||||
|
// Formula syntax errors
|
||||||
|
ecUNEXPECTED_OPERATOR = 0, ///< Unexpected binary operator found
|
||||||
|
ecUNASSIGNABLE_TOKEN = 1, ///< Token can't be identified.
|
||||||
|
ecUNEXPECTED_EOF = 2, ///< Unexpected end of formula. (Example: "2+sin(")
|
||||||
|
ecUNEXPECTED_ARG_SEP = 3, ///< An unexpected comma has been found. (Example: "1,23")
|
||||||
|
ecUNEXPECTED_ARG = 4, ///< An unexpected argument has been found
|
||||||
|
ecUNEXPECTED_VAL = 5, ///< An unexpected value token has been found
|
||||||
|
ecUNEXPECTED_VAR = 6, ///< An unexpected variable token has been found
|
||||||
|
ecUNEXPECTED_PARENS = 7, ///< Unexpected Parenthesis, opening or closing
|
||||||
|
ecUNEXPECTED_STR = 8, ///< A string has been found at an inapropriate position
|
||||||
|
ecSTRING_EXPECTED = 9, ///< A string function has been called with a different type of argument
|
||||||
|
ecVAL_EXPECTED = 10, ///< A numerical function has been called with a non value type of argument
|
||||||
|
ecMISSING_PARENS = 11, ///< Missing parens. (Example: "3*sin(3")
|
||||||
|
ecUNEXPECTED_FUN = 12, ///< Unexpected function found. (Example: "sin(8)cos(9)")
|
||||||
|
ecUNTERMINATED_STRING = 13, ///< unterminated string constant. (Example: "3*valueof("hello)")
|
||||||
|
ecTOO_MANY_PARAMS = 14, ///< Too many function parameters
|
||||||
|
ecTOO_FEW_PARAMS = 15, ///< Too few function parameters. (Example: "ite(1<2,2)")
|
||||||
|
ecOPRT_TYPE_CONFLICT = 16, ///< binary operators may only be applied to value items of the same type
|
||||||
|
ecSTR_RESULT = 17, ///< result is a string
|
||||||
|
|
||||||
|
// Invalid Parser input Parameters
|
||||||
|
ecINVALID_NAME = 18, ///< Invalid function, variable or constant name.
|
||||||
|
ecINVALID_BINOP_IDENT = 19, ///< Invalid binary operator identifier
|
||||||
|
ecINVALID_INFIX_IDENT = 20, ///< Invalid function, variable or constant name.
|
||||||
|
ecINVALID_POSTFIX_IDENT = 21, ///< Invalid function, variable or constant name.
|
||||||
|
|
||||||
|
ecBUILTIN_OVERLOAD = 22, ///< Trying to overload builtin operator
|
||||||
|
ecINVALID_FUN_PTR = 23, ///< Invalid callback function pointer
|
||||||
|
ecINVALID_VAR_PTR = 24, ///< Invalid variable pointer
|
||||||
|
ecEMPTY_EXPRESSION = 25, ///< The Expression is empty
|
||||||
|
ecNAME_CONFLICT = 26, ///< Name conflict
|
||||||
|
ecOPT_PRI = 27, ///< Invalid operator priority
|
||||||
|
//
|
||||||
|
ecDOMAIN_ERROR = 28, ///< catch division by zero, sqrt(-1), log(0) (currently unused)
|
||||||
|
ecDIV_BY_ZERO = 29, ///< Division by zero (currently unused)
|
||||||
|
ecGENERIC = 30, ///< Generic error
|
||||||
|
ecLOCALE = 31, ///< Conflict with current locale
|
||||||
|
|
||||||
|
ecUNEXPECTED_CONDITIONAL = 32,
|
||||||
|
ecMISSING_ELSE_CLAUSE = 33,
|
||||||
|
ecMISPLACED_COLON = 34,
|
||||||
|
|
||||||
|
ecUNREASONABLE_NUMBER_OF_COMPUTATIONS = 35,
|
||||||
|
|
||||||
|
// internal errors
|
||||||
|
ecINTERNAL_ERROR = 36, ///< Internal error of any kind.
|
||||||
|
|
||||||
|
// The last two are special entries
|
||||||
|
ecCOUNT, ///< This is no error code, It just stores just the total number of error codes
|
||||||
|
ecUNDEFINED = -1 ///< Undefined message, placeholder to detect unassigned error messages
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief A class that handles the error messages.
|
||||||
|
*/
|
||||||
|
class ParserErrorMsg
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef ParserErrorMsg self_type;
|
||||||
|
|
||||||
|
ParserErrorMsg& operator=(const ParserErrorMsg &);
|
||||||
|
ParserErrorMsg(const ParserErrorMsg&);
|
||||||
|
ParserErrorMsg();
|
||||||
|
|
||||||
|
~ParserErrorMsg();
|
||||||
|
|
||||||
|
static const ParserErrorMsg& Instance();
|
||||||
|
string_type operator[](unsigned a_iIdx) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<string_type> m_vErrMsg; ///< A vector with the predefined error messages
|
||||||
|
static const self_type m_Instance; ///< The instance pointer
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Error class of the parser.
|
||||||
|
\author Ingo Berg
|
||||||
|
|
||||||
|
Part of the math parser package.
|
||||||
|
*/
|
||||||
|
class ParserError
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** \brief Replace all ocuurences of a substring with another string. */
|
||||||
|
void ReplaceSubString( string_type &strSource,
|
||||||
|
const string_type &strFind,
|
||||||
|
const string_type &strReplaceWith);
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
ParserError();
|
||||||
|
explicit ParserError(EErrorCodes a_iErrc);
|
||||||
|
explicit ParserError(const string_type &sMsg);
|
||||||
|
ParserError( EErrorCodes a_iErrc,
|
||||||
|
const string_type &sTok,
|
||||||
|
const string_type &sFormula = string_type(),
|
||||||
|
int a_iPos = -1);
|
||||||
|
ParserError( EErrorCodes a_iErrc,
|
||||||
|
int a_iPos,
|
||||||
|
const string_type &sTok);
|
||||||
|
ParserError( const char_type *a_szMsg,
|
||||||
|
int a_iPos = -1,
|
||||||
|
const string_type &sTok = string_type());
|
||||||
|
ParserError(const ParserError &a_Obj);
|
||||||
|
ParserError& operator=(const ParserError &a_Obj);
|
||||||
|
~ParserError();
|
||||||
|
|
||||||
|
void SetFormula(const string_type &a_strFormula);
|
||||||
|
const string_type& GetExpr() const;
|
||||||
|
const string_type& GetMsg() const;
|
||||||
|
int GetPos() const;
|
||||||
|
const string_type& GetToken() const;
|
||||||
|
EErrorCodes GetCode() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
string_type m_strMsg; ///< The message string
|
||||||
|
string_type m_strFormula; ///< Formula string
|
||||||
|
string_type m_strTok; ///< Token related with the error
|
||||||
|
int m_iPos; ///< Formula position related to the error
|
||||||
|
EErrorCodes m_iErrc; ///< Error code
|
||||||
|
const ParserErrorMsg &m_ErrMsg;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mu
|
||||||
|
|
||||||
|
#endif
|
60
Modules/gin/3rdparty/muParser/muParserFixes.h
vendored
Executable file
60
Modules/gin/3rdparty/muParser/muParserFixes.h
vendored
Executable file
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
__________
|
||||||
|
_____ __ __\______ \_____ _______ ______ ____ _______
|
||||||
|
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
||||||
|
| Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/
|
||||||
|
|__|_| /|____/ |____| (____ /|__| /____ > \___ >|__|
|
||||||
|
\/ \/ \/ \/
|
||||||
|
Copyright (C) 2013 Ingo Berg
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
without restriction, including without limitation the rights to use, copy, modify,
|
||||||
|
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||||
|
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MU_PARSER_FIXES_H
|
||||||
|
#define MU_PARSER_FIXES_H
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
\brief This file contains compatibility fixes for some platforms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//
|
||||||
|
// Compatibility fixes
|
||||||
|
//
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Intel Compiler
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifdef __INTEL_COMPILER
|
||||||
|
|
||||||
|
// remark #981: operands are evaluated in unspecified order
|
||||||
|
// disabled -> completely pointless if the functions do not have side effects
|
||||||
|
//
|
||||||
|
#pragma warning(disable:981)
|
||||||
|
|
||||||
|
// remark #383: value copied to temporary, reference to temporary used
|
||||||
|
#pragma warning(disable:383)
|
||||||
|
|
||||||
|
// remark #1572: floating-point equality and inequality comparisons are unreliable
|
||||||
|
// disabled -> everyone knows it, the parser passes this problem
|
||||||
|
// deliberately to the user
|
||||||
|
#pragma warning(disable:1572)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // include guard
|
125
Modules/gin/3rdparty/muParser/muParserStack.h
vendored
Executable file
125
Modules/gin/3rdparty/muParser/muParserStack.h
vendored
Executable file
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
__________
|
||||||
|
_____ __ __\______ \_____ _______ ______ ____ _______
|
||||||
|
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
||||||
|
| Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/
|
||||||
|
|__|_| /|____/ |____| (____ /|__| /____ > \___ >|__|
|
||||||
|
\/ \/ \/ \/
|
||||||
|
Copyright (C) 2004-2011 Ingo Berg
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
without restriction, including without limitation the rights to use, copy, modify,
|
||||||
|
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||||
|
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MU_PARSER_STACK_H
|
||||||
|
#define MU_PARSER_STACK_H
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <string>
|
||||||
|
#include <stack>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "muParserError.h"
|
||||||
|
#include "muParserToken.h"
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
\brief This file defines the stack used by muparser.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace mu
|
||||||
|
{
|
||||||
|
|
||||||
|
/** \brief Parser stack implementation.
|
||||||
|
|
||||||
|
Stack implementation based on a std::stack. The behaviour of pop() had been
|
||||||
|
slightly changed in order to get an error code if the stack is empty.
|
||||||
|
The stack is used within the Parser both as a value stack and as an operator stack.
|
||||||
|
|
||||||
|
\author (C) 2004-2011 Ingo Berg
|
||||||
|
*/
|
||||||
|
template <typename TValueType>
|
||||||
|
class ParserStack
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** \brief Type of the underlying stack implementation. */
|
||||||
|
typedef std::stack<TValueType, std::vector<TValueType> > impl_type;
|
||||||
|
|
||||||
|
impl_type m_Stack; ///< This is the actual stack.
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserStack()
|
||||||
|
:m_Stack()
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
virtual ~ParserStack()
|
||||||
|
{}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Pop a value from the stack.
|
||||||
|
|
||||||
|
Unlike the standard implementation this function will return the value that
|
||||||
|
is going to be taken from the stack.
|
||||||
|
|
||||||
|
\throw ParserException in case the stack is empty.
|
||||||
|
\sa pop(int &a_iErrc)
|
||||||
|
*/
|
||||||
|
TValueType pop()
|
||||||
|
{
|
||||||
|
if (empty())
|
||||||
|
throw ParserError( _T("stack is empty.") );
|
||||||
|
|
||||||
|
TValueType el = top();
|
||||||
|
m_Stack.pop();
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief Push an object into the stack.
|
||||||
|
|
||||||
|
\param a_Val object to push into the stack.
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
void push(const TValueType& a_Val)
|
||||||
|
{
|
||||||
|
m_Stack.push(a_Val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief Return the number of stored elements. */
|
||||||
|
unsigned size() const
|
||||||
|
{
|
||||||
|
return (unsigned)m_Stack.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief Returns true if stack is empty false otherwise. */
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return m_Stack.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief Return reference to the top object in the stack.
|
||||||
|
|
||||||
|
The top object is the one pushed most recently.
|
||||||
|
*/
|
||||||
|
TValueType& top()
|
||||||
|
{
|
||||||
|
return m_Stack.top();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace MathUtils
|
||||||
|
|
||||||
|
#endif
|
113
Modules/gin/3rdparty/muParser/muParserTemplateMagic.h
vendored
Executable file
113
Modules/gin/3rdparty/muParser/muParserTemplateMagic.h
vendored
Executable file
|
@ -0,0 +1,113 @@
|
||||||
|
#ifndef MU_PARSER_TEMPLATE_MAGIC_H
|
||||||
|
#define MU_PARSER_TEMPLATE_MAGIC_H
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include "muParserError.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace mu
|
||||||
|
{
|
||||||
|
//-----------------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Compile time type detection
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** \brief A class singling out integer types at compile time using
|
||||||
|
template meta programming.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
struct TypeInfo
|
||||||
|
{
|
||||||
|
static bool IsInteger() { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct TypeInfo<char>
|
||||||
|
{
|
||||||
|
static bool IsInteger() { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct TypeInfo<short>
|
||||||
|
{
|
||||||
|
static bool IsInteger() { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct TypeInfo<int>
|
||||||
|
{
|
||||||
|
static bool IsInteger() { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct TypeInfo<long>
|
||||||
|
{
|
||||||
|
static bool IsInteger() { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct TypeInfo<unsigned char>
|
||||||
|
{
|
||||||
|
static bool IsInteger() { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct TypeInfo<unsigned short>
|
||||||
|
{
|
||||||
|
static bool IsInteger() { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct TypeInfo<unsigned int>
|
||||||
|
{
|
||||||
|
static bool IsInteger() { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct TypeInfo<unsigned long>
|
||||||
|
{
|
||||||
|
static bool IsInteger() { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Standard math functions with dummy overload for integer types
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** \brief A template class for providing wrappers for essential math functions.
|
||||||
|
|
||||||
|
This template is spezialized for several types in order to provide a unified interface
|
||||||
|
for parser internal math function calls regardless of the data type.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
struct MathImpl
|
||||||
|
{
|
||||||
|
static T Sin(T v) { return sin(v); }
|
||||||
|
static T Cos(T v) { return cos(v); }
|
||||||
|
static T Tan(T v) { return tan(v); }
|
||||||
|
static T ASin(T v) { return asin(v); }
|
||||||
|
static T ACos(T v) { return acos(v); }
|
||||||
|
static T ATan(T v) { return atan(v); }
|
||||||
|
static T ATan2(T v1, T v2) { return atan2(v1, v2); }
|
||||||
|
static T Sinh(T v) { return sinh(v); }
|
||||||
|
static T Cosh(T v) { return cosh(v); }
|
||||||
|
static T Tanh(T v) { return tanh(v); }
|
||||||
|
static T ASinh(T v) { return log(v + sqrt(v * v + 1)); }
|
||||||
|
static T ACosh(T v) { return log(v + sqrt(v * v - 1)); }
|
||||||
|
static T ATanh(T v) { return ((T)0.5 * log((1 + v) / (1 - v))); }
|
||||||
|
static T Log(T v) { return log(v); }
|
||||||
|
static T Log2(T v) { return log(v)/log((T)2); } // Logarithm base 2
|
||||||
|
static T Log10(T v) { return log10(v); } // Logarithm base 10
|
||||||
|
static T Exp(T v) { return exp(v); }
|
||||||
|
static T Abs(T v) { return (v>=0) ? v : -v; }
|
||||||
|
static T Sqrt(T v) { return sqrt(v); }
|
||||||
|
static T Rint(T v) { return floor(v + (T)0.5); }
|
||||||
|
static T Sign(T v) { return (T)((v<0) ? -1 : (v>0) ? 1 : 0); }
|
||||||
|
static T Pow(T v1, T v2) { return std::pow(v1, v2); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
406
Modules/gin/3rdparty/muParser/muParserToken.h
vendored
Executable file
406
Modules/gin/3rdparty/muParser/muParserToken.h
vendored
Executable file
|
@ -0,0 +1,406 @@
|
||||||
|
/*
|
||||||
|
__________
|
||||||
|
_____ __ __\______ \_____ _______ ______ ____ _______
|
||||||
|
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
||||||
|
| Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/
|
||||||
|
|__|_| /|____/ |____| (____ /|__| /____ > \___ >|__|
|
||||||
|
\/ \/ \/ \/
|
||||||
|
Copyright (C) 2004-2013 Ingo Berg
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
without restriction, including without limitation the rights to use, copy, modify,
|
||||||
|
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||||
|
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MU_PARSER_TOKEN_H
|
||||||
|
#define MU_PARSER_TOKEN_H
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <string>
|
||||||
|
#include <stack>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "muParserError.h"
|
||||||
|
#include "muParserCallback.h"
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
\brief This file contains the parser token definition.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace mu
|
||||||
|
{
|
||||||
|
/** \brief Encapsulation of the data for a single formula token.
|
||||||
|
|
||||||
|
Formula token implementation. Part of the Math Parser Package.
|
||||||
|
Formula tokens can be either one of the following:
|
||||||
|
<ul>
|
||||||
|
<li>value</li>
|
||||||
|
<li>variable</li>
|
||||||
|
<li>function with numerical arguments</li>
|
||||||
|
<li>functions with a string as argument</li>
|
||||||
|
<li>prefix operators</li>
|
||||||
|
<li>infix operators</li>
|
||||||
|
<li>binary operator</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
\author (C) 2004-2013 Ingo Berg
|
||||||
|
*/
|
||||||
|
template<typename TBase, typename TString>
|
||||||
|
class ParserToken
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
ECmdCode m_iCode; ///< Type of the token; The token type is a constant of type #ECmdCode.
|
||||||
|
ETypeCode m_iType;
|
||||||
|
void *m_pTok; ///< Stores Token pointer; not applicable for all tokens
|
||||||
|
int m_iIdx; ///< An otional index to an external buffer storing the token data
|
||||||
|
TString m_strTok; ///< Token string
|
||||||
|
TString m_strVal; ///< Value for string variables
|
||||||
|
value_type m_fVal; ///< the value
|
||||||
|
std::unique_ptr<ParserCallback> m_pCallback;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Constructor (default).
|
||||||
|
|
||||||
|
Sets token to an neutral state of type cmUNKNOWN.
|
||||||
|
\throw nothrow
|
||||||
|
\sa ECmdCode
|
||||||
|
*/
|
||||||
|
ParserToken()
|
||||||
|
:m_iCode(cmUNKNOWN)
|
||||||
|
,m_iType(tpVOID)
|
||||||
|
,m_pTok(nullptr)
|
||||||
|
,m_iIdx(-1)
|
||||||
|
,m_strTok()
|
||||||
|
,m_strVal()
|
||||||
|
,m_fVal(0)
|
||||||
|
,m_pCallback()
|
||||||
|
{}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Create token from another one.
|
||||||
|
|
||||||
|
Implemented by calling Assign(...)
|
||||||
|
\throw nothrow
|
||||||
|
\post m_iType==cmUNKNOWN
|
||||||
|
\sa #Assign
|
||||||
|
*/
|
||||||
|
ParserToken(const ParserToken &a_Tok)
|
||||||
|
{
|
||||||
|
Assign(a_Tok);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Assignment operator.
|
||||||
|
|
||||||
|
Copy token state from another token and return this.
|
||||||
|
Implemented by calling Assign(...).
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
ParserToken& operator=(const ParserToken &a_Tok)
|
||||||
|
{
|
||||||
|
Assign(a_Tok);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Copy token information from argument.
|
||||||
|
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
void Assign(const ParserToken &a_Tok)
|
||||||
|
{
|
||||||
|
m_iCode = a_Tok.m_iCode;
|
||||||
|
m_pTok = a_Tok.m_pTok;
|
||||||
|
m_strTok = a_Tok.m_strTok;
|
||||||
|
m_iIdx = a_Tok.m_iIdx;
|
||||||
|
m_strVal = a_Tok.m_strVal;
|
||||||
|
m_iType = a_Tok.m_iType;
|
||||||
|
m_fVal = a_Tok.m_fVal;
|
||||||
|
// create new callback object if a_Tok has one
|
||||||
|
m_pCallback.reset(a_Tok.m_pCallback.get() ? a_Tok.m_pCallback->Clone() : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Assign a token type.
|
||||||
|
|
||||||
|
Token may not be of type value, variable or function. Those have separate set functions.
|
||||||
|
|
||||||
|
\pre [assert] a_iType!=cmVAR
|
||||||
|
\pre [assert] a_iType!=cmVAL
|
||||||
|
\pre [assert] a_iType!=cmFUNC
|
||||||
|
\post m_fVal = 0
|
||||||
|
\post m_pTok = 0
|
||||||
|
*/
|
||||||
|
ParserToken& Set(ECmdCode a_iType, const TString &a_strTok=TString())
|
||||||
|
{
|
||||||
|
// The following types can't be set this way, they have special Set functions
|
||||||
|
assert(a_iType!=cmVAR);
|
||||||
|
assert(a_iType!=cmVAL);
|
||||||
|
assert(a_iType!=cmFUNC);
|
||||||
|
|
||||||
|
m_iCode = a_iType;
|
||||||
|
m_iType = tpVOID;
|
||||||
|
m_pTok = nullptr;
|
||||||
|
m_strTok = a_strTok;
|
||||||
|
m_iIdx = -1;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Set Callback type. */
|
||||||
|
ParserToken& Set(const ParserCallback &a_pCallback, const TString &a_sTok)
|
||||||
|
{
|
||||||
|
assert(a_pCallback.GetAddr());
|
||||||
|
|
||||||
|
m_iCode = a_pCallback.GetCode();
|
||||||
|
m_iType = tpVOID;
|
||||||
|
m_strTok = a_sTok;
|
||||||
|
m_pCallback.reset(new ParserCallback(a_pCallback));
|
||||||
|
|
||||||
|
m_pTok = nullptr;
|
||||||
|
m_iIdx = -1;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Make this token a value token.
|
||||||
|
|
||||||
|
Member variables not necessary for value tokens will be invalidated.
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
ParserToken& SetVal(TBase a_fVal, const TString &a_strTok=TString())
|
||||||
|
{
|
||||||
|
m_iCode = cmVAL;
|
||||||
|
m_iType = tpDBL;
|
||||||
|
m_fVal = a_fVal;
|
||||||
|
m_strTok = a_strTok;
|
||||||
|
m_iIdx = -1;
|
||||||
|
|
||||||
|
m_pTok = nullptr;
|
||||||
|
m_pCallback.reset(nullptr);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief make this token a variable token.
|
||||||
|
|
||||||
|
Member variables not necessary for variable tokens will be invalidated.
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
ParserToken& SetVar(TBase *a_pVar, const TString &a_strTok)
|
||||||
|
{
|
||||||
|
m_iCode = cmVAR;
|
||||||
|
m_iType = tpDBL;
|
||||||
|
m_strTok = a_strTok;
|
||||||
|
m_iIdx = -1;
|
||||||
|
m_pTok = (void*)a_pVar;
|
||||||
|
m_pCallback.reset(nullptr);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Make this token a variable token.
|
||||||
|
|
||||||
|
Member variables not necessary for variable tokens will be invalidated.
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
ParserToken& SetString(const TString &a_strTok, std::size_t a_iSize)
|
||||||
|
{
|
||||||
|
m_iCode = cmSTRING;
|
||||||
|
m_iType = tpSTR;
|
||||||
|
m_strTok = a_strTok;
|
||||||
|
m_iIdx = static_cast<int>(a_iSize);
|
||||||
|
|
||||||
|
m_pTok = nullptr;
|
||||||
|
m_pCallback.reset(nullptr);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Set an index associated with the token related data.
|
||||||
|
|
||||||
|
In cmSTRFUNC - This is the index to a string table in the main parser.
|
||||||
|
\param a_iIdx The index the string function result will take in the bytecode parser.
|
||||||
|
\throw exception_type if #a_iIdx<0 or #m_iType!=cmSTRING
|
||||||
|
*/
|
||||||
|
void SetIdx(int a_iIdx)
|
||||||
|
{
|
||||||
|
if (m_iCode!=cmSTRING || a_iIdx<0)
|
||||||
|
throw ParserError(ecINTERNAL_ERROR);
|
||||||
|
|
||||||
|
m_iIdx = a_iIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Return Index associated with the token related data.
|
||||||
|
|
||||||
|
In cmSTRFUNC - This is the index to a string table in the main parser.
|
||||||
|
|
||||||
|
\throw exception_type if #m_iIdx<0 or #m_iType!=cmSTRING
|
||||||
|
\return The index the result will take in the Bytecode calculatin array (#m_iIdx).
|
||||||
|
*/
|
||||||
|
int GetIdx() const
|
||||||
|
{
|
||||||
|
if (m_iIdx<0 || m_iCode!=cmSTRING )
|
||||||
|
throw ParserError(ecINTERNAL_ERROR);
|
||||||
|
|
||||||
|
return m_iIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Return the token type.
|
||||||
|
|
||||||
|
\return #m_iType
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
ECmdCode GetCode() const
|
||||||
|
{
|
||||||
|
if (m_pCallback.get())
|
||||||
|
{
|
||||||
|
return m_pCallback->GetCode();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return m_iCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
ETypeCode GetType() const
|
||||||
|
{
|
||||||
|
if (m_pCallback.get())
|
||||||
|
{
|
||||||
|
return m_pCallback->GetType();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return m_iType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int GetPri() const
|
||||||
|
{
|
||||||
|
if ( !m_pCallback.get())
|
||||||
|
throw ParserError(ecINTERNAL_ERROR);
|
||||||
|
|
||||||
|
if ( m_pCallback->GetCode()!=cmOPRT_BIN && m_pCallback->GetCode()!=cmOPRT_INFIX)
|
||||||
|
throw ParserError(ecINTERNAL_ERROR);
|
||||||
|
|
||||||
|
return m_pCallback->GetPri();
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
EOprtAssociativity GetAssociativity() const
|
||||||
|
{
|
||||||
|
if (m_pCallback.get()==nullptr || m_pCallback->GetCode()!=cmOPRT_BIN)
|
||||||
|
throw ParserError(ecINTERNAL_ERROR);
|
||||||
|
|
||||||
|
return m_pCallback->GetAssociativity();
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Return the address of the callback function assoziated with
|
||||||
|
function and operator tokens.
|
||||||
|
|
||||||
|
\return The pointer stored in #m_pTok.
|
||||||
|
\throw exception_type if token type is non of:
|
||||||
|
<ul>
|
||||||
|
<li>cmFUNC</li>
|
||||||
|
<li>cmSTRFUNC</li>
|
||||||
|
<li>cmPOSTOP</li>
|
||||||
|
<li>cmINFIXOP</li>
|
||||||
|
<li>cmOPRT_BIN</li>
|
||||||
|
</ul>
|
||||||
|
\sa ECmdCode
|
||||||
|
*/
|
||||||
|
generic_fun_type GetFuncAddr() const
|
||||||
|
{
|
||||||
|
return (m_pCallback.get()) ? (generic_fun_type)m_pCallback->GetAddr() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* GetParam() const
|
||||||
|
{
|
||||||
|
return (m_pCallback.get()) ? m_pCallback->GetParam() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \biref Get value of the token.
|
||||||
|
|
||||||
|
Only applicable to variable and value tokens.
|
||||||
|
\throw exception_type if token is no value/variable token.
|
||||||
|
*/
|
||||||
|
TBase GetVal() const
|
||||||
|
{
|
||||||
|
switch (m_iCode)
|
||||||
|
{
|
||||||
|
case cmVAL: return m_fVal;
|
||||||
|
case cmVAR: return *((TBase*)m_pTok);
|
||||||
|
default: throw ParserError(ecVAL_EXPECTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Get address of a variable token.
|
||||||
|
|
||||||
|
Valid only if m_iType==CmdVar.
|
||||||
|
\throw exception_type if token is no variable token.
|
||||||
|
*/
|
||||||
|
TBase* GetVar() const
|
||||||
|
{
|
||||||
|
if (m_iCode!=cmVAR)
|
||||||
|
throw ParserError(ecINTERNAL_ERROR);
|
||||||
|
|
||||||
|
return (TBase*)m_pTok;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Return the number of function arguments.
|
||||||
|
|
||||||
|
Valid only if m_iType==CmdFUNC.
|
||||||
|
*/
|
||||||
|
int GetArgCount() const
|
||||||
|
{
|
||||||
|
assert(m_pCallback.get());
|
||||||
|
|
||||||
|
if (!m_pCallback->GetAddr())
|
||||||
|
throw ParserError(ecINTERNAL_ERROR);
|
||||||
|
|
||||||
|
return m_pCallback->GetArgc();
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \brief Return the token identifier.
|
||||||
|
|
||||||
|
If #m_iType is cmSTRING the token identifier is the value of the string argument
|
||||||
|
for a string function.
|
||||||
|
\return #m_strTok
|
||||||
|
\throw nothrow
|
||||||
|
\sa m_strTok
|
||||||
|
*/
|
||||||
|
const TString& GetAsString() const
|
||||||
|
{
|
||||||
|
return m_strTok;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace mu
|
||||||
|
|
||||||
|
#endif
|
982
Modules/gin/3rdparty/muParser/muParserTokenReader.cpp
vendored
Executable file
982
Modules/gin/3rdparty/muParser/muParserTokenReader.cpp
vendored
Executable file
|
@ -0,0 +1,982 @@
|
||||||
|
/*
|
||||||
|
__________
|
||||||
|
_____ __ __\______ \_____ _______ ______ ____ _______
|
||||||
|
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
||||||
|
| Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/
|
||||||
|
|__|_| /|____/ |____| (____ /|__| /____ > \___ >|__|
|
||||||
|
\/ \/ \/ \/
|
||||||
|
Copyright (C) 2013 Ingo Berg
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
without restriction, including without limitation the rights to use, copy, modify,
|
||||||
|
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||||
|
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <map>
|
||||||
|
#include <stack>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "muParserTokenReader.h"
|
||||||
|
#include "muParserBase.h"
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
\brief This file contains the parser token reader implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4310)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __clang__
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wconversion"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace mu
|
||||||
|
{
|
||||||
|
|
||||||
|
// Forward declaration
|
||||||
|
class ParserBase;
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Copy constructor.
|
||||||
|
|
||||||
|
\sa Assign
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
ParserTokenReader::ParserTokenReader(const ParserTokenReader &a_Reader)
|
||||||
|
{
|
||||||
|
Assign(a_Reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Assignment operator.
|
||||||
|
|
||||||
|
Self assignment will be suppressed otherwise #Assign is called.
|
||||||
|
|
||||||
|
\param a_Reader Object to copy to this token reader.
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
ParserTokenReader& ParserTokenReader::operator=(const ParserTokenReader &a_Reader)
|
||||||
|
{
|
||||||
|
if (&a_Reader!=this)
|
||||||
|
Assign(a_Reader);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Assign state of a token reader to this token reader.
|
||||||
|
|
||||||
|
\param a_Reader Object from which the state should be copied.
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
void ParserTokenReader::Assign(const ParserTokenReader &a_Reader)
|
||||||
|
{
|
||||||
|
m_pParser = a_Reader.m_pParser;
|
||||||
|
m_strFormula = a_Reader.m_strFormula;
|
||||||
|
m_iPos = a_Reader.m_iPos;
|
||||||
|
m_iSynFlags = a_Reader.m_iSynFlags;
|
||||||
|
|
||||||
|
m_UsedVar = a_Reader.m_UsedVar;
|
||||||
|
m_pFunDef = a_Reader.m_pFunDef;
|
||||||
|
m_pConstDef = a_Reader.m_pConstDef;
|
||||||
|
m_pVarDef = a_Reader.m_pVarDef;
|
||||||
|
m_pStrVarDef = a_Reader.m_pStrVarDef;
|
||||||
|
m_pPostOprtDef = a_Reader.m_pPostOprtDef;
|
||||||
|
m_pInfixOprtDef = a_Reader.m_pInfixOprtDef;
|
||||||
|
m_pOprtDef = a_Reader.m_pOprtDef;
|
||||||
|
m_bIgnoreUndefVar = a_Reader.m_bIgnoreUndefVar;
|
||||||
|
m_vIdentFun = a_Reader.m_vIdentFun;
|
||||||
|
m_pFactory = a_Reader.m_pFactory;
|
||||||
|
m_pFactoryData = a_Reader.m_pFactoryData;
|
||||||
|
m_iBrackets = a_Reader.m_iBrackets;
|
||||||
|
m_cArgSep = a_Reader.m_cArgSep;
|
||||||
|
m_fZero = a_Reader.m_fZero;
|
||||||
|
m_lastTok = a_Reader.m_lastTok;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Constructor.
|
||||||
|
|
||||||
|
Create a Token reader and bind it to a parser object.
|
||||||
|
|
||||||
|
\pre [assert] a_pParser may not be NULL
|
||||||
|
\post #m_pParser==a_pParser
|
||||||
|
\param a_pParent Parent parser object of the token reader.
|
||||||
|
*/
|
||||||
|
ParserTokenReader::ParserTokenReader(ParserBase *a_pParent)
|
||||||
|
:m_pParser(a_pParent)
|
||||||
|
,m_strFormula()
|
||||||
|
,m_iPos(0)
|
||||||
|
,m_iSynFlags(0)
|
||||||
|
,m_bIgnoreUndefVar(false)
|
||||||
|
,m_pFunDef(nullptr)
|
||||||
|
,m_pPostOprtDef(nullptr)
|
||||||
|
,m_pInfixOprtDef(nullptr)
|
||||||
|
,m_pOprtDef(nullptr)
|
||||||
|
,m_pConstDef(nullptr)
|
||||||
|
,m_pStrVarDef(nullptr)
|
||||||
|
,m_pVarDef(nullptr)
|
||||||
|
,m_pFactory(nullptr)
|
||||||
|
,m_pFactoryData(nullptr)
|
||||||
|
,m_vIdentFun()
|
||||||
|
,m_UsedVar()
|
||||||
|
,m_fZero(0)
|
||||||
|
,m_iBrackets(0)
|
||||||
|
,m_lastTok()
|
||||||
|
,m_cArgSep(',')
|
||||||
|
{
|
||||||
|
assert(m_pParser);
|
||||||
|
SetParent(m_pParser);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Create instance of a ParserTokenReader identical with this
|
||||||
|
and return its pointer.
|
||||||
|
|
||||||
|
This is a factory method the calling function must take care of the object destruction.
|
||||||
|
|
||||||
|
\return A new ParserTokenReader object.
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
ParserTokenReader* ParserTokenReader::Clone(ParserBase *a_pParent) const
|
||||||
|
{
|
||||||
|
std::unique_ptr<ParserTokenReader> ptr(new ParserTokenReader(*this));
|
||||||
|
ptr->SetParent(a_pParent);
|
||||||
|
return ptr.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
ParserTokenReader::token_type& ParserTokenReader::SaveBeforeReturn(const token_type &tok)
|
||||||
|
{
|
||||||
|
m_lastTok = tok;
|
||||||
|
return m_lastTok;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
void ParserTokenReader::AddValIdent(identfun_type a_pCallback)
|
||||||
|
{
|
||||||
|
// Use push_front is used to give user defined callbacks a higher priority than
|
||||||
|
// the built in ones. Otherwise reading hex numbers would not work
|
||||||
|
// since the "0" in "0xff" would always be read first making parsing of
|
||||||
|
// the rest impossible.
|
||||||
|
// reference:
|
||||||
|
// http://sourceforge.net/projects/muparser/forums/forum/462843/topic/4824956
|
||||||
|
m_vIdentFun.push_front(a_pCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
void ParserTokenReader::SetVarCreator(facfun_type a_pFactory, void *pUserData)
|
||||||
|
{
|
||||||
|
m_pFactory = a_pFactory;
|
||||||
|
m_pFactoryData = pUserData;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Return the current position of the token reader in the formula string.
|
||||||
|
|
||||||
|
\return #m_iPos
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
int ParserTokenReader::GetPos() const
|
||||||
|
{
|
||||||
|
return m_iPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Return a reference to the formula.
|
||||||
|
|
||||||
|
\return #m_strFormula
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
const string_type& ParserTokenReader::GetExpr() const
|
||||||
|
{
|
||||||
|
return m_strFormula;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Return a map containing the used variables only. */
|
||||||
|
varmap_type& ParserTokenReader::GetUsedVar()
|
||||||
|
{
|
||||||
|
return m_UsedVar;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Initialize the token Reader.
|
||||||
|
|
||||||
|
Sets the formula position index to zero and set Syntax flags to default for initial formula parsing.
|
||||||
|
\pre [assert] triggered if a_szFormula==0
|
||||||
|
*/
|
||||||
|
void ParserTokenReader::SetFormula(const string_type &a_strFormula)
|
||||||
|
{
|
||||||
|
m_strFormula = a_strFormula;
|
||||||
|
ReInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Set Flag that controls behaviour in case of undefined variables being found.
|
||||||
|
|
||||||
|
If true, the parser does not throw an exception if an undefined variable is found.
|
||||||
|
otherwise it does. This variable is used internally only!
|
||||||
|
It suppresses a "undefined variable" exception in GetUsedVar().
|
||||||
|
Those function should return a complete list of variables including
|
||||||
|
those the are not defined by the time of it's call.
|
||||||
|
*/
|
||||||
|
void ParserTokenReader::IgnoreUndefVar(bool bIgnore)
|
||||||
|
{
|
||||||
|
m_bIgnoreUndefVar = bIgnore;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Reset the token reader to the start of the formula.
|
||||||
|
|
||||||
|
The syntax flags will be reset to a value appropriate for the
|
||||||
|
start of a formula.
|
||||||
|
\post #m_iPos==0, #m_iSynFlags = noOPT | noBC | noPOSTOP | noSTR
|
||||||
|
\throw nothrow
|
||||||
|
\sa ESynCodes
|
||||||
|
*/
|
||||||
|
void ParserTokenReader::ReInit()
|
||||||
|
{
|
||||||
|
m_iPos = 0;
|
||||||
|
m_iSynFlags = sfSTART_OF_LINE;
|
||||||
|
m_iBrackets = 0;
|
||||||
|
m_UsedVar.clear();
|
||||||
|
m_lastTok = token_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Read the next token from the string. */
|
||||||
|
ParserTokenReader::token_type ParserTokenReader::ReadNextToken()
|
||||||
|
{
|
||||||
|
assert(m_pParser);
|
||||||
|
|
||||||
|
const char_type *szFormula = m_strFormula.c_str();
|
||||||
|
token_type tok;
|
||||||
|
|
||||||
|
// Ignore all non printable characters when reading the expression
|
||||||
|
while (szFormula[m_iPos]>0 && szFormula[m_iPos]<=0x20)
|
||||||
|
++m_iPos;
|
||||||
|
|
||||||
|
if ( IsEOF(tok) ) return SaveBeforeReturn(tok); // Check for end of formula
|
||||||
|
if ( IsOprt(tok) ) return SaveBeforeReturn(tok); // Check for user defined binary operator
|
||||||
|
if ( IsFunTok(tok) ) return SaveBeforeReturn(tok); // Check for function token
|
||||||
|
if ( IsBuiltIn(tok) ) return SaveBeforeReturn(tok); // Check built in operators / tokens
|
||||||
|
if ( IsArgSep(tok) ) return SaveBeforeReturn(tok); // Check for function argument separators
|
||||||
|
if ( IsValTok(tok) ) return SaveBeforeReturn(tok); // Check for values / constant tokens
|
||||||
|
if ( IsVarTok(tok) ) return SaveBeforeReturn(tok); // Check for variable tokens
|
||||||
|
if ( IsStrVarTok(tok) ) return SaveBeforeReturn(tok); // Check for string variables
|
||||||
|
if ( IsString(tok) ) return SaveBeforeReturn(tok); // Check for String tokens
|
||||||
|
if ( IsInfixOpTok(tok) ) return SaveBeforeReturn(tok); // Check for unary operators
|
||||||
|
if ( IsPostOpTok(tok) ) return SaveBeforeReturn(tok); // Check for unary operators
|
||||||
|
|
||||||
|
// Check String for undefined variable token. Done only if a
|
||||||
|
// flag is set indicating to ignore undefined variables.
|
||||||
|
// This is a way to conditionally avoid an error if
|
||||||
|
// undefined variables occur.
|
||||||
|
// (The GetUsedVar function must suppress the error for
|
||||||
|
// undefined variables in order to collect all variable
|
||||||
|
// names including the undefined ones.)
|
||||||
|
if ( (m_bIgnoreUndefVar || m_pFactory) && IsUndefVarTok(tok) )
|
||||||
|
return SaveBeforeReturn(tok);
|
||||||
|
|
||||||
|
// Check for unknown token
|
||||||
|
//
|
||||||
|
// !!! From this point on there is no exit without an exception possible...
|
||||||
|
//
|
||||||
|
string_type strTok;
|
||||||
|
int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos);
|
||||||
|
if (iEnd!=m_iPos)
|
||||||
|
Error(ecUNASSIGNABLE_TOKEN, m_iPos, strTok);
|
||||||
|
|
||||||
|
Error(ecUNASSIGNABLE_TOKEN, m_iPos, m_strFormula.substr(m_iPos));
|
||||||
|
return token_type(); // never reached
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
void ParserTokenReader::SetParent(ParserBase *a_pParent)
|
||||||
|
{
|
||||||
|
m_pParser = a_pParent;
|
||||||
|
m_pFunDef = &a_pParent->m_FunDef;
|
||||||
|
m_pOprtDef = &a_pParent->m_OprtDef;
|
||||||
|
m_pInfixOprtDef = &a_pParent->m_InfixOprtDef;
|
||||||
|
m_pPostOprtDef = &a_pParent->m_PostOprtDef;
|
||||||
|
m_pVarDef = &a_pParent->m_VarDef;
|
||||||
|
m_pStrVarDef = &a_pParent->m_StrVarDef;
|
||||||
|
m_pConstDef = &a_pParent->m_ConstDef;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Extract all characters that belong to a certain charset.
|
||||||
|
|
||||||
|
\param a_szCharSet [in] Const char array of the characters allowed in the token.
|
||||||
|
\param a_strTok [out] The string that consists entirely of characters listed in a_szCharSet.
|
||||||
|
\param a_iPos [in] Position in the string from where to start reading.
|
||||||
|
\return The Position of the first character not listed in a_szCharSet.
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
int ParserTokenReader::ExtractToken(const char_type *a_szCharSet,
|
||||||
|
string_type &a_sTok,
|
||||||
|
int a_iPos) const
|
||||||
|
{
|
||||||
|
int iEnd = (int)m_strFormula.find_first_not_of(a_szCharSet, a_iPos);
|
||||||
|
|
||||||
|
if (iEnd==(int)string_type::npos)
|
||||||
|
iEnd = (int)m_strFormula.length();
|
||||||
|
|
||||||
|
// Assign token string if there was something found
|
||||||
|
if (a_iPos!=iEnd)
|
||||||
|
a_sTok = string_type( m_strFormula.begin()+a_iPos, m_strFormula.begin()+iEnd);
|
||||||
|
|
||||||
|
return iEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Check Expression for the presence of a binary operator token.
|
||||||
|
|
||||||
|
Userdefined binary operator "++" gives inconsistent parsing result for
|
||||||
|
the equations "a++b" and "a ++ b" if alphabetic characters are allowed
|
||||||
|
in operator tokens. To avoid this this function checks specifically
|
||||||
|
for operator tokens.
|
||||||
|
*/
|
||||||
|
int ParserTokenReader::ExtractOperatorToken(string_type &a_sTok,
|
||||||
|
int a_iPos) const
|
||||||
|
{
|
||||||
|
// Changed as per Issue 6: https://code.google.com/p/muparser/issues/detail?id=6
|
||||||
|
int iEnd = (int)m_strFormula.find_first_not_of(m_pParser->ValidOprtChars(), a_iPos);
|
||||||
|
if (iEnd==(int)string_type::npos)
|
||||||
|
iEnd = (int)m_strFormula.length();
|
||||||
|
|
||||||
|
// Assign token string if there was something found
|
||||||
|
if (a_iPos!=iEnd)
|
||||||
|
{
|
||||||
|
a_sTok = string_type( m_strFormula.begin() + a_iPos, m_strFormula.begin() + iEnd);
|
||||||
|
return iEnd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// There is still the chance of having to deal with an operator consisting exclusively
|
||||||
|
// of alphabetic characters.
|
||||||
|
return ExtractToken(MUP_CHARS, a_sTok, a_iPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Check if a built in operator or other token can be found
|
||||||
|
\param a_Tok [out] Operator token if one is found. This can either be a binary operator or an infix operator token.
|
||||||
|
\return true if an operator token has been found.
|
||||||
|
*/
|
||||||
|
bool ParserTokenReader::IsBuiltIn(token_type &a_Tok)
|
||||||
|
{
|
||||||
|
const char_type **const pOprtDef = m_pParser->GetOprtDef(),
|
||||||
|
*const szFormula = m_strFormula.c_str();
|
||||||
|
|
||||||
|
// Compare token with function and operator strings
|
||||||
|
// check string for operator/function
|
||||||
|
for (int i=0; pOprtDef[i]; i++)
|
||||||
|
{
|
||||||
|
std::size_t len( std::char_traits<char_type>::length(pOprtDef[i]) );
|
||||||
|
if ( string_type(pOprtDef[i]) == string_type(szFormula + m_iPos, szFormula + m_iPos + len) )
|
||||||
|
{
|
||||||
|
switch(i)
|
||||||
|
{
|
||||||
|
//case cmAND:
|
||||||
|
//case cmOR:
|
||||||
|
//case cmXOR:
|
||||||
|
case cmLAND:
|
||||||
|
case cmLOR:
|
||||||
|
case cmLT:
|
||||||
|
case cmGT:
|
||||||
|
case cmLE:
|
||||||
|
case cmGE:
|
||||||
|
case cmNEQ:
|
||||||
|
case cmEQ:
|
||||||
|
case cmADD:
|
||||||
|
case cmSUB:
|
||||||
|
case cmMUL:
|
||||||
|
case cmDIV:
|
||||||
|
case cmPOW:
|
||||||
|
case cmASSIGN:
|
||||||
|
//if (len!=sTok.length())
|
||||||
|
// continue;
|
||||||
|
|
||||||
|
// The assignment operator need special treatment
|
||||||
|
if (i==cmASSIGN && m_iSynFlags & noASSIGN)
|
||||||
|
Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]);
|
||||||
|
|
||||||
|
if (!m_pParser->HasBuiltInOprt()) continue;
|
||||||
|
if (m_iSynFlags & noOPT)
|
||||||
|
{
|
||||||
|
// Maybe its an infix operator not an operator
|
||||||
|
// Both operator types can share characters in
|
||||||
|
// their identifiers
|
||||||
|
if ( IsInfixOpTok(a_Tok) )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE | noEND;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmBO:
|
||||||
|
if (m_iSynFlags & noBO)
|
||||||
|
Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]);
|
||||||
|
|
||||||
|
if (m_lastTok.GetCode()==cmFUNC)
|
||||||
|
m_iSynFlags = noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE;
|
||||||
|
else
|
||||||
|
m_iSynFlags = noBC | noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN| noIF | noELSE;
|
||||||
|
|
||||||
|
++m_iBrackets;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmBC:
|
||||||
|
if (m_iSynFlags & noBC)
|
||||||
|
Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]);
|
||||||
|
|
||||||
|
m_iSynFlags = noBO | noVAR | noVAL | noFUN | noINFIXOP | noSTR | noASSIGN;
|
||||||
|
|
||||||
|
if (--m_iBrackets<0)
|
||||||
|
Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmELSE:
|
||||||
|
if (m_iSynFlags & noELSE)
|
||||||
|
Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]);
|
||||||
|
|
||||||
|
m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cmIF:
|
||||||
|
if (m_iSynFlags & noIF)
|
||||||
|
Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]);
|
||||||
|
|
||||||
|
m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // The operator is listed in c_DefaultOprt, but not here. This is a bad thing...
|
||||||
|
Error(ecINTERNAL_ERROR);
|
||||||
|
} // switch operator id
|
||||||
|
|
||||||
|
m_iPos += (int)len;
|
||||||
|
a_Tok.Set( (ECmdCode)i, pOprtDef[i] );
|
||||||
|
return true;
|
||||||
|
} // if operator string found
|
||||||
|
} // end of for all operator strings
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
bool ParserTokenReader::IsArgSep(token_type &a_Tok)
|
||||||
|
{
|
||||||
|
const char_type* szFormula = m_strFormula.c_str();
|
||||||
|
|
||||||
|
if (szFormula[m_iPos]==m_cArgSep)
|
||||||
|
{
|
||||||
|
// copy the separator into null terminated string
|
||||||
|
char_type szSep[2];
|
||||||
|
szSep[0] = m_cArgSep;
|
||||||
|
szSep[1] = 0;
|
||||||
|
|
||||||
|
if (m_iSynFlags & noARG_SEP)
|
||||||
|
Error(ecUNEXPECTED_ARG_SEP, m_iPos, szSep);
|
||||||
|
|
||||||
|
m_iSynFlags = noBC | noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN;
|
||||||
|
m_iPos++;
|
||||||
|
a_Tok.Set(cmARG_SEP, szSep);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Check for End of Formula.
|
||||||
|
|
||||||
|
\return true if an end of formula is found false otherwise.
|
||||||
|
\param a_Tok [out] If an eof is found the corresponding token will be stored there.
|
||||||
|
\throw nothrow
|
||||||
|
\sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsString, IsInfixOpTok, IsPostOpTok
|
||||||
|
*/
|
||||||
|
bool ParserTokenReader::IsEOF(token_type &a_Tok)
|
||||||
|
{
|
||||||
|
const char_type* szFormula = m_strFormula.c_str();
|
||||||
|
|
||||||
|
// check for EOF
|
||||||
|
if ( !szFormula[m_iPos] /*|| szFormula[m_iPos] == '\n'*/)
|
||||||
|
{
|
||||||
|
if ( m_iSynFlags & noEND )
|
||||||
|
Error(ecUNEXPECTED_EOF, m_iPos);
|
||||||
|
|
||||||
|
if (m_iBrackets>0)
|
||||||
|
Error(ecMISSING_PARENS, m_iPos, _T(")"));
|
||||||
|
|
||||||
|
m_iSynFlags = 0;
|
||||||
|
a_Tok.Set(cmEND);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Check if a string position contains a unary infix operator.
|
||||||
|
\return true if a function token has been found false otherwise.
|
||||||
|
*/
|
||||||
|
bool ParserTokenReader::IsInfixOpTok(token_type &a_Tok)
|
||||||
|
{
|
||||||
|
string_type sTok;
|
||||||
|
int iEnd = ExtractToken(m_pParser->ValidInfixOprtChars(), sTok, m_iPos);
|
||||||
|
if (iEnd==m_iPos)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// iterate over all postfix operator strings
|
||||||
|
funmap_type::const_reverse_iterator it = m_pInfixOprtDef->rbegin();
|
||||||
|
for ( ; it!=m_pInfixOprtDef->rend(); ++it)
|
||||||
|
{
|
||||||
|
if (sTok.find(it->first)!=0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
a_Tok.Set(it->second, it->first);
|
||||||
|
m_iPos += (int)it->first.length();
|
||||||
|
|
||||||
|
if (m_iSynFlags & noINFIXOP)
|
||||||
|
Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString());
|
||||||
|
|
||||||
|
m_iSynFlags = noPOSTOP | noINFIXOP | noOPT | noBC | noSTR | noASSIGN;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
a_Tok.Set(item->second, sTok);
|
||||||
|
m_iPos = (int)iEnd;
|
||||||
|
|
||||||
|
if (m_iSynFlags & noINFIXOP)
|
||||||
|
Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString());
|
||||||
|
|
||||||
|
m_iSynFlags = noPOSTOP | noINFIXOP | noOPT | noBC | noSTR | noASSIGN;
|
||||||
|
return true;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Check whether the token at a given position is a function token.
|
||||||
|
\param a_Tok [out] If a value token is found it will be placed here.
|
||||||
|
\throw ParserException if Syntaxflags do not allow a function at a_iPos
|
||||||
|
\return true if a function token has been found false otherwise.
|
||||||
|
\pre [assert] m_pParser!=0
|
||||||
|
*/
|
||||||
|
bool ParserTokenReader::IsFunTok(token_type &a_Tok)
|
||||||
|
{
|
||||||
|
string_type strTok;
|
||||||
|
int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos);
|
||||||
|
if (iEnd==m_iPos)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
funmap_type::const_iterator item = m_pFunDef->find(strTok);
|
||||||
|
if (item==m_pFunDef->end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if the next sign is an opening bracket
|
||||||
|
const char_type *szFormula = m_strFormula.c_str();
|
||||||
|
if (szFormula[iEnd]!='(')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
a_Tok.Set(item->second, strTok);
|
||||||
|
|
||||||
|
m_iPos = (int)iEnd;
|
||||||
|
if (m_iSynFlags & noFUN)
|
||||||
|
Error(ecUNEXPECTED_FUN, m_iPos-(int)a_Tok.GetAsString().length(), a_Tok.GetAsString());
|
||||||
|
|
||||||
|
m_iSynFlags = noANY ^ noBO;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Check if a string position contains a binary operator.
|
||||||
|
\param a_Tok [out] Operator token if one is found. This can either be a binary operator or an infix operator token.
|
||||||
|
\return true if an operator token has been found.
|
||||||
|
*/
|
||||||
|
bool ParserTokenReader::IsOprt(token_type &a_Tok)
|
||||||
|
{
|
||||||
|
const char_type *const szExpr = m_strFormula.c_str();
|
||||||
|
string_type strTok;
|
||||||
|
|
||||||
|
int iEnd = ExtractOperatorToken(strTok, m_iPos);
|
||||||
|
if (iEnd==m_iPos)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if the operator is a built in operator, if so ignore it here
|
||||||
|
const char_type **const pOprtDef = m_pParser->GetOprtDef();
|
||||||
|
for (int i=0; m_pParser->HasBuiltInOprt() && pOprtDef[i]; ++i)
|
||||||
|
{
|
||||||
|
if (string_type(pOprtDef[i])==strTok)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note:
|
||||||
|
// All tokens in oprt_bin_maptype are have been sorted by their length
|
||||||
|
// Long operators must come first! Otherwise short names (like: "add") that
|
||||||
|
// are part of long token names (like: "add123") will be found instead
|
||||||
|
// of the long ones.
|
||||||
|
// Length sorting is done with ascending length so we use a reverse iterator here.
|
||||||
|
funmap_type::const_reverse_iterator it = m_pOprtDef->rbegin();
|
||||||
|
for ( ; it!=m_pOprtDef->rend(); ++it)
|
||||||
|
{
|
||||||
|
const string_type &sID = it->first;
|
||||||
|
if ( sID == string_type(szExpr + m_iPos, szExpr + m_iPos + sID.length()) )
|
||||||
|
{
|
||||||
|
a_Tok.Set(it->second, strTok);
|
||||||
|
|
||||||
|
// operator was found
|
||||||
|
if (m_iSynFlags & noOPT)
|
||||||
|
{
|
||||||
|
// An operator was found but is not expected to occur at
|
||||||
|
// this position of the formula, maybe it is an infix
|
||||||
|
// operator, not a binary operator. Both operator types
|
||||||
|
// can share characters in their identifiers.
|
||||||
|
if ( IsInfixOpTok(a_Tok) )
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// nope, no infix operator
|
||||||
|
return false;
|
||||||
|
//Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
m_iPos += (int)sID.length();
|
||||||
|
m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noEND | noASSIGN;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Check if a string position contains a unary post value operator. */
|
||||||
|
bool ParserTokenReader::IsPostOpTok(token_type &a_Tok)
|
||||||
|
{
|
||||||
|
// <ibg 20110629> Do not check for postfix operators if they are not allowed at
|
||||||
|
// the current expression index.
|
||||||
|
//
|
||||||
|
// This will fix the bug reported here:
|
||||||
|
//
|
||||||
|
// http://sourceforge.net/tracker/index.php?func=detail&aid=3343891&group_id=137191&atid=737979
|
||||||
|
//
|
||||||
|
if (m_iSynFlags & noPOSTOP)
|
||||||
|
return false;
|
||||||
|
// </ibg>
|
||||||
|
|
||||||
|
// Tricky problem with equations like "3m+5":
|
||||||
|
// m is a postfix operator, + is a valid sign for postfix operators and
|
||||||
|
// for binary operators parser detects "m+" as operator string and
|
||||||
|
// finds no matching postfix operator.
|
||||||
|
//
|
||||||
|
// This is a special case so this routine slightly differs from the other
|
||||||
|
// token readers.
|
||||||
|
|
||||||
|
// Test if there could be a postfix operator
|
||||||
|
string_type sTok;
|
||||||
|
int iEnd = ExtractToken(m_pParser->ValidOprtChars(), sTok, m_iPos);
|
||||||
|
if (iEnd==m_iPos)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// iterate over all postfix operator strings
|
||||||
|
funmap_type::const_reverse_iterator it = m_pPostOprtDef->rbegin();
|
||||||
|
for ( ; it!=m_pPostOprtDef->rend(); ++it)
|
||||||
|
{
|
||||||
|
if (sTok.find(it->first)!=0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
a_Tok.Set(it->second, sTok);
|
||||||
|
m_iPos += (int)it->first.length();
|
||||||
|
|
||||||
|
m_iSynFlags = noVAL | noVAR | noFUN | noBO | noPOSTOP | noSTR | noASSIGN;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Check whether the token at a given position is a value token.
|
||||||
|
|
||||||
|
Value tokens are either values or constants.
|
||||||
|
|
||||||
|
\param a_Tok [out] If a value token is found it will be placed here.
|
||||||
|
\return true if a value token has been found.
|
||||||
|
*/
|
||||||
|
bool ParserTokenReader::IsValTok(token_type &a_Tok)
|
||||||
|
{
|
||||||
|
assert(m_pConstDef);
|
||||||
|
assert(m_pParser);
|
||||||
|
|
||||||
|
string_type strTok;
|
||||||
|
value_type fVal(0);
|
||||||
|
int iEnd(0);
|
||||||
|
|
||||||
|
// 2.) Check for user defined constant
|
||||||
|
// Read everything that could be a constant name
|
||||||
|
iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos);
|
||||||
|
if (iEnd!=m_iPos)
|
||||||
|
{
|
||||||
|
valmap_type::const_iterator item = m_pConstDef->find(strTok);
|
||||||
|
if (item!=m_pConstDef->end())
|
||||||
|
{
|
||||||
|
m_iPos = iEnd;
|
||||||
|
a_Tok.SetVal(item->second, strTok);
|
||||||
|
|
||||||
|
if (m_iSynFlags & noVAL)
|
||||||
|
Error(ecUNEXPECTED_VAL, m_iPos - (int)strTok.length(), strTok);
|
||||||
|
|
||||||
|
m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SParam p;
|
||||||
|
p.id = -1;
|
||||||
|
p.param = nullptr;
|
||||||
|
|
||||||
|
// 3.call the value recognition functions provided by the user
|
||||||
|
// Call user defined value recognition functions
|
||||||
|
std::list<identfun_type>::const_iterator item = m_vIdentFun.begin();
|
||||||
|
for (item = m_vIdentFun.begin(); item!=m_vIdentFun.end(); ++item)
|
||||||
|
{
|
||||||
|
int iStart = m_iPos;
|
||||||
|
if ( (*item)(p, m_strFormula.c_str() + m_iPos, &m_iPos, &fVal)==1 )
|
||||||
|
{
|
||||||
|
// 2013-11-27 Issue 2: https://code.google.com/p/muparser/issues/detail?id=2
|
||||||
|
strTok.assign(m_strFormula.c_str(), iStart, m_iPos-iStart);
|
||||||
|
|
||||||
|
if (m_iSynFlags & noVAL)
|
||||||
|
Error(ecUNEXPECTED_VAL, m_iPos - (int)strTok.length(), strTok);
|
||||||
|
|
||||||
|
a_Tok.SetVal(fVal, strTok);
|
||||||
|
m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Check wheter a token at a given position is a variable token.
|
||||||
|
\param a_Tok [out] If a variable token has been found it will be placed here.
|
||||||
|
\return true if a variable token has been found.
|
||||||
|
*/
|
||||||
|
bool ParserTokenReader::IsVarTok(token_type &a_Tok)
|
||||||
|
{
|
||||||
|
if (m_pVarDef->empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
string_type strTok;
|
||||||
|
int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos);
|
||||||
|
if (iEnd==m_iPos)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
varmap_type::const_iterator item = m_pVarDef->find(strTok);
|
||||||
|
if (item==m_pVarDef->end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_iSynFlags & noVAR)
|
||||||
|
Error(ecUNEXPECTED_VAR, m_iPos, strTok);
|
||||||
|
|
||||||
|
m_pParser->OnDetectVar(&m_strFormula, m_iPos, iEnd);
|
||||||
|
|
||||||
|
m_iPos = iEnd;
|
||||||
|
a_Tok.SetVar(item->second, strTok);
|
||||||
|
m_UsedVar[item->first] = item->second; // Add variable to used-var-list
|
||||||
|
|
||||||
|
m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR;
|
||||||
|
|
||||||
|
// Zur Info hier die SynFlags von IsVal():
|
||||||
|
// m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
bool ParserTokenReader::IsStrVarTok(token_type &a_Tok)
|
||||||
|
{
|
||||||
|
if (!m_pStrVarDef || m_pStrVarDef->empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
string_type strTok;
|
||||||
|
int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos);
|
||||||
|
if (iEnd==m_iPos)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
strmap_type::const_iterator item = m_pStrVarDef->find(strTok);
|
||||||
|
if (item==m_pStrVarDef->end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_iSynFlags & noSTR)
|
||||||
|
Error(ecUNEXPECTED_VAR, m_iPos, strTok);
|
||||||
|
|
||||||
|
m_iPos = iEnd;
|
||||||
|
if (!m_pParser->m_vStringVarBuf.size())
|
||||||
|
Error(ecINTERNAL_ERROR);
|
||||||
|
|
||||||
|
a_Tok.SetString(m_pParser->m_vStringVarBuf[item->second], m_pParser->m_vStringVarBuf.size() );
|
||||||
|
|
||||||
|
m_iSynFlags = noANY ^ ( noBC | noOPT | noEND | noARG_SEP);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Check wheter a token at a given position is an undefined variable.
|
||||||
|
|
||||||
|
\param a_Tok [out] If a variable tom_pParser->m_vStringBufken has been found it will be placed here.
|
||||||
|
\return true if a variable token has been found.
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
bool ParserTokenReader::IsUndefVarTok(token_type &a_Tok)
|
||||||
|
{
|
||||||
|
string_type strTok;
|
||||||
|
int iEnd( ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos) );
|
||||||
|
if ( iEnd==m_iPos )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_iSynFlags & noVAR)
|
||||||
|
{
|
||||||
|
// <ibg/> 20061021 added token string strTok instead of a_Tok.GetAsString() as the
|
||||||
|
// token identifier.
|
||||||
|
// related bug report:
|
||||||
|
// http://sourceforge.net/tracker/index.php?func=detail&aid=1578779&group_id=137191&atid=737979
|
||||||
|
Error(ecUNEXPECTED_VAR, m_iPos - (int)a_Tok.GetAsString().length(), strTok);
|
||||||
|
}
|
||||||
|
|
||||||
|
SParam p;
|
||||||
|
p.id = -1;
|
||||||
|
p.param = nullptr;
|
||||||
|
|
||||||
|
// If a factory is available implicitely create new variables
|
||||||
|
if (m_pFactory)
|
||||||
|
{
|
||||||
|
value_type *fVar = m_pFactory(p, strTok.c_str(), m_pFactoryData);
|
||||||
|
a_Tok.SetVar(fVar, strTok );
|
||||||
|
|
||||||
|
// Do not use m_pParser->DefineVar( strTok, fVar );
|
||||||
|
// in order to define the new variable, it will clear the
|
||||||
|
// m_UsedVar array which will kill previously defined variables
|
||||||
|
// from the list
|
||||||
|
// This is safe because the new variable can never override an existing one
|
||||||
|
// because they are checked first!
|
||||||
|
(*m_pVarDef)[strTok] = fVar;
|
||||||
|
m_UsedVar[strTok] = fVar; // Add variable to used-var-list
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
a_Tok.SetVar((value_type*)&m_fZero, strTok);
|
||||||
|
m_UsedVar[strTok] = nullptr; // Add variable to used-var-list
|
||||||
|
}
|
||||||
|
|
||||||
|
m_iPos = iEnd;
|
||||||
|
|
||||||
|
// Call the variable factory in order to let it define a new parser variable
|
||||||
|
m_iSynFlags = noVAL | noVAR | noFUN | noBO | noPOSTOP | noINFIXOP | noSTR;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Check wheter a token at a given position is a string.
|
||||||
|
\param a_Tok [out] If a variable token has been found it will be placed here.
|
||||||
|
\return true if a string token has been found.
|
||||||
|
\sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsEOF, IsInfixOpTok, IsPostOpTok
|
||||||
|
\throw nothrow
|
||||||
|
*/
|
||||||
|
bool ParserTokenReader::IsString(token_type &a_Tok)
|
||||||
|
{
|
||||||
|
if (m_strFormula[m_iPos]!='"')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
string_type strBuf(&m_strFormula[m_iPos+1]);
|
||||||
|
std::size_t iEnd(0), iSkip(0);
|
||||||
|
|
||||||
|
// parser over escaped '\"' end replace them with '"'
|
||||||
|
for(iEnd=(int)strBuf.find( _T("\"") ); iEnd!=0 && iEnd!=string_type::npos; iEnd=(int)strBuf.find( _T("\""), iEnd))
|
||||||
|
{
|
||||||
|
if (strBuf[iEnd-1]!='\\') break;
|
||||||
|
strBuf.replace(iEnd-1, 2, _T("\"") );
|
||||||
|
iSkip++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iEnd==string_type::npos)
|
||||||
|
Error(ecUNTERMINATED_STRING, m_iPos, _T("\"") );
|
||||||
|
|
||||||
|
string_type strTok(strBuf.begin(), strBuf.begin()+iEnd);
|
||||||
|
|
||||||
|
if (m_iSynFlags & noSTR)
|
||||||
|
Error(ecUNEXPECTED_STR, m_iPos, strTok);
|
||||||
|
|
||||||
|
m_pParser->m_vStringBuf.push_back(strTok); // Store string in internal buffer
|
||||||
|
a_Tok.SetString(strTok, m_pParser->m_vStringBuf.size());
|
||||||
|
|
||||||
|
m_iPos += (int)strTok.length() + 2 + (int)iSkip; // +2 for quotes; +iSkip for escape characters
|
||||||
|
m_iSynFlags = noANY ^ ( noARG_SEP | noBC | noOPT | noEND );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
/** \brief Create an error containing the parse error position.
|
||||||
|
|
||||||
|
This function will create an Parser Exception object containing the error text and its position.
|
||||||
|
|
||||||
|
\param a_iErrc [in] The error code of type #EErrorCodes.
|
||||||
|
\param a_iPos [in] The position where the error was detected.
|
||||||
|
\param a_strTok [in] The token string representation associated with the error.
|
||||||
|
\throw ParserException always throws thats the only purpose of this function.
|
||||||
|
*/
|
||||||
|
void ParserTokenReader::Error( EErrorCodes a_iErrc,
|
||||||
|
int a_iPos,
|
||||||
|
const string_type &a_sTok) const
|
||||||
|
{
|
||||||
|
m_pParser->Error(a_iErrc, a_iPos, a_sTok);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
void ParserTokenReader::SetArgSep(char_type cArgSep)
|
||||||
|
{
|
||||||
|
m_cArgSep = cArgSep;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
char_type ParserTokenReader::GetArgSep() const
|
||||||
|
{
|
||||||
|
return m_cArgSep;
|
||||||
|
}
|
||||||
|
} // namespace mu
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __clang__
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
159
Modules/gin/3rdparty/muParser/muParserTokenReader.h
vendored
Executable file
159
Modules/gin/3rdparty/muParser/muParserTokenReader.h
vendored
Executable file
|
@ -0,0 +1,159 @@
|
||||||
|
/*
|
||||||
|
__________
|
||||||
|
_____ __ __\______ \_____ _______ ______ ____ _______
|
||||||
|
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
||||||
|
| Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/
|
||||||
|
|__|_| /|____/ |____| (____ /|__| /____ > \___ >|__|
|
||||||
|
\/ \/ \/ \/
|
||||||
|
Copyright (C) 2004-2013 Ingo Berg
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
without restriction, including without limitation the rights to use, copy, modify,
|
||||||
|
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||||
|
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MU_PARSER_TOKEN_READER_H
|
||||||
|
#define MU_PARSER_TOKEN_READER_H
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <stack>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "muParserDef.h"
|
||||||
|
#include "muParserToken.h"
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
\brief This file contains the parser token reader definition.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
namespace mu
|
||||||
|
{
|
||||||
|
// Forward declaration
|
||||||
|
class ParserBase;
|
||||||
|
|
||||||
|
/** \brief Token reader for the ParserBase class.
|
||||||
|
|
||||||
|
*/
|
||||||
|
class ParserTokenReader
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
typedef ParserToken<value_type, string_type> token_type;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
ParserTokenReader(ParserBase *a_pParent);
|
||||||
|
ParserTokenReader* Clone(ParserBase *a_pParent) const;
|
||||||
|
|
||||||
|
void AddValIdent(identfun_type a_pCallback);
|
||||||
|
void SetVarCreator(facfun_type a_pFactory, void *pUserData);
|
||||||
|
void SetFormula(const string_type &a_strFormula);
|
||||||
|
void SetArgSep(char_type cArgSep);
|
||||||
|
|
||||||
|
int GetPos() const;
|
||||||
|
const string_type& GetExpr() const;
|
||||||
|
varmap_type& GetUsedVar();
|
||||||
|
char_type GetArgSep() const;
|
||||||
|
|
||||||
|
void IgnoreUndefVar(bool bIgnore);
|
||||||
|
void ReInit();
|
||||||
|
token_type ReadNextToken();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** \brief Syntax codes.
|
||||||
|
|
||||||
|
The syntax codes control the syntax check done during the first time parsing of
|
||||||
|
the expression string. They are flags that indicate which tokens are allowed next
|
||||||
|
if certain tokens are identified.
|
||||||
|
*/
|
||||||
|
enum ESynCodes
|
||||||
|
{
|
||||||
|
noBO = 1 << 0, ///< to avoid i.e. "cos(7)("
|
||||||
|
noBC = 1 << 1, ///< to avoid i.e. "sin)" or "()"
|
||||||
|
noVAL = 1 << 2, ///< to avoid i.e. "tan 2" or "sin(8)3.14"
|
||||||
|
noVAR = 1 << 3, ///< to avoid i.e. "sin a" or "sin(8)a"
|
||||||
|
noARG_SEP = 1 << 4, ///< to avoid i.e. ",," or "+," ...
|
||||||
|
noFUN = 1 << 5, ///< to avoid i.e. "sqrt cos" or "(1)sin"
|
||||||
|
noOPT = 1 << 6, ///< to avoid i.e. "(+)"
|
||||||
|
noPOSTOP = 1 << 7, ///< to avoid i.e. "(5!!)" "sin!"
|
||||||
|
noINFIXOP = 1 << 8, ///< to avoid i.e. "++4" "!!4"
|
||||||
|
noEND = 1 << 9, ///< to avoid unexpected end of formula
|
||||||
|
noSTR = 1 << 10, ///< to block numeric arguments on string functions
|
||||||
|
noASSIGN = 1 << 11, ///< to block assignment to constant i.e. "4=7"
|
||||||
|
noIF = 1 << 12,
|
||||||
|
noELSE = 1 << 13,
|
||||||
|
sfSTART_OF_LINE = noOPT | noBC | noPOSTOP | noASSIGN | noIF | noELSE | noARG_SEP,
|
||||||
|
noANY = ~0 ///< All of he above flags set
|
||||||
|
};
|
||||||
|
|
||||||
|
ParserTokenReader(const ParserTokenReader &a_Reader);
|
||||||
|
ParserTokenReader& operator=(const ParserTokenReader &a_Reader);
|
||||||
|
void Assign(const ParserTokenReader &a_Reader);
|
||||||
|
|
||||||
|
void SetParent(ParserBase *a_pParent);
|
||||||
|
int ExtractToken(const char_type *a_szCharSet,
|
||||||
|
string_type &a_strTok,
|
||||||
|
int a_iPos) const;
|
||||||
|
int ExtractOperatorToken(string_type &a_sTok, int a_iPos) const;
|
||||||
|
|
||||||
|
bool IsBuiltIn(token_type &a_Tok);
|
||||||
|
bool IsArgSep(token_type &a_Tok);
|
||||||
|
bool IsEOF(token_type &a_Tok);
|
||||||
|
bool IsInfixOpTok(token_type &a_Tok);
|
||||||
|
bool IsFunTok(token_type &a_Tok);
|
||||||
|
bool IsPostOpTok(token_type &a_Tok);
|
||||||
|
bool IsOprt(token_type &a_Tok);
|
||||||
|
bool IsValTok(token_type &a_Tok);
|
||||||
|
bool IsVarTok(token_type &a_Tok);
|
||||||
|
bool IsStrVarTok(token_type &a_Tok);
|
||||||
|
bool IsUndefVarTok(token_type &a_Tok);
|
||||||
|
bool IsString(token_type &a_Tok);
|
||||||
|
void Error(EErrorCodes a_iErrc,
|
||||||
|
int a_iPos = -1,
|
||||||
|
const string_type &a_sTok = string_type() ) const;
|
||||||
|
|
||||||
|
token_type& SaveBeforeReturn(const token_type &tok);
|
||||||
|
|
||||||
|
ParserBase *m_pParser;
|
||||||
|
string_type m_strFormula;
|
||||||
|
int m_iPos;
|
||||||
|
int m_iSynFlags;
|
||||||
|
bool m_bIgnoreUndefVar;
|
||||||
|
|
||||||
|
const funmap_type *m_pFunDef;
|
||||||
|
const funmap_type *m_pPostOprtDef;
|
||||||
|
const funmap_type *m_pInfixOprtDef;
|
||||||
|
const funmap_type *m_pOprtDef;
|
||||||
|
const valmap_type *m_pConstDef;
|
||||||
|
const strmap_type *m_pStrVarDef;
|
||||||
|
varmap_type *m_pVarDef; ///< The only non const pointer to parser internals
|
||||||
|
facfun_type m_pFactory;
|
||||||
|
void *m_pFactoryData;
|
||||||
|
std::list<identfun_type> m_vIdentFun; ///< Value token identification function
|
||||||
|
varmap_type m_UsedVar;
|
||||||
|
value_type m_fZero; ///< Dummy value of zero, referenced by undefined variables
|
||||||
|
int m_iBrackets;
|
||||||
|
token_type m_lastTok;
|
||||||
|
char_type m_cArgSep; ///< The character used for separating function arguments
|
||||||
|
};
|
||||||
|
} // namespace mu
|
||||||
|
|
||||||
|
#endif
|
2
Modules/gin/README.md
Executable file
2
Modules/gin/README.md
Executable file
|
@ -0,0 +1,2 @@
|
||||||
|
# gin
|
||||||
|
Shared generic tools, utilities and UI classes
|
20
Modules/gin/components/componentutils.h
Executable file
20
Modules/gin/components/componentutils.h
Executable file
|
@ -0,0 +1,20 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2020 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
inline void addAndMakeVisible (Component& parent, Array<Component*> children)
|
||||||
|
{
|
||||||
|
for (auto child : children)
|
||||||
|
parent.addAndMakeVisible (child);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void addAndMakeVisible (Component& parent, std::initializer_list<Component*> children)
|
||||||
|
{
|
||||||
|
for (auto child : children)
|
||||||
|
parent.addAndMakeVisible ( child );
|
||||||
|
}
|
364
Modules/gin/components/componentviewer.cpp
Executable file
364
Modules/gin/components/componentviewer.cpp
Executable file
|
@ -0,0 +1,364 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2019 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
Component* realGetComponent (Component& p, juce::Point<int> screenPos)
|
||||||
|
{
|
||||||
|
if (p.getScreenBounds().contains (screenPos))
|
||||||
|
{
|
||||||
|
for (auto c : p.getChildren())
|
||||||
|
if (auto r = realGetComponent (*c, screenPos))
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return &p;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component* realGetComponentUnderMouse()
|
||||||
|
{
|
||||||
|
auto mouse = Desktop::getInstance().getMainMouseSource();
|
||||||
|
auto pos = mouse.getScreenPosition().toInt();
|
||||||
|
|
||||||
|
auto& desktop = Desktop::getInstance();
|
||||||
|
|
||||||
|
for (int i = desktop.getNumComponents(); --i >= 0;)
|
||||||
|
{
|
||||||
|
if (auto dtc = desktop.getComponent (i))
|
||||||
|
if (dtc->isVisible())
|
||||||
|
if (auto c = realGetComponent (*dtc, pos))
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getClassName (Component* c)
|
||||||
|
{
|
||||||
|
#if __clang__ || __GNUC__
|
||||||
|
int status = 0;
|
||||||
|
if (char* demangled = abi::__cxa_demangle (typeid (*c).name(), nullptr, nullptr, &status))
|
||||||
|
{
|
||||||
|
auto res = String (demangled);
|
||||||
|
free (demangled);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
#else
|
||||||
|
String res = typeid (*c).name();
|
||||||
|
if (res.startsWith ("class ")) res = res.substring (6);
|
||||||
|
if (res.startsWith ("struct ")) res = res.substring (7);
|
||||||
|
return res;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
class ComponentViewer::Snapshot : public Component
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void update (Component* c, juce::Point<int> pos, int zoom_)
|
||||||
|
{
|
||||||
|
zoom = zoom_;
|
||||||
|
if (c != nullptr)
|
||||||
|
{
|
||||||
|
int w = getWidth() / zoom + 1;
|
||||||
|
int h = getHeight() / zoom + 1;
|
||||||
|
|
||||||
|
auto root = c->getTopLevelComponent();
|
||||||
|
auto rootPos = root->getLocalPoint (c, pos);
|
||||||
|
|
||||||
|
auto disp = Desktop::getInstance().getDisplays().findDisplayForPoint (c->localPointToGlobal (pos));
|
||||||
|
scale = float (disp.scale);
|
||||||
|
|
||||||
|
image = root->createComponentSnapshot ({rootPos.getX() - w / 2, rootPos.getY() - h / 2, w, h}, false, scale);
|
||||||
|
image = image.rescaled (w * zoom, h * zoom, Graphics::lowResamplingQuality);
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
image = {};
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
Colour getColourUnderMouse()
|
||||||
|
{
|
||||||
|
if (! image.isNull() && zoom > 0)
|
||||||
|
{
|
||||||
|
int w = getWidth() / zoom + 1;
|
||||||
|
int h = getHeight() / zoom + 1;
|
||||||
|
|
||||||
|
int x = w / 2 * zoom;
|
||||||
|
int y = h / 2 * zoom;
|
||||||
|
|
||||||
|
return image.getPixelAt (x, y);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void paint (Graphics& g) override
|
||||||
|
{
|
||||||
|
if (! image.isNull() && zoom > 0 && scale != 0.0f)
|
||||||
|
{
|
||||||
|
g.drawImageAt (image, 0, 0);
|
||||||
|
|
||||||
|
int w = getWidth() / zoom + 1;
|
||||||
|
int h = getHeight() / zoom + 1;
|
||||||
|
|
||||||
|
Rectangle<int> rc (w / 2 * zoom, h / 2 * zoom, int (zoom / scale), int (zoom / scale));
|
||||||
|
|
||||||
|
auto c = image.getPixelAt (rc.getX(), rc.getY());
|
||||||
|
g.setColour (c.contrasting());
|
||||||
|
g.drawRect (rc, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image image;
|
||||||
|
int zoom = 10;
|
||||||
|
float scale = 1.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
class ComponentViewer::ContentComponent : public Component,
|
||||||
|
private Timer,
|
||||||
|
private Slider::Listener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ContentComponent (PropertiesFile& settings_)
|
||||||
|
: settings (settings_)
|
||||||
|
{
|
||||||
|
Font f (Font::getDefaultMonospacedFontName(), 12.0f, Font::plain);
|
||||||
|
|
||||||
|
addAndMakeVisible (mouseDetails);
|
||||||
|
mouseDetails.setMultiLine (true, false);
|
||||||
|
mouseDetails.setFont (f);
|
||||||
|
mouseDetails.setReadOnly (true);
|
||||||
|
|
||||||
|
addAndMakeVisible (componentDetails);
|
||||||
|
componentDetails.setFont (f);
|
||||||
|
componentDetails.setMultiLine (true, false);
|
||||||
|
componentDetails.setReadOnly (true);
|
||||||
|
|
||||||
|
addAndMakeVisible (snapshotDetails);
|
||||||
|
snapshotDetails.setFont (f);
|
||||||
|
snapshotDetails.setReadOnly (true);
|
||||||
|
snapshotDetails.setJustification (Justification::centred);
|
||||||
|
|
||||||
|
addAndMakeVisible (zoom);
|
||||||
|
zoom.setTextBoxStyle (Slider::NoTextBox, false, 0, 0);
|
||||||
|
zoom.setRange (1, 30, 1);
|
||||||
|
zoom.setValue (settings.getIntValue ("ginZoom", 10));
|
||||||
|
zoom.addListener (this);
|
||||||
|
|
||||||
|
addAndMakeVisible (snapshot);
|
||||||
|
|
||||||
|
Desktop::getInstance().addGlobalMouseListener (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~ContentComponent() override
|
||||||
|
{
|
||||||
|
Desktop::getInstance().removeGlobalMouseListener (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resized() override
|
||||||
|
{
|
||||||
|
auto rc = getLocalBounds();
|
||||||
|
|
||||||
|
mouseDetails.setBounds (rc.removeFromTop (50));
|
||||||
|
componentDetails.setBounds (rc.removeFromTop (int (rc.getHeight() * 0.4)));
|
||||||
|
|
||||||
|
auto row = rc.removeFromTop (25);
|
||||||
|
int w = row.getWidth() / 3;
|
||||||
|
zoom.setBounds (row.removeFromLeft (w));
|
||||||
|
snapshotDetails.setBounds (row.removeFromLeft (w));
|
||||||
|
|
||||||
|
snapshot.setBounds (rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mouseUp (const MouseEvent& ) override { updateComponentDetailsAsync(); }
|
||||||
|
void mouseDown (const MouseEvent& ) override { updateComponentDetailsAsync(); }
|
||||||
|
void mouseDrag (const MouseEvent& ) override { updateComponentDetailsAsync(); }
|
||||||
|
void mouseMove (const MouseEvent& ) override { updateComponentDetailsAsync(); }
|
||||||
|
|
||||||
|
void updateComponentDetailsAsync()
|
||||||
|
{
|
||||||
|
if (! isTimerRunning())
|
||||||
|
startTimer (50);
|
||||||
|
}
|
||||||
|
|
||||||
|
void timerCallback() override
|
||||||
|
{
|
||||||
|
stopTimer();
|
||||||
|
updateComponentDetails();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sliderValueChanged (Slider*) override
|
||||||
|
{
|
||||||
|
settings.setValue ("ginZoom", int (zoom.getValue()));
|
||||||
|
updateComponentDetails();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateComponentDetails()
|
||||||
|
{
|
||||||
|
auto mouse = Desktop::getInstance().getMainMouseSource();
|
||||||
|
|
||||||
|
auto pos = mouse.getScreenPosition().toInt();
|
||||||
|
|
||||||
|
StringArray componentHierarchy;
|
||||||
|
String cursorPos, colourDetails;
|
||||||
|
|
||||||
|
if (auto c = realGetComponentUnderMouse())
|
||||||
|
{
|
||||||
|
snapshot.update (c, c->getLocalPoint (nullptr, pos), int (zoom.getValue()));
|
||||||
|
componentHierarchy = getComponentHierarchy (c);
|
||||||
|
|
||||||
|
cursorPos += "Component: (" + c->getLocalPoint (nullptr, pos).toString() + ")\n";
|
||||||
|
cursorPos += "Window: (" + c->getTopLevelComponent()->getLocalPoint (nullptr, pos).toString() + ")\n";
|
||||||
|
|
||||||
|
auto col = snapshot.getColourUnderMouse();
|
||||||
|
colourDetails = col.toDisplayString (true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snapshot.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
cursorPos += "Screen: (" + pos.toString() + ")";
|
||||||
|
|
||||||
|
mouseDetails.setText (cursorPos);
|
||||||
|
snapshotDetails.setText (colourDetails);
|
||||||
|
componentDetails.setText (componentHierarchy.joinIntoString ("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
StringArray getComponentHierarchy (Component* c)
|
||||||
|
{
|
||||||
|
StringArray res;
|
||||||
|
|
||||||
|
while (c != nullptr)
|
||||||
|
{
|
||||||
|
String str;
|
||||||
|
|
||||||
|
str += ("[" + String (getClassName (c)) + "]").paddedRight (' ', 60);
|
||||||
|
str += (" \"" + c->getName() + "\"").paddedRight (' ', 20);
|
||||||
|
str += (" (" + c->getBounds().toString() + ")").paddedRight (' ', 20);
|
||||||
|
str += String (c->isOpaque() ? " Opaque" : "").paddedRight (' ', 8);
|
||||||
|
str += String (c->isPaintingUnclipped() ? " Unclipped" : "").paddedRight (' ', 11);
|
||||||
|
|
||||||
|
res.add (str);
|
||||||
|
|
||||||
|
c = c->getParentComponent();
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertiesFile& settings;
|
||||||
|
|
||||||
|
TextEditor mouseDetails, componentDetails, snapshotDetails;
|
||||||
|
Slider zoom;
|
||||||
|
Snapshot snapshot;
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
ComponentViewer::ComponentViewer (Component* toTrack_, PropertiesFile* settings_, bool alwaysOnTop)
|
||||||
|
: DocumentWindow ("Component Viewer Window", Colours::white, allButtons, true),
|
||||||
|
toTrack (toTrack_)
|
||||||
|
{
|
||||||
|
if (settings_ != nullptr)
|
||||||
|
{
|
||||||
|
settings.set (settings_, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PropertiesFile::Options opts;
|
||||||
|
|
||||||
|
opts.applicationName = "Gin";
|
||||||
|
opts.filenameSuffix = ".xml";
|
||||||
|
opts.folderName = "Gin";
|
||||||
|
opts.osxLibrarySubFolder = "Application Support";
|
||||||
|
opts.commonToAllUsers = false;
|
||||||
|
opts.ignoreCaseOfKeyNames = false;
|
||||||
|
opts.doNotSave = false;
|
||||||
|
opts.millisecondsBeforeSaving = 1;
|
||||||
|
opts.storageFormat = juce::PropertiesFile::storeAsXML;
|
||||||
|
|
||||||
|
settings.set (new PropertiesFile (opts), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toTrack != nullptr)
|
||||||
|
toTrack->addComponentListener (this);
|
||||||
|
|
||||||
|
auto position = settings->getValue ("ginComponentViewerPosition", "");
|
||||||
|
|
||||||
|
if (position.isNotEmpty())
|
||||||
|
restoreWindowStateFromString (position);
|
||||||
|
else
|
||||||
|
centreWithSize (640, 480);
|
||||||
|
|
||||||
|
setVisible (true);
|
||||||
|
setAlwaysOnTop (alwaysOnTop);
|
||||||
|
setResizable (true, false);
|
||||||
|
|
||||||
|
setContentOwned (new ContentComponent (*settings), false);
|
||||||
|
|
||||||
|
onClose = [this] { delete this; };
|
||||||
|
}
|
||||||
|
|
||||||
|
ComponentViewer::~ComponentViewer()
|
||||||
|
{
|
||||||
|
if (toTrack != nullptr)
|
||||||
|
toTrack->removeComponentListener (this);
|
||||||
|
|
||||||
|
saveWindowPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComponentViewer::closeButtonPressed()
|
||||||
|
{
|
||||||
|
if (onClose)
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComponentViewer::moved()
|
||||||
|
{
|
||||||
|
DocumentWindow::resized();
|
||||||
|
saveWindowPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComponentViewer::resized()
|
||||||
|
{
|
||||||
|
DocumentWindow::resized();
|
||||||
|
saveWindowPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComponentViewer::saveWindowPosition()
|
||||||
|
{
|
||||||
|
if (settings != nullptr)
|
||||||
|
{
|
||||||
|
settings->setValue ("ginComponentViewerPosition", getWindowStateAsString());
|
||||||
|
settings->saveIfNeeded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComponentViewer::componentBeingDeleted (Component&)
|
||||||
|
{
|
||||||
|
settings.reset();
|
||||||
|
if (toTrack != nullptr)
|
||||||
|
toTrack = nullptr;
|
||||||
|
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComponentViewer::lookAndFeelChanged()
|
||||||
|
{
|
||||||
|
auto& lf = getLookAndFeel ();
|
||||||
|
setBackgroundColour (lf.findColour (ResizableWindow::backgroundColourId));
|
||||||
|
}
|
47
Modules/gin/components/componentviewer.h
Executable file
47
Modules/gin/components/componentviewer.h
Executable file
|
@ -0,0 +1,47 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2019 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//==============================================================================*/
|
||||||
|
|
||||||
|
/** Show the component under the mouse and component hierarchy.
|
||||||
|
Useful for debugging */
|
||||||
|
class ComponentViewer : public DocumentWindow,
|
||||||
|
public ComponentListener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//==============================================================================*/
|
||||||
|
/* Pass in a settings file to store window location, otherwise default file will
|
||||||
|
be used. Pass in a component to track and when this component is deleted, the
|
||||||
|
ComponentViewer will be deleted as well, or nullptr if you don't want this feature
|
||||||
|
*/
|
||||||
|
ComponentViewer (Component* toTrack = nullptr, PropertiesFile* settings = nullptr, bool alwaysOnTop = true);
|
||||||
|
~ComponentViewer() override;
|
||||||
|
|
||||||
|
/* User wants to close window, you should delete it. By default will delete itself */
|
||||||
|
std::function<void ()> onClose;
|
||||||
|
|
||||||
|
private:
|
||||||
|
//==============================================================================*/
|
||||||
|
void componentBeingDeleted (Component&) override;
|
||||||
|
void closeButtonPressed() override;
|
||||||
|
|
||||||
|
void lookAndFeelChanged() override;
|
||||||
|
|
||||||
|
void moved() override;
|
||||||
|
void resized() override;
|
||||||
|
|
||||||
|
void saveWindowPosition();
|
||||||
|
|
||||||
|
class Snapshot;
|
||||||
|
class ContentComponent;
|
||||||
|
|
||||||
|
//==============================================================================*/
|
||||||
|
OptionalScopedPointer<PropertiesFile> settings;
|
||||||
|
Component* toTrack = nullptr;
|
||||||
|
};
|
193
Modules/gin/components/ginlookandfeel.cpp
Executable file
193
Modules/gin/components/ginlookandfeel.cpp
Executable file
|
@ -0,0 +1,193 @@
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
GinLookAndFeel::GinLookAndFeel()
|
||||||
|
{
|
||||||
|
setColour (GinLookAndFeel::colourId1, Colour (0xff020202));
|
||||||
|
setColour (GinLookAndFeel::colourId2, Colour (0xff393d3f));
|
||||||
|
setColour (GinLookAndFeel::colourId3, Colour (0xffc6c5b9));
|
||||||
|
setColour (GinLookAndFeel::colourId4, Colour (0xfff4f4f9));
|
||||||
|
setColour (GinLookAndFeel::colourId5, Colour (0xfffdfdff));
|
||||||
|
|
||||||
|
setColour (Label::textColourId, defaultColour (4).withAlpha (0.9f));
|
||||||
|
|
||||||
|
setColour (Slider::trackColourId, defaultColour (4));
|
||||||
|
setColour (Slider::rotarySliderFillColourId, defaultColour (4));
|
||||||
|
|
||||||
|
setColour (TextButton::buttonColourId, defaultColour (0));
|
||||||
|
setColour (TextButton::buttonOnColourId, defaultColour (4));
|
||||||
|
setColour (TextButton::textColourOffId, defaultColour (4));
|
||||||
|
setColour (TextButton::textColourOnId, defaultColour (0));
|
||||||
|
|
||||||
|
setColour (ComboBox::backgroundColourId, Colours::transparentWhite);
|
||||||
|
setColour (ComboBox::outlineColourId, defaultColour (4));
|
||||||
|
|
||||||
|
setColour (TextEditor::backgroundColourId, Colours::transparentWhite);
|
||||||
|
setColour (TextEditor::textColourId, defaultColour (4));
|
||||||
|
setColour (TextEditor::highlightColourId, defaultColour (4));
|
||||||
|
setColour (TextEditor::highlightedTextColourId, defaultColour (0));
|
||||||
|
setColour (TextEditor::outlineColourId, defaultColour (4));
|
||||||
|
setColour (TextEditor::focusedOutlineColourId, defaultColour (4));
|
||||||
|
setColour (TextEditor::shadowColourId, Colours::transparentWhite);
|
||||||
|
}
|
||||||
|
|
||||||
|
Colour GinLookAndFeel::defaultColour (int idx)
|
||||||
|
{
|
||||||
|
switch (idx)
|
||||||
|
{
|
||||||
|
case 0: return findColour (GinLookAndFeel::colourId1);
|
||||||
|
case 1: return findColour (GinLookAndFeel::colourId2);
|
||||||
|
case 2: return findColour (GinLookAndFeel::colourId3);
|
||||||
|
case 3: return findColour (GinLookAndFeel::colourId4);
|
||||||
|
case 4: return findColour (GinLookAndFeel::colourId5);
|
||||||
|
}
|
||||||
|
return Colours::transparentWhite;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GinLookAndFeel::drawLinearSlider (Graphics& g, int x, int y, int width, int height,
|
||||||
|
float sliderPos, float /*minSliderPos*/, float /*maxSliderPos*/,
|
||||||
|
const Slider::SliderStyle, Slider& slider)
|
||||||
|
{
|
||||||
|
const bool isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled();
|
||||||
|
auto rc = Rectangle<int> (x, y, width, height);
|
||||||
|
rc = rc.withSizeKeepingCentre (width, jmin (height, 4));
|
||||||
|
|
||||||
|
g.setColour (slider.findColour (Slider::trackColourId).withAlpha (0.1f));
|
||||||
|
g.fillRect (rc);
|
||||||
|
|
||||||
|
if (slider.isEnabled())
|
||||||
|
g.setColour (slider.findColour (Slider::trackColourId).withAlpha (isMouseOver ? 0.95f : 0.85f));
|
||||||
|
|
||||||
|
if (slider.isHorizontal())
|
||||||
|
g.fillRect (Rectangle<float> (static_cast<float> (rc.getX()), rc.getY() + 0.5f, sliderPos - rc.getX(), rc.getHeight() - 1.0f));
|
||||||
|
else
|
||||||
|
g.fillRect (Rectangle<float> (rc.getX() + 0.5f, sliderPos, rc.getWidth() - 1.0f, rc.getY() + (rc.getHeight() - sliderPos)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GinLookAndFeel::drawRotarySlider (Graphics& g, int x, int y, int width, int height, float sliderPos,
|
||||||
|
const float rotaryStartAngleIn, const float rotaryEndAngle, Slider& slider)
|
||||||
|
{
|
||||||
|
float rotaryStartAngle = rotaryStartAngleIn;
|
||||||
|
const float radius = jmin (width / 2, height / 2) - 2.0f;
|
||||||
|
const float centreX = x + width * 0.5f;
|
||||||
|
const float centreY = y + height * 0.5f;
|
||||||
|
const float rx = centreX - radius;
|
||||||
|
const float ry = centreY - radius;
|
||||||
|
const float rw = radius * 2.0f;
|
||||||
|
const float angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
|
||||||
|
const bool isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled();
|
||||||
|
|
||||||
|
const float thickness = (radius - 1) / radius;
|
||||||
|
|
||||||
|
g.setColour (slider.findColour (Slider::rotarySliderFillColourId).withAlpha (0.1f));
|
||||||
|
|
||||||
|
{
|
||||||
|
Path filledArc;
|
||||||
|
filledArc.addPieSegment (rx, ry, rw, rw, rotaryStartAngle, rotaryEndAngle, thickness);
|
||||||
|
g.fillPath (filledArc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slider.isEnabled())
|
||||||
|
g.setColour (slider.findColour (Slider::rotarySliderFillColourId).withAlpha (isMouseOver ? 0.95f : 0.85f));
|
||||||
|
|
||||||
|
auto fillStartAngle = rotaryStartAngle;
|
||||||
|
if (slider.getProperties().contains ("fromCentre"))
|
||||||
|
fillStartAngle = (rotaryStartAngle + rotaryEndAngle) / 2;
|
||||||
|
|
||||||
|
{
|
||||||
|
Path filledArc;
|
||||||
|
filledArc.addPieSegment (rx, ry, rw, rw, fillStartAngle, angle, thickness);
|
||||||
|
g.fillPath (filledArc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slider.getProperties().contains ("modDepth"))
|
||||||
|
{
|
||||||
|
auto depth = (float)slider.getProperties()["modDepth"];
|
||||||
|
bool bipolar = (bool)slider.getProperties()["modBipolar"];
|
||||||
|
|
||||||
|
g.setColour (Colours::red.withAlpha (0.8f));
|
||||||
|
|
||||||
|
Path filledArc;
|
||||||
|
if (bipolar)
|
||||||
|
{
|
||||||
|
auto a = jlimit (rotaryStartAngle, rotaryEndAngle, angle - depth * (rotaryEndAngle - rotaryStartAngle));
|
||||||
|
auto b = jlimit (rotaryStartAngle, rotaryEndAngle, angle + depth * (rotaryEndAngle - rotaryStartAngle));
|
||||||
|
filledArc.addPieSegment (rx, ry, rw, rw, std::min (a, b), std::max (a, b), thickness);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto modPos = jlimit (rotaryStartAngle, rotaryEndAngle, angle + depth * (rotaryEndAngle - rotaryStartAngle));
|
||||||
|
filledArc.addPieSegment (rx, ry, rw, rw, angle, modPos, thickness);
|
||||||
|
}
|
||||||
|
|
||||||
|
g.fillPath (filledArc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slider.getProperties().contains ("modValues"))
|
||||||
|
{
|
||||||
|
g.setColour (Colours::red.withAlpha (0.8f));
|
||||||
|
|
||||||
|
auto varArray = slider.getProperties()["modValues"];
|
||||||
|
if (varArray.isArray())
|
||||||
|
{
|
||||||
|
for (auto value : *varArray.getArray())
|
||||||
|
{
|
||||||
|
float modAngle = float (value) * (rotaryEndAngle - rotaryStartAngle) + rotaryStartAngle;
|
||||||
|
|
||||||
|
float modX = centreX + std::sin (modAngle) * radius;
|
||||||
|
float modY = centreY - std::cos (modAngle) * radius;
|
||||||
|
|
||||||
|
g.fillEllipse (modX - 2, modY - 2, 4.0f, 4.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GinLookAndFeel::drawButtonBackground (Graphics& g, Button& b, const Colour&,
|
||||||
|
bool, bool)
|
||||||
|
{
|
||||||
|
if (b.isMouseOver() && b.isEnabled())
|
||||||
|
{
|
||||||
|
g.setColour (b.findColour (TextButton::buttonOnColourId).withMultipliedAlpha (0.2f));
|
||||||
|
g.fillRect (b.getLocalBounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
g.setColour (b.findColour (TextButton::buttonOnColourId).withMultipliedAlpha (b.isEnabled() ? 1.0f : 0.5f));
|
||||||
|
if (b.getToggleState())
|
||||||
|
g.fillRect (b.getLocalBounds());
|
||||||
|
else
|
||||||
|
g.drawRect (b.getLocalBounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GinLookAndFeel::drawButtonText (Graphics& g, TextButton& b, bool, bool)
|
||||||
|
{
|
||||||
|
g.setFont (getTextButtonFont (b, b.getHeight()));
|
||||||
|
|
||||||
|
g.setColour (b.findColour (b.getToggleState() ? TextButton::textColourOnId : TextButton::textColourOffId).withMultipliedAlpha (b.isEnabled() ? 1.0f : 0.5f));
|
||||||
|
g.drawText (b.getButtonText(), b.getLocalBounds(), Justification::centred);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GinLookAndFeel::drawComboBox (Graphics& g, int width, int height, bool /*isButtonDown*/,
|
||||||
|
int /*buttonX*/, int /*buttonY*/, int /*buttonW*/, int /*buttonH*/,
|
||||||
|
ComboBox& box)
|
||||||
|
{
|
||||||
|
const Rectangle<int> boxBounds (0, 0, width, height);
|
||||||
|
|
||||||
|
g.setColour (box.findColour (ComboBox::backgroundColourId));
|
||||||
|
g.fillRect (boxBounds.toFloat());
|
||||||
|
|
||||||
|
g.setColour (box.findColour (ComboBox::outlineColourId));
|
||||||
|
g.drawRect (boxBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GinLookAndFeel::positionComboBoxText (ComboBox& box, Label& label)
|
||||||
|
{
|
||||||
|
label.setBounds (1, 1, box.getWidth() - 1, box.getHeight() - 1);
|
||||||
|
label.setFont (getComboBoxFont (box));
|
||||||
|
label.setJustificationType (Justification::centred);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GinLookAndFeel::drawTextEditorOutline (Graphics& g, int width, int height, TextEditor&)
|
||||||
|
{
|
||||||
|
g.setColour (defaultColour (4));
|
||||||
|
g.drawRect (0, 0, width, height);
|
||||||
|
}
|
41
Modules/gin/components/ginlookandfeel.h
Executable file
41
Modules/gin/components/ginlookandfeel.h
Executable file
|
@ -0,0 +1,41 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
class GinLookAndFeel : public LookAndFeel_V4
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GinLookAndFeel();
|
||||||
|
|
||||||
|
enum ColourIds
|
||||||
|
{
|
||||||
|
colourId1 = 0x8700001,
|
||||||
|
colourId2 = 0x8700002,
|
||||||
|
colourId3 = 0x8700003,
|
||||||
|
colourId4 = 0x8700004,
|
||||||
|
colourId5 = 0x8700005,
|
||||||
|
};
|
||||||
|
|
||||||
|
Colour defaultColour (int idx);
|
||||||
|
|
||||||
|
void drawRotarySlider (Graphics&, int x, int y, int width, int height,
|
||||||
|
float sliderPosProportional, float rotaryStartAngle, float rotaryEndAngle,
|
||||||
|
Slider&) override;
|
||||||
|
|
||||||
|
void drawLinearSlider (Graphics&, int x, int y, int width, int height,
|
||||||
|
float sliderPos, float minSliderPos, float maxSliderPos,
|
||||||
|
const Slider::SliderStyle, Slider&) override;
|
||||||
|
|
||||||
|
|
||||||
|
void drawButtonBackground (Graphics&, Button&, const Colour& backgroundColour,
|
||||||
|
bool isMouseOverButton, bool isButtonDown) override;
|
||||||
|
|
||||||
|
void drawButtonText (Graphics&, TextButton&, bool isMouseOverButton, bool isButtonDown) override;
|
||||||
|
|
||||||
|
void drawComboBox (Graphics&, int width, int height, bool isButtonDown,
|
||||||
|
int buttonX, int buttonY, int buttonW, int buttonH,
|
||||||
|
ComboBox&) override;
|
||||||
|
|
||||||
|
void positionComboBoxText (ComboBox&, Label&) override;
|
||||||
|
|
||||||
|
void drawTextEditorOutline (Graphics&, int width, int height, TextEditor&) override;
|
||||||
|
};
|
165
Modules/gin/components/mapviewer.cpp
Executable file
165
Modules/gin/components/mapviewer.cpp
Executable file
|
@ -0,0 +1,165 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
|
||||||
|
MapViewer::MapViewer()
|
||||||
|
: zoom (14),
|
||||||
|
xoffset (0),
|
||||||
|
yoffset (0),
|
||||||
|
userAdjusted (false),
|
||||||
|
posMarker (juce::Point<double>())
|
||||||
|
{
|
||||||
|
mapsize = osm->getMapWidthPixels(zoom);
|
||||||
|
|
||||||
|
osm->addListener (this);
|
||||||
|
|
||||||
|
centerOn (-123.41480970382690, 48.490145885498649);
|
||||||
|
}
|
||||||
|
|
||||||
|
MapViewer::~MapViewer()
|
||||||
|
{
|
||||||
|
osm->removeListener (this);
|
||||||
|
|
||||||
|
clearDoubleBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapViewer::setZoom (int zoom_)
|
||||||
|
{
|
||||||
|
zoom_ = jlimit (0, 18, zoom_);
|
||||||
|
if (zoom != zoom_)
|
||||||
|
{
|
||||||
|
zoom = zoom_;
|
||||||
|
mapsize = osm->getMapWidthPixels(zoom);
|
||||||
|
|
||||||
|
osm->clearQueue();
|
||||||
|
|
||||||
|
mapUpdated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapViewer::tileFetched (int zoom_, int x, int y)
|
||||||
|
{
|
||||||
|
if (zoom == zoom_)
|
||||||
|
{
|
||||||
|
mapUpdated();
|
||||||
|
repaint (x * 256 - xoffset, y * 256 - yoffset, 256, 256);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapViewer::resized()
|
||||||
|
{
|
||||||
|
centerOn (centerPt.getX(), centerPt.getY());
|
||||||
|
|
||||||
|
mapUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapViewer::paint (Graphics& g)
|
||||||
|
{
|
||||||
|
updateDoubleBuffer();
|
||||||
|
|
||||||
|
if (doubleBuffer != nullptr)
|
||||||
|
g.drawImageAt (*doubleBuffer, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapViewer::mouseDown (const MouseEvent& e)
|
||||||
|
{
|
||||||
|
lastPos = e.getPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapViewer::mouseDrag (const MouseEvent& e)
|
||||||
|
{
|
||||||
|
userAdjusted = true;
|
||||||
|
|
||||||
|
auto curPos = e.getPosition();
|
||||||
|
|
||||||
|
xoffset = jlimit (0, mapsize - getWidth(), xoffset - (curPos.getX() - lastPos.getX()));
|
||||||
|
yoffset = jlimit (0, mapsize - getHeight(), yoffset - (curPos.getY() - lastPos.getY()));
|
||||||
|
|
||||||
|
centerPt = osm->displayToCoordinate (juce::Point<double>(xoffset + getWidth() / 2, yoffset + getHeight() / 2), zoom);
|
||||||
|
|
||||||
|
lastPos = curPos;
|
||||||
|
|
||||||
|
mapUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapViewer::mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wheel)
|
||||||
|
{
|
||||||
|
userAdjusted = true;
|
||||||
|
|
||||||
|
wheelDelta += wheel.deltaY;
|
||||||
|
|
||||||
|
if (std::abs (wheelDelta) > 0.1 || ! wheel.isSmooth)
|
||||||
|
{
|
||||||
|
wheelDelta = 0;
|
||||||
|
|
||||||
|
juce::Point<double> centerP = osm->displayToCoordinate (juce::Point<double> (xoffset + e.x, yoffset + e.y), zoom);
|
||||||
|
|
||||||
|
if (wheel.deltaY < 0)
|
||||||
|
setZoom (zoom - 1);
|
||||||
|
else
|
||||||
|
setZoom (zoom + 1);
|
||||||
|
|
||||||
|
centerUnderPt (centerP, e.getPosition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapViewer::centerOn (double longCenter, double latCenter)
|
||||||
|
{
|
||||||
|
juce::Point<double> p = osm->coordinateToDisplay(juce::Point<double>(longCenter, latCenter), zoom);
|
||||||
|
|
||||||
|
xoffset = jlimit (0, (mapsize - getWidth()), int (p.getX()) - getWidth() / 2);
|
||||||
|
yoffset = jlimit (0, (mapsize - getHeight()), int (p.getY()) - getHeight() / 2);
|
||||||
|
|
||||||
|
centerPt = juce::Point<double>(longCenter, latCenter);
|
||||||
|
|
||||||
|
mapUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapViewer::centerUnderPt (juce::Point<double> world, juce::Point<int> view)
|
||||||
|
{
|
||||||
|
juce::Point<double> p = osm->coordinateToDisplay (world, zoom);
|
||||||
|
|
||||||
|
xoffset = jlimit (0, jmax (1, (mapsize - getWidth())), int (p.getX()) - view.getX());
|
||||||
|
yoffset = jlimit (0, jmax (1, (mapsize - getHeight())), int (p.getY()) - view.getY());
|
||||||
|
|
||||||
|
centerPt = osm->displayToCoordinate (juce::Point<double>(xoffset + getWidth() / 2, yoffset + getHeight() / 2), zoom);
|
||||||
|
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapViewer::updateDoubleBuffer()
|
||||||
|
{
|
||||||
|
if (doubleBuffer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Rectangle<int> rc (0, 0, getWidth(), getHeight());
|
||||||
|
doubleBuffer = std::make_unique<Image> (Image::ARGB, rc.getWidth(), rc.getHeight(), true);
|
||||||
|
|
||||||
|
Graphics g (*doubleBuffer);
|
||||||
|
|
||||||
|
// draw the map tiles
|
||||||
|
for (int x = (rc.getX() + xoffset) / 256 * 256; x <= rc.getRight() + xoffset; x += 256)
|
||||||
|
{
|
||||||
|
for (int y = (rc.getY() + yoffset) / 256 * 256; y <= rc.getBottom() + yoffset; y += 256)
|
||||||
|
{
|
||||||
|
Image tile = osm->fetchTile (zoom, x / 256, y / 256);
|
||||||
|
g.drawImageAt (tile, x - xoffset, y - yoffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapViewer::clearDoubleBuffer()
|
||||||
|
{
|
||||||
|
if (doubleBuffer)
|
||||||
|
doubleBuffer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapViewer::mapUpdated()
|
||||||
|
{
|
||||||
|
clearDoubleBuffer();
|
||||||
|
repaint();
|
||||||
|
}
|
62
Modules/gin/components/mapviewer.h
Executable file
62
Modules/gin/components/mapviewer.h
Executable file
|
@ -0,0 +1,62 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//==============================================================================*/
|
||||||
|
// Draws an OSM map
|
||||||
|
class MapViewer : public Component,
|
||||||
|
private OpenStreetMaps::Listener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MapViewer();
|
||||||
|
~MapViewer() override;
|
||||||
|
|
||||||
|
void setZoom (int zoom);
|
||||||
|
void centerOn (double longCenter, double latCenter);
|
||||||
|
void centerUnderPt (juce::Point<double> world, juce::Point<int> view);
|
||||||
|
|
||||||
|
OpenStreetMaps* getOpenStreetMaps() { return osm; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void resized() override;
|
||||||
|
void paint (Graphics& g) override;
|
||||||
|
void mouseDown (const MouseEvent& e) override;
|
||||||
|
void mouseDrag (const MouseEvent& e) override;
|
||||||
|
void mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wheel) override;
|
||||||
|
|
||||||
|
void tileFetched (int zoom, int x, int y) override;
|
||||||
|
void updateMap();
|
||||||
|
void mapUpdated();
|
||||||
|
void preferencesChanged();
|
||||||
|
void saveSnapshot();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateDoubleBuffer();
|
||||||
|
void clearDoubleBuffer();
|
||||||
|
|
||||||
|
int zoom;
|
||||||
|
int mapsize;
|
||||||
|
|
||||||
|
int xoffset;
|
||||||
|
int yoffset;
|
||||||
|
|
||||||
|
double wheelDelta = 0;
|
||||||
|
|
||||||
|
SharedResourcePointer<OpenStreetMaps> osm;
|
||||||
|
|
||||||
|
juce::Point<int> lastPos;
|
||||||
|
juce::Point<double> centerPt;
|
||||||
|
|
||||||
|
bool userAdjusted;
|
||||||
|
|
||||||
|
juce::Point<double> posMarker;
|
||||||
|
|
||||||
|
std::unique_ptr<Image> doubleBuffer;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
8
Modules/gin/components/propertycomponents.cpp
Executable file
8
Modules/gin/components/propertycomponents.cpp
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
|
154
Modules/gin/components/propertycomponents.h
Executable file
154
Modules/gin/components/propertycomponents.h
Executable file
|
@ -0,0 +1,154 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
class PropertyComponentBase : public PropertyComponent,
|
||||||
|
private Value::Listener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PropertyComponentBase (const Value& valueToControl, const String& propertyName)
|
||||||
|
: PropertyComponent (propertyName), value (valueToControl)
|
||||||
|
{
|
||||||
|
value.addListener (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void valueChanged (Value&) override
|
||||||
|
{
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
Value value;
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================*/
|
||||||
|
class FilePropertyComponent : public PropertyComponentBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FilePropertyComponent (const Value& valueToControl, const String& propertyName, const String& title_ = "Open", const String pattern_ = "*.*")
|
||||||
|
: PropertyComponentBase (valueToControl, propertyName), title (title_), pattern (pattern_)
|
||||||
|
{
|
||||||
|
addAndMakeVisible (container);
|
||||||
|
|
||||||
|
container.browse.onClick = [this]
|
||||||
|
{
|
||||||
|
FileChooser box (title, File (value.toString()), pattern);
|
||||||
|
|
||||||
|
if (box.browseForFileToOpen())
|
||||||
|
value.setValue (box.getResult().getFullPathName());
|
||||||
|
};
|
||||||
|
|
||||||
|
container.clear.onClick = [this] { value.setValue (""); };
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh() override
|
||||||
|
{
|
||||||
|
container.filename.setText (value.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Container : public Component
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Container()
|
||||||
|
{
|
||||||
|
addAndMakeVisible (filename);
|
||||||
|
addAndMakeVisible (browse);
|
||||||
|
addAndMakeVisible (clear);
|
||||||
|
|
||||||
|
filename.setReadOnly (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resized() override
|
||||||
|
{
|
||||||
|
auto rc = getLocalBounds();
|
||||||
|
clear.setBounds (rc.removeFromRight (rc.getHeight()));
|
||||||
|
browse.setBounds (rc.removeFromRight (rc.getHeight()));
|
||||||
|
rc.removeFromRight (3);
|
||||||
|
filename.setBounds (rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextEditor filename;
|
||||||
|
TextButton browse {"..."};
|
||||||
|
TextButton clear {"X"};
|
||||||
|
};
|
||||||
|
|
||||||
|
Container container;
|
||||||
|
|
||||||
|
String title, pattern;
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================*/
|
||||||
|
class ColourPropertyComponent : public PropertyComponentBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ColourPropertyComponent (const Value& valueToControl, const String& propertyName, bool showAlpha = false)
|
||||||
|
: PropertyComponentBase (valueToControl, propertyName), container (value, showAlpha)
|
||||||
|
{
|
||||||
|
addAndMakeVisible (container);
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh() override
|
||||||
|
{
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void paint (Graphics& g) override
|
||||||
|
{
|
||||||
|
PropertyComponent::paint (g);
|
||||||
|
|
||||||
|
g.setColour (findColour (BooleanPropertyComponent::backgroundColourId));
|
||||||
|
g.fillRect (container.getBounds());
|
||||||
|
|
||||||
|
g.setColour (findColour (BooleanPropertyComponent::outlineColourId));
|
||||||
|
g.drawRect (container.getBounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Container : public Component
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Container (Value& value_, bool a)
|
||||||
|
: value (value_), alpha (a)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void paint (Graphics& g) override
|
||||||
|
{
|
||||||
|
Colour c = Colour::fromString (value.toString());
|
||||||
|
|
||||||
|
g.setColour (c);
|
||||||
|
g.fillRect (getLocalBounds().reduced (4));
|
||||||
|
|
||||||
|
g.setColour (c.contrasting());
|
||||||
|
g.drawText (c.toDisplayString (alpha), getLocalBounds(), Justification::centred);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mouseUp (const MouseEvent& e) override
|
||||||
|
{
|
||||||
|
if (e.mouseWasClicked())
|
||||||
|
{
|
||||||
|
ColourSelector colourSelector (ColourSelector::showColourAtTop | ColourSelector::showSliders | ColourSelector::showColourspace);
|
||||||
|
|
||||||
|
colourSelector.setSize (300, 280);
|
||||||
|
colourSelector.setCurrentColour (Colour::fromString (value.toString()), dontSendNotification);
|
||||||
|
|
||||||
|
CallOutBox callOut (colourSelector, getScreenBounds(), nullptr);
|
||||||
|
callOut.runModalLoop();
|
||||||
|
|
||||||
|
value = colourSelector.getCurrentColour().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Value& value;
|
||||||
|
bool alpha;
|
||||||
|
};
|
||||||
|
|
||||||
|
Container container;
|
||||||
|
};
|
2214
Modules/gin/components/singlelinetexteditor.cpp
Executable file
2214
Modules/gin/components/singlelinetexteditor.cpp
Executable file
File diff suppressed because it is too large
Load diff
617
Modules/gin/components/singlelinetexteditor.h
Executable file
617
Modules/gin/components/singlelinetexteditor.h
Executable file
|
@ -0,0 +1,617 @@
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
This file is part of the JUCE library.
|
||||||
|
Copyright (c) 2015 - ROLI Ltd.
|
||||||
|
|
||||||
|
Permission is granted to use this software under the terms of either:
|
||||||
|
a) the GPL v2 (or any later version)
|
||||||
|
b) the Affero GPL v3
|
||||||
|
|
||||||
|
Details of these licenses can be found at: www.gnu.org/licenses
|
||||||
|
|
||||||
|
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
To release a closed-source product which uses JUCE, commercial licenses are
|
||||||
|
available: visit www.juce.com for more information.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/**
|
||||||
|
An editable text box.
|
||||||
|
|
||||||
|
A TextEditor can either be in single- or multi-line mode, and supports mixed
|
||||||
|
fonts and colours.
|
||||||
|
|
||||||
|
@see TextEditor::Listener, Label
|
||||||
|
*/
|
||||||
|
class SingleLineTextEditor : public Component,
|
||||||
|
public TextInputTarget,
|
||||||
|
public SettableTooltipClient
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//==============================================================================
|
||||||
|
/** Creates a new, empty text editor.
|
||||||
|
|
||||||
|
@param componentName the name to pass to the component for it to use as its name
|
||||||
|
*/
|
||||||
|
explicit SingleLineTextEditor (const String& componentName = String());
|
||||||
|
|
||||||
|
/** Destructor. */
|
||||||
|
~SingleLineTextEditor() override;
|
||||||
|
|
||||||
|
/** Indicates whether the tab key should be accepted and used to input a tab character,
|
||||||
|
or whether it gets ignored.
|
||||||
|
|
||||||
|
By default the tab key is ignored, so that it can be used to switch keyboard focus
|
||||||
|
between components.
|
||||||
|
*/
|
||||||
|
void setTabKeyUsedAsCharacter (bool shouldTabKeyBeUsed);
|
||||||
|
|
||||||
|
/** Returns true if the tab key is being used for input.
|
||||||
|
@see setTabKeyUsedAsCharacter
|
||||||
|
*/
|
||||||
|
bool isTabKeyUsedAsCharacter() const { return tabKeyUsed; }
|
||||||
|
|
||||||
|
/** This can be used to change whether escape and return keypress events are
|
||||||
|
propagated up to the parent component.
|
||||||
|
The default here is true, meaning that these events are not allowed to reach the
|
||||||
|
parent, but you may want to allow them through so that they can trigger other
|
||||||
|
actions, e.g. closing a dialog box, etc.
|
||||||
|
*/
|
||||||
|
void setEscapeAndReturnKeysConsumed (bool shouldBeConsumed) noexcept;
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/** Changes the editor to read-only mode.
|
||||||
|
|
||||||
|
By default, the text editor is not read-only. If you're making it read-only, you
|
||||||
|
might also want to call setCaretVisible (false) to get rid of the caret.
|
||||||
|
|
||||||
|
The text can still be highlighted and copied when in read-only mode.
|
||||||
|
|
||||||
|
@see isReadOnly, setCaretVisible
|
||||||
|
*/
|
||||||
|
void setReadOnly (bool shouldBeReadOnly);
|
||||||
|
|
||||||
|
/** Returns true if the editor is in read-only mode. */
|
||||||
|
bool isReadOnly() const noexcept;
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/** Makes the caret visible or invisible.
|
||||||
|
By default the caret is visible.
|
||||||
|
@see setCaretColour, setCaretPosition
|
||||||
|
*/
|
||||||
|
void setCaretVisible (bool shouldBeVisible);
|
||||||
|
|
||||||
|
/** Returns true if the caret is enabled.
|
||||||
|
@see setCaretVisible
|
||||||
|
*/
|
||||||
|
bool isCaretVisible() const noexcept { return caretVisible && ! isReadOnly(); }
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/** Allows a right-click menu to appear for the editor.
|
||||||
|
|
||||||
|
(This defaults to being enabled).
|
||||||
|
|
||||||
|
If enabled, right-clicking (or command-clicking on the Mac) will pop up a menu
|
||||||
|
of options such as cut/copy/paste, undo/redo, etc.
|
||||||
|
*/
|
||||||
|
void setPopupMenuEnabled (bool menuEnabled);
|
||||||
|
|
||||||
|
/** Returns true if the right-click menu is enabled.
|
||||||
|
@see setPopupMenuEnabled
|
||||||
|
*/
|
||||||
|
bool isPopupMenuEnabled() const noexcept { return popupMenuEnabled; }
|
||||||
|
|
||||||
|
/** Returns true if a popup-menu is currently being displayed. */
|
||||||
|
bool isPopupMenuCurrentlyActive() const noexcept { return menuActive; }
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/** A set of colour IDs to use to change the colour of various aspects of the editor.
|
||||||
|
|
||||||
|
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||||
|
methods.
|
||||||
|
|
||||||
|
NB: You can also set the caret colour using CaretComponent::caretColourId
|
||||||
|
|
||||||
|
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||||
|
*/
|
||||||
|
enum ColourIds
|
||||||
|
{
|
||||||
|
backgroundColourId = 0x1000200, /**< The colour to use for the text component's background - this can be
|
||||||
|
transparent if necessary. */
|
||||||
|
|
||||||
|
textColourId = 0x1000201, /**< The colour that will be used when text is added to the editor. Note
|
||||||
|
that because the editor can contain multiple colours, calling this
|
||||||
|
method won't change the colour of existing text - to do that, call
|
||||||
|
applyFontToAllText() after calling this method.*/
|
||||||
|
|
||||||
|
highlightColourId = 0x1000202, /**< The colour with which to fill the background of highlighted sections of
|
||||||
|
the text - this can be transparent if you don't want to show any
|
||||||
|
highlighting.*/
|
||||||
|
|
||||||
|
highlightedTextColourId = 0x1000203, /**< The colour with which to draw the text in highlighted sections. */
|
||||||
|
|
||||||
|
outlineColourId = 0x1000205, /**< If this is non-transparent, it will be used to draw a box around
|
||||||
|
the edge of the component. */
|
||||||
|
|
||||||
|
focusedOutlineColourId = 0x1000206, /**< If this is non-transparent, it will be used to draw a box around
|
||||||
|
the edge of the component when it has focus. */
|
||||||
|
|
||||||
|
shadowColourId = 0x1000207, /**< If this is non-transparent, it'll be used to draw an inner shadow
|
||||||
|
around the edge of the editor. */
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/** Sets the font to use for newly added text.
|
||||||
|
|
||||||
|
This will change the font that will be used next time any text is added or entered
|
||||||
|
into the editor. It won't change the font of any existing text - to do that, use
|
||||||
|
applyFontToAllText() instead.
|
||||||
|
|
||||||
|
@see applyFontToAllText
|
||||||
|
*/
|
||||||
|
void setFont (const Font& newFont);
|
||||||
|
|
||||||
|
/** Applies a font to all the text in the editor.
|
||||||
|
This will also set the current font to use for any new text that's added.
|
||||||
|
@see setFont
|
||||||
|
*/
|
||||||
|
void applyFontToAllText (const Font& newFont);
|
||||||
|
|
||||||
|
/** Returns the font that's currently being used for new text.
|
||||||
|
@see setFont
|
||||||
|
*/
|
||||||
|
const Font& getFont() const noexcept { return currentFont; }
|
||||||
|
|
||||||
|
void setJustificationType (Justification just) { justification = just; }
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/** If set to true, focusing on the editor will highlight all its text.
|
||||||
|
|
||||||
|
(Set to false by default).
|
||||||
|
|
||||||
|
This is useful for boxes where you expect the user to re-enter all the
|
||||||
|
text when they focus on the component, rather than editing what's already there.
|
||||||
|
*/
|
||||||
|
void setSelectAllWhenFocused (bool shouldSelectAll);
|
||||||
|
|
||||||
|
/** When the text editor is empty, it can be set to display a message.
|
||||||
|
|
||||||
|
This is handy for things like telling the user what to type in the box - the
|
||||||
|
string is only displayed, it's not taken to actually be the contents of
|
||||||
|
the editor.
|
||||||
|
*/
|
||||||
|
void setTextToShowWhenEmpty (const String& text, Colour colourToUse);
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/**
|
||||||
|
Receives callbacks from a TextEditor component when it changes.
|
||||||
|
|
||||||
|
@see TextEditor::addListener
|
||||||
|
*/
|
||||||
|
class JUCE_API Listener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** Destructor. */
|
||||||
|
virtual ~Listener() {}
|
||||||
|
|
||||||
|
/** Called when the user changes the text in some way. */
|
||||||
|
virtual void sltextEditorTextChanged (SingleLineTextEditor&) {}
|
||||||
|
|
||||||
|
/** Called when the user presses the return key. */
|
||||||
|
virtual void sltextEditorReturnKeyPressed (SingleLineTextEditor&) {}
|
||||||
|
|
||||||
|
/** Called when the user presses the escape key. */
|
||||||
|
virtual void sltextEditorEscapeKeyPressed (SingleLineTextEditor&) {}
|
||||||
|
|
||||||
|
/** Called when the text editor loses focus. */
|
||||||
|
virtual void sltextEditorFocusLost (SingleLineTextEditor&) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
/** Registers a listener to be told when things happen to the text.
|
||||||
|
@see removeListener
|
||||||
|
*/
|
||||||
|
void addListener (SingleLineTextEditor::Listener* newListener);
|
||||||
|
|
||||||
|
/** Deregisters a listener.
|
||||||
|
@see addListener
|
||||||
|
*/
|
||||||
|
void removeListener (SingleLineTextEditor::Listener* listenerToRemove);
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/** Returns the entire contents of the editor. */
|
||||||
|
String getText() const;
|
||||||
|
|
||||||
|
/** Returns a section of the contents of the editor. */
|
||||||
|
String getTextInRange (const Range<int>& textRange) const override;
|
||||||
|
|
||||||
|
/** Returns true if there are no characters in the editor.
|
||||||
|
This is far more efficient than calling getText().isEmpty().
|
||||||
|
*/
|
||||||
|
bool isEmpty() const;
|
||||||
|
|
||||||
|
/** Sets the entire content of the editor.
|
||||||
|
|
||||||
|
This will clear the editor and insert the given text (using the current text colour
|
||||||
|
and font). You can set the current text colour using
|
||||||
|
@code setColour (TextEditor::textColourId, ...);
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@param newText the text to add
|
||||||
|
@param sendTextChangeMessage if true, this will cause a change message to
|
||||||
|
be sent to all the listeners.
|
||||||
|
@see insertTextAtCaret
|
||||||
|
*/
|
||||||
|
void setText (const String& newText,
|
||||||
|
bool sendTextChangeMessage = true);
|
||||||
|
|
||||||
|
/** Returns a Value object that can be used to get or set the text.
|
||||||
|
|
||||||
|
Bear in mind that this operate quite slowly if your text box contains large
|
||||||
|
amounts of text, as it needs to dynamically build the string that's involved.
|
||||||
|
It's best used for small text boxes.
|
||||||
|
*/
|
||||||
|
Value& getTextValue();
|
||||||
|
|
||||||
|
/** Inserts some text at the current caret position.
|
||||||
|
|
||||||
|
If a section of the text is highlighted, it will be replaced by
|
||||||
|
this string, otherwise it will be inserted.
|
||||||
|
|
||||||
|
To delete a section of text, you can use setHighlightedRegion() to
|
||||||
|
highlight it, and call insertTextAtCaret (String()).
|
||||||
|
|
||||||
|
@see setCaretPosition, getCaretPosition, setHighlightedRegion
|
||||||
|
*/
|
||||||
|
void insertTextAtCaret (const String& textToInsert) override;
|
||||||
|
|
||||||
|
/** Deletes all the text from the editor. */
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
/** Deletes the currently selected region.
|
||||||
|
This doesn't copy the deleted section to the clipboard - if you need to do that, call copy() first.
|
||||||
|
@see copy, paste, SystemClipboard
|
||||||
|
*/
|
||||||
|
void cut();
|
||||||
|
|
||||||
|
/** Copies the currently selected region to the clipboard.
|
||||||
|
@see cut, paste, SystemClipboard
|
||||||
|
*/
|
||||||
|
void copy();
|
||||||
|
|
||||||
|
/** Pastes the contents of the clipboard into the editor at the caret position.
|
||||||
|
@see cut, copy, SystemClipboard
|
||||||
|
*/
|
||||||
|
void paste();
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/** Returns the current index of the caret.
|
||||||
|
@see setCaretPosition
|
||||||
|
*/
|
||||||
|
int getCaretPosition() const;
|
||||||
|
|
||||||
|
/** Moves the caret to be in front of a given character.
|
||||||
|
@see getCaretPosition, moveCaretToEnd
|
||||||
|
*/
|
||||||
|
void setCaretPosition (int newIndex);
|
||||||
|
|
||||||
|
/** Get the graphical position of the caret.
|
||||||
|
|
||||||
|
The rectangle returned is relative to the component's top-left corner.
|
||||||
|
@see scrollEditorToPositionCaret
|
||||||
|
*/
|
||||||
|
Rectangle<int> getCaretRectangle() override;
|
||||||
|
|
||||||
|
/** Selects a section of the text. */
|
||||||
|
void setHighlightedRegion (const Range<int>& newSelection) override;
|
||||||
|
|
||||||
|
/** Returns the range of characters that are selected.
|
||||||
|
If nothing is selected, this will return an empty range.
|
||||||
|
@see setHighlightedRegion
|
||||||
|
*/
|
||||||
|
Range<int> getHighlightedRegion() const override { return selection; }
|
||||||
|
|
||||||
|
/** Returns the section of text that is currently selected. */
|
||||||
|
String getHighlightedText() const;
|
||||||
|
|
||||||
|
/** Finds the index of the character at a given position.
|
||||||
|
The coordinates are relative to the component's top-left.
|
||||||
|
*/
|
||||||
|
int getTextIndexAt (int x, int y);
|
||||||
|
|
||||||
|
/** Counts the number of characters in the text.
|
||||||
|
|
||||||
|
This is quicker than getting the text as a string if you just need to know
|
||||||
|
the length.
|
||||||
|
*/
|
||||||
|
int getTotalNumChars() const;
|
||||||
|
|
||||||
|
/** Returns the total width of the text, as it is currently laid-out.
|
||||||
|
|
||||||
|
This may be larger than the size of the TextEditor, and can change when
|
||||||
|
the TextEditor is resized or the text changes.
|
||||||
|
*/
|
||||||
|
int getTextWidth() const;
|
||||||
|
|
||||||
|
/** Returns the maximum height of the text, as it is currently laid-out.
|
||||||
|
|
||||||
|
This may be larger than the size of the TextEditor, and can change when
|
||||||
|
the TextEditor is resized or the text changes.
|
||||||
|
*/
|
||||||
|
int getTextHeight() const;
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
void moveCaretToEnd();
|
||||||
|
bool moveCaretLeft (bool moveInWholeWordSteps, bool selecting);
|
||||||
|
bool moveCaretRight (bool moveInWholeWordSteps, bool selecting);
|
||||||
|
bool moveCaretUp (bool selecting);
|
||||||
|
bool moveCaretDown (bool selecting);
|
||||||
|
bool pageUp (bool selecting);
|
||||||
|
bool pageDown (bool selecting);
|
||||||
|
bool scrollDown() { return false; }
|
||||||
|
bool scrollUp() { return false; }
|
||||||
|
bool moveCaretToTop (bool selecting);
|
||||||
|
bool moveCaretToStartOfLine (bool selecting);
|
||||||
|
bool moveCaretToEnd (bool selecting);
|
||||||
|
bool moveCaretToEndOfLine (bool selecting);
|
||||||
|
bool deleteBackwards (bool moveInWholeWordSteps);
|
||||||
|
bool deleteForwards (bool moveInWholeWordSteps);
|
||||||
|
bool copyToClipboard();
|
||||||
|
bool cutToClipboard();
|
||||||
|
bool pasteFromClipboard();
|
||||||
|
bool selectAll();
|
||||||
|
bool undo();
|
||||||
|
bool redo();
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/** This adds the items to the popup menu.
|
||||||
|
|
||||||
|
By default it adds the cut/copy/paste items, but you can override this if
|
||||||
|
you need to replace these with your own items.
|
||||||
|
|
||||||
|
If you want to add your own items to the existing ones, you can override this,
|
||||||
|
call the base class's addPopupMenuItems() method, then append your own items.
|
||||||
|
|
||||||
|
When the menu has been shown, performPopupMenuAction() will be called to
|
||||||
|
perform the item that the user has chosen.
|
||||||
|
|
||||||
|
The default menu items will be added using item IDs from the
|
||||||
|
StandardApplicationCommandIDs namespace.
|
||||||
|
|
||||||
|
If this was triggered by a mouse-click, the mouseClickEvent parameter will be
|
||||||
|
a pointer to the info about it, or may be null if the menu is being triggered
|
||||||
|
by some other means.
|
||||||
|
|
||||||
|
@see performPopupMenuAction, setPopupMenuEnabled, isPopupMenuEnabled
|
||||||
|
*/
|
||||||
|
virtual void addPopupMenuItems (PopupMenu& menuToAddTo,
|
||||||
|
const MouseEvent* mouseClickEvent);
|
||||||
|
|
||||||
|
/** This is called to perform one of the items that was shown on the popup menu.
|
||||||
|
|
||||||
|
If you've overridden addPopupMenuItems(), you should also override this
|
||||||
|
to perform the actions that you've added.
|
||||||
|
|
||||||
|
If you've overridden addPopupMenuItems() but have still left the default items
|
||||||
|
on the menu, remember to call the superclass's performPopupMenuAction()
|
||||||
|
so that it can perform the default actions if that's what the user clicked on.
|
||||||
|
|
||||||
|
@see addPopupMenuItems, setPopupMenuEnabled, isPopupMenuEnabled
|
||||||
|
*/
|
||||||
|
virtual void performPopupMenuAction (int menuItemID);
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/** Base class for input filters that can be applied to a TextEditor to restrict
|
||||||
|
the text that can be entered.
|
||||||
|
*/
|
||||||
|
class JUCE_API InputFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InputFilter() {}
|
||||||
|
virtual ~InputFilter() {}
|
||||||
|
|
||||||
|
/** This method is called whenever text is entered into the editor.
|
||||||
|
An implementation of this class should should check the input string,
|
||||||
|
and return an edited version of it that should be used.
|
||||||
|
*/
|
||||||
|
virtual String filterNewText (SingleLineTextEditor&, const String& newInput) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** An input filter for a TextEditor that limits the length of text and/or the
|
||||||
|
characters that it may contain.
|
||||||
|
*/
|
||||||
|
class JUCE_API LengthAndCharacterRestriction : public InputFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** Creates a filter that limits the length of text, and/or the characters that it can contain.
|
||||||
|
@param maxNumChars if this is > 0, it sets a maximum length limit; if <= 0, no
|
||||||
|
limit is set
|
||||||
|
@param allowedCharacters if this is non-empty, then only characters that occur in
|
||||||
|
this string are allowed to be entered into the editor.
|
||||||
|
*/
|
||||||
|
LengthAndCharacterRestriction (int maxNumChars, const String& allowedCharacters);
|
||||||
|
|
||||||
|
String filterNewText (SingleLineTextEditor&, const String&) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
String allowedCharacters;
|
||||||
|
int maxLength;
|
||||||
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LengthAndCharacterRestriction)
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Sets an input filter that should be applied to this editor.
|
||||||
|
The filter can be nullptr, to remove any existing filters.
|
||||||
|
If takeOwnership is true, then the filter will be owned and deleted by the editor
|
||||||
|
when no longer needed.
|
||||||
|
*/
|
||||||
|
void setInputFilter (InputFilter* newFilter, bool takeOwnership);
|
||||||
|
|
||||||
|
/** Returns the current InputFilter, as set by setInputFilter(). */
|
||||||
|
InputFilter* getInputFilter() const noexcept { return inputFilter; }
|
||||||
|
|
||||||
|
/** Sets limits on the characters that can be entered.
|
||||||
|
This is just a shortcut that passes an instance of the LengthAndCharacterRestriction
|
||||||
|
class to setInputFilter().
|
||||||
|
|
||||||
|
@param maxTextLength if this is > 0, it sets a maximum length limit; if 0, no
|
||||||
|
limit is set
|
||||||
|
@param allowedCharacters if this is non-empty, then only characters that occur in
|
||||||
|
this string are allowed to be entered into the editor.
|
||||||
|
*/
|
||||||
|
void setInputRestrictions (int maxTextLength,
|
||||||
|
const String& allowedCharacters = String());
|
||||||
|
|
||||||
|
void setKeyboardType (VirtualKeyboardType type) noexcept { keyboardType = type; }
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/** This abstract base class is implemented by LookAndFeel classes to provide
|
||||||
|
TextEditor drawing functionality.
|
||||||
|
*/
|
||||||
|
struct JUCE_API LookAndFeelMethods
|
||||||
|
{
|
||||||
|
virtual ~LookAndFeelMethods() {}
|
||||||
|
|
||||||
|
virtual void fillSingleLineTextEditorBackground (Graphics&, int width, int height, SingleLineTextEditor&) = 0;
|
||||||
|
virtual void drawSingleLineTextEditorOutline (Graphics&, int width, int height, SingleLineTextEditor&) = 0;
|
||||||
|
|
||||||
|
virtual CaretComponent* createSingleLineCaretComponent (Component* keyFocusOwner) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/** @internal */
|
||||||
|
void paint (Graphics&) override;
|
||||||
|
/** @internal */
|
||||||
|
void paintOverChildren (Graphics&) override;
|
||||||
|
/** @internal */
|
||||||
|
void mouseDown (const MouseEvent&) override;
|
||||||
|
/** @internal */
|
||||||
|
void mouseUp (const MouseEvent&) override;
|
||||||
|
/** @internal */
|
||||||
|
void mouseDrag (const MouseEvent&) override;
|
||||||
|
/** @internal */
|
||||||
|
void mouseDoubleClick (const MouseEvent&) override;
|
||||||
|
/** @internal */
|
||||||
|
void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override;
|
||||||
|
/** @internal */
|
||||||
|
bool keyPressed (const KeyPress&) override;
|
||||||
|
/** @internal */
|
||||||
|
bool keyStateChanged (bool) override;
|
||||||
|
/** @internal */
|
||||||
|
void focusGained (FocusChangeType) override;
|
||||||
|
/** @internal */
|
||||||
|
void focusLost (FocusChangeType) override;
|
||||||
|
/** @internal */
|
||||||
|
void resized() override;
|
||||||
|
/** @internal */
|
||||||
|
void enablementChanged() override;
|
||||||
|
/** @internal */
|
||||||
|
void colourChanged() override;
|
||||||
|
/** @internal */
|
||||||
|
void lookAndFeelChanged() override;
|
||||||
|
/** @internal */
|
||||||
|
bool isTextInputActive() const override;
|
||||||
|
/** @internal */
|
||||||
|
void setTemporaryUnderlining (const Array<Range<int> >&) override;
|
||||||
|
/** @internal */
|
||||||
|
VirtualKeyboardType getKeyboardType() override { return keyboardType; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** Used internally to dispatch a text-change message. */
|
||||||
|
void textChanged();
|
||||||
|
|
||||||
|
/** Begins a new transaction in the UndoManager. */
|
||||||
|
void newTransaction();
|
||||||
|
|
||||||
|
/** Can be overridden to intercept return key presses directly */
|
||||||
|
virtual void returnPressed();
|
||||||
|
|
||||||
|
/** Can be overridden to intercept escape key presses directly */
|
||||||
|
virtual void escapePressed();
|
||||||
|
|
||||||
|
private:
|
||||||
|
//==============================================================================
|
||||||
|
class Iterator;
|
||||||
|
JUCE_PUBLIC_IN_DLL_BUILD (class UniformTextSection)
|
||||||
|
class TextHolderComponent;
|
||||||
|
class InsertAction;
|
||||||
|
class RemoveAction;
|
||||||
|
friend class InsertAction;
|
||||||
|
friend class RemoveAction;
|
||||||
|
|
||||||
|
std::unique_ptr<TextHolderComponent> textHolder;
|
||||||
|
|
||||||
|
bool readOnly;
|
||||||
|
bool caretVisible;
|
||||||
|
bool popupMenuEnabled;
|
||||||
|
bool selectAllTextWhenFocused;
|
||||||
|
bool wasFocused;
|
||||||
|
bool tabKeyUsed;
|
||||||
|
bool menuActive;
|
||||||
|
bool valueTextNeedsUpdating;
|
||||||
|
bool consumeEscAndReturnKeys;
|
||||||
|
|
||||||
|
UndoManager undoManager;
|
||||||
|
std::unique_ptr<CaretComponent> caret;
|
||||||
|
Range<int> selection;
|
||||||
|
unsigned int lastTransactionTime;
|
||||||
|
Font currentFont;
|
||||||
|
mutable int totalNumChars;
|
||||||
|
int caretPosition;
|
||||||
|
OwnedArray<UniformTextSection> sections;
|
||||||
|
String textToShowWhenEmpty;
|
||||||
|
Colour colourForTextWhenEmpty;
|
||||||
|
OptionalScopedPointer<InputFilter> inputFilter;
|
||||||
|
Value textValue;
|
||||||
|
VirtualKeyboardType keyboardType;
|
||||||
|
Justification justification = Justification::centred;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
notDragging,
|
||||||
|
draggingSelectionStart,
|
||||||
|
draggingSelectionEnd
|
||||||
|
} dragType;
|
||||||
|
|
||||||
|
ListenerList<Listener> listeners;
|
||||||
|
Array<Range<int> > underlinedSections;
|
||||||
|
|
||||||
|
void moveCaret (int newCaretPos);
|
||||||
|
void moveCaretTo (int newPosition, bool isSelecting);
|
||||||
|
void recreateCaret();
|
||||||
|
void handleCommandMessage (int) override;
|
||||||
|
void coalesceSimilarSections();
|
||||||
|
void splitSection (int sectionIndex, int charToSplitAt);
|
||||||
|
void clearInternal (UndoManager*);
|
||||||
|
void insert (const String&, int insertIndex, const Font&, const Colour, UndoManager*, int newCaretPos);
|
||||||
|
void reinsert (int insertIndex, const OwnedArray<UniformTextSection>&);
|
||||||
|
void remove (Range<int> range, UndoManager*, int caretPositionToMoveTo);
|
||||||
|
void getCharPosition (int index, float& x, float& y, float& lineHeight) const;
|
||||||
|
void updateCaretPosition();
|
||||||
|
void updateValueFromText();
|
||||||
|
void textWasChangedByValue();
|
||||||
|
int indexAtPosition (float x, float y);
|
||||||
|
int findWordBreakAfter (int position) const;
|
||||||
|
int findWordBreakBefore (int position) const;
|
||||||
|
bool moveCaretWithTransaction (int newPos, bool selecting);
|
||||||
|
friend class TextHolderComponent;
|
||||||
|
void drawContent (Graphics&);
|
||||||
|
float getWordWrapWidth() const;
|
||||||
|
void timerCallbackInt();
|
||||||
|
void repaintText (Range<int>);
|
||||||
|
bool undoOrRedo (bool shouldUndo);
|
||||||
|
UndoManager* getUndoManager() noexcept;
|
||||||
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SingleLineTextEditor)
|
||||||
|
};
|
112
Modules/gin/geometry/geometry.h
Executable file
112
Modules/gin/geometry/geometry.h
Executable file
|
@ -0,0 +1,112 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T square (T in)
|
||||||
|
{
|
||||||
|
return in * in;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class Ellipse
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Ellipse (T a_, T b_) : a (a_), b (b_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isPointOn (juce::Point<T> pt, T accuracy = 0.00001)
|
||||||
|
{
|
||||||
|
return std::abs (1.0 - (square (pt.getX()) / square (a) + square (pt.getY()) / square (b))) < accuracy;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isPointOutside (juce::Point<T> pt)
|
||||||
|
{
|
||||||
|
return (square (pt.getX()) / square (a) + square (pt.getY()) / square (b)) > 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isPointInside (juce::Point<T> pt)
|
||||||
|
{
|
||||||
|
return (square (pt.getX()) / square (a) + square (pt.getY()) / square (b)) < 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
juce::Point<T> pointAtAngle (T angle)
|
||||||
|
{
|
||||||
|
T x = (a * b) / std::sqrt (square (b) + square (a) * square (std::tan (angle)));
|
||||||
|
T y = (a * b) / std::sqrt (square (a) + square (b) / square (std::tan (angle)));
|
||||||
|
|
||||||
|
while (angle < 0) angle += double_Pi * 2;
|
||||||
|
angle = std::fmod (angle, double_Pi * 2);
|
||||||
|
|
||||||
|
if (angle >= double_Pi / 2 * 3)
|
||||||
|
{
|
||||||
|
y = -y;
|
||||||
|
}
|
||||||
|
else if (angle >= double_Pi)
|
||||||
|
{
|
||||||
|
y = -y;
|
||||||
|
x = -x;
|
||||||
|
}
|
||||||
|
else if (angle >= double_Pi / 2)
|
||||||
|
{
|
||||||
|
x = -x;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {x, y};
|
||||||
|
}
|
||||||
|
|
||||||
|
T a = 0, b = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Solves for the slope and intercept of a line.
|
||||||
|
template <typename T>
|
||||||
|
bool solveLine (T x1, T y1, T x2, T y2, T& m, T& b)
|
||||||
|
{
|
||||||
|
if (x2 != x1)
|
||||||
|
{
|
||||||
|
m = (y2 - y1) / (x2 - x1);
|
||||||
|
b = y2 - m * x2;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m = 0;
|
||||||
|
b = 0;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool solveLine (Line<T> l, T& m, T& b)
|
||||||
|
{
|
||||||
|
T x1 = l.getStartX();
|
||||||
|
T x2 = l.getEndX();
|
||||||
|
T y1 = l.getStartY();
|
||||||
|
T y2 = l.getEndY();
|
||||||
|
|
||||||
|
if (x2 != x1)
|
||||||
|
{
|
||||||
|
m = (y2 - y1) / (x2 - x1);
|
||||||
|
b = y2 - m * x2;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m = 0;
|
||||||
|
b = 0;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
134
Modules/gin/gin.cpp
Executable file
134
Modules/gin/gin.cpp
Executable file
|
@ -0,0 +1,134 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
// Your project must contain an AppConfig.h file with your project-specific settings in it,
|
||||||
|
// and your header search path must make it accessible to the module's files.
|
||||||
|
#include "AppConfig.h"
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <ctime>
|
||||||
|
#else
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
#include <ctime>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <sys/inotify.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __clang__ || __GNUC__
|
||||||
|
#include <cxxabi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cfloat>
|
||||||
|
|
||||||
|
#include "gin.h"
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#if __clang__
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wconversion"
|
||||||
|
#pragma clang diagnostic ignored "-Wshadow"
|
||||||
|
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if _MSC_VER
|
||||||
|
#pragma warning (push)
|
||||||
|
#pragma warning (disable: 4100)
|
||||||
|
#pragma warning (disable: 4127)
|
||||||
|
#pragma warning (disable: 4456)
|
||||||
|
#pragma warning (disable: 4457)
|
||||||
|
#pragma warning (disable: 4244)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "3rdparty/avir/avir.h"
|
||||||
|
#if JUCE_INTEL
|
||||||
|
#include "3rdparty/avir/avir_float4_sse.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __clang__
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if _MSC_VER
|
||||||
|
#pragma warning (pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace gin
|
||||||
|
{
|
||||||
|
using namespace juce;
|
||||||
|
using juce::Rectangle;
|
||||||
|
using juce::MemoryBlock;
|
||||||
|
|
||||||
|
#include "utilities/asyncutilities.cpp"
|
||||||
|
#include "utilities/downloadmanager.cpp"
|
||||||
|
#include "utilities/elevatedfilecopy.cpp"
|
||||||
|
#include "utilities/filesystemwatcher.cpp"
|
||||||
|
#include "utilities/fileutilities.cpp"
|
||||||
|
#include "utilities/integrator.cpp"
|
||||||
|
#include "utilities/leastsquaresregression.cpp"
|
||||||
|
#include "utilities/linearregression.cpp"
|
||||||
|
#include "utilities/openstreetmaps.cpp"
|
||||||
|
#include "utilities/messagepack.cpp"
|
||||||
|
#include "utilities/realtimeasyncupdater.cpp"
|
||||||
|
#include "utilities/plist.cpp"
|
||||||
|
#include "utilities/sharedmemory.cpp"
|
||||||
|
#include "utilities/spline.cpp"
|
||||||
|
#include "utilities/systemsemaphore.cpp"
|
||||||
|
#include "utilities/threading.cpp"
|
||||||
|
#include "utilities/util.cpp"
|
||||||
|
#include "utilities/valuetreeobject.cpp"
|
||||||
|
#include "utilities/valuetreeutilities.cpp"
|
||||||
|
|
||||||
|
#include "images/imageeffects.cpp"
|
||||||
|
#include "images/imageutilities.cpp"
|
||||||
|
#include "images/imageeffects_blending.cpp"
|
||||||
|
#include "images/imageeffects_stackblur.cpp"
|
||||||
|
#include "images/bmpimageformat.cpp"
|
||||||
|
|
||||||
|
#include "components/componentviewer.cpp"
|
||||||
|
#include "components/ginlookandfeel.cpp"
|
||||||
|
#include "components/mapviewer.cpp"
|
||||||
|
#include "components/propertycomponents.cpp"
|
||||||
|
#include "components/singlelinetexteditor.cpp"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "utilities/equationparser.cpp"
|
||||||
|
|
||||||
|
#if __clang__
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wswitch-enum"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "3rdparty/muParser/muParser.cpp"
|
||||||
|
#include "3rdparty/muParser/muParserBase.cpp"
|
||||||
|
#include "3rdparty/muParser/muParserBytecode.cpp"
|
||||||
|
#include "3rdparty/muParser/muParserCallback.cpp"
|
||||||
|
#include "3rdparty/muParser/muParserError.cpp"
|
||||||
|
#include "3rdparty/muParser/muParserTokenReader.cpp"
|
||||||
|
|
||||||
|
#if __clang__
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
106
Modules/gin/gin.h
Executable file
106
Modules/gin/gin.h
Executable file
|
@ -0,0 +1,106 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
The block below describes the properties of this module, and is read by
|
||||||
|
the Projucer to automatically generate project code that uses it.
|
||||||
|
For details about the syntax and how to create or use a module, see the
|
||||||
|
JUCE Module Format.txt file.
|
||||||
|
|
||||||
|
|
||||||
|
BEGIN_JUCE_MODULE_DECLARATION
|
||||||
|
|
||||||
|
ID: gin
|
||||||
|
vendor: Roland Rabien
|
||||||
|
version: 1.0.0
|
||||||
|
name: Gin
|
||||||
|
description: Gin
|
||||||
|
website: www.rabiensoftware.com
|
||||||
|
license: BSD
|
||||||
|
|
||||||
|
dependencies: juce_gui_basics juce_gui_extra juce_events
|
||||||
|
OSXFrameworks: Security
|
||||||
|
|
||||||
|
END_JUCE_MODULE_DECLARATION
|
||||||
|
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef JUCE_MODULE_AVAILABLE_gin
|
||||||
|
/* If you fail to make sure that all your compile units are building JUCE with the same set of
|
||||||
|
option flags, then there's a risk that different compile units will treat the classes as having
|
||||||
|
different memory layouts, leading to very nasty memory corruption errors when they all get
|
||||||
|
linked together. That's why it's best to always include the Introjucer-generated AppConfig.h
|
||||||
|
file before any juce headers.
|
||||||
|
|
||||||
|
Note that if you do have an AppConfig.h file and hit this warning, it means that it doesn't
|
||||||
|
contain the JUCE_MODULE_AVAILABLE_xxx flags, which are necessary for some inter-module
|
||||||
|
functionality to work correctly. In that case, you should either rebuild your AppConfig.h with
|
||||||
|
the latest introjucer, or fix it manually to contain these flags.
|
||||||
|
*/
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma message ("Have you included your AppConfig.h file before including the JUCE headers?")
|
||||||
|
#else
|
||||||
|
#warning "Have you included your AppConfig.h file before including the JUCE headers?"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
#include <juce_gui_basics/juce_gui_basics.h>
|
||||||
|
#include <juce_gui_extra/juce_gui_extra.h>
|
||||||
|
#include <juce_audio_utils/juce_audio_utils.h>
|
||||||
|
#include <juce_events/juce_events.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace gin
|
||||||
|
{
|
||||||
|
using namespace juce;
|
||||||
|
using juce::Rectangle;
|
||||||
|
using juce::Component;
|
||||||
|
|
||||||
|
#include "utilities/asyncutilities.h"
|
||||||
|
#include "utilities/coalescedtimer.h"
|
||||||
|
#include "utilities/downloadmanager.h"
|
||||||
|
#include "utilities/equationparser.h"
|
||||||
|
#include "utilities/util.h"
|
||||||
|
#include "utilities/easing.h"
|
||||||
|
#include "utilities/elevatedfilecopy.h"
|
||||||
|
#include "utilities/filesystemwatcher.h"
|
||||||
|
#include "utilities/fileutilities.h"
|
||||||
|
#include "utilities/integrator.h"
|
||||||
|
#include "utilities/lagrange.h"
|
||||||
|
#include "utilities/leastsquaresregression.h"
|
||||||
|
#include "utilities/linearregression.h"
|
||||||
|
#include "utilities/openstreetmaps.h"
|
||||||
|
#include "utilities/messagepack.h"
|
||||||
|
#include "utilities/plist.h"
|
||||||
|
#include "utilities/realtimeasyncupdater.h"
|
||||||
|
#include "utilities/sharedmemory.h"
|
||||||
|
#include "utilities/spline.h"
|
||||||
|
#include "utilities/systemsemaphore.h"
|
||||||
|
#include "utilities/threading.h"
|
||||||
|
#include "utilities/valuetreeobject.h"
|
||||||
|
#include "utilities/valuetreeutilities.h"
|
||||||
|
|
||||||
|
#include "images/bmpimageformat.h"
|
||||||
|
#include "images/imageeffects.h"
|
||||||
|
#include "images/imageutilities.h"
|
||||||
|
|
||||||
|
#include "geometry/geometry.h"
|
||||||
|
|
||||||
|
#include "components/componentutils.h"
|
||||||
|
#include "components/componentviewer.h"
|
||||||
|
#include "components/ginlookandfeel.h"
|
||||||
|
#include "components/mapviewer.h"
|
||||||
|
#include "components/propertycomponents.h"
|
||||||
|
#include "components/singlelinetexteditor.h"
|
||||||
|
|
||||||
|
}
|
9
Modules/gin/gin.mm
Executable file
9
Modules/gin/gin.mm
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#include "gin.cpp"
|
||||||
|
|
153
Modules/gin/images/bmpimageformat.cpp
Executable file
153
Modules/gin/images/bmpimageformat.cpp
Executable file
|
@ -0,0 +1,153 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
struct BMPHeader
|
||||||
|
{
|
||||||
|
uint16 magic;
|
||||||
|
uint32 fileSize;
|
||||||
|
uint16 reserved1;
|
||||||
|
uint16 reserved2;
|
||||||
|
uint32 dataOffset;
|
||||||
|
uint32 headerSize;
|
||||||
|
int32 width;
|
||||||
|
int32 height;
|
||||||
|
uint16 planes;
|
||||||
|
uint16 bitsPerPixel;
|
||||||
|
uint32 compression;
|
||||||
|
uint32 imageDataSize;
|
||||||
|
int32 hPixelsPerMeter;
|
||||||
|
int32 vPixelsPerMeter;
|
||||||
|
uint32 coloursUsed;
|
||||||
|
uint32 coloursRequired;
|
||||||
|
};
|
||||||
|
|
||||||
|
String BMPImageFormat::getFormatName()
|
||||||
|
{
|
||||||
|
return "Windows Bitmap";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BMPImageFormat::canUnderstand (InputStream& input)
|
||||||
|
{
|
||||||
|
return input.readByte() == 'B' &&
|
||||||
|
input.readByte() == 'M';
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BMPImageFormat::usesFileExtension (const File& possibleFile)
|
||||||
|
{
|
||||||
|
return possibleFile.hasFileExtension ("bmp");
|
||||||
|
}
|
||||||
|
|
||||||
|
Image BMPImageFormat::decodeImage (InputStream& input)
|
||||||
|
{
|
||||||
|
BMPHeader hdr;
|
||||||
|
hdr.magic = uint16 (input.readShort());
|
||||||
|
hdr.fileSize = uint32 (input.readInt());
|
||||||
|
hdr.reserved1 = uint16 (input.readShort());
|
||||||
|
hdr.reserved2 = uint16 (input.readShort());
|
||||||
|
hdr.dataOffset = uint32 (input.readInt());
|
||||||
|
hdr.headerSize = uint32 (input.readInt());
|
||||||
|
hdr.width = int32 (input.readInt());
|
||||||
|
hdr.height = int32 (input.readInt());
|
||||||
|
hdr.planes = uint16 (input.readShort());
|
||||||
|
hdr.bitsPerPixel = uint16 (input.readShort());
|
||||||
|
hdr.compression = uint32 (input.readInt());
|
||||||
|
hdr.imageDataSize = uint32 (input.readInt());
|
||||||
|
hdr.hPixelsPerMeter = int32 (input.readInt());
|
||||||
|
hdr.vPixelsPerMeter = int32 (input.readInt());
|
||||||
|
hdr.coloursUsed = uint32 (input.readInt());
|
||||||
|
hdr.coloursRequired = uint32 (input.readInt());
|
||||||
|
|
||||||
|
if (hdr.compression != 0 || (hdr.bitsPerPixel != 8 && hdr.bitsPerPixel != 24 && hdr.bitsPerPixel != 32))
|
||||||
|
{
|
||||||
|
jassertfalse; // Unsupported BMP format
|
||||||
|
return Image();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hdr.bitsPerPixel == 8 && hdr.coloursUsed == 0)
|
||||||
|
hdr.coloursUsed = 256;
|
||||||
|
|
||||||
|
Array<PixelARGB> colourTable;
|
||||||
|
|
||||||
|
for (int i = 0; i < int (hdr.coloursUsed); i++)
|
||||||
|
{
|
||||||
|
uint8 b = uint8 (input.readByte());
|
||||||
|
uint8 g = uint8 (input.readByte());
|
||||||
|
uint8 r = uint8 (input.readByte());
|
||||||
|
input.readByte();
|
||||||
|
|
||||||
|
colourTable.add (PixelARGB (255, r, g, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bottomUp = hdr.height < 0;
|
||||||
|
hdr.height = std::abs (hdr.height);
|
||||||
|
|
||||||
|
Image img (Image::ARGB, int (hdr.width), int (hdr.height), true);
|
||||||
|
Image::BitmapData data (img, Image::BitmapData::writeOnly);
|
||||||
|
|
||||||
|
input.setPosition (hdr.dataOffset);
|
||||||
|
|
||||||
|
int bytesPerPixel = hdr.bitsPerPixel / 8;
|
||||||
|
int bytesPerRow = int (std::floor ((hdr.bitsPerPixel * hdr.width + 31) / 32.0) * 4);
|
||||||
|
|
||||||
|
uint8* rowData = new uint8[size_t (bytesPerRow)];
|
||||||
|
for (int y = 0; y < int (hdr.height); y++)
|
||||||
|
{
|
||||||
|
input.read (rowData, bytesPerRow);
|
||||||
|
|
||||||
|
for (int x = 0; x < int (hdr.width); x++)
|
||||||
|
{
|
||||||
|
uint8* d = &rowData[x * bytesPerPixel];
|
||||||
|
|
||||||
|
PixelARGB* p = (PixelARGB*)data.getPixelPointer (x, int (bottomUp ? y : hdr.height - y - 1));
|
||||||
|
if (hdr.bitsPerPixel == 8)
|
||||||
|
*p = colourTable[d[0]];
|
||||||
|
else
|
||||||
|
p->setARGB (bytesPerPixel == 4 ? d[3] : 255, d[2], d[1], d[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete[] rowData;
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BMPImageFormat::writeImageToStream (const Image& sourceImage, OutputStream& dst)
|
||||||
|
{
|
||||||
|
Image img = sourceImage.convertedToFormat (Image::ARGB);
|
||||||
|
|
||||||
|
dst.writeByte ('B');
|
||||||
|
dst.writeByte ('M');
|
||||||
|
dst.writeInt (40 + img.getWidth() * img.getHeight() * 4);
|
||||||
|
dst.writeShort (0);
|
||||||
|
dst.writeShort (0);
|
||||||
|
dst.writeInt (54);
|
||||||
|
dst.writeInt (40);
|
||||||
|
dst.writeInt (img.getWidth());
|
||||||
|
dst.writeInt (img.getHeight());
|
||||||
|
dst.writeShort (1);
|
||||||
|
dst.writeShort (32);
|
||||||
|
dst.writeInt (0);
|
||||||
|
dst.writeInt (img.getWidth() * img.getHeight() * 4);
|
||||||
|
dst.writeInt (2835);
|
||||||
|
dst.writeInt (2835);
|
||||||
|
dst.writeInt (0);
|
||||||
|
dst.writeInt (0);
|
||||||
|
|
||||||
|
Image::BitmapData data (img, Image::BitmapData::readOnly);
|
||||||
|
for (int y = 0; y < img.getHeight(); y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < img.getWidth(); x++)
|
||||||
|
{
|
||||||
|
PixelARGB* p = (PixelARGB*)data.getPixelPointer (x, int (img.getHeight() - y - 1));
|
||||||
|
dst.writeByte (char (p->getBlue()));
|
||||||
|
dst.writeByte (char (p->getGreen()));
|
||||||
|
dst.writeByte (char (p->getRed()));
|
||||||
|
dst.writeByte (char (p->getAlpha()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
28
Modules/gin/images/bmpimageformat.h
Executable file
28
Modules/gin/images/bmpimageformat.h
Executable file
|
@ -0,0 +1,28 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
Support for reading and writing Windows Bitmap files. Supports uncompressed
|
||||||
|
8, 24 and 32 bit images. Always writes 32 bit images. That should be enough
|
||||||
|
to cover 99.9% of BMP files. Does not support 1, 4, 16 bit colour images or
|
||||||
|
images with RLE compression.
|
||||||
|
*/
|
||||||
|
class BMPImageFormat : public ImageFileFormat
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
String getFormatName() override;
|
||||||
|
|
||||||
|
bool canUnderstand (InputStream& input) override;
|
||||||
|
|
||||||
|
bool usesFileExtension (const File& possibleFile) override;
|
||||||
|
|
||||||
|
Image decodeImage (InputStream& input) override;
|
||||||
|
|
||||||
|
bool writeImageToStream (const Image& sourceImage, OutputStream& destStream) override;
|
||||||
|
};
|
811
Modules/gin/images/imageeffects.cpp
Executable file
811
Modules/gin/images/imageeffects.cpp
Executable file
|
@ -0,0 +1,811 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#if JUCE_INTEL
|
||||||
|
#define USE_SSE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline uint8 toByte (T v)
|
||||||
|
{
|
||||||
|
if (v < 0) return 0;
|
||||||
|
if (v > 255) return 255;
|
||||||
|
return uint8 (v);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint8 getIntensity (uint8 r, uint8 g, uint8 b)
|
||||||
|
{
|
||||||
|
return (uint8)((7471 * b + 38470 * g + 19595 * r) >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint8 computeAlpha (uint8 la, uint8 ra)
|
||||||
|
{
|
||||||
|
return (uint8)(((la * (256 - (ra + (ra >> 7)))) >> 8) + ra);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline T blend (const T& c1, const T& c2)
|
||||||
|
{
|
||||||
|
int a = c1.getAlpha();
|
||||||
|
int invA = 255 - a;
|
||||||
|
|
||||||
|
int r = ((c2.getRed() * invA) + (c1.getRed() * a)) / 256;
|
||||||
|
int g = ((c2.getGreen() * invA) + (c1.getGreen() * a)) / 256;
|
||||||
|
int b = ((c2.getBlue() * invA) + (c1.getBlue() * a)) / 256;
|
||||||
|
uint8 a2 = computeAlpha (c2.getAlpha(), c1.getAlpha());
|
||||||
|
|
||||||
|
T res;
|
||||||
|
res.setARGB (a2, toByte (r), toByte (g), toByte (b));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T1, class T2>
|
||||||
|
inline T2 convert (const T1& in)
|
||||||
|
{
|
||||||
|
T2 out;
|
||||||
|
out.setARGB (in.getAlpha(), in.getRed(), in.getGreen(), in.getBlue());
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
template <class T>
|
||||||
|
void applyVignette (Image& img, float amountIn, float radiusIn, float fallOff, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
const int w = img.getWidth();
|
||||||
|
const int h = img.getHeight();
|
||||||
|
threadPool = (w >= 256 || h >= 256) ? threadPool : nullptr;
|
||||||
|
|
||||||
|
double outA = w * 0.5 * radiusIn;
|
||||||
|
double outB = h * 0.5 * radiusIn;
|
||||||
|
|
||||||
|
double inA = outA * fallOff;
|
||||||
|
double inB = outB * fallOff;
|
||||||
|
|
||||||
|
double cx = w * 0.5;
|
||||||
|
double cy = h * 0.5;
|
||||||
|
|
||||||
|
double amount = 1.0 - amountIn;
|
||||||
|
|
||||||
|
Image::BitmapData data (img, Image::BitmapData::readWrite);
|
||||||
|
|
||||||
|
Ellipse<double> outE { outA, outB };
|
||||||
|
Ellipse<double> inE { inA, inB };
|
||||||
|
|
||||||
|
multiThreadedFor<int> (0, h, 1, threadPool, [&] (int y)
|
||||||
|
{
|
||||||
|
uint8* p = data.getLinePointer (y);
|
||||||
|
|
||||||
|
double dy = y - cy;
|
||||||
|
|
||||||
|
for (int x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
double dx = x - cx;
|
||||||
|
|
||||||
|
bool outside = outE.isPointOutside ({dx, dy});
|
||||||
|
bool inside = inE.isPointInside ({dx, dy});
|
||||||
|
|
||||||
|
T* s = (T*)p;
|
||||||
|
|
||||||
|
if (outside)
|
||||||
|
{
|
||||||
|
uint8 r = toByte (0.5 + (s->getRed() * amount));
|
||||||
|
uint8 g = toByte (0.5 + (s->getGreen() * amount));
|
||||||
|
uint8 b = toByte (0.5 + (s->getBlue() * amount));
|
||||||
|
uint8 a = s->getAlpha();
|
||||||
|
|
||||||
|
s->setARGB (a, r, g, b);
|
||||||
|
}
|
||||||
|
else if (! inside)
|
||||||
|
{
|
||||||
|
double angle = std::atan2 (dy, dx);
|
||||||
|
|
||||||
|
auto p1 = outE.pointAtAngle (angle);
|
||||||
|
auto p2 = inE.pointAtAngle (angle);
|
||||||
|
|
||||||
|
auto l1 = Line<double> ({dx,dy}, p2);
|
||||||
|
auto l2 = Line<double> (p1, p2);
|
||||||
|
|
||||||
|
double factor = 1.0 - (amountIn * jlimit (0.0, 1.0, l1.getLength() / l2.getLength()));
|
||||||
|
|
||||||
|
uint8 r = toByte (0.5 + (s->getRed() * factor));
|
||||||
|
uint8 g = toByte (0.5 + (s->getGreen() * factor));
|
||||||
|
uint8 b = toByte (0.5 + (s->getBlue() * factor));
|
||||||
|
uint8 a = s->getAlpha();
|
||||||
|
|
||||||
|
s->setARGB (a, r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
p += data.pixelStride;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyVignette (Image& img, float amountIn, float radiusIn, float fallOff, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
if (img.getFormat() == Image::ARGB) applyVignette<PixelARGB> (img, amountIn, radiusIn, fallOff, threadPool);
|
||||||
|
else if (img.getFormat() == Image::RGB) applyVignette<PixelRGB> (img, amountIn, radiusIn, fallOff, threadPool);
|
||||||
|
else jassertfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void applySepia (Image& img, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
const int w = img.getWidth();
|
||||||
|
const int h = img.getHeight();
|
||||||
|
threadPool = (w >= 256 || h >= 256) ? threadPool : nullptr;
|
||||||
|
|
||||||
|
Image::BitmapData data (img, Image::BitmapData::readWrite);
|
||||||
|
|
||||||
|
multiThreadedFor<int> (0, h, 1, threadPool, [&] (int y)
|
||||||
|
{
|
||||||
|
uint8* p = data.getLinePointer (y);
|
||||||
|
|
||||||
|
for (int x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
PixelARGB* s = (PixelARGB*)p;
|
||||||
|
|
||||||
|
uint8 r = s->getRed();
|
||||||
|
uint8 g = s->getGreen();
|
||||||
|
uint8 b = s->getBlue();
|
||||||
|
uint8 a = s->getAlpha();
|
||||||
|
|
||||||
|
uint8 ro = toByte ((r * .393) + (g *.769) + (b * .189));
|
||||||
|
uint8 go = toByte ((r * .349) + (g *.686) + (b * .168));
|
||||||
|
uint8 bo = toByte ((r * .272) + (g *.534) + (b * .131));
|
||||||
|
|
||||||
|
s->setARGB (a, ro, go, bo);
|
||||||
|
|
||||||
|
p += data.pixelStride;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void applySepia (Image& img, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
if (img.getFormat() == Image::ARGB) applySepia<PixelARGB> (img, threadPool);
|
||||||
|
else if (img.getFormat() == Image::RGB) applySepia<PixelRGB> (img, threadPool);
|
||||||
|
else jassertfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void applyGreyScale (Image& img, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
const int w = img.getWidth();
|
||||||
|
const int h = img.getHeight();
|
||||||
|
threadPool = (w >= 256 || h >= 256) ? threadPool : nullptr;
|
||||||
|
|
||||||
|
Image::BitmapData data (img, Image::BitmapData::readWrite);
|
||||||
|
|
||||||
|
multiThreadedFor<int> (0, h, 1, threadPool, [&] (int y)
|
||||||
|
{
|
||||||
|
uint8* p = data.getLinePointer (y);
|
||||||
|
|
||||||
|
for (int x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
T* s = (T*)p;
|
||||||
|
|
||||||
|
uint8 r = s->getRed();
|
||||||
|
uint8 g = s->getGreen();
|
||||||
|
uint8 b = s->getBlue();
|
||||||
|
uint8 a = s->getAlpha();
|
||||||
|
|
||||||
|
uint8 ro = toByte (r * 0.30 + 0.5);
|
||||||
|
uint8 go = toByte (g * 0.59 + 0.5);
|
||||||
|
uint8 bo = toByte (b * 0.11 + 0.5);
|
||||||
|
|
||||||
|
s->setARGB (a,
|
||||||
|
toByte (ro + go + bo),
|
||||||
|
toByte (ro + go + bo),
|
||||||
|
toByte (ro + go + bo));
|
||||||
|
|
||||||
|
p += data.pixelStride;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyGreyScale (Image& img, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
if (img.getFormat() == Image::ARGB) applyGreyScale<PixelARGB> (img, threadPool);
|
||||||
|
else if (img.getFormat() == Image::RGB) applyGreyScale<PixelRGB> (img, threadPool);
|
||||||
|
else jassertfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void applySoften (Image& img, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
const int w = img.getWidth();
|
||||||
|
const int h = img.getHeight();
|
||||||
|
threadPool = (w >= 256 || h >= 256) ? threadPool : nullptr;
|
||||||
|
|
||||||
|
Image dst (img.getFormat(), w, h, true);
|
||||||
|
|
||||||
|
Image::BitmapData srcData (img, Image::BitmapData::readOnly);
|
||||||
|
Image::BitmapData dstData (dst, Image::BitmapData::writeOnly);
|
||||||
|
|
||||||
|
multiThreadedFor<int> (0, h, 1, threadPool, [&] (int y)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
int ro = 0, go = 0, bo = 0;
|
||||||
|
uint8 a = 0;
|
||||||
|
|
||||||
|
for (int m = -1; m <= 1; m++)
|
||||||
|
{
|
||||||
|
for (int n = -1; n <= 1; n++)
|
||||||
|
{
|
||||||
|
int cx = jlimit (0, w - 1, x + m);
|
||||||
|
int cy = jlimit (0, h - 1, y + n);
|
||||||
|
|
||||||
|
T* s = (T*) srcData.getPixelPointer (cx, cy);
|
||||||
|
|
||||||
|
ro += s->getRed();
|
||||||
|
go += s->getGreen();
|
||||||
|
bo += s->getBlue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
T* s = (T*) srcData.getPixelPointer (x, y);
|
||||||
|
a = s->getAlpha();
|
||||||
|
|
||||||
|
T* d = (T*) dstData.getPixelPointer (x, y);
|
||||||
|
|
||||||
|
d->setARGB (a, toByte (ro / 9), toByte (go / 9), toByte (bo / 9));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
img = dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
void applySoften (Image& img, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
if (img.getFormat() == Image::ARGB) applySoften<PixelARGB> (img, threadPool);
|
||||||
|
else if (img.getFormat() == Image::RGB) applySoften<PixelRGB> (img, threadPool);
|
||||||
|
else jassertfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void applySharpen (Image& img, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
const int w = img.getWidth();
|
||||||
|
const int h = img.getHeight();
|
||||||
|
threadPool = (w >= 256 || h >= 256) ? threadPool : nullptr;
|
||||||
|
|
||||||
|
Image dst (img.getFormat(), w, h, true);
|
||||||
|
|
||||||
|
Image::BitmapData srcData (img, Image::BitmapData::readOnly);
|
||||||
|
Image::BitmapData dstData (dst, Image::BitmapData::writeOnly);
|
||||||
|
|
||||||
|
multiThreadedFor<int> (0, h, 1, threadPool, [&] (int y)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
auto getPixelPointer = [&] (int cx, int cy) -> T*
|
||||||
|
{
|
||||||
|
cx = jlimit (0, w - 1, cx);
|
||||||
|
cy = jlimit (0, h - 1, cy);
|
||||||
|
|
||||||
|
return (T*) srcData.getPixelPointer (cx, cy);
|
||||||
|
};
|
||||||
|
|
||||||
|
int ro = 0, go = 0, bo = 0;
|
||||||
|
uint8 ao = 0;
|
||||||
|
|
||||||
|
T* s = getPixelPointer (x, y);
|
||||||
|
|
||||||
|
ro = s->getRed() * 5;
|
||||||
|
go = s->getGreen() * 5;
|
||||||
|
bo = s->getBlue() * 5;
|
||||||
|
ao = s->getAlpha();
|
||||||
|
|
||||||
|
s = getPixelPointer (x, y - 1);
|
||||||
|
ro -= s->getRed();
|
||||||
|
go -= s->getGreen();
|
||||||
|
bo -= s->getBlue();
|
||||||
|
|
||||||
|
s = getPixelPointer (x - 1, y);
|
||||||
|
ro -= s->getRed();
|
||||||
|
go -= s->getGreen();
|
||||||
|
bo -= s->getBlue();
|
||||||
|
|
||||||
|
s = getPixelPointer (x + 1, y);
|
||||||
|
ro -= s->getRed();
|
||||||
|
go -= s->getGreen();
|
||||||
|
bo -= s->getBlue();
|
||||||
|
|
||||||
|
s = getPixelPointer (x, y + 1);
|
||||||
|
ro -= s->getRed();
|
||||||
|
go -= s->getGreen();
|
||||||
|
bo -= s->getBlue();
|
||||||
|
|
||||||
|
T* d = (T*) dstData.getPixelPointer (x, y);
|
||||||
|
|
||||||
|
d->setARGB (ao, toByte (ro), toByte (go), toByte (bo));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
img = dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
void applySharpen (Image& img, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
if (img.getFormat() == Image::ARGB) applySharpen<PixelARGB> (img, threadPool);
|
||||||
|
else if (img.getFormat() == Image::RGB) applySharpen<PixelRGB> (img, threadPool);
|
||||||
|
else jassertfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void applyGamma (Image& img, float gamma, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
const int w = img.getWidth();
|
||||||
|
const int h = img.getHeight();
|
||||||
|
threadPool = (w >= 256 || h >= 256) ? threadPool : nullptr;
|
||||||
|
|
||||||
|
Image::BitmapData data (img, Image::BitmapData::readWrite);
|
||||||
|
|
||||||
|
multiThreadedFor<int> (0, h, 1, threadPool, [&] (int y)
|
||||||
|
{
|
||||||
|
uint8* p = data.getLinePointer (y);
|
||||||
|
|
||||||
|
for (int x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
T* s = (T*)p;
|
||||||
|
|
||||||
|
uint8 r = s->getRed();
|
||||||
|
uint8 g = s->getGreen();
|
||||||
|
uint8 b = s->getBlue();
|
||||||
|
uint8 a = s->getAlpha();
|
||||||
|
|
||||||
|
uint8 ro = toByte (std::pow (r / 255.0, gamma) * 255.0 + 0.5);
|
||||||
|
uint8 go = toByte (std::pow (g / 255.0, gamma) * 255.0 + 0.5);
|
||||||
|
uint8 bo = toByte (std::pow (b / 255.0, gamma) * 255.0 + 0.5);
|
||||||
|
|
||||||
|
s->setARGB (a, ro, go, bo);
|
||||||
|
|
||||||
|
p += data.pixelStride;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyGamma (Image& img, float gamma, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
if (img.getFormat() == Image::ARGB) applyGamma<PixelARGB> (img, gamma, threadPool);
|
||||||
|
else if (img.getFormat() == Image::RGB) applyGamma<PixelRGB> (img, gamma, threadPool);
|
||||||
|
else jassertfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void applyInvert (Image& img, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
const int w = img.getWidth();
|
||||||
|
const int h = img.getHeight();
|
||||||
|
threadPool = (w >= 256 || h >= 256) ? threadPool : nullptr;
|
||||||
|
|
||||||
|
Image::BitmapData data (img, Image::BitmapData::readWrite);
|
||||||
|
|
||||||
|
multiThreadedFor<int> (0, h, 1, threadPool, [&] (int y)
|
||||||
|
{
|
||||||
|
uint8* p = data.getLinePointer (y);
|
||||||
|
|
||||||
|
for (int x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
T* s = (T*)p;
|
||||||
|
|
||||||
|
uint8 r = s->getRed();
|
||||||
|
uint8 g = s->getGreen();
|
||||||
|
uint8 b = s->getBlue();
|
||||||
|
uint8 a = s->getAlpha();
|
||||||
|
|
||||||
|
uint8 ro = 255 - r;
|
||||||
|
uint8 go = 255 - g;
|
||||||
|
uint8 bo = 255 - b;
|
||||||
|
|
||||||
|
s->setARGB (a, ro, go, bo);
|
||||||
|
|
||||||
|
p += data.pixelStride;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyInvert (Image& img, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
if (img.getFormat() == Image::ARGB) applyInvert<PixelARGB> (img, threadPool);
|
||||||
|
else if (img.getFormat() == Image::RGB) applyInvert<PixelRGB> (img, threadPool);
|
||||||
|
else jassertfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void applyContrast (Image& img, float contrast, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
const int w = img.getWidth();
|
||||||
|
const int h = img.getHeight();
|
||||||
|
threadPool = (w >= 256 || h >= 256) ? threadPool : nullptr;
|
||||||
|
|
||||||
|
contrast = (100.0f + contrast) / 100.0f;
|
||||||
|
contrast = square (contrast);
|
||||||
|
|
||||||
|
Image::BitmapData data (img, Image::BitmapData::readWrite);
|
||||||
|
|
||||||
|
multiThreadedFor<int> (0, h, 1, threadPool, [&] (int y)
|
||||||
|
{
|
||||||
|
uint8* p = data.getLinePointer (y);
|
||||||
|
|
||||||
|
for (int x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
T* s = (T*)p;
|
||||||
|
|
||||||
|
uint8 r = s->getRed();
|
||||||
|
uint8 g = s->getGreen();
|
||||||
|
uint8 b = s->getBlue();
|
||||||
|
uint8 a = s->getAlpha();
|
||||||
|
|
||||||
|
double ro = (double) r / 255.0;
|
||||||
|
ro = ro - 0.5;
|
||||||
|
ro = ro * contrast;
|
||||||
|
ro = ro + 0.5;
|
||||||
|
ro = ro * 255.0;
|
||||||
|
|
||||||
|
double go = (double) g / 255.0;
|
||||||
|
go = go - 0.5;
|
||||||
|
go = go * contrast;
|
||||||
|
go = go + 0.5;
|
||||||
|
go = go * 255.0;
|
||||||
|
|
||||||
|
double bo = (double) b / 255.0;
|
||||||
|
bo = bo - 0.5;
|
||||||
|
bo = bo * contrast;
|
||||||
|
bo = bo + 0.5;
|
||||||
|
bo = bo * 255.0;
|
||||||
|
|
||||||
|
ro = toByte (ro);
|
||||||
|
go = toByte (go);
|
||||||
|
bo = toByte (bo);
|
||||||
|
|
||||||
|
s->setARGB (a, toByte (ro), toByte (go), toByte (bo));
|
||||||
|
|
||||||
|
p += data.pixelStride;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyContrast (Image& img, float contrast, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
if (img.getFormat() == Image::ARGB) applyContrast<PixelARGB> (img, contrast, threadPool);
|
||||||
|
else if (img.getFormat() == Image::RGB) applyContrast<PixelRGB> (img, contrast, threadPool);
|
||||||
|
else jassertfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void applyBrightnessContrast (Image& img, float brightness, float contrast, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
const int w = img.getWidth();
|
||||||
|
const int h = img.getHeight();
|
||||||
|
threadPool = (w >= 256 || h >= 256) ? threadPool : nullptr;
|
||||||
|
|
||||||
|
Image::BitmapData data (img, Image::BitmapData::readWrite);
|
||||||
|
|
||||||
|
double multiply = 1;
|
||||||
|
double divide = 1;
|
||||||
|
|
||||||
|
if (contrast < 0)
|
||||||
|
{
|
||||||
|
multiply = contrast + 100;
|
||||||
|
divide = 100;
|
||||||
|
}
|
||||||
|
else if (contrast > 0)
|
||||||
|
{
|
||||||
|
multiply = 100;
|
||||||
|
divide = 100 - contrast;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
multiply = 1;
|
||||||
|
divide = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8* rgbTable = new uint8[65536];
|
||||||
|
|
||||||
|
if (divide == 0)
|
||||||
|
{
|
||||||
|
for (int intensity = 0; intensity < 256; intensity++)
|
||||||
|
{
|
||||||
|
if (intensity + brightness < 128)
|
||||||
|
rgbTable[intensity] = 0;
|
||||||
|
else
|
||||||
|
rgbTable[intensity] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (divide == 100)
|
||||||
|
{
|
||||||
|
for (int intensity = 0; intensity < 256; intensity++)
|
||||||
|
{
|
||||||
|
int shift = int ((intensity - 127) * multiply / divide + 127 - intensity + brightness);
|
||||||
|
|
||||||
|
for (int col = 0; col < 256; col++)
|
||||||
|
{
|
||||||
|
int index = (intensity * 256) + col;
|
||||||
|
rgbTable[index] = toByte (col + shift);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int intensity = 0; intensity < 256; intensity++)
|
||||||
|
{
|
||||||
|
int shift = int ((intensity - 127 + brightness) * multiply / divide + 127 - intensity);
|
||||||
|
|
||||||
|
for (int col = 0; col < 256; col++)
|
||||||
|
{
|
||||||
|
int index = (intensity * 256) + col;
|
||||||
|
rgbTable[index] = toByte (col + shift);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
multiThreadedFor<int> (0, h, 1, threadPool, [&] (int y)
|
||||||
|
{
|
||||||
|
uint8* p = data.getLinePointer (y);
|
||||||
|
|
||||||
|
for (int x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
T* s = (T*)p;
|
||||||
|
|
||||||
|
uint8 r = s->getRed();
|
||||||
|
uint8 g = s->getGreen();
|
||||||
|
uint8 b = s->getBlue();
|
||||||
|
uint8 a = s->getAlpha();
|
||||||
|
|
||||||
|
if (divide == 0)
|
||||||
|
{
|
||||||
|
int i = getIntensity (toByte (r), toByte (g), toByte (b));
|
||||||
|
uint8 c = rgbTable[i];
|
||||||
|
|
||||||
|
s->setARGB (a, c, c, c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int i = getIntensity (toByte (r), toByte (g), toByte (b));
|
||||||
|
int shiftIndex = i * 256;
|
||||||
|
|
||||||
|
uint8 ro = rgbTable[shiftIndex + r];
|
||||||
|
uint8 go = rgbTable[shiftIndex + g];
|
||||||
|
uint8 bo = rgbTable[shiftIndex + b];
|
||||||
|
|
||||||
|
ro = toByte (ro);
|
||||||
|
go = toByte (go);
|
||||||
|
bo = toByte (bo);
|
||||||
|
|
||||||
|
s->setARGB (a, ro, go, bo);
|
||||||
|
}
|
||||||
|
|
||||||
|
p += data.pixelStride;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
delete[] rgbTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyBrightnessContrast (Image& img, float brightness, float contrast, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
if (img.getFormat() == Image::ARGB) applyBrightnessContrast<PixelARGB> (img, brightness, contrast, threadPool);
|
||||||
|
else if (img.getFormat() == Image::RGB) applyBrightnessContrast<PixelRGB> (img, brightness, contrast, threadPool);
|
||||||
|
else jassertfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void applyHueSaturationLightness (Image& img, float hueIn, float saturation, float lightness, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
const int w = img.getWidth();
|
||||||
|
const int h = img.getHeight();
|
||||||
|
threadPool = (w >= 256 || h >= 256) ? threadPool : nullptr;
|
||||||
|
|
||||||
|
if (saturation > 100)
|
||||||
|
saturation = ((saturation - 100) * 3) + 100;
|
||||||
|
saturation = (saturation * 1024) / 100;
|
||||||
|
|
||||||
|
hueIn /= 360.0f;
|
||||||
|
|
||||||
|
Image::BitmapData data (img, Image::BitmapData::readWrite);
|
||||||
|
|
||||||
|
multiThreadedFor<int> (0, h, 1, threadPool, [&] (int y)
|
||||||
|
{
|
||||||
|
uint8* p = data.getLinePointer (y);
|
||||||
|
|
||||||
|
for (int x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
T* s = (T*)p;
|
||||||
|
|
||||||
|
uint8 r = s->getRed();
|
||||||
|
uint8 g = s->getGreen();
|
||||||
|
uint8 b = s->getBlue();
|
||||||
|
uint8 a = s->getAlpha();
|
||||||
|
|
||||||
|
int intensity = getIntensity (toByte (r), toByte (g), toByte (b));
|
||||||
|
int ro = toByte (int (intensity * 1024 + (r - intensity) * saturation) >> 10);
|
||||||
|
int go = toByte (int (intensity * 1024 + (g - intensity) * saturation) >> 10);
|
||||||
|
int bo = toByte (int (intensity * 1024 + (b - intensity) * saturation) >> 10);
|
||||||
|
|
||||||
|
Colour c (toByte (ro), toByte (go), toByte (bo));
|
||||||
|
float hue = c.getHue();
|
||||||
|
hue += hueIn;
|
||||||
|
|
||||||
|
while (hue < 0.0f) hue += 1.0f;
|
||||||
|
while (hue >= 1.0f) hue -= 1.0f;
|
||||||
|
|
||||||
|
c = Colour::fromHSV (hue, c.getSaturation(), c.getBrightness(), float (a));
|
||||||
|
ro = c.getRed();
|
||||||
|
go = c.getGreen();
|
||||||
|
bo = c.getBlue();
|
||||||
|
|
||||||
|
ro = toByte (ro);
|
||||||
|
go = toByte (go);
|
||||||
|
bo = toByte (bo);
|
||||||
|
|
||||||
|
s->setARGB (a, toByte (ro), toByte (go), toByte (bo));
|
||||||
|
|
||||||
|
if (lightness > 0)
|
||||||
|
{
|
||||||
|
auto blended = blend (PixelARGB (toByte ((lightness * 255) / 100 * (a / 255.0)), 255, 255, 255), convert<T, PixelARGB> (*s));
|
||||||
|
*s = convert<PixelARGB, T> (blended);
|
||||||
|
}
|
||||||
|
else if (lightness < 0)
|
||||||
|
{
|
||||||
|
auto blended = blend (PixelARGB (toByte ((-lightness * 255) / 100 * (a / 255.0)), 0, 0, 0), convert<T, PixelARGB> (*s));
|
||||||
|
*s = convert<PixelARGB, T> (blended);
|
||||||
|
}
|
||||||
|
|
||||||
|
p += data.pixelStride;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyHueSaturationLightness (Image& img, float hue, float saturation, float lightness, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
if (img.getFormat() == Image::ARGB) applyHueSaturationLightness<PixelARGB> (img, hue, saturation, lightness, threadPool);
|
||||||
|
else if (img.getFormat() == Image::RGB) applyHueSaturationLightness<PixelRGB> (img, hue, saturation, lightness, threadPool);
|
||||||
|
else jassertfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
Image applyResize (const Image& src, int width, int height)
|
||||||
|
{
|
||||||
|
Image dst (src.getFormat(), width, height, true);
|
||||||
|
|
||||||
|
Image::BitmapData srcData (src, Image::BitmapData::readOnly);
|
||||||
|
Image::BitmapData dstData (dst, Image::BitmapData::readWrite);
|
||||||
|
|
||||||
|
int channels = 0;
|
||||||
|
if (src.getFormat() == Image::ARGB) channels = 4;
|
||||||
|
else if (src.getFormat() == Image::RGB) channels = 3;
|
||||||
|
else if (src.getFormat() == Image::SingleChannel) channels = 1;
|
||||||
|
else return {};
|
||||||
|
|
||||||
|
// JUCE images may have padding at the end of each scan line.
|
||||||
|
// Avir expects the image data to be packed. So we need to
|
||||||
|
// pack and unpack the image data before and after resizing.
|
||||||
|
HeapBlock<uint8> srcPacked (src.getWidth() * src.getHeight() * channels);
|
||||||
|
HeapBlock<uint8> dstPacked (dst.getWidth() * dst.getHeight() * channels);
|
||||||
|
|
||||||
|
uint8* rawSrc = srcPacked.getData();
|
||||||
|
uint8* rawDst = dstPacked.getData();
|
||||||
|
|
||||||
|
for (int y = 0; y < src.getHeight(); y++)
|
||||||
|
memcpy (rawSrc + y * src.getWidth() * channels,
|
||||||
|
srcData.getLinePointer (y),
|
||||||
|
(size_t) (src.getWidth() * channels));
|
||||||
|
|
||||||
|
#if USE_SSE
|
||||||
|
avir::CImageResizer<avir::fpclass_float4> imageResizer (8);
|
||||||
|
imageResizer.resizeImage (rawSrc, src.getWidth(), src.getHeight(), 0,
|
||||||
|
rawDst, dst.getWidth(), dst.getHeight(), channels, 0);
|
||||||
|
#else
|
||||||
|
avir::CImageResizer<> imageResizer (8);
|
||||||
|
imageResizer.resizeImage (rawSrc, src.getWidth(), src.getHeight(), 0,
|
||||||
|
rawDst, dst.getWidth(), dst.getHeight(), channels, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (int y = 0; y < dst.getHeight(); y++)
|
||||||
|
memcpy (dstData.getLinePointer (y),
|
||||||
|
rawDst + y * dst.getWidth() * channels,
|
||||||
|
(size_t) (dst.getWidth() * channels));
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
Image applyResize (const Image& src, float factor)
|
||||||
|
{
|
||||||
|
return applyResize (src,
|
||||||
|
roundToInt (factor * src.getWidth()),
|
||||||
|
roundToInt (factor * src.getHeight()));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void applyGradientMap (Image& img, const ColourGradient& gradient, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
const int w = img.getWidth();
|
||||||
|
const int h = img.getHeight();
|
||||||
|
threadPool = (w >= 256 || h >= 256) ? threadPool : nullptr;
|
||||||
|
|
||||||
|
Image::BitmapData data (img, Image::BitmapData::readWrite);
|
||||||
|
|
||||||
|
multiThreadedFor<int> (0, h, 1, threadPool, [&] (int y)
|
||||||
|
{
|
||||||
|
uint8* p = data.getLinePointer (y);
|
||||||
|
|
||||||
|
for (int x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
T* s = (T*)p;
|
||||||
|
|
||||||
|
uint8 r = s->getRed();
|
||||||
|
uint8 g = s->getGreen();
|
||||||
|
uint8 b = s->getBlue();
|
||||||
|
uint8 a = s->getAlpha();
|
||||||
|
|
||||||
|
uint8 ro = toByte (r * 0.30 + 0.5);
|
||||||
|
uint8 go = toByte (g * 0.59 + 0.5);
|
||||||
|
uint8 bo = toByte (b * 0.11 + 0.5);
|
||||||
|
|
||||||
|
float proportion = float (ro + go + bo) / 256.0f;
|
||||||
|
|
||||||
|
auto c = gradient.getColourAtPosition (proportion);
|
||||||
|
|
||||||
|
s->setARGB (a,
|
||||||
|
c.getRed(),
|
||||||
|
c.getGreen(),
|
||||||
|
c.getBlue());
|
||||||
|
|
||||||
|
p += data.pixelStride;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyGradientMap (Image& img, const ColourGradient& gradient, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
if (img.getFormat() == Image::ARGB) applyGradientMap<PixelARGB> (img, gradient, threadPool);
|
||||||
|
else if (img.getFormat() == Image::RGB) applyGradientMap<PixelRGB> (img, gradient, threadPool);
|
||||||
|
else jassertfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyGradientMap (Image& img, const Colour c1, const Colour c2, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
ColourGradient g;
|
||||||
|
g.addColour (0.0, c1);
|
||||||
|
g.addColour (1.0, c2);
|
||||||
|
|
||||||
|
applyGradientMap (img, g, threadPool);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void applyColour (Image& img, Colour c, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
const int w = img.getWidth();
|
||||||
|
const int h = img.getHeight();
|
||||||
|
threadPool = (w >= 256 || h >= 256) ? threadPool : nullptr;
|
||||||
|
|
||||||
|
uint8 r = c.getRed();
|
||||||
|
uint8 g = c.getGreen();
|
||||||
|
uint8 b = c.getBlue();
|
||||||
|
uint8 a = c.getAlpha();
|
||||||
|
|
||||||
|
Image::BitmapData data (img, Image::BitmapData::readWrite);
|
||||||
|
|
||||||
|
multiThreadedFor<int> (0, h, 1, threadPool, [&] (int y)
|
||||||
|
{
|
||||||
|
uint8* p = data.getLinePointer (y);
|
||||||
|
|
||||||
|
for (int x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
T* s = (T*)p;
|
||||||
|
s->setARGB (a, r, g, b);
|
||||||
|
p += data.pixelStride;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyColour (Image& img, Colour c, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
if (img.getFormat() == Image::ARGB) applyColour<PixelARGB> (img, c, threadPool);
|
||||||
|
else if (img.getFormat() == Image::RGB) applyColour<PixelRGB> (img, c, threadPool);
|
||||||
|
else jassertfalse;
|
||||||
|
}
|
||||||
|
|
117
Modules/gin/images/imageeffects.h
Executable file
117
Modules/gin/images/imageeffects.h
Executable file
|
@ -0,0 +1,117 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/** Apply vignette
|
||||||
|
*
|
||||||
|
\param amount Amount to darken outside of vignette. 0 no darkening. 1 is black.
|
||||||
|
\param radius Size of vignette. 1 size of image. 0 is 0 size.
|
||||||
|
\param falloff Relative size of inner boundry of vignette 0,1
|
||||||
|
*/
|
||||||
|
void applyVignette (Image& img, float amount, float radius, float falloff, ThreadPool* threadPool = nullptr);
|
||||||
|
|
||||||
|
/** Make it look old */
|
||||||
|
void applySepia (Image& img, ThreadPool* threadPool = nullptr);
|
||||||
|
|
||||||
|
/** Converts image to B/W, heavier weighting towards greens */
|
||||||
|
void applyGreyScale (Image& img, ThreadPool* threadPool = nullptr);
|
||||||
|
|
||||||
|
/** Softens an image */
|
||||||
|
void applySoften (Image& img, ThreadPool* threadPool = nullptr);
|
||||||
|
|
||||||
|
/** Sharpens an image */
|
||||||
|
void applySharpen (Image& img, ThreadPool* threadPool = nullptr);
|
||||||
|
|
||||||
|
void applyGamma (Image& img, float gamma, ThreadPool* threadPool = nullptr);
|
||||||
|
|
||||||
|
/** Inverts colours of an image */
|
||||||
|
void applyInvert (Image& img, ThreadPool* threadPool = nullptr);
|
||||||
|
|
||||||
|
/** Adjust contrast of an image
|
||||||
|
*
|
||||||
|
\param contrast Amount to adjust contrast. Negative values increase, positive values increase
|
||||||
|
*/
|
||||||
|
void applyContrast (Image& img, float contrast, ThreadPool* threadPool = nullptr);
|
||||||
|
|
||||||
|
/** Adjust brightness and contrast of an image
|
||||||
|
*
|
||||||
|
\param brightness Amount to adjust brightness -100,100
|
||||||
|
\param contrast Amount to adjust contrast -100,100
|
||||||
|
*/
|
||||||
|
void applyBrightnessContrast (Image& img, float brightness, float contrast, ThreadPool* threadPool = nullptr);
|
||||||
|
|
||||||
|
/** Adjust hue, saturation and lightness of an image
|
||||||
|
*
|
||||||
|
\param hue Amount to adjust hue -180,180
|
||||||
|
\param saturation Amount to adjust saturation 0,200
|
||||||
|
\param lightness Amount to adjust lightness -100,100
|
||||||
|
*/
|
||||||
|
void applyHueSaturationLightness (Image& img, float hue, float saturation, float lightness, ThreadPool* threadPool = nullptr);
|
||||||
|
|
||||||
|
/** A very fast blur. This is a compromise between Gaussian Blur and Box blur.
|
||||||
|
It creates much better looking blurs than Box Blur, but is 7x faster than some Gaussian Blur
|
||||||
|
implementations.
|
||||||
|
*
|
||||||
|
\param radius from 2 to 254
|
||||||
|
*/
|
||||||
|
void applyStackBlur (Image& img, int radius);
|
||||||
|
|
||||||
|
/** A very high quality image resize using a bank of sinc
|
||||||
|
* function-based fractional delay filters */
|
||||||
|
Image applyResize (const Image& img, int width, int height);
|
||||||
|
|
||||||
|
Image applyResize (const Image& img, float factor);
|
||||||
|
|
||||||
|
/** GradientMap a image. Brightness gets remapped to colour on a gradient.
|
||||||
|
*/
|
||||||
|
void applyGradientMap (Image& img, const ColourGradient& gradient, ThreadPool* threadPool = nullptr);
|
||||||
|
|
||||||
|
void applyGradientMap (Image& img, const Colour c1, const Colour c2, ThreadPool* threadPool = nullptr);
|
||||||
|
|
||||||
|
/** Set an image to a solid colour
|
||||||
|
*/
|
||||||
|
void applyColour (Image& img, Colour c, ThreadPool* threadPool = nullptr);
|
||||||
|
|
||||||
|
/** Blend two images
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum BlendMode
|
||||||
|
{
|
||||||
|
Normal,
|
||||||
|
Lighten,
|
||||||
|
Darken,
|
||||||
|
Multiply,
|
||||||
|
Average,
|
||||||
|
Add,
|
||||||
|
Subtract,
|
||||||
|
Difference,
|
||||||
|
Negation,
|
||||||
|
Screen,
|
||||||
|
Exclusion,
|
||||||
|
Overlay,
|
||||||
|
SoftLight,
|
||||||
|
HardLight,
|
||||||
|
ColorDodge,
|
||||||
|
ColorBurn,
|
||||||
|
LinearDodge,
|
||||||
|
LinearBurn,
|
||||||
|
LinearLight,
|
||||||
|
VividLight,
|
||||||
|
PinLight,
|
||||||
|
HardMix,
|
||||||
|
Reflect,
|
||||||
|
Glow,
|
||||||
|
Phoenix,
|
||||||
|
};
|
||||||
|
|
||||||
|
void applyBlend (Image& dst, const Image& src, BlendMode mode, float alpha = 1.0f, juce::Point<int> position = {}, ThreadPool* threadPool = nullptr);
|
||||||
|
|
||||||
|
void applyBlend (Image& dst, BlendMode mode, Colour c, ThreadPool* threadPool = nullptr);
|
||||||
|
|
||||||
|
|
282
Modules/gin/images/imageeffects_blending.cpp
Executable file
282
Modules/gin/images/imageeffects_blending.cpp
Executable file
|
@ -0,0 +1,282 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
inline uint8 channelBlendNormal (int A, int) { return ((uint8)(A)); }
|
||||||
|
inline uint8 channelBlendLighten (int A, int B) { return ((uint8)((B > A) ? B : A)); }
|
||||||
|
inline uint8 channelBlendDarken (int A, int B) { return ((uint8)((B > A) ? A : B)); }
|
||||||
|
inline uint8 channelBlendMultiply (int A, int B) { return ((uint8)((A * B) / 255)); }
|
||||||
|
inline uint8 channelBlendAverage (int A, int B) { return ((uint8)((A + B) / 2)); }
|
||||||
|
inline uint8 channelBlendAdd (int A, int B) { return ((uint8)(jmin (255, (A + B)))); }
|
||||||
|
inline uint8 channelBlendSubtract (int A, int B) { return ((uint8)((A + B < 255) ? 0 : (A + B - 255))); }
|
||||||
|
inline uint8 channelBlendDifference (int A, int B) { return ((uint8)(std::abs (A - B))); }
|
||||||
|
inline uint8 channelBlendNegation (int A, int B) { return ((uint8)(255 - std::abs (255 - A - B))); }
|
||||||
|
inline uint8 channelBlendScreen (int A, int B) { return ((uint8)(255 - (((255 - A) * (255 - B)) >> 8))); }
|
||||||
|
inline uint8 channelBlendExclusion (int A, int B) { return ((uint8)(A + B - 2 * A * B / 255)); }
|
||||||
|
inline uint8 channelBlendOverlay (int A, int B) { return ((uint8)((B < 128) ? (2 * A * B / 255) : (255 - 2 * (255 - A) * (255 - B) / 255))); }
|
||||||
|
inline uint8 channelBlendSoftLight (int A, int B) { return ((uint8)((B < 128) ? (2 * ((A >> 1) + 64)) * ((float)B / 255) : (255 - (2 * (255 - ((A >> 1) + 64)) * (float)(255 - B) / 255)))); }
|
||||||
|
inline uint8 channelBlendHardLight (int A, int B) { return (channelBlendOverlay (B,A)); }
|
||||||
|
inline uint8 channelBlendColorDodge (int A, int B) { return ((uint8)((B == 255) ? B : jmin (255, ((A << 8 ) / (255 - B))))); }
|
||||||
|
inline uint8 channelBlendColorBurn (int A, int B) { return ((uint8)((B == 0) ? B : jmax (0, (255 - ((255 - A) << 8 ) / B)))); }
|
||||||
|
inline uint8 channelBlendLinearDodge (int A, int B) { return (channelBlendAdd (A, B)); }
|
||||||
|
inline uint8 channelBlendLinearBurn (int A, int B) { return (channelBlendSubtract (A, B)); }
|
||||||
|
inline uint8 channelBlendLinearLight (int A, int B) { return ((uint8)(B < 128) ? channelBlendLinearBurn (A,(2 * B)) : channelBlendLinearDodge (A, (2 * (B - 128)))); }
|
||||||
|
inline uint8 channelBlendVividLight (int A, int B) { return ((uint8)(B < 128) ? channelBlendColorBurn (A,(2 * B)) : channelBlendColorDodge (A, (2 * (B - 128)))); }
|
||||||
|
inline uint8 channelBlendPinLight (int A, int B) { return ((uint8)(B < 128) ? channelBlendDarken (A,(2 * B)) : channelBlendLighten (A, (2 * (B - 128)))); }
|
||||||
|
inline uint8 channelBlendHardMix (int A, int B) { return ((uint8)((channelBlendVividLight (A, B) < 128) ? 0:255)); }
|
||||||
|
inline uint8 channelBlendReflect (int A, int B) { return ((uint8)((B == 255) ? B : jmin (255, (A * A / (255 - B))))); }
|
||||||
|
inline uint8 channelBlendGlow (int A, int B) { return (channelBlendReflect (B, A)); }
|
||||||
|
inline uint8 channelBlendPhoenix (int A, int B) { return ((uint8)(jmin (A, B) - jmax (A, B) + 255)); }
|
||||||
|
|
||||||
|
inline uint8 channelBlendAlpha (uint8 A, uint8 B, float O)
|
||||||
|
{
|
||||||
|
return ((uint8)(O * A + (1 - O) * B));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, uint8 (*F)(int, int)>
|
||||||
|
void applyBlend (Image& dst, const Image& src, float alpha, juce::Point<int> position, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
auto rcLower = Rectangle<int> (0, 0, dst.getWidth(), dst.getHeight());
|
||||||
|
auto rcUpper = Rectangle<int> (position.x, position.y, src.getWidth(), src.getHeight());
|
||||||
|
|
||||||
|
auto rcOverlap = rcLower.getIntersection (rcUpper);
|
||||||
|
if (rcOverlap.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int w = rcOverlap.getWidth();
|
||||||
|
int h = rcOverlap.getHeight();
|
||||||
|
|
||||||
|
int cropX = position.x < 0 ? -position.x : 0;
|
||||||
|
int cropY = position.y < 0 ? -position.y : 0;
|
||||||
|
|
||||||
|
threadPool = (w >= 256 || h >= 256) ? threadPool : nullptr;
|
||||||
|
|
||||||
|
Image::BitmapData srcData (src, Image::BitmapData::readOnly);
|
||||||
|
Image::BitmapData dstData (dst, Image::BitmapData::readWrite);
|
||||||
|
|
||||||
|
multiThreadedFor<int> (0, h, 1, threadPool, [&] (int y)
|
||||||
|
{
|
||||||
|
uint8* pSrc = srcData.getLinePointer (cropY + y);
|
||||||
|
uint8* pDst = dstData.getLinePointer (rcOverlap.getY() + y);
|
||||||
|
|
||||||
|
pSrc += srcData.pixelStride * cropX;
|
||||||
|
pDst += dstData.pixelStride * rcOverlap.getX();
|
||||||
|
|
||||||
|
for (int x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
T* ac = (T*)pSrc;
|
||||||
|
T* bc = (T*)pDst;
|
||||||
|
|
||||||
|
uint8 ar = ac->getRed();
|
||||||
|
uint8 ag = ac->getGreen();
|
||||||
|
uint8 ab = ac->getBlue();
|
||||||
|
uint8 aa = ac->getAlpha();
|
||||||
|
|
||||||
|
uint8 br = bc->getRed();
|
||||||
|
uint8 bg = bc->getGreen();
|
||||||
|
uint8 bb = bc->getBlue();
|
||||||
|
uint8 ba = bc->getAlpha();
|
||||||
|
|
||||||
|
if (ba == 255)
|
||||||
|
{
|
||||||
|
float pixelAlpha = alpha * aa / 255.0f;
|
||||||
|
|
||||||
|
br = channelBlendAlpha (F (ar, br), br, pixelAlpha);
|
||||||
|
bg = channelBlendAlpha (F (ag, bg), bg, pixelAlpha);
|
||||||
|
bb = channelBlendAlpha (F (ab, bb), bb, pixelAlpha);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float srcAlpha = alpha * aa / 255.0f;
|
||||||
|
float dstAlpha = ba / 255.0f;
|
||||||
|
|
||||||
|
float outAlpha = srcAlpha + dstAlpha * (1.0f - srcAlpha);
|
||||||
|
|
||||||
|
if (outAlpha == 0.0)
|
||||||
|
{
|
||||||
|
br = 0;
|
||||||
|
bg = 0;
|
||||||
|
bb = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint8 r = F (ar, br);
|
||||||
|
uint8 g = F (ag, bg);
|
||||||
|
uint8 b = F (ab, bb);
|
||||||
|
|
||||||
|
br = uint8 ((r * srcAlpha + br * dstAlpha * (1.0f - srcAlpha)) / outAlpha);
|
||||||
|
bg = uint8 ((g * srcAlpha + bg * dstAlpha * (1.0f - srcAlpha)) / outAlpha);
|
||||||
|
bb = uint8 ((b * srcAlpha + bb * dstAlpha * (1.0f - srcAlpha)) / outAlpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bc->setARGB (ba, br, bg, bb);
|
||||||
|
|
||||||
|
pSrc += srcData.pixelStride;
|
||||||
|
pDst += dstData.pixelStride;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void applyBlend (Image& dst, const Image& src, BlendMode mode, float alpha, juce::Point<int> position, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case Normal: applyBlend<T, channelBlendNormal> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case Lighten: applyBlend<T, channelBlendLighten> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case Darken: applyBlend<T, channelBlendDarken> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case Multiply: applyBlend<T, channelBlendMultiply> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case Average: applyBlend<T, channelBlendAverage> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case Add: applyBlend<T, channelBlendAdd> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case Subtract: applyBlend<T, channelBlendSubtract> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case Difference: applyBlend<T, channelBlendDifference> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case Negation: applyBlend<T, channelBlendNegation> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case Screen: applyBlend<T, channelBlendScreen> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case Exclusion: applyBlend<T, channelBlendExclusion> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case Overlay: applyBlend<T, channelBlendOverlay> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case SoftLight: applyBlend<T, channelBlendSoftLight> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case HardLight: applyBlend<T, channelBlendHardLight> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case ColorDodge: applyBlend<T, channelBlendColorDodge> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case ColorBurn: applyBlend<T, channelBlendColorBurn> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case LinearDodge: applyBlend<T, channelBlendLinearDodge> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case LinearBurn: applyBlend<T, channelBlendLinearBurn> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case LinearLight: applyBlend<T, channelBlendLinearLight> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case VividLight: applyBlend<T, channelBlendVividLight> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case PinLight: applyBlend<T, channelBlendPinLight> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case HardMix: applyBlend<T, channelBlendHardMix> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case Reflect: applyBlend<T, channelBlendReflect> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case Glow: applyBlend<T, channelBlendGlow> (dst, src, alpha, position, threadPool); break;
|
||||||
|
case Phoenix: applyBlend<T, channelBlendPhoenix> (dst, src, alpha, position, threadPool); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyBlend (Image& dst, const Image& src, BlendMode mode, float alpha, juce::Point<int> position, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
if (src.getFormat() != dst.getFormat())
|
||||||
|
{
|
||||||
|
Image copy = src.createCopy();
|
||||||
|
copy = copy.convertedToFormat (dst.getFormat());
|
||||||
|
|
||||||
|
if (src.getFormat() == Image::ARGB) applyBlend<PixelARGB> (dst, copy, mode, alpha, position, threadPool);
|
||||||
|
else if (src.getFormat() == Image::RGB) applyBlend<PixelRGB> (dst, copy, mode, alpha, position, threadPool);
|
||||||
|
else jassertfalse;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (src.getFormat() == Image::ARGB) applyBlend<PixelARGB> (dst, src, mode, alpha, position, threadPool);
|
||||||
|
else if (src.getFormat() == Image::RGB) applyBlend<PixelRGB> (dst, src, mode, alpha, position, threadPool);
|
||||||
|
else jassertfalse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, uint8 (*F)(int, int)>
|
||||||
|
void applyBlend (Image& dst, Colour c, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
int w = dst.getWidth();
|
||||||
|
int h = dst.getHeight();
|
||||||
|
|
||||||
|
threadPool = (w >= 256 || h >= 256) ? threadPool : nullptr;
|
||||||
|
|
||||||
|
Image::BitmapData dstData (dst, Image::BitmapData::readWrite);
|
||||||
|
|
||||||
|
uint8 ar = c.getRed();
|
||||||
|
uint8 ag = c.getGreen();
|
||||||
|
uint8 ab = c.getBlue();
|
||||||
|
uint8 aa = c.getAlpha();
|
||||||
|
|
||||||
|
multiThreadedFor<int> (0, h, 1, threadPool, [&] (int y)
|
||||||
|
{
|
||||||
|
uint8* pDst = dstData.getLinePointer (y);
|
||||||
|
|
||||||
|
for (int x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
T* bc = (T*)pDst;
|
||||||
|
|
||||||
|
uint8 br = bc->getRed();
|
||||||
|
uint8 bg = bc->getGreen();
|
||||||
|
uint8 bb = bc->getBlue();
|
||||||
|
uint8 ba = bc->getAlpha();
|
||||||
|
|
||||||
|
if (ba == 255)
|
||||||
|
{
|
||||||
|
float pixelAlpha = aa / 255.0f;
|
||||||
|
|
||||||
|
br = channelBlendAlpha (F (ar, br), br, pixelAlpha);
|
||||||
|
bg = channelBlendAlpha (F (ag, bg), bg, pixelAlpha);
|
||||||
|
bb = channelBlendAlpha (F (ab, bb), bb, pixelAlpha);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float srcAlpha = aa / 255.0f;
|
||||||
|
float dstAlpha = ba / 255.0f;
|
||||||
|
|
||||||
|
float outAlpha = srcAlpha + dstAlpha * (1.0f - srcAlpha);
|
||||||
|
|
||||||
|
if (outAlpha == 0.0)
|
||||||
|
{
|
||||||
|
br = 0;
|
||||||
|
bg = 0;
|
||||||
|
bb = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint8 r = F (ar, br);
|
||||||
|
uint8 g = F (ag, bg);
|
||||||
|
uint8 b = F (ab, bb);
|
||||||
|
|
||||||
|
br = uint8 ((r * srcAlpha + br * dstAlpha * (1.0f - srcAlpha)) / outAlpha);
|
||||||
|
bg = uint8 ((g * srcAlpha + bg * dstAlpha * (1.0f - srcAlpha)) / outAlpha);
|
||||||
|
bb = uint8 ((b * srcAlpha + bb * dstAlpha * (1.0f - srcAlpha)) / outAlpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bc->setARGB (ba, br, bg, bb);
|
||||||
|
|
||||||
|
pDst += dstData.pixelStride;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void applyBlend (Image& dst, BlendMode mode, Colour c, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case Normal: applyBlend<T, channelBlendNormal> (dst, c, threadPool); break;
|
||||||
|
case Lighten: applyBlend<T, channelBlendLighten> (dst, c, threadPool); break;
|
||||||
|
case Darken: applyBlend<T, channelBlendDarken> (dst, c, threadPool); break;
|
||||||
|
case Multiply: applyBlend<T, channelBlendMultiply> (dst, c, threadPool); break;
|
||||||
|
case Average: applyBlend<T, channelBlendAverage> (dst, c, threadPool); break;
|
||||||
|
case Add: applyBlend<T, channelBlendAdd> (dst, c, threadPool); break;
|
||||||
|
case Subtract: applyBlend<T, channelBlendSubtract> (dst, c, threadPool); break;
|
||||||
|
case Difference: applyBlend<T, channelBlendDifference> (dst, c, threadPool); break;
|
||||||
|
case Negation: applyBlend<T, channelBlendNegation> (dst, c, threadPool); break;
|
||||||
|
case Screen: applyBlend<T, channelBlendScreen> (dst, c, threadPool); break;
|
||||||
|
case Exclusion: applyBlend<T, channelBlendExclusion> (dst, c, threadPool); break;
|
||||||
|
case Overlay: applyBlend<T, channelBlendOverlay> (dst, c, threadPool); break;
|
||||||
|
case SoftLight: applyBlend<T, channelBlendSoftLight> (dst, c, threadPool); break;
|
||||||
|
case HardLight: applyBlend<T, channelBlendHardLight> (dst, c, threadPool); break;
|
||||||
|
case ColorDodge: applyBlend<T, channelBlendColorDodge> (dst, c, threadPool); break;
|
||||||
|
case ColorBurn: applyBlend<T, channelBlendColorBurn> (dst, c, threadPool); break;
|
||||||
|
case LinearDodge: applyBlend<T, channelBlendLinearDodge> (dst, c, threadPool); break;
|
||||||
|
case LinearBurn: applyBlend<T, channelBlendLinearBurn> (dst, c, threadPool); break;
|
||||||
|
case LinearLight: applyBlend<T, channelBlendLinearLight> (dst, c, threadPool); break;
|
||||||
|
case VividLight: applyBlend<T, channelBlendVividLight> (dst, c, threadPool); break;
|
||||||
|
case PinLight: applyBlend<T, channelBlendPinLight> (dst, c, threadPool); break;
|
||||||
|
case HardMix: applyBlend<T, channelBlendHardMix> (dst, c, threadPool); break;
|
||||||
|
case Reflect: applyBlend<T, channelBlendReflect> (dst, c, threadPool); break;
|
||||||
|
case Glow: applyBlend<T, channelBlendGlow> (dst, c, threadPool); break;
|
||||||
|
case Phoenix: applyBlend<T, channelBlendPhoenix> (dst, c, threadPool); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyBlend (Image& dst, BlendMode mode, Colour c, ThreadPool* threadPool)
|
||||||
|
{
|
||||||
|
if (dst.getFormat() == Image::ARGB) applyBlend<PixelARGB> (dst, mode, c, threadPool);
|
||||||
|
else if (dst.getFormat() == Image::RGB) applyBlend<PixelRGB> (dst, mode, c, threadPool);
|
||||||
|
else jassertfalse;
|
||||||
|
}
|
717
Modules/gin/images/imageeffects_stackblur.cpp
Executable file
717
Modules/gin/images/imageeffects_stackblur.cpp
Executable file
|
@ -0,0 +1,717 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
static unsigned short const stackblur_mul[255] =
|
||||||
|
{
|
||||||
|
512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,
|
||||||
|
454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,
|
||||||
|
482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,
|
||||||
|
437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,
|
||||||
|
497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,
|
||||||
|
320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,
|
||||||
|
446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,
|
||||||
|
329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,
|
||||||
|
505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,
|
||||||
|
399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,
|
||||||
|
324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,
|
||||||
|
268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,
|
||||||
|
451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,
|
||||||
|
385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,
|
||||||
|
332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,
|
||||||
|
289,287,285,282,280,278,275,273,271,269,267,265,263,261,259
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned char const stackblur_shr[255] =
|
||||||
|
{
|
||||||
|
9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
|
||||||
|
17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
|
||||||
|
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
|
||||||
|
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
|
||||||
|
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
|
||||||
|
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
|
||||||
|
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||||
|
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
|
||||||
|
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||||
|
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||||
|
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||||
|
23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||||
|
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||||
|
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||||
|
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||||
|
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
|
||||||
|
};
|
||||||
|
|
||||||
|
static void applyStackBlurBW (Image& img, unsigned int radius)
|
||||||
|
{
|
||||||
|
const unsigned int w = (unsigned int)img.getWidth();
|
||||||
|
const unsigned int h = (unsigned int)img.getHeight();
|
||||||
|
|
||||||
|
Image::BitmapData data (img, Image::BitmapData::readWrite);
|
||||||
|
|
||||||
|
radius = jlimit (2u, 254u, radius);
|
||||||
|
|
||||||
|
unsigned char stack[(254 * 2 + 1) * 1];
|
||||||
|
|
||||||
|
unsigned int x, y, xp, yp, i, sp, stack_start;
|
||||||
|
|
||||||
|
unsigned char* stack_ptr = nullptr;
|
||||||
|
unsigned char* src_ptr = nullptr;
|
||||||
|
unsigned char* dst_ptr = nullptr;
|
||||||
|
|
||||||
|
unsigned long sum, sum_in, sum_out;
|
||||||
|
|
||||||
|
unsigned int wm = w - 1;
|
||||||
|
unsigned int hm = h - 1;
|
||||||
|
unsigned int w1 = (unsigned int) data.lineStride;
|
||||||
|
unsigned int div = (unsigned int) (radius * 2) + 1;
|
||||||
|
unsigned int mul_sum = stackblur_mul[radius];
|
||||||
|
unsigned char shr_sum = stackblur_shr[radius];
|
||||||
|
|
||||||
|
for (y = 0; y < h; ++y)
|
||||||
|
{
|
||||||
|
sum = sum_in = sum_out = 0;
|
||||||
|
|
||||||
|
src_ptr = data.getLinePointer (int (y));
|
||||||
|
|
||||||
|
for (i = 0; i <= radius; ++i)
|
||||||
|
{
|
||||||
|
stack_ptr = &stack[i];
|
||||||
|
stack_ptr[0] = src_ptr[0];
|
||||||
|
sum += src_ptr[0] * (i + 1);
|
||||||
|
sum_out += src_ptr[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i <= radius; ++i)
|
||||||
|
{
|
||||||
|
if (i <= wm)
|
||||||
|
src_ptr += 1;
|
||||||
|
|
||||||
|
stack_ptr = &stack[1 * (i + radius)];
|
||||||
|
stack_ptr[0] = src_ptr[0];
|
||||||
|
sum += src_ptr[0] * (radius + 1 - i);
|
||||||
|
sum_in += src_ptr[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
sp = radius;
|
||||||
|
xp = radius;
|
||||||
|
if (xp > wm)
|
||||||
|
xp = wm;
|
||||||
|
|
||||||
|
src_ptr = data.getLinePointer (int (y)) + (unsigned int)data.pixelStride * xp;
|
||||||
|
dst_ptr = data.getLinePointer (int (y));
|
||||||
|
|
||||||
|
for (x = 0; x < w; ++x)
|
||||||
|
{
|
||||||
|
dst_ptr[0] = (unsigned char)((sum * mul_sum) >> shr_sum);
|
||||||
|
dst_ptr += 1;
|
||||||
|
|
||||||
|
sum -= sum_out;
|
||||||
|
|
||||||
|
stack_start = sp + div - radius;
|
||||||
|
|
||||||
|
if (stack_start >= div)
|
||||||
|
stack_start -= div;
|
||||||
|
|
||||||
|
stack_ptr = &stack[1 * stack_start];
|
||||||
|
|
||||||
|
sum_out -= stack_ptr[0];
|
||||||
|
|
||||||
|
if (xp < wm)
|
||||||
|
{
|
||||||
|
src_ptr += 1;
|
||||||
|
++xp;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_ptr[0] = src_ptr[0];
|
||||||
|
|
||||||
|
sum_in += src_ptr[0];
|
||||||
|
sum += sum_in;
|
||||||
|
|
||||||
|
++sp;
|
||||||
|
if (sp >= div)
|
||||||
|
sp = 0;
|
||||||
|
|
||||||
|
stack_ptr = &stack[sp*1];
|
||||||
|
|
||||||
|
sum_out += stack_ptr[0];
|
||||||
|
sum_in -= stack_ptr[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (x = 0; x < w; ++x)
|
||||||
|
{
|
||||||
|
sum = sum_in = sum_out = 0;
|
||||||
|
|
||||||
|
src_ptr = data.getLinePointer (0) + (unsigned int)data.pixelStride * x;
|
||||||
|
|
||||||
|
for (i = 0; i <= radius; ++i)
|
||||||
|
{
|
||||||
|
stack_ptr = &stack[i * 1];
|
||||||
|
stack_ptr[0] = src_ptr[0];
|
||||||
|
sum += src_ptr[0] * (i + 1);
|
||||||
|
sum_out += src_ptr[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i <= radius; ++i)
|
||||||
|
{
|
||||||
|
if (i <= hm)
|
||||||
|
src_ptr += w1;
|
||||||
|
|
||||||
|
stack_ptr = &stack[1 * (i + radius)];
|
||||||
|
stack_ptr[0] = src_ptr[0];
|
||||||
|
sum += src_ptr[0] * (radius + 1 - i);
|
||||||
|
sum_in += src_ptr[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
sp = radius;
|
||||||
|
yp = radius;
|
||||||
|
if (yp > hm)
|
||||||
|
yp = hm;
|
||||||
|
|
||||||
|
src_ptr = data.getLinePointer (int (yp)) + (unsigned int)data.pixelStride * x;
|
||||||
|
dst_ptr = data.getLinePointer (0) + (unsigned int)data.pixelStride * x;
|
||||||
|
|
||||||
|
for (y = 0; y < h; ++y)
|
||||||
|
{
|
||||||
|
dst_ptr[0] = (unsigned char)((sum * mul_sum) >> shr_sum);
|
||||||
|
dst_ptr += w1;
|
||||||
|
|
||||||
|
sum -= sum_out;
|
||||||
|
|
||||||
|
stack_start = sp + div - radius;
|
||||||
|
if (stack_start >= div)
|
||||||
|
stack_start -= div;
|
||||||
|
|
||||||
|
stack_ptr = &stack[1 * stack_start];
|
||||||
|
|
||||||
|
sum_out -= stack_ptr[0];
|
||||||
|
|
||||||
|
if (yp < hm)
|
||||||
|
{
|
||||||
|
src_ptr += w1;
|
||||||
|
++yp;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_ptr[0] = src_ptr[0];
|
||||||
|
|
||||||
|
sum_in += src_ptr[0];
|
||||||
|
sum += sum_in;
|
||||||
|
|
||||||
|
++sp;
|
||||||
|
if (sp >= div)
|
||||||
|
sp = 0;
|
||||||
|
|
||||||
|
stack_ptr = &stack[sp * 1];
|
||||||
|
|
||||||
|
sum_out += stack_ptr[0];
|
||||||
|
sum_in -= stack_ptr[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void applyStackBlurRGB (Image& img, unsigned int radius)
|
||||||
|
{
|
||||||
|
const unsigned int w = (unsigned int)img.getWidth();
|
||||||
|
const unsigned int h = (unsigned int)img.getHeight();
|
||||||
|
|
||||||
|
Image::BitmapData data (img, Image::BitmapData::readWrite);
|
||||||
|
|
||||||
|
radius = jlimit (2u, 254u, radius);
|
||||||
|
|
||||||
|
unsigned char stack[(254 * 2 + 1) * 3];
|
||||||
|
|
||||||
|
unsigned int x, y, xp, yp, i, sp, stack_start;
|
||||||
|
|
||||||
|
unsigned char* stack_ptr = nullptr;
|
||||||
|
unsigned char* src_ptr = nullptr;
|
||||||
|
unsigned char* dst_ptr = nullptr;
|
||||||
|
|
||||||
|
unsigned long sum_r, sum_g, sum_b, sum_in_r, sum_in_g, sum_in_b,
|
||||||
|
sum_out_r, sum_out_g, sum_out_b;
|
||||||
|
|
||||||
|
unsigned int wm = w - 1;
|
||||||
|
unsigned int hm = h - 1;
|
||||||
|
unsigned int w3 = (unsigned int) data.lineStride;
|
||||||
|
unsigned int div = (unsigned int)(radius * 2) + 1;
|
||||||
|
unsigned int mul_sum = stackblur_mul[radius];
|
||||||
|
unsigned char shr_sum = stackblur_shr[radius];
|
||||||
|
|
||||||
|
for (y = 0; y < h; ++y)
|
||||||
|
{
|
||||||
|
sum_r = sum_g = sum_b =
|
||||||
|
sum_in_r = sum_in_g = sum_in_b =
|
||||||
|
sum_out_r = sum_out_g = sum_out_b = 0;
|
||||||
|
|
||||||
|
src_ptr = data.getLinePointer (int (y));
|
||||||
|
|
||||||
|
for (i = 0; i <= radius; ++i)
|
||||||
|
{
|
||||||
|
stack_ptr = &stack[3 * i];
|
||||||
|
stack_ptr[0] = src_ptr[0];
|
||||||
|
stack_ptr[1] = src_ptr[1];
|
||||||
|
stack_ptr[2] = src_ptr[2];
|
||||||
|
sum_r += src_ptr[0] * (i + 1);
|
||||||
|
sum_g += src_ptr[1] * (i + 1);
|
||||||
|
sum_b += src_ptr[2] * (i + 1);
|
||||||
|
sum_out_r += src_ptr[0];
|
||||||
|
sum_out_g += src_ptr[1];
|
||||||
|
sum_out_b += src_ptr[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i <= radius; ++i)
|
||||||
|
{
|
||||||
|
if (i <= wm)
|
||||||
|
src_ptr += 3;
|
||||||
|
|
||||||
|
stack_ptr = &stack[3 * (i + radius)];
|
||||||
|
stack_ptr[0] = src_ptr[0];
|
||||||
|
stack_ptr[1] = src_ptr[1];
|
||||||
|
stack_ptr[2] = src_ptr[2];
|
||||||
|
sum_r += src_ptr[0] * (radius + 1 - i);
|
||||||
|
sum_g += src_ptr[1] * (radius + 1 - i);
|
||||||
|
sum_b += src_ptr[2] * (radius + 1 - i);
|
||||||
|
sum_in_r += src_ptr[0];
|
||||||
|
sum_in_g += src_ptr[1];
|
||||||
|
sum_in_b += src_ptr[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
sp = radius;
|
||||||
|
xp = radius;
|
||||||
|
if (xp > wm)
|
||||||
|
xp = wm;
|
||||||
|
|
||||||
|
src_ptr = data.getLinePointer (int (y)) + (unsigned int)data.pixelStride * xp;
|
||||||
|
dst_ptr = data.getLinePointer (int (y));
|
||||||
|
|
||||||
|
for (x = 0; x < w; ++x)
|
||||||
|
{
|
||||||
|
dst_ptr[0] = (unsigned char)((sum_r * mul_sum) >> shr_sum);
|
||||||
|
dst_ptr[1] = (unsigned char)((sum_g * mul_sum) >> shr_sum);
|
||||||
|
dst_ptr[2] = (unsigned char)((sum_b * mul_sum) >> shr_sum);
|
||||||
|
dst_ptr += 3;
|
||||||
|
|
||||||
|
sum_r -= sum_out_r;
|
||||||
|
sum_g -= sum_out_g;
|
||||||
|
sum_b -= sum_out_b;
|
||||||
|
|
||||||
|
stack_start = sp + div - radius;
|
||||||
|
|
||||||
|
if (stack_start >= div)
|
||||||
|
stack_start -= div;
|
||||||
|
|
||||||
|
stack_ptr = &stack[3 * stack_start];
|
||||||
|
|
||||||
|
sum_out_r -= stack_ptr[0];
|
||||||
|
sum_out_g -= stack_ptr[1];
|
||||||
|
sum_out_b -= stack_ptr[2];
|
||||||
|
|
||||||
|
if (xp < wm)
|
||||||
|
{
|
||||||
|
src_ptr += 3;
|
||||||
|
++xp;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_ptr[0] = src_ptr[0];
|
||||||
|
stack_ptr[1] = src_ptr[1];
|
||||||
|
stack_ptr[2] = src_ptr[2];
|
||||||
|
|
||||||
|
sum_in_r += src_ptr[0];
|
||||||
|
sum_in_g += src_ptr[1];
|
||||||
|
sum_in_b += src_ptr[2];
|
||||||
|
sum_r += sum_in_r;
|
||||||
|
sum_g += sum_in_g;
|
||||||
|
sum_b += sum_in_b;
|
||||||
|
|
||||||
|
++sp;
|
||||||
|
if (sp >= div)
|
||||||
|
sp = 0;
|
||||||
|
|
||||||
|
stack_ptr = &stack[sp*3];
|
||||||
|
|
||||||
|
sum_out_r += stack_ptr[0];
|
||||||
|
sum_out_g += stack_ptr[1];
|
||||||
|
sum_out_b += stack_ptr[2];
|
||||||
|
sum_in_r -= stack_ptr[0];
|
||||||
|
sum_in_g -= stack_ptr[1];
|
||||||
|
sum_in_b -= stack_ptr[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (x = 0; x < w; ++x)
|
||||||
|
{
|
||||||
|
sum_r = sum_g = sum_b =
|
||||||
|
sum_in_r = sum_in_g = sum_in_b =
|
||||||
|
sum_out_r = sum_out_g = sum_out_b = 0;
|
||||||
|
|
||||||
|
src_ptr = data.getLinePointer (0) + (unsigned int)data.pixelStride * x;
|
||||||
|
|
||||||
|
for (i = 0; i <= radius; ++i)
|
||||||
|
{
|
||||||
|
stack_ptr = &stack[i * 3];
|
||||||
|
stack_ptr[0] = src_ptr[0];
|
||||||
|
stack_ptr[1] = src_ptr[1];
|
||||||
|
stack_ptr[2] = src_ptr[2];
|
||||||
|
sum_r += src_ptr[0] * (i + 1);
|
||||||
|
sum_g += src_ptr[1] * (i + 1);
|
||||||
|
sum_b += src_ptr[2] * (i + 1);
|
||||||
|
sum_out_r += src_ptr[0];
|
||||||
|
sum_out_g += src_ptr[1];
|
||||||
|
sum_out_b += src_ptr[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i <= radius; ++i)
|
||||||
|
{
|
||||||
|
if (i <= hm)
|
||||||
|
src_ptr += w3;
|
||||||
|
|
||||||
|
stack_ptr = &stack[3 * (i + radius)];
|
||||||
|
stack_ptr[0] = src_ptr[0];
|
||||||
|
stack_ptr[1] = src_ptr[1];
|
||||||
|
stack_ptr[2] = src_ptr[2];
|
||||||
|
sum_r += src_ptr[0] * (radius + 1 - i);
|
||||||
|
sum_g += src_ptr[1] * (radius + 1 - i);
|
||||||
|
sum_b += src_ptr[2] * (radius + 1 - i);
|
||||||
|
sum_in_r += src_ptr[0];
|
||||||
|
sum_in_g += src_ptr[1];
|
||||||
|
sum_in_b += src_ptr[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
sp = radius;
|
||||||
|
yp = radius;
|
||||||
|
if (yp > hm)
|
||||||
|
yp = hm;
|
||||||
|
|
||||||
|
src_ptr = data.getLinePointer (int (yp)) + (unsigned int)data.pixelStride * x;
|
||||||
|
dst_ptr = data.getLinePointer (0) + (unsigned int)data.pixelStride * x;
|
||||||
|
|
||||||
|
for (y = 0; y < h; ++y)
|
||||||
|
{
|
||||||
|
dst_ptr[0] = (unsigned char)((sum_r * mul_sum) >> shr_sum);
|
||||||
|
dst_ptr[1] = (unsigned char)((sum_g * mul_sum) >> shr_sum);
|
||||||
|
dst_ptr[2] = (unsigned char)((sum_b * mul_sum) >> shr_sum);
|
||||||
|
dst_ptr += w3;
|
||||||
|
|
||||||
|
sum_r -= sum_out_r;
|
||||||
|
sum_g -= sum_out_g;
|
||||||
|
sum_b -= sum_out_b;
|
||||||
|
|
||||||
|
stack_start = sp + div - radius;
|
||||||
|
if (stack_start >= div)
|
||||||
|
stack_start -= div;
|
||||||
|
|
||||||
|
stack_ptr = &stack[3 * stack_start];
|
||||||
|
|
||||||
|
sum_out_r -= stack_ptr[0];
|
||||||
|
sum_out_g -= stack_ptr[1];
|
||||||
|
sum_out_b -= stack_ptr[2];
|
||||||
|
|
||||||
|
if (yp < hm)
|
||||||
|
{
|
||||||
|
src_ptr += w3;
|
||||||
|
++yp;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_ptr[0] = src_ptr[0];
|
||||||
|
stack_ptr[1] = src_ptr[1];
|
||||||
|
stack_ptr[2] = src_ptr[2];
|
||||||
|
|
||||||
|
sum_in_r += src_ptr[0];
|
||||||
|
sum_in_g += src_ptr[1];
|
||||||
|
sum_in_b += src_ptr[2];
|
||||||
|
sum_r += sum_in_r;
|
||||||
|
sum_g += sum_in_g;
|
||||||
|
sum_b += sum_in_b;
|
||||||
|
|
||||||
|
++sp;
|
||||||
|
if (sp >= div)
|
||||||
|
sp = 0;
|
||||||
|
|
||||||
|
stack_ptr = &stack[sp * 3];
|
||||||
|
|
||||||
|
sum_out_r += stack_ptr[0];
|
||||||
|
sum_out_g += stack_ptr[1];
|
||||||
|
sum_out_b += stack_ptr[2];
|
||||||
|
sum_in_r -= stack_ptr[0];
|
||||||
|
sum_in_g -= stack_ptr[1];
|
||||||
|
sum_in_b -= stack_ptr[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void applyStackBlurARGB (Image& img, unsigned int radius)
|
||||||
|
{
|
||||||
|
const unsigned int w = (unsigned int)img.getWidth();
|
||||||
|
const unsigned int h = (unsigned int)img.getHeight();
|
||||||
|
|
||||||
|
Image::BitmapData data (img, Image::BitmapData::readWrite);
|
||||||
|
|
||||||
|
radius = jlimit (2u, 254u, radius);
|
||||||
|
|
||||||
|
unsigned char stack[(254 * 2 + 1) * 4];
|
||||||
|
|
||||||
|
unsigned int x, y, xp, yp, i, sp, stack_start;
|
||||||
|
|
||||||
|
unsigned char* stack_ptr = nullptr;
|
||||||
|
unsigned char* src_ptr = nullptr;
|
||||||
|
unsigned char* dst_ptr = nullptr;
|
||||||
|
|
||||||
|
unsigned long sum_r, sum_g, sum_b, sum_a, sum_in_r, sum_in_g, sum_in_b, sum_in_a,
|
||||||
|
sum_out_r, sum_out_g, sum_out_b, sum_out_a;
|
||||||
|
|
||||||
|
unsigned int wm = w - 1;
|
||||||
|
unsigned int hm = h - 1;
|
||||||
|
unsigned int w4 = (unsigned int) data.lineStride;
|
||||||
|
unsigned int div = (unsigned int)(radius * 2) + 1;
|
||||||
|
unsigned int mul_sum = stackblur_mul[radius];
|
||||||
|
unsigned char shr_sum = stackblur_shr[radius];
|
||||||
|
|
||||||
|
for (y = 0; y < h; ++y)
|
||||||
|
{
|
||||||
|
sum_r = sum_g = sum_b = sum_a =
|
||||||
|
sum_in_r = sum_in_g = sum_in_b = sum_in_a =
|
||||||
|
sum_out_r = sum_out_g = sum_out_b = sum_out_a = 0;
|
||||||
|
|
||||||
|
src_ptr = data.getLinePointer (int (y));
|
||||||
|
|
||||||
|
for (i = 0; i <= radius; ++i)
|
||||||
|
{
|
||||||
|
stack_ptr = &stack[4 * i];
|
||||||
|
stack_ptr[0] = src_ptr[0];
|
||||||
|
stack_ptr[1] = src_ptr[1];
|
||||||
|
stack_ptr[2] = src_ptr[2];
|
||||||
|
stack_ptr[3] = src_ptr[3];
|
||||||
|
sum_r += src_ptr[0] * (i + 1);
|
||||||
|
sum_g += src_ptr[1] * (i + 1);
|
||||||
|
sum_b += src_ptr[2] * (i + 1);
|
||||||
|
sum_a += src_ptr[3] * (i + 1);
|
||||||
|
sum_out_r += src_ptr[0];
|
||||||
|
sum_out_g += src_ptr[1];
|
||||||
|
sum_out_b += src_ptr[2];
|
||||||
|
sum_out_a += src_ptr[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i <= radius; ++i)
|
||||||
|
{
|
||||||
|
if (i <= wm)
|
||||||
|
src_ptr += 4;
|
||||||
|
|
||||||
|
stack_ptr = &stack[4 * (i + radius)];
|
||||||
|
stack_ptr[0] = src_ptr[0];
|
||||||
|
stack_ptr[1] = src_ptr[1];
|
||||||
|
stack_ptr[2] = src_ptr[2];
|
||||||
|
stack_ptr[3] = src_ptr[3];
|
||||||
|
sum_r += src_ptr[0] * (radius + 1 - i);
|
||||||
|
sum_g += src_ptr[1] * (radius + 1 - i);
|
||||||
|
sum_b += src_ptr[2] * (radius + 1 - i);
|
||||||
|
sum_a += src_ptr[3] * (radius + 1 - i);
|
||||||
|
sum_in_r += src_ptr[0];
|
||||||
|
sum_in_g += src_ptr[1];
|
||||||
|
sum_in_b += src_ptr[2];
|
||||||
|
sum_in_a += src_ptr[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
sp = radius;
|
||||||
|
xp = radius;
|
||||||
|
if (xp > wm)
|
||||||
|
xp = wm;
|
||||||
|
|
||||||
|
src_ptr = data.getLinePointer (int (y)) + (unsigned int)data.pixelStride * xp;
|
||||||
|
dst_ptr = data.getLinePointer (int (y));
|
||||||
|
|
||||||
|
for (x = 0; x < w; ++x)
|
||||||
|
{
|
||||||
|
dst_ptr[0] = (unsigned char)((sum_r * mul_sum) >> shr_sum);
|
||||||
|
dst_ptr[1] = (unsigned char)((sum_g * mul_sum) >> shr_sum);
|
||||||
|
dst_ptr[2] = (unsigned char)((sum_b * mul_sum) >> shr_sum);
|
||||||
|
dst_ptr[3] = (unsigned char)((sum_a * mul_sum) >> shr_sum);
|
||||||
|
dst_ptr += 4;
|
||||||
|
|
||||||
|
sum_r -= sum_out_r;
|
||||||
|
sum_g -= sum_out_g;
|
||||||
|
sum_b -= sum_out_b;
|
||||||
|
sum_a -= sum_out_a;
|
||||||
|
|
||||||
|
stack_start = sp + div - radius;
|
||||||
|
|
||||||
|
if (stack_start >= div)
|
||||||
|
stack_start -= div;
|
||||||
|
|
||||||
|
stack_ptr = &stack[4 * stack_start];
|
||||||
|
|
||||||
|
sum_out_r -= stack_ptr[0];
|
||||||
|
sum_out_g -= stack_ptr[1];
|
||||||
|
sum_out_b -= stack_ptr[2];
|
||||||
|
sum_out_a -= stack_ptr[3];
|
||||||
|
|
||||||
|
if (xp < wm)
|
||||||
|
{
|
||||||
|
src_ptr += 4;
|
||||||
|
++xp;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_ptr[0] = src_ptr[0];
|
||||||
|
stack_ptr[1] = src_ptr[1];
|
||||||
|
stack_ptr[2] = src_ptr[2];
|
||||||
|
stack_ptr[3] = src_ptr[3];
|
||||||
|
|
||||||
|
sum_in_r += src_ptr[0];
|
||||||
|
sum_in_g += src_ptr[1];
|
||||||
|
sum_in_b += src_ptr[2];
|
||||||
|
sum_in_a += src_ptr[3];
|
||||||
|
sum_r += sum_in_r;
|
||||||
|
sum_g += sum_in_g;
|
||||||
|
sum_b += sum_in_b;
|
||||||
|
sum_a += sum_in_a;
|
||||||
|
|
||||||
|
++sp;
|
||||||
|
if (sp >= div)
|
||||||
|
sp = 0;
|
||||||
|
|
||||||
|
stack_ptr = &stack[sp*4];
|
||||||
|
|
||||||
|
sum_out_r += stack_ptr[0];
|
||||||
|
sum_out_g += stack_ptr[1];
|
||||||
|
sum_out_b += stack_ptr[2];
|
||||||
|
sum_out_a += stack_ptr[3];
|
||||||
|
sum_in_r -= stack_ptr[0];
|
||||||
|
sum_in_g -= stack_ptr[1];
|
||||||
|
sum_in_b -= stack_ptr[2];
|
||||||
|
sum_in_a -= stack_ptr[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (x = 0; x < w; ++x)
|
||||||
|
{
|
||||||
|
sum_r = sum_g = sum_b = sum_a =
|
||||||
|
sum_in_r = sum_in_g = sum_in_b = sum_in_a =
|
||||||
|
sum_out_r = sum_out_g = sum_out_b = sum_out_a = 0;
|
||||||
|
|
||||||
|
src_ptr = data.getLinePointer (0) + (unsigned int)data.pixelStride * x;
|
||||||
|
|
||||||
|
for (i = 0; i <= radius; ++i)
|
||||||
|
{
|
||||||
|
stack_ptr = &stack[i * 4];
|
||||||
|
stack_ptr[0] = src_ptr[0];
|
||||||
|
stack_ptr[1] = src_ptr[1];
|
||||||
|
stack_ptr[2] = src_ptr[2];
|
||||||
|
stack_ptr[3] = src_ptr[3];
|
||||||
|
sum_r += src_ptr[0] * (i + 1);
|
||||||
|
sum_g += src_ptr[1] * (i + 1);
|
||||||
|
sum_b += src_ptr[2] * (i + 1);
|
||||||
|
sum_a += src_ptr[3] * (i + 1);
|
||||||
|
sum_out_r += src_ptr[0];
|
||||||
|
sum_out_g += src_ptr[1];
|
||||||
|
sum_out_b += src_ptr[2];
|
||||||
|
sum_out_a += src_ptr[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i <= radius; ++i)
|
||||||
|
{
|
||||||
|
if (i <= hm)
|
||||||
|
src_ptr += w4;
|
||||||
|
|
||||||
|
stack_ptr = &stack[4 * (i + radius)];
|
||||||
|
stack_ptr[0] = src_ptr[0];
|
||||||
|
stack_ptr[1] = src_ptr[1];
|
||||||
|
stack_ptr[2] = src_ptr[2];
|
||||||
|
stack_ptr[3] = src_ptr[3];
|
||||||
|
sum_r += src_ptr[0] * (radius + 1 - i);
|
||||||
|
sum_g += src_ptr[1] * (radius + 1 - i);
|
||||||
|
sum_b += src_ptr[2] * (radius + 1 - i);
|
||||||
|
sum_a += src_ptr[3] * (radius + 1 - i);
|
||||||
|
sum_in_r += src_ptr[0];
|
||||||
|
sum_in_g += src_ptr[1];
|
||||||
|
sum_in_b += src_ptr[2];
|
||||||
|
sum_in_a += src_ptr[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
sp = radius;
|
||||||
|
yp = radius;
|
||||||
|
if (yp > hm)
|
||||||
|
yp = hm;
|
||||||
|
|
||||||
|
src_ptr = data.getLinePointer (int (yp)) + (unsigned int)data.pixelStride * x;
|
||||||
|
dst_ptr = data.getLinePointer (0) + (unsigned int)data.pixelStride * x;
|
||||||
|
|
||||||
|
for (y = 0; y < h; ++y)
|
||||||
|
{
|
||||||
|
dst_ptr[0] = (unsigned char)((sum_r * mul_sum) >> shr_sum);
|
||||||
|
dst_ptr[1] = (unsigned char)((sum_g * mul_sum) >> shr_sum);
|
||||||
|
dst_ptr[2] = (unsigned char)((sum_b * mul_sum) >> shr_sum);
|
||||||
|
dst_ptr[3] = (unsigned char)((sum_a * mul_sum) >> shr_sum);
|
||||||
|
dst_ptr += w4;
|
||||||
|
|
||||||
|
sum_r -= sum_out_r;
|
||||||
|
sum_g -= sum_out_g;
|
||||||
|
sum_b -= sum_out_b;
|
||||||
|
sum_a -= sum_out_a;
|
||||||
|
|
||||||
|
stack_start = sp + div - radius;
|
||||||
|
if (stack_start >= div)
|
||||||
|
stack_start -= div;
|
||||||
|
|
||||||
|
stack_ptr = &stack[4 * stack_start];
|
||||||
|
|
||||||
|
sum_out_r -= stack_ptr[0];
|
||||||
|
sum_out_g -= stack_ptr[1];
|
||||||
|
sum_out_b -= stack_ptr[2];
|
||||||
|
sum_out_a -= stack_ptr[3];
|
||||||
|
|
||||||
|
if (yp < hm)
|
||||||
|
{
|
||||||
|
src_ptr += w4;
|
||||||
|
++yp;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_ptr[0] = src_ptr[0];
|
||||||
|
stack_ptr[1] = src_ptr[1];
|
||||||
|
stack_ptr[2] = src_ptr[2];
|
||||||
|
stack_ptr[3] = src_ptr[3];
|
||||||
|
|
||||||
|
sum_in_r += src_ptr[0];
|
||||||
|
sum_in_g += src_ptr[1];
|
||||||
|
sum_in_b += src_ptr[2];
|
||||||
|
sum_in_a += src_ptr[3];
|
||||||
|
sum_r += sum_in_r;
|
||||||
|
sum_g += sum_in_g;
|
||||||
|
sum_b += sum_in_b;
|
||||||
|
sum_a += sum_in_a;
|
||||||
|
|
||||||
|
++sp;
|
||||||
|
if (sp >= div)
|
||||||
|
sp = 0;
|
||||||
|
|
||||||
|
stack_ptr = &stack[sp * 4];
|
||||||
|
|
||||||
|
sum_out_r += stack_ptr[0];
|
||||||
|
sum_out_g += stack_ptr[1];
|
||||||
|
sum_out_b += stack_ptr[2];
|
||||||
|
sum_out_a += stack_ptr[3];
|
||||||
|
sum_in_r -= stack_ptr[0];
|
||||||
|
sum_in_g -= stack_ptr[1];
|
||||||
|
sum_in_b -= stack_ptr[2];
|
||||||
|
sum_in_a -= stack_ptr[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Stack Blur Algorithm was invented by Mario Klingemann,
|
||||||
|
// mario@quasimondo.com and described here:
|
||||||
|
// http://incubator.quasimondo.com/processing/fast_blur_deluxe.php
|
||||||
|
|
||||||
|
// Stackblur algorithm by Mario Klingemann
|
||||||
|
// Details here:
|
||||||
|
// http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
|
||||||
|
// C++ implemenation base from:
|
||||||
|
// https://gist.github.com/benjamin9999/3809142
|
||||||
|
// http://www.antigrain.com/__code/include/agg_blur.h.html
|
||||||
|
void applyStackBlur (Image& img, int radius)
|
||||||
|
{
|
||||||
|
if (img.getFormat() == Image::ARGB) applyStackBlurARGB (img, (unsigned int)radius);
|
||||||
|
if (img.getFormat() == Image::RGB) applyStackBlurRGB (img, (unsigned int)radius);
|
||||||
|
if (img.getFormat() == Image::SingleChannel) applyStackBlurBW (img, (unsigned int)radius);
|
||||||
|
}
|
23
Modules/gin/images/imageutilities.cpp
Executable file
23
Modules/gin/images/imageutilities.cpp
Executable file
|
@ -0,0 +1,23 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2019 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
juce::Image rasterizeSVG ( juce::String svgText, int w, int h )
|
||||||
|
{
|
||||||
|
Image img ( Image::ARGB, w, h, true );
|
||||||
|
|
||||||
|
if ( auto svg = XmlDocument::parse ( svgText ) )
|
||||||
|
{
|
||||||
|
const MessageManagerLock mmLock;
|
||||||
|
|
||||||
|
auto drawable = Drawable::createFromSVG ( *svg );
|
||||||
|
|
||||||
|
Graphics g ( img );
|
||||||
|
drawable->drawWithin ( g, Rectangle<float> ( float ( w ), float ( h ) ), 0, 1.f );
|
||||||
|
}
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
10
Modules/gin/images/imageutilities.h
Executable file
10
Modules/gin/images/imageutilities.h
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2019 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
juce::Image rasterizeSVG (juce::String svgText, int w, int h);
|
32
Modules/gin/utilities/asyncutilities.cpp
Executable file
32
Modules/gin/utilities/asyncutilities.cpp
Executable file
|
@ -0,0 +1,32 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
struct BlockingData
|
||||||
|
{
|
||||||
|
std::function<void ()> func;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void* blockingCallback (void* param)
|
||||||
|
{
|
||||||
|
auto data = (BlockingData*)param;
|
||||||
|
|
||||||
|
if (data->func)
|
||||||
|
data->func();
|
||||||
|
|
||||||
|
delete data;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void callOnMainThreadBlocking ( std::function<void ()> func )
|
||||||
|
{
|
||||||
|
auto data = new BlockingData();
|
||||||
|
data->func = func;
|
||||||
|
|
||||||
|
MessageManager::getInstance()->callFunctionOnMessageThread (blockingCallback, data);
|
||||||
|
}
|
40
Modules/gin/utilities/asyncutilities.h
Executable file
40
Modules/gin/utilities/asyncutilities.h
Executable file
|
@ -0,0 +1,40 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class LambdaTimer : public Timer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LambdaTimer (std::function<void()> func = nullptr) : onTimer (func) {}
|
||||||
|
|
||||||
|
std::function<void()> onTimer;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void timerCallback() override
|
||||||
|
{
|
||||||
|
if (onTimer)
|
||||||
|
onTimer();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class LambdaAsyncUpdater : public AsyncUpdater
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LambdaAsyncUpdater (std::function<void()> func) : onAsyncUpdate (func) {}
|
||||||
|
|
||||||
|
std::function<void()> onAsyncUpdate;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleAsyncUpdate () override
|
||||||
|
{
|
||||||
|
if (onAsyncUpdate)
|
||||||
|
onAsyncUpdate();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void callOnMainThreadBlocking ( std::function<void ()> func );
|
89
Modules/gin/utilities/coalescedtimer.h
Executable file
89
Modules/gin/utilities/coalescedtimer.h
Executable file
|
@ -0,0 +1,89 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2020 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class CoalescedTimer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CoalescedTimer () = default;
|
||||||
|
|
||||||
|
void startTimer (int ms)
|
||||||
|
{
|
||||||
|
stopTimer();
|
||||||
|
|
||||||
|
delay = ms;
|
||||||
|
sharedTimers->add (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void startTimerHz (int hz)
|
||||||
|
{
|
||||||
|
if (hz > 0)
|
||||||
|
startTimer (1000 / hz);
|
||||||
|
else
|
||||||
|
stopTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopTimer()
|
||||||
|
{
|
||||||
|
sharedTimers->remove (this);
|
||||||
|
delay = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::function<void ()> onTimer;
|
||||||
|
|
||||||
|
private:
|
||||||
|
class SharedTimer : public Timer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void timerCallback() override
|
||||||
|
{
|
||||||
|
for (auto t : timers)
|
||||||
|
if (t->onTimer)
|
||||||
|
t->onTimer ();
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<CoalescedTimer*> timers;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SharedTimers
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void add (CoalescedTimer* t)
|
||||||
|
{
|
||||||
|
auto itr = timers.find (t->delay);
|
||||||
|
if (itr == timers.end())
|
||||||
|
{
|
||||||
|
auto st = std::make_unique<SharedTimer>();
|
||||||
|
st->timers.add (t);
|
||||||
|
st->startTimer (t->delay);
|
||||||
|
|
||||||
|
timers[t->delay] = std::move (st);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
itr->second->timers.add (t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove (CoalescedTimer* t)
|
||||||
|
{
|
||||||
|
auto itr = timers.find (t->delay);
|
||||||
|
if (itr != timers.end())
|
||||||
|
{
|
||||||
|
itr->second->timers.removeFirstMatchingValue (t);
|
||||||
|
if (itr->second->timers.size() == 0)
|
||||||
|
timers.erase (t->delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<int, std::unique_ptr<SharedTimer>> timers;
|
||||||
|
};
|
||||||
|
|
||||||
|
int delay = 0;
|
||||||
|
SharedResourcePointer<SharedTimers> sharedTimers;
|
||||||
|
};
|
308
Modules/gin/utilities/downloadmanager.cpp
Executable file
308
Modules/gin/utilities/downloadmanager.cpp
Executable file
|
@ -0,0 +1,308 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
DownloadManager::DownloadManager (int connect, int shutdown)
|
||||||
|
: connectTimeout (connect), shutdownTimeout (shutdown)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadManager::~DownloadManager()
|
||||||
|
{
|
||||||
|
cancelAllDownloads();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadManager::setConcurrentDownloadLimit (int limit)
|
||||||
|
{
|
||||||
|
maxDownloads = limit;
|
||||||
|
triggerNextDownload();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadManager::triggerNextDownload()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < downloads.size() && runningDownloads < maxDownloads; i++)
|
||||||
|
{
|
||||||
|
auto d = downloads[i];
|
||||||
|
if (! d->started)
|
||||||
|
{
|
||||||
|
runningDownloads++;
|
||||||
|
d->started = true;
|
||||||
|
d->startThread (priority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadManager::DownloadResult DownloadManager::blockingDownload (String url, String postData, String extraHeaders)
|
||||||
|
{
|
||||||
|
return blockingDownload (URL (url).withPOSTData (postData), extraHeaders);
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadManager::DownloadResult DownloadManager::blockingDownload (URL url, String extraHeaders)
|
||||||
|
{
|
||||||
|
#if JUCE_WINDOWS
|
||||||
|
auto headerList = StringArray::fromTokens (extraHeaders, "\n", "");
|
||||||
|
headerList.add ("Accept-Encoding: gzip");
|
||||||
|
extraHeaders = headerList.joinIntoString ("\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Download download (*this);
|
||||||
|
download.async = false;
|
||||||
|
download.result.url = url;
|
||||||
|
download.headers = extraHeaders;
|
||||||
|
download.result.downloadId = 0;
|
||||||
|
download.completionCallback = nullptr;
|
||||||
|
download.progressCallback = nullptr;
|
||||||
|
|
||||||
|
download.run();
|
||||||
|
|
||||||
|
return download.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DownloadManager::startAsyncDownload (String url, String postData,
|
||||||
|
std::function<void (DownloadResult)> completionCallback,
|
||||||
|
std::function<void (int64, int64, int64)> progressCallback,
|
||||||
|
String extraHeaders)
|
||||||
|
{
|
||||||
|
return startAsyncDownload (URL (url).withPOSTData (postData), completionCallback, progressCallback, extraHeaders);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DownloadManager::startAsyncDownload (URL url,
|
||||||
|
std::function<void (DownloadResult)> completionCallback,
|
||||||
|
std::function<void (int64, int64, int64)> progressCallback,
|
||||||
|
String extraHeaders)
|
||||||
|
{
|
||||||
|
#if JUCE_WINDOWS
|
||||||
|
// macOS does this automatically
|
||||||
|
if (gzipDeflate)
|
||||||
|
{
|
||||||
|
auto headerList = StringArray::fromTokens (extraHeaders, "\n", "");
|
||||||
|
headerList.add ("Accept-Encoding: gzip");
|
||||||
|
extraHeaders = headerList.joinIntoString ("\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto download = new Download (*this);
|
||||||
|
download->result.url = url;
|
||||||
|
download->headers = extraHeaders;
|
||||||
|
download->result.downloadId = ++nextId;
|
||||||
|
download->completionCallback = completionCallback;
|
||||||
|
download->progressCallback = progressCallback;
|
||||||
|
|
||||||
|
downloads.add (download);
|
||||||
|
|
||||||
|
triggerNextDownload();
|
||||||
|
|
||||||
|
return download->result.downloadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadManager::cancelAllDownloads()
|
||||||
|
{
|
||||||
|
runningDownloads = 0;
|
||||||
|
downloads.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadManager::cancelDownload (int downloadId)
|
||||||
|
{
|
||||||
|
for (int i = downloads.size(); --i >= 0;)
|
||||||
|
{
|
||||||
|
if (downloads[i]->result.downloadId == downloadId)
|
||||||
|
{
|
||||||
|
if (downloads[i]->isThreadRunning())
|
||||||
|
runningDownloads--;
|
||||||
|
|
||||||
|
downloads.remove (i);
|
||||||
|
triggerNextDownload();
|
||||||
|
|
||||||
|
if (downloads.size() == 0 && queueFinishedCallback)
|
||||||
|
queueFinishedCallback();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadManager::downloadFinished (Download* download)
|
||||||
|
{
|
||||||
|
runningDownloads--;
|
||||||
|
downloads.removeObject (download);
|
||||||
|
|
||||||
|
triggerNextDownload();
|
||||||
|
|
||||||
|
if (downloads.size() == 0 && queueFinishedCallback)
|
||||||
|
queueFinishedCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadManager::pauseDownloads (bool p)
|
||||||
|
{
|
||||||
|
pause = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
DownloadManager::Download::~Download()
|
||||||
|
{
|
||||||
|
// Cancel any blocking reads
|
||||||
|
if (is != nullptr)
|
||||||
|
is->cancel();
|
||||||
|
|
||||||
|
// Wait a long time before cancelling, WebInputStream could be stuck in
|
||||||
|
// connect. Unlikely but possible.
|
||||||
|
if (async)
|
||||||
|
stopThread (owner.shutdownTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadManager::Download::run()
|
||||||
|
{
|
||||||
|
int attemps = owner.retryLimit + 1;
|
||||||
|
while (attemps-- && ! threadShouldExit())
|
||||||
|
{
|
||||||
|
result.attempts++;
|
||||||
|
if (tryDownload())
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (owner.retryDelay > 0)
|
||||||
|
wait (roundToInt (owner.retryDelay * 1000));
|
||||||
|
|
||||||
|
while (owner.pause.get())
|
||||||
|
wait (500);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (async && ! threadShouldExit())
|
||||||
|
{
|
||||||
|
// Get a weak reference to self, to check if we get deleted before
|
||||||
|
// async call happens.
|
||||||
|
WeakReference<Download> self = this;
|
||||||
|
MessageManager::callAsync ([self]
|
||||||
|
{
|
||||||
|
if (self != nullptr)
|
||||||
|
self->completionCallback (self->result);
|
||||||
|
if (self != nullptr)
|
||||||
|
self->owner.downloadFinished (self);
|
||||||
|
// DownloadManager has now delete us, don't do anything else
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DownloadManager::Download::tryDownload()
|
||||||
|
{
|
||||||
|
// Use post if we have post data
|
||||||
|
const bool post = result.url.getPostData().isNotEmpty();
|
||||||
|
|
||||||
|
if ((is = std::make_unique<WebInputStream> (result.url, post)) != nullptr)
|
||||||
|
{
|
||||||
|
if (headers.isNotEmpty())
|
||||||
|
is->withExtraHeaders (headers);
|
||||||
|
is->withConnectionTimeout (owner.connectTimeout);
|
||||||
|
|
||||||
|
if (is->connect (nullptr))
|
||||||
|
{
|
||||||
|
// Save headers and http response code
|
||||||
|
result.httpCode = is->getStatusCode();
|
||||||
|
result.responseHeaders = is->getResponseHeaders();
|
||||||
|
|
||||||
|
auto keys = result.responseHeaders.getAllKeys();
|
||||||
|
auto vals = result.responseHeaders.getAllValues();
|
||||||
|
|
||||||
|
MemoryOutputStream os (result.data, false);
|
||||||
|
|
||||||
|
lastBytesSent = 0;
|
||||||
|
lastProgress = Time::getMillisecondCounter();
|
||||||
|
int64 downloaded = 0;
|
||||||
|
int64 totalLength = is->getTotalLength();
|
||||||
|
|
||||||
|
// For chunked http encoding, overall length may not be given
|
||||||
|
if (totalLength < 0)
|
||||||
|
totalLength = std::numeric_limits<int64>::max();
|
||||||
|
|
||||||
|
// Download all data
|
||||||
|
char buffer[128 * 1000];
|
||||||
|
while (! is->isExhausted() && ! threadShouldExit() && downloaded < totalLength)
|
||||||
|
{
|
||||||
|
int64 toRead = jmin (int64 (sizeof (buffer)), int64 (owner.downloadBlockSize), totalLength - downloaded);
|
||||||
|
|
||||||
|
int read = is->read (buffer, int (toRead));
|
||||||
|
|
||||||
|
if (owner.pause.get())
|
||||||
|
{
|
||||||
|
result.ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (read > 0)
|
||||||
|
{
|
||||||
|
os.write (buffer, size_t (read));
|
||||||
|
downloaded += read;
|
||||||
|
result.ok = (is->isExhausted() || downloaded == totalLength) && result.httpCode == 200;
|
||||||
|
|
||||||
|
updateProgress (downloaded, totalLength, false);
|
||||||
|
}
|
||||||
|
else if (read == 0 && is->isExhausted())
|
||||||
|
{
|
||||||
|
// For chunked encoding, assume we have it all, otherwise check the length
|
||||||
|
if (totalLength < std::numeric_limits<int64>::max())
|
||||||
|
result.ok = (totalLength == downloaded) && result.httpCode == 200;
|
||||||
|
else
|
||||||
|
result.ok = result.httpCode == 200;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProgress (downloaded, totalLength, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if JUCE_WINDOWS
|
||||||
|
// Decompress the gzip encoded data. This happens automatically on macOS
|
||||||
|
if (result.ok && result.responseHeaders["Content-Encoding"] == "gzip")
|
||||||
|
{
|
||||||
|
MemoryInputStream mis (result.data, true);
|
||||||
|
GZIPDecompressorInputStream gis (&mis, false, GZIPDecompressorInputStream::gzipFormat);
|
||||||
|
|
||||||
|
result.data.reset();
|
||||||
|
|
||||||
|
while (! gis.isExhausted())
|
||||||
|
{
|
||||||
|
char buffer[10 * 1024];
|
||||||
|
int read = gis.read (buffer, sizeof (buffer));
|
||||||
|
if (read > 0)
|
||||||
|
result.data.append (buffer, size_t (read));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return result.ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadManager::Download::updateProgress (int64 current, int64 total, bool forceNotification)
|
||||||
|
{
|
||||||
|
if (progressCallback)
|
||||||
|
{
|
||||||
|
// Update progress no more than once per second
|
||||||
|
uint32 now = Time::getMillisecondCounter();
|
||||||
|
if ((now >= lastProgress + uint32 (owner.downloadIntervalMS)) || forceNotification)
|
||||||
|
{
|
||||||
|
int64 delta = current - lastBytesSent;
|
||||||
|
lastBytesSent = current;
|
||||||
|
lastProgress = now;
|
||||||
|
|
||||||
|
if (delta > 0)
|
||||||
|
{
|
||||||
|
// Get a weak reference to self, to check if we get deleted before
|
||||||
|
// async call happens.
|
||||||
|
WeakReference<Download> self = this;
|
||||||
|
MessageManager::callAsync ([self, current, total, delta]
|
||||||
|
{
|
||||||
|
if (self != nullptr)
|
||||||
|
self->progressCallback (current, total, delta);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
162
Modules/gin/utilities/downloadmanager.h
Executable file
162
Modules/gin/utilities/downloadmanager.h
Executable file
|
@ -0,0 +1,162 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
|
||||||
|
Downloads files to a memory block and then calls a lambda
|
||||||
|
on the message thread with the results. Does not block the
|
||||||
|
message thread while establishing the HTTP connect like
|
||||||
|
URL::downloadToFile
|
||||||
|
|
||||||
|
*/
|
||||||
|
class DownloadManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//==============================================================================
|
||||||
|
DownloadManager (int connectTimeout = 30 * 1000, int shutdownTimeout = 30 * 1000);
|
||||||
|
~DownloadManager();
|
||||||
|
|
||||||
|
/** This callback is called every time all downloads are finished */
|
||||||
|
void setQueueFinishedCallback (std::function<void ()> callback)
|
||||||
|
{
|
||||||
|
queueFinishedCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** How long connecting is given before it times out */
|
||||||
|
void setConnectTimeout (int timeout) { connectTimeout = timeout; }
|
||||||
|
|
||||||
|
/** If a download fails, how long many times it should retry */
|
||||||
|
void setRetryLimit (int limit) { retryLimit = limit; }
|
||||||
|
|
||||||
|
/** If a download fails, how long to wait until trying again */
|
||||||
|
void setRetryDelay (double seconds) { retryDelay = seconds; }
|
||||||
|
|
||||||
|
/** Maximum number of downloads to allow at once */
|
||||||
|
void setConcurrentDownloadLimit (int l);
|
||||||
|
|
||||||
|
/** Number of items in download queue */
|
||||||
|
int getNumberOfDownloads() { return downloads.size(); }
|
||||||
|
|
||||||
|
/** Set download thread priority. Does not affect priority of
|
||||||
|
already running threads */
|
||||||
|
void setThreadPriority (int p) { priority = p; }
|
||||||
|
|
||||||
|
/** Sets minimum time between download progress callbacks in milliseconds */
|
||||||
|
void setProgressInterval (int ms) { downloadIntervalMS = jmax (1, ms); }
|
||||||
|
|
||||||
|
/** Sets the block size of chunks to download. Progress callbacks and
|
||||||
|
cancelling downloads can only happen between these blocks. Max size is 128 KB */
|
||||||
|
void setDownloadBlockSize (int bs) { downloadBlockSize = jlimit (1, 128 * 1000, bs); }
|
||||||
|
|
||||||
|
int getNumDownloadsInQueue() { return downloads.size(); }
|
||||||
|
|
||||||
|
/** If enabled, will request the server sends the data compressed
|
||||||
|
This only has effect on windows. On macOS it is handled by the system libraries
|
||||||
|
and is always on.
|
||||||
|
*/
|
||||||
|
void enableGzipDeflate (bool e) { gzipDeflate = e; }
|
||||||
|
|
||||||
|
/** Pause / resume all downloads. This actually stops any running downloads
|
||||||
|
and then restarts them when unpaused. You will loose some downloaded data
|
||||||
|
that will need to be redownloaded. */
|
||||||
|
void pauseDownloads (bool);
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
struct DownloadResult
|
||||||
|
{
|
||||||
|
URL url;
|
||||||
|
int downloadId = 0;
|
||||||
|
int attempts = 0;
|
||||||
|
|
||||||
|
juce::MemoryBlock data;
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
|
int httpCode = 0;
|
||||||
|
StringPairArray responseHeaders;
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/** Starts a download and returns the download id which can be used to cancel the download
|
||||||
|
|
||||||
|
progressCallback returns current amount downloaded, total amount to download, and amount
|
||||||
|
downloaded since last callback. Note that for http chunk encoding total size is unknown
|
||||||
|
and will be maximum int64 value.
|
||||||
|
*/
|
||||||
|
int startAsyncDownload (String url, String postData,
|
||||||
|
std::function<void (DownloadResult)> completionCallback,
|
||||||
|
std::function<void (int64, int64, int64)> progressCallback = nullptr,
|
||||||
|
String extraHeaders = {});
|
||||||
|
|
||||||
|
int startAsyncDownload (URL url,
|
||||||
|
std::function<void (DownloadResult)> completionCallback,
|
||||||
|
std::function<void (int64, int64, int64)> progressCallback = nullptr,
|
||||||
|
String extraHeaders = {});
|
||||||
|
|
||||||
|
/** Cancels all downloads */
|
||||||
|
void cancelAllDownloads();
|
||||||
|
|
||||||
|
/** Cancels a download with a given id */
|
||||||
|
void cancelDownload (int downloadId);
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
DownloadResult blockingDownload (String url, String postData, String extraHeaders = {});
|
||||||
|
|
||||||
|
DownloadResult blockingDownload (URL url, String extraHeaders = {});
|
||||||
|
|
||||||
|
private:
|
||||||
|
//==============================================================================
|
||||||
|
/** Manages a download on a background thread */
|
||||||
|
struct Download : public Thread
|
||||||
|
{
|
||||||
|
Download (DownloadManager& o) : Thread ("DownloadManager::Download"), owner (o) {}
|
||||||
|
~Download() override;
|
||||||
|
|
||||||
|
void run() override;
|
||||||
|
bool tryDownload();
|
||||||
|
void updateProgress (int64 current, int64 total, bool );
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
DownloadResult result;
|
||||||
|
std::function<void (DownloadResult)> completionCallback;
|
||||||
|
std::function<void (int64, int64, int64)> progressCallback;
|
||||||
|
|
||||||
|
std::unique_ptr<WebInputStream> is;
|
||||||
|
|
||||||
|
DownloadManager& owner;
|
||||||
|
|
||||||
|
String headers;
|
||||||
|
bool started = false, async = true;
|
||||||
|
uint32 lastProgress = 0;
|
||||||
|
int64 lastBytesSent = 0;
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
JUCE_DECLARE_WEAK_REFERENCEABLE (Download)
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Download)
|
||||||
|
};
|
||||||
|
|
||||||
|
void downloadFinished (Download*);
|
||||||
|
void triggerNextDownload();
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
int nextId = 0;
|
||||||
|
int connectTimeout = 30 * 1000;
|
||||||
|
int shutdownTimeout = 30 * 1000;
|
||||||
|
int retryLimit = 0, priority = 5, downloadIntervalMS = 1000, downloadBlockSize = 128 * 1000;
|
||||||
|
|
||||||
|
double retryDelay = 0.0;
|
||||||
|
int runningDownloads = 0, maxDownloads = 100;
|
||||||
|
OwnedArray<Download> downloads;
|
||||||
|
std::function<void ()> queueFinishedCallback;
|
||||||
|
bool gzipDeflate = true;
|
||||||
|
juce::Atomic<bool> pause;
|
||||||
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DownloadManager)
|
||||||
|
};
|
289
Modules/gin/utilities/easing.h
Executable file
289
Modules/gin/utilities/easing.h
Executable file
|
@ -0,0 +1,289 @@
|
||||||
|
// Easing functions based on AHEasing
|
||||||
|
// Converted to template functions for Gin
|
||||||
|
//
|
||||||
|
// Copyright (c) 2011, Auerhaus Development, LLC
|
||||||
|
//
|
||||||
|
// This program is free software. It comes without any warranty, to
|
||||||
|
// the extent permitted by applicable law. You can redistribute it
|
||||||
|
// and/or modify it under the terms of the Do What The Fuck You Want
|
||||||
|
// To Public License, Version 2, as published by Sam Hocevar. See
|
||||||
|
// http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||||
|
|
||||||
|
// Modeled after the line y = x
|
||||||
|
template<class T>
|
||||||
|
T easeLinear (T p)
|
||||||
|
{
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the parabola y = x^2
|
||||||
|
template<class T>
|
||||||
|
T easeQuadraticIn (T p)
|
||||||
|
{
|
||||||
|
return p * p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the parabola y = -x^2 + 2x
|
||||||
|
template<class T>
|
||||||
|
T easeQuadraticOut (T p)
|
||||||
|
{
|
||||||
|
return -(p * (p - 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the piecewise quadratic
|
||||||
|
// y = (1/2)((2x)^2) ; [0, 0.5)
|
||||||
|
// y = -(1/2)((2x-1)*(2x-3) - 1) ; [0.5, 1]
|
||||||
|
template<class T>
|
||||||
|
T easeQuadraticInOut (T p)
|
||||||
|
{
|
||||||
|
if (p < 0.5)
|
||||||
|
return 2 * p * p;
|
||||||
|
else
|
||||||
|
return (-2 * p * p) + (4 * p) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the cubic y = x^3
|
||||||
|
template<class T>
|
||||||
|
T easeCubicIn (T p)
|
||||||
|
{
|
||||||
|
return p * p * p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the cubic y = (x - 1)^3 + 1
|
||||||
|
template<class T>
|
||||||
|
T easeCubicOut (T p)
|
||||||
|
{
|
||||||
|
T f = (p - 1);
|
||||||
|
return f * f * f + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the piecewise cubic
|
||||||
|
// y = (1/2)((2x)^3) ; [0, 0.5)
|
||||||
|
// y = (1/2)((2x-2)^3 + 2) ; [0.5, 1]
|
||||||
|
template<class T>
|
||||||
|
T easeCubicInOut (T p)
|
||||||
|
{
|
||||||
|
if (p < 0.5)
|
||||||
|
return 4 * p * p * p;
|
||||||
|
|
||||||
|
T f = ((2 * p) - 2);
|
||||||
|
return 0.5 * f * f * f + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the quartic x^4
|
||||||
|
template<class T>
|
||||||
|
T easeQuarticIn (T p)
|
||||||
|
{
|
||||||
|
return p * p * p * p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the quartic y = 1 - (x - 1)^4
|
||||||
|
template<class T>
|
||||||
|
T easeQuarticOut (T p)
|
||||||
|
{
|
||||||
|
T f = (p - 1);
|
||||||
|
return f * f * f * (1 - p) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the piecewise quartic
|
||||||
|
// y = (1/2)((2x)^4) ; [0, 0.5)
|
||||||
|
// y = -(1/2)((2x-2)^4 - 2) ; [0.5, 1]
|
||||||
|
template<class T>
|
||||||
|
T easeQuarticInOut (T p)
|
||||||
|
{
|
||||||
|
if (p < 0.5)
|
||||||
|
return 8 * p * p * p * p;
|
||||||
|
|
||||||
|
T f = (p - 1);
|
||||||
|
return -8 * f * f * f * f + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the quintic y = x^5
|
||||||
|
template<class T>
|
||||||
|
T easeQuinticIn (T p)
|
||||||
|
{
|
||||||
|
return p * p * p * p * p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the quintic y = (x - 1)^5 + 1
|
||||||
|
template<class T>
|
||||||
|
T easeQuinticOut (T p)
|
||||||
|
{
|
||||||
|
T f = (p - 1);
|
||||||
|
return f * f * f * f * f + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the piecewise quintic
|
||||||
|
// y = (1/2)((2x)^5) ; [0, 0.5)
|
||||||
|
// y = (1/2)((2x-2)^5 + 2) ; [0.5, 1]
|
||||||
|
template<class T>
|
||||||
|
T easeQuinticInOut (T p)
|
||||||
|
{
|
||||||
|
if (p < 0.5)
|
||||||
|
return 16 * p * p * p * p * p;
|
||||||
|
|
||||||
|
T f = ((2 * p) - 2);
|
||||||
|
return 0.5 * f * f * f * f * f + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after quarter-cycle of sine wave
|
||||||
|
template<class T>
|
||||||
|
T easeSineIn (T p)
|
||||||
|
{
|
||||||
|
return std::sin ((p - 1) * (MathConstants<T>::pi / 2)) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after quarter-cycle of sine wave (different phase)
|
||||||
|
template<class T>
|
||||||
|
T easeSineOut (T p)
|
||||||
|
{
|
||||||
|
return std::sin (p * MathConstants<T>::pi / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after half sine wave
|
||||||
|
template<class T>
|
||||||
|
T easeSineInOut (T p)
|
||||||
|
{
|
||||||
|
return T (0.5) * (1 - std::cos (p * MathConstants<T>::pi));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after shifted quadrant IV of unit circle
|
||||||
|
template<class T>
|
||||||
|
T easeCircularIn (T p)
|
||||||
|
{
|
||||||
|
return 1 - std::sqrt (1 - (p * p));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after shifted quadrant II of unit circle
|
||||||
|
template<class T>
|
||||||
|
T easeCircularOut (T p)
|
||||||
|
{
|
||||||
|
return std::sqrt ((2 - p) * p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the piecewise circular function
|
||||||
|
// y = (1/2)(1 - sqrt(1 - 4x^2)) ; [0, 0.5)
|
||||||
|
// y = (1/2)(sqrt(-(2x - 3)*(2x - 1)) + 1) ; [0.5, 1]
|
||||||
|
template<class T>
|
||||||
|
T easeCircularInOut (T p)
|
||||||
|
{
|
||||||
|
if (p < 0.5)
|
||||||
|
return 0.5 * (1 - std::sqrt (1 - 4 * (p * p)));
|
||||||
|
else
|
||||||
|
return 0.5 * (std::sqrt (-((2 * p) - 3) * ((2 * p) - 1)) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the exponential function y = 2^(10(x - 1))
|
||||||
|
template<class T>
|
||||||
|
T easeExponentialIn (T p)
|
||||||
|
{
|
||||||
|
return (p == 0.0) ? p : std::pow (2, 10 * (p - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the exponential function y = -2^(-10x) + 1
|
||||||
|
template<class T>
|
||||||
|
T easeExponentialOut (T p)
|
||||||
|
{
|
||||||
|
return (p == 1.0) ? p : 1 - std::pow (2, -10 * p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the piecewise exponential
|
||||||
|
// y = (1/2)2^(10(2x - 1)) ; [0,0.5)
|
||||||
|
// y = -(1/2)*2^(-10(2x - 1))) + 1 ; [0.5,1]
|
||||||
|
template<class T>
|
||||||
|
T easeExponentialInOut (T p)
|
||||||
|
{
|
||||||
|
if (p == 0.0 || p == 1.0) return p;
|
||||||
|
|
||||||
|
if (p < 0.5)
|
||||||
|
return 0.5 * std::pow (2, (20 * p) - 10);
|
||||||
|
else
|
||||||
|
return -0.5 * std::pow (2, (-20 * p) + 10) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the damped sine wave y = sin(13pi/2*x)*pow(2, 10 * (x - 1))
|
||||||
|
template<class T>
|
||||||
|
T easeElasticIn (T p)
|
||||||
|
{
|
||||||
|
return std::sin (13 * (MathConstants<T>::pi / 2) * p) * std::pow (2, 10 * (p - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the damped sine wave y = sin(-13pi/2*(x + 1))*pow(2, -10x) + 1
|
||||||
|
template<class T>
|
||||||
|
T easeElasticOut (T p)
|
||||||
|
{
|
||||||
|
return std::sin (-13 * (MathConstants<T>::pi / 2) * (p + 1)) * std::pow (2, -10 * p) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the piecewise exponentially-damped sine wave:
|
||||||
|
// y = (1/2)*sin(13pi/2*(2*x))*pow(2, 10 * ((2*x) - 1)) ; [0,0.5)
|
||||||
|
// y = (1/2)*(sin(-13pi/2*((2x-1)+1))*pow(2,-10(2*x-1)) + 2) ; [0.5, 1]
|
||||||
|
template<class T>
|
||||||
|
T easeElasticInOut (T p)
|
||||||
|
{
|
||||||
|
if (p < 0.5)
|
||||||
|
return 0.5 * std::sin (13 * (MathConstants<T>::pi / 2) * (2 * p)) * std::pow (2, 10 * ((2 * p) - 1));
|
||||||
|
else
|
||||||
|
return 0.5 * (std::sin (-13 * (MathConstants<T>::pi / 2) * ((2 * p - 1) + 1)) * std::pow (2, -10 * (2 * p - 1)) + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the overshooting cubic y = x^3-x*sin(x*pi)
|
||||||
|
template<class T>
|
||||||
|
T easeBackIn (T p)
|
||||||
|
{
|
||||||
|
return p * p * p - p * std::sin (p * MathConstants<T>::pi);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after overshooting cubic y = 1-((1-x)^3-(1-x)*sin((1-x)*pi))
|
||||||
|
template<class T>
|
||||||
|
T easeBackOut (T p)
|
||||||
|
{
|
||||||
|
T f = (1 - p);
|
||||||
|
return 1 - (f * f * f - f * std::sin (f * MathConstants<T>::pi));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modeled after the piecewise overshooting cubic function:
|
||||||
|
// y = (1/2)*((2x)^3-(2x)*sin(2*x*pi)) ; [0, 0.5)
|
||||||
|
// y = (1/2)*(1-((1-x)^3-(1-x)*sin((1-x)*pi))+1) ; [0.5, 1]
|
||||||
|
template<class T>
|
||||||
|
T easeBackInOut (T p)
|
||||||
|
{
|
||||||
|
if (p < 0.5)
|
||||||
|
{
|
||||||
|
T f = 2 * p;
|
||||||
|
return 0.5 * (f * f * f - f * std::sin (f * MathConstants<T>::pi));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
T f = (1 - (2*p - 1));
|
||||||
|
return 0.5 * (1 - (f * f * f - f * std::sin (f * MathConstants<T>::pi))) + 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
T easeBounceIn (T p)
|
||||||
|
{
|
||||||
|
return 1 - easeBounceOut (1 - p);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
T easeBounceOut (T p)
|
||||||
|
{
|
||||||
|
if (p < 4/11.0)
|
||||||
|
return (121 * p * p) / 16.0;
|
||||||
|
else if (p < 8/11.0)
|
||||||
|
return (363/40.0 * p * p) - (99/10.0 * p) + 17/5.0;
|
||||||
|
else if (p < 9/10.0)
|
||||||
|
return (4356/361.0 * p * p) - (35442/1805.0 * p) + 16061/1805.0;
|
||||||
|
else
|
||||||
|
return (54/5.0 * p * p) - (513/25.0 * p) + 268/25.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
T easeBounceInOut (T p)
|
||||||
|
{
|
||||||
|
if (p < 0.5)
|
||||||
|
return 0.5 * easeBounceEaseIn (p * 2);
|
||||||
|
else
|
||||||
|
return 0.5 * easeBounceEaseOut (p * 2 - 1) + 0.5;
|
||||||
|
}
|
354
Modules/gin/utilities/elevatedfilecopy.cpp
Executable file
354
Modules/gin/utilities/elevatedfilecopy.cpp
Executable file
|
@ -0,0 +1,354 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2019 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#if JUCE_MAC
|
||||||
|
|
||||||
|
ElevatedFileCopy::Result runWithPermissions (String cmd, StringArray params)
|
||||||
|
{
|
||||||
|
OSStatus err = noErr;
|
||||||
|
auto path = cmd.toRawUTF8();
|
||||||
|
|
||||||
|
Array<const char*> rawParams;
|
||||||
|
for (auto& s : params)
|
||||||
|
rawParams.add (s.toRawUTF8());
|
||||||
|
rawParams.add (nullptr);
|
||||||
|
|
||||||
|
AuthorizationRef authorizationRef;
|
||||||
|
AuthorizationItem item = { kAuthorizationRightExecute, strlen (path), &path, 0 };
|
||||||
|
AuthorizationRights rights = { 1, &item };
|
||||||
|
AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights;
|
||||||
|
|
||||||
|
err = AuthorizationCreate (nullptr, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef);
|
||||||
|
if (err != errAuthorizationSuccess)
|
||||||
|
return ElevatedFileCopy::failed;
|
||||||
|
|
||||||
|
err = AuthorizationCopyRights (authorizationRef, &rights, kAuthorizationEmptyEnvironment, flags, nullptr);
|
||||||
|
if (err == errAuthorizationCanceled)
|
||||||
|
return ElevatedFileCopy::cancelled;
|
||||||
|
|
||||||
|
if (err != errAuthorizationSuccess)
|
||||||
|
return ElevatedFileCopy::nopermissions;
|
||||||
|
|
||||||
|
FILE* outputFile = nullptr;
|
||||||
|
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||||
|
err = AuthorizationExecuteWithPrivileges (authorizationRef, path, kAuthorizationFlagDefaults, (char* const*)rawParams.getRawDataPointer(), &outputFile);
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
|
||||||
|
if (err == noErr)
|
||||||
|
{
|
||||||
|
auto processIdentifier = fcntl (fileno (outputFile), F_GETOWN, 0);
|
||||||
|
|
||||||
|
AuthorizationFree (authorizationRef, kAuthorizationFlagDefaults);
|
||||||
|
|
||||||
|
int status;
|
||||||
|
pid_t pid = 0;
|
||||||
|
|
||||||
|
while ((pid = waitpid (processIdentifier, &status, WNOHANG)) == 0)
|
||||||
|
Thread::sleep (10);
|
||||||
|
|
||||||
|
fclose (outputFile);
|
||||||
|
|
||||||
|
auto terminationStatus = WEXITSTATUS (status);
|
||||||
|
if (terminationStatus == 0)
|
||||||
|
return ElevatedFileCopy::success;
|
||||||
|
|
||||||
|
return ElevatedFileCopy::failed;
|
||||||
|
}
|
||||||
|
return ElevatedFileCopy::nopermissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String escape (const String& in)
|
||||||
|
{
|
||||||
|
return in.replace (" ", "\\ ");
|
||||||
|
}
|
||||||
|
|
||||||
|
ElevatedFileCopy::Result ElevatedFileCopy::runScriptWithAdminAccess (File script, bool launchSelf)
|
||||||
|
{
|
||||||
|
ignoreUnused (launchSelf);
|
||||||
|
runWithPermissions ("/bin/sh", { script.getFullPathName() });
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
File ElevatedFileCopy::createScript (const Array<File>& toDelete,
|
||||||
|
const Array<File>& dirsThatNeedAdminAccess,
|
||||||
|
const Array<ElevatedFileCopy::FileItem>& filesThatNeedAdminAccess)
|
||||||
|
{
|
||||||
|
auto script = File::getSpecialLocation (File::tempDirectory).getNonexistentChildFile ("copy", ".sh", false);
|
||||||
|
|
||||||
|
String scriptText;
|
||||||
|
|
||||||
|
scriptText += "#!/bin/sh\n";
|
||||||
|
|
||||||
|
Array<File> dirs;
|
||||||
|
|
||||||
|
for (auto f : toDelete)
|
||||||
|
scriptText += "rm -Rf " + escape (f.getFullPathName()) + "\n";
|
||||||
|
|
||||||
|
for (auto f : dirsThatNeedAdminAccess)
|
||||||
|
dirs.add (f);
|
||||||
|
|
||||||
|
for (auto f : filesThatNeedAdminAccess)
|
||||||
|
if (! f.dst.getParentDirectory().isDirectory())
|
||||||
|
dirs.addIfNotAlreadyThere (f.dst.getParentDirectory());
|
||||||
|
|
||||||
|
for (auto d : dirs)
|
||||||
|
scriptText += "mkdir -p " + escape (d.getFullPathName()) + "\n";
|
||||||
|
|
||||||
|
for (auto d : dirsThatNeedAdminAccess)
|
||||||
|
scriptText += "chmod 777 " + escape (d.getFullPathName()) + "\n";
|
||||||
|
|
||||||
|
scriptText += "\n";
|
||||||
|
|
||||||
|
for (auto f : filesThatNeedAdminAccess)
|
||||||
|
scriptText += "cp -p " + escape (f.src.getFullPathName()) + " " + escape (f.dst.getFullPathName()) + " || exit 1\n";
|
||||||
|
|
||||||
|
script.replaceWithText (scriptText, false, false, "\n");
|
||||||
|
|
||||||
|
return script;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if JUCE_WINDOWS
|
||||||
|
static std::wstring toWideString (const std::string& s)
|
||||||
|
{
|
||||||
|
int len = MultiByteToWideChar (CP_UTF8, 0, s.c_str(), (int)s.length() + 1, 0, 0);
|
||||||
|
|
||||||
|
wchar_t* buffer = new wchar_t[len];
|
||||||
|
MultiByteToWideChar (CP_UTF8, 0, s.c_str(), (int)s.length() + 1, buffer, len);
|
||||||
|
|
||||||
|
std::wstring res (buffer);
|
||||||
|
delete[] buffer;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
ElevatedFileCopy::Result ElevatedFileCopy::runScriptWithAdminAccess (File script, bool launchSelf)
|
||||||
|
{
|
||||||
|
String app;
|
||||||
|
String params;
|
||||||
|
|
||||||
|
if (launchSelf)
|
||||||
|
{
|
||||||
|
app = File::getSpecialLocation (File::currentExecutableFile).getFullPathName();
|
||||||
|
params = "--elevatedfilecopy \"" + script.getFullPathName() + "\"";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app = "cmd.exe";
|
||||||
|
params = "/c \"" + script.getFullPathName() + "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto wideParams = toWideString (params.toRawUTF8());
|
||||||
|
auto wideApp = toWideString (app.toRawUTF8());
|
||||||
|
|
||||||
|
SHELLEXECUTEINFOW info;
|
||||||
|
memset (&info, 0, sizeof (info));
|
||||||
|
info.cbSize = sizeof (info);
|
||||||
|
info.fMask = SEE_MASK_NOCLOSEPROCESS;
|
||||||
|
info.lpVerb = L"runas";
|
||||||
|
info.lpFile = wideApp.c_str();
|
||||||
|
info.lpParameters = wideParams.c_str();
|
||||||
|
info.nShow = SW_HIDE;
|
||||||
|
|
||||||
|
if (ShellExecuteExW (&info))
|
||||||
|
{
|
||||||
|
WaitForSingleObject (info.hProcess, INFINITE);
|
||||||
|
|
||||||
|
DWORD exitCode = 0;
|
||||||
|
GetExitCodeProcess (info.hProcess, &exitCode);
|
||||||
|
CloseHandle (info.hProcess);
|
||||||
|
|
||||||
|
return exitCode == 0 ? success : failed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto err = GetLastError();
|
||||||
|
if (err == ERROR_CANCELLED)
|
||||||
|
return cancelled;
|
||||||
|
|
||||||
|
return nopermissions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File ElevatedFileCopy::createScript (const Array<File>& toDelete,
|
||||||
|
const Array<File>& dirsThatNeedAdminAccess,
|
||||||
|
const Array<ElevatedFileCopy::FileItem>& filesThatNeedAdminAccess)
|
||||||
|
{
|
||||||
|
auto script = File::getSpecialLocation (File::tempDirectory).getNonexistentChildFile ("copy", ".bat", false);
|
||||||
|
|
||||||
|
String scriptText;
|
||||||
|
|
||||||
|
Array<File> dirs;
|
||||||
|
|
||||||
|
for (auto f : toDelete)
|
||||||
|
{
|
||||||
|
if (f.isDirectory())
|
||||||
|
scriptText += "rmdir /s /q " + f.getFullPathName().quoted() + "\r\n";
|
||||||
|
else if (f.existsAsFile())
|
||||||
|
scriptText += "del /f " + f.getFullPathName().quoted() + "\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto f : dirsThatNeedAdminAccess)
|
||||||
|
dirs.add (f);
|
||||||
|
|
||||||
|
for (auto f : filesThatNeedAdminAccess)
|
||||||
|
if (! f.dst.getParentDirectory().isDirectory())
|
||||||
|
dirs.addIfNotAlreadyThere (f.dst.getParentDirectory());
|
||||||
|
|
||||||
|
for (auto d : dirs)
|
||||||
|
scriptText += "if not exist \"" + d.getFullPathName() + "\" mkdir " + d.getFullPathName().quoted() + "\r\n";
|
||||||
|
|
||||||
|
scriptText += "\r\n";
|
||||||
|
|
||||||
|
for (auto f : filesThatNeedAdminAccess)
|
||||||
|
{
|
||||||
|
scriptText += "copy " + f.src.getFullPathName().quoted() + " " + f.dst.getFullPathName().quoted() + "\r\n";
|
||||||
|
scriptText += "if %errorlevel% neq 0 goto :error\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptText += "exit /b 0\r\n";
|
||||||
|
scriptText += ":error\r\n";
|
||||||
|
scriptText += "exit /b 1\r\n";
|
||||||
|
|
||||||
|
script.replaceWithText (scriptText);
|
||||||
|
|
||||||
|
return script;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined JUCE_MAC || defined JUCE_WINDOWS
|
||||||
|
|
||||||
|
void ElevatedFileCopy::createDir (const File& dir)
|
||||||
|
{
|
||||||
|
dirsToCreate.add (dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElevatedFileCopy::copyFile (const File& src, const File& dst)
|
||||||
|
{
|
||||||
|
filesToCopy.add ({ src, dst });
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElevatedFileCopy::deleteFile (const File& f)
|
||||||
|
{
|
||||||
|
filesToDelete.add (f);
|
||||||
|
}
|
||||||
|
|
||||||
|
ElevatedFileCopy::Result ElevatedFileCopy::execute (bool launchSelf)
|
||||||
|
{
|
||||||
|
Array<File> filesToDeleteThatNeedAdminAccess;
|
||||||
|
Array<File> dirsThatNeedAdminAccess;
|
||||||
|
Array<FileItem> filesThatNeedAdminAccess;
|
||||||
|
|
||||||
|
for (auto f : filesToDelete)
|
||||||
|
{
|
||||||
|
if (f.existsAsFile())
|
||||||
|
{
|
||||||
|
if (! f.deleteFile())
|
||||||
|
filesToDeleteThatNeedAdminAccess.add (f);
|
||||||
|
}
|
||||||
|
else if (f.isDirectory())
|
||||||
|
{
|
||||||
|
if (! f.deleteRecursively())
|
||||||
|
filesToDeleteThatNeedAdminAccess.add (f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto f : dirsToCreate)
|
||||||
|
{
|
||||||
|
if (! f.isDirectory())
|
||||||
|
f.createDirectory();
|
||||||
|
|
||||||
|
bool ok = f.isDirectory();
|
||||||
|
|
||||||
|
if (! ok)
|
||||||
|
dirsThatNeedAdminAccess.add (f);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto f : filesToCopy)
|
||||||
|
{
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
auto dstDir = f.dst.getParentDirectory();
|
||||||
|
if (! dstDir.isDirectory())
|
||||||
|
dstDir.createDirectory();
|
||||||
|
|
||||||
|
if (dstDir.isDirectory())
|
||||||
|
ok = f.src.copyFileTo (f.dst);
|
||||||
|
|
||||||
|
if (! ok)
|
||||||
|
filesThatNeedAdminAccess.add (f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filesToDeleteThatNeedAdminAccess.size() > 0 || dirsThatNeedAdminAccess.size() > 0 || filesThatNeedAdminAccess.size() > 0)
|
||||||
|
{
|
||||||
|
File script = createScript (filesToDeleteThatNeedAdminAccess, dirsThatNeedAdminAccess, filesThatNeedAdminAccess);
|
||||||
|
auto res = runScriptWithAdminAccess (script, launchSelf);
|
||||||
|
script.deleteFile();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ElevatedFileCopy::processCommandLine (juce::String commandLine)
|
||||||
|
{
|
||||||
|
#if JUCE_WINDOWS
|
||||||
|
if (commandLine.contains ("--elevatedfilecopy"))
|
||||||
|
{
|
||||||
|
String script = commandLine.fromFirstOccurrenceOf ("--elevatedfilecopy \"", false, false).upToFirstOccurrenceOf ("\"", false, false);
|
||||||
|
|
||||||
|
if (File (script).existsAsFile())
|
||||||
|
{
|
||||||
|
String params = "/c \"" + script + "\"";
|
||||||
|
auto wideParams = toWideString (params.toRawUTF8());
|
||||||
|
|
||||||
|
SHELLEXECUTEINFOW info;
|
||||||
|
memset (&info, 0, sizeof (info));
|
||||||
|
info.cbSize = sizeof (info);
|
||||||
|
info.fMask = SEE_MASK_NOCLOSEPROCESS;
|
||||||
|
info.lpVerb = L"runas";
|
||||||
|
info.lpFile = L"cmd.exe";
|
||||||
|
info.lpParameters = wideParams.c_str();
|
||||||
|
info.nShow = SW_HIDE;
|
||||||
|
|
||||||
|
if (ShellExecuteExW (&info))
|
||||||
|
{
|
||||||
|
WaitForSingleObject (info.hProcess, INFINITE);
|
||||||
|
|
||||||
|
DWORD exitCode = 0;
|
||||||
|
GetExitCodeProcess (info.hProcess, &exitCode);
|
||||||
|
CloseHandle (info.hProcess);
|
||||||
|
|
||||||
|
if (auto inst = JUCEApplication::getInstance())
|
||||||
|
inst->setApplicationReturnValue (exitCode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (auto inst = JUCEApplication::getInstance())
|
||||||
|
inst->setApplicationReturnValue (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JUCEApplication::quit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ignoreUnused (commandLine);
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElevatedFileCopy::clear()
|
||||||
|
{
|
||||||
|
filesToCopy.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
82
Modules/gin/utilities/elevatedfilecopy.h
Executable file
82
Modules/gin/utilities/elevatedfilecopy.h
Executable file
|
@ -0,0 +1,82 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2019 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// TODO: no implementation for Linux yet
|
||||||
|
|
||||||
|
#if defined JUCE_MAC || defined JUCE_WINDOWS
|
||||||
|
|
||||||
|
/** Copies files, creating folders where required
|
||||||
|
requesting admin access only if required
|
||||||
|
*/
|
||||||
|
class ElevatedFileCopy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Result
|
||||||
|
{
|
||||||
|
success,
|
||||||
|
failed,
|
||||||
|
cancelled,
|
||||||
|
nopermissions
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Add a directory to create */
|
||||||
|
void createDir (const File& dir);
|
||||||
|
|
||||||
|
/** Add a file to copy. src and dst must be complete file names,
|
||||||
|
neither can be a directory */
|
||||||
|
void copyFile (const File& src, const File& dst);
|
||||||
|
|
||||||
|
/** Delete a file or directory */
|
||||||
|
void deleteFile (const File& f);
|
||||||
|
|
||||||
|
/** Perform the copy
|
||||||
|
|
||||||
|
If launchSelf is false, then on Windows cmd.exe will be executed
|
||||||
|
with admin permissions to copy the files. This looks odd to the user
|
||||||
|
since Windows Command Processor will ask for permissions to make changes
|
||||||
|
rather than your app. Pass true to make your app get launched with admin
|
||||||
|
permissions instead. In this case, you must call processCommandLine from
|
||||||
|
yours apps initialise instead, and if it returns true, return from initialise
|
||||||
|
asap and your app will quit as it's just a temporary process.
|
||||||
|
*/
|
||||||
|
Result execute (bool launchSelf = false);
|
||||||
|
|
||||||
|
/** Clear all files to be copied */
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
/** Call from JUCEApplication::initialise() and abort the initialise process
|
||||||
|
if returns true */
|
||||||
|
static bool processCommandLine (juce::String commandLine);
|
||||||
|
|
||||||
|
/** Run a script as root. See execute for the meaning of launch self */
|
||||||
|
static Result runScriptWithAdminAccess (File script, bool launchSelf);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct FileItem
|
||||||
|
{
|
||||||
|
FileItem (File s = {}, File d = {})
|
||||||
|
: src (s), dst (d)
|
||||||
|
{}
|
||||||
|
|
||||||
|
File src;
|
||||||
|
File dst;
|
||||||
|
};
|
||||||
|
|
||||||
|
File createScript (const Array<File>& toDelete,
|
||||||
|
const Array<File>& dirsThatNeedAdminAccess,
|
||||||
|
const Array<ElevatedFileCopy::FileItem>& filesThatNeedAdminAccess);
|
||||||
|
|
||||||
|
Array<FileItem> filesToCopy;
|
||||||
|
Array<File> dirsToCreate;
|
||||||
|
Array<File> filesToDelete;
|
||||||
|
|
||||||
|
JUCE_LEAK_DETECTOR (ElevatedFileCopy)
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
233
Modules/gin/utilities/equationparser.cpp
Executable file
233
Modules/gin/utilities/equationparser.cpp
Executable file
|
@ -0,0 +1,233 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#if __clang__
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wswitch-enum"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "../3rdparty/muParser/muParser.h"
|
||||||
|
|
||||||
|
#if __clang__
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace gin {
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
class EquationParser::Callback0 : public EquationParser::Callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Callback0 (std::function <double(int)> f) : fun (f) {}
|
||||||
|
std::function <double(int)> fun;
|
||||||
|
};
|
||||||
|
class EquationParser::Callback1 : public EquationParser::Callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Callback1 (std::function <double(int, double)> f) : fun (f) {}
|
||||||
|
std::function <double(int, double)> fun;
|
||||||
|
};
|
||||||
|
class EquationParser::Callback2 : public EquationParser::Callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Callback2 (std::function <double(int, double, double)> f) : fun (f) {}
|
||||||
|
std::function <double(int, double, double)> fun;
|
||||||
|
};
|
||||||
|
class EquationParser::Callback3 : public EquationParser::Callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Callback3 (std::function <double(int, double, double, double)> f) : fun (f) {}
|
||||||
|
std::function <double(int, double, double, double)> fun;
|
||||||
|
};
|
||||||
|
class EquationParser::Callback4 : public EquationParser::Callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Callback4 (std::function <double(int, double, double, double, double)> f) : fun (f) {}
|
||||||
|
std::function <double(int, double, double, double, double)> fun;
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
double modFunc(mu::SParam, double a, double b)
|
||||||
|
{
|
||||||
|
return std::fmod (a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
class EquationParser::EquationParserImpl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
mu::Parser parser;
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
EquationParser::EquationParser()
|
||||||
|
{
|
||||||
|
impl = std::make_unique<EquationParserImpl>();
|
||||||
|
}
|
||||||
|
|
||||||
|
EquationParser::EquationParser (juce::String equation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
impl = std::make_unique<EquationParserImpl>();
|
||||||
|
impl->parser.SetExpr (equation.toRawUTF8());
|
||||||
|
|
||||||
|
impl->parser.DefineOprt ("%", modFunc, mu::prMUL_DIV, mu::oaLEFT, false);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EquationParser::~EquationParser()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void EquationParser::setEquation (juce::String equation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
impl->parser.SetExpr (equation.toRawUTF8());
|
||||||
|
}
|
||||||
|
catch (mu::Parser::exception_type& e)
|
||||||
|
{
|
||||||
|
errorMessage = String (e.GetMsg());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EquationParser::addVariable (juce::String name, double* value)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
impl->parser.DefineVar (name.toRawUTF8(), value);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EquationParser::addConstant (juce::String name, double value)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
impl->parser.DefineConst (name.toRawUTF8(), value);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EquationParser::addFunction (juce::String name, std::function<double (int id)> fun)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto cb = new Callback0 (fun);
|
||||||
|
impl->parser.DefineFun (name.toRawUTF8(), [] (mu::SParam s)
|
||||||
|
{
|
||||||
|
auto c = (Callback0*)s.param;
|
||||||
|
return c->fun (s.id);
|
||||||
|
}, cb, false);
|
||||||
|
callbacks.add (cb);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EquationParser::addFunction (juce::String name, std::function<double (int id, double)> fun)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto cb = new Callback1 (fun);
|
||||||
|
impl->parser.DefineFun (name.toRawUTF8(), [] (mu::SParam s, double p1)
|
||||||
|
{
|
||||||
|
auto c = (Callback1*)s.param;
|
||||||
|
return c->fun (s.id, p1);
|
||||||
|
}, cb, false);
|
||||||
|
callbacks.add (cb);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EquationParser::addFunction (juce::String name, std::function<double (int id, double, double)> fun)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto cb = new Callback2 (fun);
|
||||||
|
impl->parser.DefineFun (name.toRawUTF8(), [] (mu::SParam s, double p1, double p2)
|
||||||
|
{
|
||||||
|
auto c = (Callback2*)s.param;
|
||||||
|
return c->fun (s.id, p1, p2);
|
||||||
|
}, cb, false);
|
||||||
|
callbacks.add (cb);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EquationParser::addFunction (juce::String name, std::function<double (int id, double, double, double)> fun)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto cb = new Callback3 (fun);
|
||||||
|
impl->parser.DefineFun (name.toRawUTF8(), [] (mu::SParam s, double p1, double p2, double p3)
|
||||||
|
{
|
||||||
|
auto c = (Callback3*)s.param;
|
||||||
|
return c->fun (s.id, p1, p2, p3);
|
||||||
|
}, cb, false);
|
||||||
|
callbacks.add (cb);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EquationParser::addFunction (juce::String name, std::function<double (int id, double, double, double, double)> fun)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto cb = new Callback4 (fun);
|
||||||
|
impl->parser.DefineFun (name.toRawUTF8(), [] (mu::SParam s, double p1, double p2, double p3, double p4)
|
||||||
|
{
|
||||||
|
auto c = (Callback4*)s.param;
|
||||||
|
return c->fun (s.id, p1, p2, p3, p4);
|
||||||
|
}, cb, false);
|
||||||
|
callbacks.add (cb);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double EquationParser::evaluate()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return impl->parser.Eval();
|
||||||
|
}
|
||||||
|
catch (mu::Parser::exception_type& e)
|
||||||
|
{
|
||||||
|
errorMessage = String (e.GetMsg());
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EquationParser::hasError()
|
||||||
|
{
|
||||||
|
return errorMessage.isNotEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
juce::String EquationParser::getError()
|
||||||
|
{
|
||||||
|
return errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
52
Modules/gin/utilities/equationparser.h
Executable file
52
Modules/gin/utilities/equationparser.h
Executable file
|
@ -0,0 +1,52 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class EquationParser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EquationParser();
|
||||||
|
EquationParser (juce::String equation);
|
||||||
|
~EquationParser();
|
||||||
|
|
||||||
|
void setEquation (juce::String equation);
|
||||||
|
void addVariable (juce::String name, double* value);
|
||||||
|
|
||||||
|
void addConstant (juce::String name, double value);
|
||||||
|
|
||||||
|
void addFunction (juce::String name, std::function<double (int id)> fun);
|
||||||
|
void addFunction (juce::String name, std::function<double (int id, double)> fun);
|
||||||
|
void addFunction (juce::String name, std::function<double (int id, double, double)> fun);
|
||||||
|
void addFunction (juce::String name, std::function<double (int id, double, double, double)> fun);
|
||||||
|
void addFunction (juce::String name, std::function<double (int id, double, double, double, double)> fun);
|
||||||
|
|
||||||
|
double evaluate();
|
||||||
|
|
||||||
|
bool hasError();
|
||||||
|
juce::String getError();
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~Callback() = default;
|
||||||
|
};
|
||||||
|
class Callback0;
|
||||||
|
class Callback1;
|
||||||
|
class Callback2;
|
||||||
|
class Callback3;
|
||||||
|
class Callback4;
|
||||||
|
|
||||||
|
OwnedArray<Callback> callbacks;
|
||||||
|
|
||||||
|
class EquationParserImpl;
|
||||||
|
std::unique_ptr<EquationParserImpl> impl;
|
||||||
|
juce::String errorMessage;
|
||||||
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EquationParser)
|
||||||
|
};
|
329
Modules/gin/utilities/filesystemwatcher.cpp
Executable file
329
Modules/gin/utilities/filesystemwatcher.cpp
Executable file
|
@ -0,0 +1,329 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
#if JUCE_MAC
|
||||||
|
class FileSystemWatcher::Impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Impl (FileSystemWatcher& o, File f) : owner (o), folder (f)
|
||||||
|
{
|
||||||
|
NSString* newPath = [NSString stringWithUTF8String:folder.getFullPathName().toRawUTF8()];
|
||||||
|
|
||||||
|
paths = [[NSArray arrayWithObject:newPath] retain];
|
||||||
|
context.version = 0L;
|
||||||
|
context.info = this;
|
||||||
|
context.retain = nil;
|
||||||
|
context.release = nil;
|
||||||
|
context.copyDescription = nil;
|
||||||
|
|
||||||
|
stream = FSEventStreamCreate (kCFAllocatorDefault, callback, &context, (CFArrayRef)paths, kFSEventStreamEventIdSinceNow, 0.05,
|
||||||
|
kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents);
|
||||||
|
if (stream)
|
||||||
|
{
|
||||||
|
FSEventStreamScheduleWithRunLoop (stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||||
|
FSEventStreamStart (stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
~Impl()
|
||||||
|
{
|
||||||
|
if (stream)
|
||||||
|
{
|
||||||
|
FSEventStreamStop (stream);
|
||||||
|
FSEventStreamUnscheduleFromRunLoop (stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||||
|
FSEventStreamInvalidate (stream);
|
||||||
|
FSEventStreamRelease (stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void callback (ConstFSEventStreamRef streamRef, void* clientCallBackInfo, size_t numEvents, void* eventPaths,
|
||||||
|
const FSEventStreamEventFlags* eventFlags, const FSEventStreamEventId* eventIds)
|
||||||
|
{
|
||||||
|
ignoreUnused (streamRef, numEvents, eventIds, eventPaths, eventFlags);
|
||||||
|
|
||||||
|
Impl* impl = (Impl*)clientCallBackInfo;
|
||||||
|
impl->owner.folderChanged (impl->folder);
|
||||||
|
|
||||||
|
char** files = (char**)eventPaths;
|
||||||
|
|
||||||
|
for (int i = 0; i < int (numEvents); i++)
|
||||||
|
{
|
||||||
|
char* file = files[i];
|
||||||
|
FSEventStreamEventFlags evt = eventFlags[i];
|
||||||
|
|
||||||
|
File path = String::fromUTF8 (file);
|
||||||
|
if (evt & kFSEventStreamEventFlagItemModified)
|
||||||
|
impl->owner.fileChanged (path, FileSystemEvent::fileUpdated);
|
||||||
|
else if (evt & kFSEventStreamEventFlagItemRemoved)
|
||||||
|
impl->owner.fileChanged (path, FileSystemEvent::fileDeleted);
|
||||||
|
else if (evt & kFSEventStreamEventFlagItemRenamed)
|
||||||
|
impl->owner.fileChanged (path, path.exists() ? FileSystemEvent::fileRenamedNewName : FileSystemEvent::fileRenamedOldName);
|
||||||
|
else if (evt & kFSEventStreamEventFlagItemCreated)
|
||||||
|
impl->owner.fileChanged (path, FileSystemEvent::fileCreated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSystemWatcher& owner;
|
||||||
|
const File folder;
|
||||||
|
|
||||||
|
NSArray* paths;
|
||||||
|
FSEventStreamRef stream;
|
||||||
|
struct FSEventStreamContext context;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
#ifdef JUCE_LINUX
|
||||||
|
#define BUF_LEN (10 * (sizeof(struct inotify_event) + NAME_MAX + 1))
|
||||||
|
|
||||||
|
class FileSystemWatcher::Impl : public Thread,
|
||||||
|
private AsyncUpdater
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Impl (FileSystemWatcher& o, File f)
|
||||||
|
: Thread ("FileSystemWatcher::Impl"), owner (o), folder (f)
|
||||||
|
{
|
||||||
|
fd = inotify_init();
|
||||||
|
|
||||||
|
wd = inotify_add_watch (fd,
|
||||||
|
folder.getFullPathName().toRawUTF8(),
|
||||||
|
IN_ATTRIB | IN_CREATE | IN_DELETE | IN_DELETE_SELF |
|
||||||
|
IN_MODIFY | IN_MOVE_SELF | IN_MOVED_TO | IN_MOVED_FROM);
|
||||||
|
|
||||||
|
startThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
~Impl()
|
||||||
|
{
|
||||||
|
signalThreadShouldExit();
|
||||||
|
inotify_rm_watch (fd, wd);
|
||||||
|
close (fd);
|
||||||
|
|
||||||
|
waitForThreadToExit (1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
char buf[BUF_LEN];
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int numRead = read (fd, buf, BUF_LEN);
|
||||||
|
|
||||||
|
if (numRead <= 0 || threadShouldExit())
|
||||||
|
break;
|
||||||
|
|
||||||
|
triggerAsyncUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleAsyncUpdate() override
|
||||||
|
{
|
||||||
|
owner.folderChanged (folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSystemWatcher& owner;
|
||||||
|
File folder;
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
int wd;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
#ifdef JUCE_WINDOWS
|
||||||
|
class FileSystemWatcher::Impl : private AsyncUpdater,
|
||||||
|
private Thread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Event
|
||||||
|
{
|
||||||
|
File file;
|
||||||
|
FileSystemEvent fsEvent;
|
||||||
|
|
||||||
|
bool operator== (const Event& other) const
|
||||||
|
{
|
||||||
|
return file == other.file && fsEvent == other.fsEvent;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Impl (FileSystemWatcher& o, File f)
|
||||||
|
: Thread ("FileSystemWatcher::Impl"), owner (o), folder (f)
|
||||||
|
{
|
||||||
|
WCHAR path[_MAX_PATH] = {0};
|
||||||
|
wcsncpy (path, folder.getFullPathName().toWideCharPointer(), _MAX_PATH - 1);
|
||||||
|
|
||||||
|
folderHandle = CreateFileW (path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||||
|
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||||
|
|
||||||
|
|
||||||
|
if (folderHandle != INVALID_HANDLE_VALUE)
|
||||||
|
startThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
~Impl()
|
||||||
|
{
|
||||||
|
if (isThreadRunning())
|
||||||
|
{
|
||||||
|
signalThreadShouldExit();
|
||||||
|
|
||||||
|
CancelIoEx (folderHandle, nullptr);
|
||||||
|
|
||||||
|
stopThread (1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle (folderHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
const int heapSize = 16 * 1024;
|
||||||
|
uint8 buffer[heapSize];
|
||||||
|
|
||||||
|
DWORD bytesOut = 0;
|
||||||
|
|
||||||
|
while (! threadShouldExit())
|
||||||
|
{
|
||||||
|
memset (buffer, 0, heapSize);
|
||||||
|
BOOL success = ReadDirectoryChangesW (folderHandle, buffer, heapSize, true,
|
||||||
|
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_CREATION,
|
||||||
|
&bytesOut, nullptr, nullptr);
|
||||||
|
|
||||||
|
if (success && bytesOut > 0)
|
||||||
|
{
|
||||||
|
ScopedLock sl (lock);
|
||||||
|
|
||||||
|
uint8* rawData = buffer;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
FILE_NOTIFY_INFORMATION* fni = (FILE_NOTIFY_INFORMATION*)rawData;
|
||||||
|
|
||||||
|
Event e;
|
||||||
|
e.file = folder.getChildFile (String (fni->FileName, fni->FileNameLength / sizeof(wchar_t)));
|
||||||
|
|
||||||
|
switch (fni->Action)
|
||||||
|
{
|
||||||
|
case FILE_ACTION_ADDED:
|
||||||
|
e.fsEvent = fileCreated;
|
||||||
|
break;
|
||||||
|
case FILE_ACTION_RENAMED_NEW_NAME:
|
||||||
|
e.fsEvent = fileRenamedNewName;
|
||||||
|
break;
|
||||||
|
case FILE_ACTION_MODIFIED:
|
||||||
|
e.fsEvent = fileUpdated;
|
||||||
|
break;
|
||||||
|
case FILE_ACTION_REMOVED:
|
||||||
|
e.fsEvent = fileDeleted;
|
||||||
|
break;
|
||||||
|
case FILE_ACTION_RENAMED_OLD_NAME:
|
||||||
|
e.fsEvent = fileRenamedOldName;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool duplicateEvent = false;
|
||||||
|
for (auto existing : events)
|
||||||
|
{
|
||||||
|
if (e == existing)
|
||||||
|
{
|
||||||
|
duplicateEvent = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! duplicateEvent)
|
||||||
|
events.add (e);
|
||||||
|
|
||||||
|
if (fni->NextEntryOffset > 0)
|
||||||
|
rawData += fni->NextEntryOffset;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (events.size() > 0)
|
||||||
|
triggerAsyncUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleAsyncUpdate() override
|
||||||
|
{
|
||||||
|
ScopedLock sl (lock);
|
||||||
|
|
||||||
|
owner.folderChanged (folder);
|
||||||
|
|
||||||
|
for (auto e : events)
|
||||||
|
owner.fileChanged (e.file, e.fsEvent);
|
||||||
|
|
||||||
|
events.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSystemWatcher& owner;
|
||||||
|
const File folder;
|
||||||
|
|
||||||
|
CriticalSection lock;
|
||||||
|
Array<Event> events;
|
||||||
|
|
||||||
|
HANDLE folderHandle;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined JUCE_MAC || defined JUCE_WINDOWS || defined JUCE_LINUX
|
||||||
|
FileSystemWatcher::FileSystemWatcher()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSystemWatcher::~FileSystemWatcher()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::addFolder (const File& folder)
|
||||||
|
{
|
||||||
|
// You can only listen to folders that exist
|
||||||
|
jassert (folder.isDirectory());
|
||||||
|
|
||||||
|
watched.add (new Impl (*this, folder));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::removeFolder (const File& folder)
|
||||||
|
{
|
||||||
|
for (int i = watched.size(); --i >= 0;)
|
||||||
|
{
|
||||||
|
if (watched[i]->folder == folder)
|
||||||
|
{
|
||||||
|
watched.remove (i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::removeAllFolders()
|
||||||
|
{
|
||||||
|
watched.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::addListener (Listener* newListener)
|
||||||
|
{
|
||||||
|
listeners.add (newListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::removeListener (Listener* listener)
|
||||||
|
{
|
||||||
|
listeners.remove (listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::folderChanged (const File& folder)
|
||||||
|
{
|
||||||
|
listeners.call (&FileSystemWatcher::Listener::folderChanged, folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::fileChanged (const File& file, FileSystemEvent fsEvent)
|
||||||
|
{
|
||||||
|
listeners.call (&FileSystemWatcher::Listener::fileChanged, file, fsEvent);
|
||||||
|
}
|
||||||
|
#endif
|
93
Modules/gin/utilities/filesystemwatcher.h
Executable file
93
Modules/gin/utilities/filesystemwatcher.h
Executable file
|
@ -0,0 +1,93 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined JUCE_MAC || defined JUCE_WINDOWS || defined JUCE_LINUX
|
||||||
|
|
||||||
|
/**
|
||||||
|
|
||||||
|
Watches a folder in the file system for changes.
|
||||||
|
|
||||||
|
Listener callbcks will be called every time a file is
|
||||||
|
created, modified, deleted or renamed in the watched
|
||||||
|
folder.
|
||||||
|
|
||||||
|
FileSystemWatcher will also recursively watch all subfolders on
|
||||||
|
macOS and windows and will not on Linux.
|
||||||
|
|
||||||
|
*/
|
||||||
|
class FileSystemWatcher
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//==============================================================================
|
||||||
|
FileSystemWatcher();
|
||||||
|
~FileSystemWatcher();
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/** Adds a folder to be watched */
|
||||||
|
void addFolder (const File& folder);
|
||||||
|
/** Removes a folder from being watched */
|
||||||
|
void removeFolder (const File& folder);
|
||||||
|
/** Removes all folders from being watched */
|
||||||
|
void removeAllFolders();
|
||||||
|
|
||||||
|
/** A set of events that can happen to a file.
|
||||||
|
When a file is renamed it will appear as the
|
||||||
|
original filename being deleted and the new
|
||||||
|
filename being created
|
||||||
|
*/
|
||||||
|
enum FileSystemEvent
|
||||||
|
{
|
||||||
|
fileCreated,
|
||||||
|
fileDeleted,
|
||||||
|
fileUpdated,
|
||||||
|
fileRenamedOldName,
|
||||||
|
fileRenamedNewName
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/** Receives callbacks from the FileSystemWatcher when a file changes */
|
||||||
|
class Listener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~Listener() = default;
|
||||||
|
|
||||||
|
/* Called when any file in the listened to folder changes with the name of
|
||||||
|
the folder that has changed. For example, use this for a file browser that
|
||||||
|
needs to refresh any time a file changes */
|
||||||
|
virtual void folderChanged (const File) {}
|
||||||
|
|
||||||
|
/* Called for each file that has changed and how it has changed. Use this callback
|
||||||
|
if you need to reload a file when it's contents change */
|
||||||
|
virtual void fileChanged (const File, FileSystemEvent) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Registers a listener to be told when things happen to the text.
|
||||||
|
@see removeListener
|
||||||
|
*/
|
||||||
|
void addListener (Listener* newListener);
|
||||||
|
|
||||||
|
/** Deregisters a listener.
|
||||||
|
@see addListener
|
||||||
|
*/
|
||||||
|
void removeListener (Listener* listener);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Impl;
|
||||||
|
|
||||||
|
void folderChanged (const File& folder);
|
||||||
|
void fileChanged (const File& file, FileSystemEvent fsEvent);
|
||||||
|
|
||||||
|
ListenerList<Listener> listeners;
|
||||||
|
|
||||||
|
OwnedArray<Impl> watched;
|
||||||
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileSystemWatcher)
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
43
Modules/gin/utilities/fileutilities.cpp
Executable file
43
Modules/gin/utilities/fileutilities.cpp
Executable file
|
@ -0,0 +1,43 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2019 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
bool overwriteWithText (const juce::File& f, const juce::String& text, bool asUnicode,
|
||||||
|
bool writeUnicodeHeaderBytes,
|
||||||
|
const char* lineEndings)
|
||||||
|
{
|
||||||
|
FileOutputStream out (f);
|
||||||
|
|
||||||
|
if (out.failedToOpen())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
out.setPosition (0);
|
||||||
|
bool ok = out.writeText (text, asUnicode, writeUnicodeHeaderBytes, lineEndings);
|
||||||
|
out.truncate();
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool overwriteWithData (const juce::File& f, const juce::MemoryBlock& data)
|
||||||
|
{
|
||||||
|
return overwriteWithData (f, data.getData(), data.getSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool overwriteWithData (const juce::File& f, const void* data, size_t size)
|
||||||
|
{
|
||||||
|
FileOutputStream out (f);
|
||||||
|
|
||||||
|
if (out.failedToOpen())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
out.setPosition (0);
|
||||||
|
bool ok = true;
|
||||||
|
if (size > 0)
|
||||||
|
ok = out.write (data, size);
|
||||||
|
out.truncate();
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
23
Modules/gin/utilities/fileutilities.h
Executable file
23
Modules/gin/utilities/fileutilities.h
Executable file
|
@ -0,0 +1,23 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2019 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/* Replaces a file with text in one operation without creating a temp file
|
||||||
|
*/
|
||||||
|
bool overwriteWithText (const juce::File& f, const juce::String& text,
|
||||||
|
bool asUnicode = false,
|
||||||
|
bool writeUnicodeHeaderBytes = false,
|
||||||
|
const char* lineEndings = nullptr);
|
||||||
|
|
||||||
|
/* Replaces a file with data in one operation without creating a temp file
|
||||||
|
*/
|
||||||
|
bool overwriteWithData (const juce::File& f, const juce::MemoryBlock& data);
|
||||||
|
|
||||||
|
bool overwriteWithData (const juce::File& f, const void* data, size_t size);
|
||||||
|
|
||||||
|
|
45
Modules/gin/utilities/integrator.cpp
Executable file
45
Modules/gin/utilities/integrator.cpp
Executable file
|
@ -0,0 +1,45 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
void Integrator::clear()
|
||||||
|
{
|
||||||
|
first = true;
|
||||||
|
curSum = 0;
|
||||||
|
oldX = 0;
|
||||||
|
oldY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double Integrator::getIntegral()
|
||||||
|
{
|
||||||
|
return curSum;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Integrator::addPoint (double x, double y)
|
||||||
|
{
|
||||||
|
if (first)
|
||||||
|
{
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double curY = (oldY + y) / 2.0;
|
||||||
|
curSum += curY * (x - oldX);
|
||||||
|
}
|
||||||
|
oldX = x;
|
||||||
|
oldY = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Integrator::addPoint (juce::Point<double> point)
|
||||||
|
{
|
||||||
|
addPoint (point.getX(), point.getY());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Integrator::addPoints (Array<juce::Point<double>> points)
|
||||||
|
{
|
||||||
|
for (auto point : points)
|
||||||
|
addPoint (point.getX(), point.getY());
|
||||||
|
}
|
31
Modules/gin/utilities/integrator.h
Executable file
31
Modules/gin/utilities/integrator.h
Executable file
|
@ -0,0 +1,31 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/** Calculates the integral of a function defined by a series of points. Points
|
||||||
|
must be added in increasing x order */
|
||||||
|
class Integrator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
double getIntegral();
|
||||||
|
|
||||||
|
void addPoint (double x, double y);
|
||||||
|
void addPoint (juce::Point<double> point);
|
||||||
|
void addPoints (Array<juce::Point<double>> points);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool first = true;
|
||||||
|
double oldX = 0;
|
||||||
|
double oldY = 0;
|
||||||
|
double curSum = 0;
|
||||||
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Integrator)
|
||||||
|
};
|
70
Modules/gin/utilities/lagrange.h
Executable file
70
Modules/gin/utilities/lagrange.h
Executable file
|
@ -0,0 +1,70 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2020 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/** Lagrange interpolation is a simple way to obtain a smooth curve from a set of
|
||||||
|
discrete points.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Lagrange
|
||||||
|
{
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
T interpolate (const Array<juce::Point<T>>& points, T x)
|
||||||
|
{
|
||||||
|
T res = 0;
|
||||||
|
|
||||||
|
const int num = points.size();
|
||||||
|
for (int i = 0; i < num; i++)
|
||||||
|
{
|
||||||
|
T term = points[i].y;
|
||||||
|
for (int j = 0; j < num; j++)
|
||||||
|
{
|
||||||
|
if (i != j)
|
||||||
|
{
|
||||||
|
auto d = points[i].x - points[j].x;
|
||||||
|
if (d != 0)
|
||||||
|
term = term * (x - points[j].x ) / (d);
|
||||||
|
else
|
||||||
|
term = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res += term;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
T interpolate (T xArr[], T yArr[], int num, T x)
|
||||||
|
{
|
||||||
|
T res = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < num; i++)
|
||||||
|
{
|
||||||
|
T term = yArr[i];
|
||||||
|
for (int j = 0; j < num; j++)
|
||||||
|
{
|
||||||
|
if (i != j)
|
||||||
|
{
|
||||||
|
auto d = xArr[i] - xArr[j];
|
||||||
|
if (d != 0)
|
||||||
|
term = term * (x - xArr[j] ) / d;
|
||||||
|
else
|
||||||
|
term = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res += term;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
266
Modules/gin/utilities/leastsquaresregression.cpp
Executable file
266
Modules/gin/utilities/leastsquaresregression.cpp
Executable file
|
@ -0,0 +1,266 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien, 2010 by 2010 by Alex Etchells
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
void LeastSquaresRegression::addPoint (double x, double y)
|
||||||
|
{
|
||||||
|
pointArray.add ({x, y});
|
||||||
|
}
|
||||||
|
|
||||||
|
void LeastSquaresRegression::addPoint (juce::Point<double> point)
|
||||||
|
{
|
||||||
|
pointArray.add (point);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LeastSquaresRegression::addPoints (Array<juce::Point<double>> points)
|
||||||
|
{
|
||||||
|
pointArray.addArray (points);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LeastSquaresRegression::clear()
|
||||||
|
{
|
||||||
|
pointArray.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LeastSquaresRegression::enoughPoints()
|
||||||
|
{
|
||||||
|
return pointArray.size() >= 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<double> LeastSquaresRegression::getTerms()
|
||||||
|
{
|
||||||
|
//notation sjk to mean the sum of x_i^j*y_i^k.
|
||||||
|
double s40 = getSx4(); //sum of x^4
|
||||||
|
double s30 = getSx3(); //sum of x^3
|
||||||
|
double s20 = getSx2(); //sum of x^2
|
||||||
|
double s10 = getSx(); //sum of x
|
||||||
|
double s00 = pointArray.size();
|
||||||
|
//sum of x^0 * y^0 ie 1 * number of entries
|
||||||
|
|
||||||
|
double s21 = getSx2y(); //sum of x^2*y
|
||||||
|
double s11 = getSxy(); //sum of x*y
|
||||||
|
double s01 = getSy(); //sum of y
|
||||||
|
|
||||||
|
double a = (s21*(s20 * s00 - s10 * s10) -
|
||||||
|
s11*(s30 * s00 - s10 * s20) +
|
||||||
|
s01*(s30 * s10 - s20 * s20))
|
||||||
|
/
|
||||||
|
(s40*(s20 * s00 - s10 * s10) -
|
||||||
|
s30*(s30 * s00 - s10 * s20) +
|
||||||
|
s20*(s30 * s10 - s20 * s20));
|
||||||
|
|
||||||
|
double b = (s40*(s11 * s00 - s01 * s10) -
|
||||||
|
s30*(s21 * s00 - s01 * s20) +
|
||||||
|
s20*(s21 * s10 - s11 * s20))
|
||||||
|
/
|
||||||
|
(s40 * (s20 * s00 - s10 * s10) -
|
||||||
|
s30 * (s30 * s00 - s10 * s20) +
|
||||||
|
s20 * (s30 * s10 - s20 * s20));
|
||||||
|
|
||||||
|
double c = (s40*(s20 * s01 - s10 * s11) -
|
||||||
|
s30*(s30 * s01 - s10 * s21) +
|
||||||
|
s20*(s30 * s11 - s20 * s21))
|
||||||
|
/
|
||||||
|
(s40 * (s20 * s00 - s10 * s10) -
|
||||||
|
s30 * (s30 * s00 - s10 * s20) +
|
||||||
|
s20 * (s30 * s10 - s20 * s20));
|
||||||
|
|
||||||
|
Array<double> terms;
|
||||||
|
terms.add (a);
|
||||||
|
terms.add (b);
|
||||||
|
terms.add (c);
|
||||||
|
return terms;
|
||||||
|
}
|
||||||
|
|
||||||
|
double LeastSquaresRegression::aTerm()
|
||||||
|
{
|
||||||
|
//notation sjk to mean the sum of x_i^j*y_i^k.
|
||||||
|
double s40 = getSx4(); //sum of x^4
|
||||||
|
double s30 = getSx3(); //sum of x^3
|
||||||
|
double s20 = getSx2(); //sum of x^2
|
||||||
|
double s10 = getSx(); //sum of x
|
||||||
|
double s00 = pointArray.size();
|
||||||
|
//sum of x^0 * y^0 ie 1 * number of entries
|
||||||
|
|
||||||
|
double s21 = getSx2y(); //sum of x^2*y
|
||||||
|
double s11 = getSxy(); //sum of x*y
|
||||||
|
double s01 = getSy(); //sum of y
|
||||||
|
|
||||||
|
//a = Da/D
|
||||||
|
return (s21*(s20 * s00 - s10 * s10) -
|
||||||
|
s11*(s30 * s00 - s10 * s20) +
|
||||||
|
s01*(s30 * s10 - s20 * s20))
|
||||||
|
/
|
||||||
|
(s40*(s20 * s00 - s10 * s10) -
|
||||||
|
s30*(s30 * s00 - s10 * s20) +
|
||||||
|
s20*(s30 * s10 - s20 * s20));
|
||||||
|
}
|
||||||
|
|
||||||
|
double LeastSquaresRegression::bTerm()
|
||||||
|
{
|
||||||
|
//notation sjk to mean the sum of x_i^j*y_i^k.
|
||||||
|
double s40 = getSx4(); //sum of x^4
|
||||||
|
double s30 = getSx3(); //sum of x^3
|
||||||
|
double s20 = getSx2(); //sum of x^2
|
||||||
|
double s10 = getSx(); //sum of x
|
||||||
|
double s00 = pointArray.size();
|
||||||
|
//sum of x^0 * y^0 ie 1 * number of entries
|
||||||
|
|
||||||
|
double s21 = getSx2y(); //sum of x^2*y
|
||||||
|
double s11 = getSxy(); //sum of x*y
|
||||||
|
double s01 = getSy(); //sum of y
|
||||||
|
|
||||||
|
//b = Db/D
|
||||||
|
return (s40*(s11 * s00 - s01 * s10) -
|
||||||
|
s30*(s21 * s00 - s01 * s20) +
|
||||||
|
s20*(s21 * s10 - s11 * s20))
|
||||||
|
/
|
||||||
|
(s40 * (s20 * s00 - s10 * s10) -
|
||||||
|
s30 * (s30 * s00 - s10 * s20) +
|
||||||
|
s20 * (s30 * s10 - s20 * s20));
|
||||||
|
}
|
||||||
|
|
||||||
|
double LeastSquaresRegression::cTerm()
|
||||||
|
{
|
||||||
|
//notation sjk to mean the sum of x_i^j*y_i^k.
|
||||||
|
double s40 = getSx4(); //sum of x^4
|
||||||
|
double s30 = getSx3(); //sum of x^3
|
||||||
|
double s20 = getSx2(); //sum of x^2
|
||||||
|
double s10 = getSx(); //sum of x
|
||||||
|
double s00 = pointArray.size();
|
||||||
|
//sum of x^0 * y^0 ie 1 * number of entries
|
||||||
|
|
||||||
|
double s21 = getSx2y(); //sum of x^2*y
|
||||||
|
double s11 = getSxy(); //sum of x*y
|
||||||
|
double s01 = getSy(); //sum of y
|
||||||
|
|
||||||
|
//c = Dc/D
|
||||||
|
return (s40*(s20 * s01 - s10 * s11) -
|
||||||
|
s30*(s30 * s01 - s10 * s21) +
|
||||||
|
s20*(s30 * s11 - s20 * s21))
|
||||||
|
/
|
||||||
|
(s40 * (s20 * s00 - s10 * s10) -
|
||||||
|
s30 * (s30 * s00 - s10 * s20) +
|
||||||
|
s20 * (s30 * s10 - s20 * s20));
|
||||||
|
}
|
||||||
|
|
||||||
|
double LeastSquaresRegression::rSquare() // get r-squared
|
||||||
|
{
|
||||||
|
// 1 - (residual sum of squares / total sum of squares)
|
||||||
|
return 1 - getSSerr() / getSStot();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*helper methods*/
|
||||||
|
double LeastSquaresRegression::getSx() // get sum of x
|
||||||
|
{
|
||||||
|
double Sx = 0;
|
||||||
|
for (auto it : pointArray)
|
||||||
|
{
|
||||||
|
Sx += it.getX();
|
||||||
|
}
|
||||||
|
return Sx;
|
||||||
|
}
|
||||||
|
|
||||||
|
double LeastSquaresRegression::getSy() // get sum of y
|
||||||
|
{
|
||||||
|
double Sy = 0;
|
||||||
|
for (auto it : pointArray)
|
||||||
|
{
|
||||||
|
Sy += it.getY();
|
||||||
|
}
|
||||||
|
return Sy;
|
||||||
|
}
|
||||||
|
|
||||||
|
double LeastSquaresRegression::getSx2() // get sum of x^2
|
||||||
|
{
|
||||||
|
double Sx2 = 0;
|
||||||
|
for (auto it : pointArray)
|
||||||
|
{
|
||||||
|
Sx2 += std::pow (it.getX(), 2); // sum of x^2
|
||||||
|
}
|
||||||
|
return Sx2;
|
||||||
|
}
|
||||||
|
|
||||||
|
double LeastSquaresRegression::getSx3() // get sum of x^3
|
||||||
|
{
|
||||||
|
double Sx3 = 0;
|
||||||
|
for (auto it : pointArray)
|
||||||
|
{
|
||||||
|
Sx3 += std::pow (it.getX(), 3); // sum of x^3
|
||||||
|
}
|
||||||
|
return Sx3;
|
||||||
|
}
|
||||||
|
|
||||||
|
double LeastSquaresRegression::getSx4() // get sum of x^4
|
||||||
|
{
|
||||||
|
double Sx4 = 0;
|
||||||
|
for (auto it : pointArray)
|
||||||
|
{
|
||||||
|
Sx4 += std::pow (it.getX(), 4); // sum of x^4
|
||||||
|
}
|
||||||
|
return Sx4;
|
||||||
|
}
|
||||||
|
|
||||||
|
double LeastSquaresRegression::getSxy() // get sum of x*y
|
||||||
|
{
|
||||||
|
double Sxy = 0;
|
||||||
|
for (auto it : pointArray)
|
||||||
|
{
|
||||||
|
Sxy += it.getX() * it.getY(); // sum of x*y
|
||||||
|
}
|
||||||
|
return Sxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
double LeastSquaresRegression::getSx2y() // get sum of x^2*y
|
||||||
|
{
|
||||||
|
double Sx2y = 0;
|
||||||
|
for (auto it : pointArray)
|
||||||
|
{
|
||||||
|
Sx2y += pow(it.getX(), 2) * it.getY(); // sum of x^2*y
|
||||||
|
}
|
||||||
|
return Sx2y;
|
||||||
|
}
|
||||||
|
|
||||||
|
double LeastSquaresRegression::getYMean() // mean value of y
|
||||||
|
{
|
||||||
|
double y_tot = 0;
|
||||||
|
for (auto it : pointArray)
|
||||||
|
{
|
||||||
|
y_tot += it.getY();
|
||||||
|
}
|
||||||
|
return y_tot / pointArray.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
double LeastSquaresRegression::getSStot() // total sum of squares
|
||||||
|
{
|
||||||
|
//the sum of the squares of the differences between
|
||||||
|
//the measured y values and the mean y value
|
||||||
|
double ss_tot = 0;
|
||||||
|
for (auto it : pointArray)
|
||||||
|
{
|
||||||
|
ss_tot += std::pow (it.getY() - getYMean(), 2);
|
||||||
|
}
|
||||||
|
return ss_tot;
|
||||||
|
}
|
||||||
|
|
||||||
|
double LeastSquaresRegression::getSSerr() // residual sum of squares
|
||||||
|
{
|
||||||
|
//the sum of the squares of te difference between
|
||||||
|
//the measured y values and the values of y predicted by the equation
|
||||||
|
double ss_err = 0;
|
||||||
|
for (auto it : pointArray)
|
||||||
|
{
|
||||||
|
ss_err += std::pow (it.getY() - getPredictedY (it.getX()), 2);
|
||||||
|
}
|
||||||
|
return ss_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
double LeastSquaresRegression::getPredictedY (double x)
|
||||||
|
{
|
||||||
|
//returns value of y predicted by the equation for a given value of x
|
||||||
|
return aTerm() * std::pow(x, 2) + bTerm() * x + cTerm();
|
||||||
|
}
|
53
Modules/gin/utilities/leastsquaresregression.h
Executable file
53
Modules/gin/utilities/leastsquaresregression.h
Executable file
|
@ -0,0 +1,53 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien, 2010 by Alex Etchells
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
See https://www.codeproject.com/Articles/63170/Least-Squares-Regression-for-Quadratic-Curve-Fitti
|
||||||
|
for original code
|
||||||
|
|
||||||
|
Derive the equation of a quadratic curve from a series of data points. That is to say, to
|
||||||
|
determine a, b, and c, where y = ax2 + bx + c. Having determined a, b, and c, I would also
|
||||||
|
need a value for R-squared (the coefficient of determination).
|
||||||
|
*/
|
||||||
|
class LeastSquaresRegression
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeastSquaresRegression() = default;
|
||||||
|
|
||||||
|
void addPoint (double x, double y);
|
||||||
|
void addPoint (juce::Point<double> point);
|
||||||
|
void addPoints (Array<juce::Point<double>> points);
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
bool enoughPoints();
|
||||||
|
Array<double> getTerms();
|
||||||
|
|
||||||
|
double aTerm();
|
||||||
|
double bTerm();
|
||||||
|
double cTerm();
|
||||||
|
double rSquare();
|
||||||
|
|
||||||
|
private:
|
||||||
|
double getSx();
|
||||||
|
double getSy();
|
||||||
|
double getSx2();
|
||||||
|
double getSx3();
|
||||||
|
double getSx4();
|
||||||
|
double getSxy();
|
||||||
|
double getSx2y();
|
||||||
|
double getYMean();
|
||||||
|
double getSStot();
|
||||||
|
double getSSerr();
|
||||||
|
double getPredictedY (double x);
|
||||||
|
|
||||||
|
Array<juce::Point<double>> pointArray;
|
||||||
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LeastSquaresRegression)
|
||||||
|
};
|
68
Modules/gin/utilities/linearregression.cpp
Executable file
68
Modules/gin/utilities/linearregression.cpp
Executable file
|
@ -0,0 +1,68 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien, 1998 David C. Swaim
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
LinearRegression::LinearRegression()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LinearRegression::LinearRegression (Array<juce::Point<double>> points)
|
||||||
|
{
|
||||||
|
for (auto p : points)
|
||||||
|
addPoint (p);
|
||||||
|
|
||||||
|
calculate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LinearRegression::clear()
|
||||||
|
{
|
||||||
|
n = 0;
|
||||||
|
sumX = 0;
|
||||||
|
sumY = 0;
|
||||||
|
sumXsquared = 0;
|
||||||
|
sumYsquared = 0;
|
||||||
|
sumXY = 0;
|
||||||
|
|
||||||
|
a = 0;
|
||||||
|
b = 0;
|
||||||
|
coefD = 0;
|
||||||
|
coefC = 0;
|
||||||
|
stdError = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LinearRegression::addPoint (juce::Point<double> pnt)
|
||||||
|
{
|
||||||
|
n++;
|
||||||
|
sumX += pnt.getX();
|
||||||
|
sumY += pnt.getY();
|
||||||
|
sumXsquared += pnt.getX() * pnt.getX();
|
||||||
|
sumYsquared += pnt.getY() * pnt.getY();
|
||||||
|
sumXY += pnt.getX() * pnt.getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LinearRegression::calculate()
|
||||||
|
{
|
||||||
|
if (haveData())
|
||||||
|
{
|
||||||
|
if (std::fabs (n * sumXsquared - sumX * sumX) > DBL_EPSILON)
|
||||||
|
{
|
||||||
|
b = (n * sumXY - sumY * sumX) / (n * sumXsquared - sumX * sumX);
|
||||||
|
a = (sumY - b * sumX) / n;
|
||||||
|
|
||||||
|
double sx = b * (sumXY - sumX * sumY / n);
|
||||||
|
double sy2 = sumYsquared - sumY * sumY / n;
|
||||||
|
double sy = sy2 - sx;
|
||||||
|
|
||||||
|
coefD = sx / sy2;
|
||||||
|
coefC = std::sqrt (coefD);
|
||||||
|
stdError = std::sqrt(sy / (n - 2));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
a = b = coefD = coefC = stdError = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
Modules/gin/utilities/linearregression.h
Executable file
49
Modules/gin/utilities/linearregression.h
Executable file
|
@ -0,0 +1,49 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien, 1998 David C. Swaim
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/** Calculates linear regression from a set of points */
|
||||||
|
class LinearRegression
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LinearRegression();
|
||||||
|
LinearRegression (Array<juce::Point<double>> points);
|
||||||
|
|
||||||
|
void addPoint (juce::Point<double> pnt);
|
||||||
|
void calculate();
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
bool haveData() const { return n > 2; }
|
||||||
|
int items() const { return n; }
|
||||||
|
|
||||||
|
double getA() const { return a; }
|
||||||
|
double getB() const { return b; }
|
||||||
|
|
||||||
|
double getCoefDeterm() const { return coefD; }
|
||||||
|
double getCoefCorrel() const { return coefC; }
|
||||||
|
double getStdErrorEst() const { return stdError; }
|
||||||
|
double estimateY (double x) const { return (a + b * x); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
int n = 0; // number of data points input so far
|
||||||
|
double sumX = 0;
|
||||||
|
double sumY = 0; // sums of x and y
|
||||||
|
double sumXsquared = 0; // sum of x squares
|
||||||
|
double sumYsquared = 0; // sum y squares
|
||||||
|
double sumXY = 0; // sum of x*y
|
||||||
|
|
||||||
|
double a = 0;
|
||||||
|
double b = 0; // coefficients of f(x) = a + b*x
|
||||||
|
double coefD = 0; // coefficient of determination
|
||||||
|
double coefC = 0; // coefficient of correlation
|
||||||
|
double stdError = 0; // standard error of estimate
|
||||||
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LinearRegression)
|
||||||
|
};
|
442
Modules/gin/utilities/messagepack.cpp
Executable file
442
Modules/gin/utilities/messagepack.cpp
Executable file
|
@ -0,0 +1,442 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if _MSC_VER
|
||||||
|
#pragma warning (push)
|
||||||
|
#pragma warning (disable: 4310)
|
||||||
|
#pragma warning (disable: 4100)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void toData (OutputStream& os, const juce::var& obj)
|
||||||
|
{
|
||||||
|
if (obj.isVoid())
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xc0));
|
||||||
|
}
|
||||||
|
else if (obj.isInt() || obj.isInt64())
|
||||||
|
{
|
||||||
|
int64 v = (int64) obj;
|
||||||
|
if (v >= 0)
|
||||||
|
{
|
||||||
|
if (v <= 127)
|
||||||
|
{
|
||||||
|
os.writeByte (char (v));
|
||||||
|
}
|
||||||
|
else if (v <= 255)
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xcc));
|
||||||
|
os.writeByte (char ((unsigned char) v));
|
||||||
|
}
|
||||||
|
else if (v <= 65535)
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xcd));
|
||||||
|
os.writeShortBigEndian (short ((unsigned short) v));
|
||||||
|
}
|
||||||
|
else if (v <= 4294967295)
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xce));
|
||||||
|
os.writeIntBigEndian (int ((unsigned int) v));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xcf));
|
||||||
|
os.writeInt64BigEndian (int64 (v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (v >= -7)
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xe0 | - char (-v)));
|
||||||
|
}
|
||||||
|
else if (v >= -128)
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xd0));
|
||||||
|
os.writeByte (char (v));
|
||||||
|
}
|
||||||
|
else if (v >= 32768)
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xd1));
|
||||||
|
os.writeShortBigEndian (short (v));
|
||||||
|
}
|
||||||
|
else if (v >= 2147483648)
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xd2));
|
||||||
|
os.writeIntBigEndian (int (v));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xd3));
|
||||||
|
os.writeInt64BigEndian (v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (obj.isBool())
|
||||||
|
{
|
||||||
|
if ((bool) obj)
|
||||||
|
os.writeByte (char (0xc3));
|
||||||
|
else
|
||||||
|
os.writeByte (char (0xc2));
|
||||||
|
}
|
||||||
|
else if (obj.isDouble())
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xcb));
|
||||||
|
os.writeDoubleBigEndian (obj);
|
||||||
|
}
|
||||||
|
else if (obj.isString())
|
||||||
|
{
|
||||||
|
auto str = obj.toString();
|
||||||
|
auto s = str.toRawUTF8();
|
||||||
|
size_t n = str.getNumBytesAsUTF8();
|
||||||
|
|
||||||
|
if (n <= 31)
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xa0 | n));
|
||||||
|
os.write (s, n);
|
||||||
|
}
|
||||||
|
else if (n <= 255)
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xd9));
|
||||||
|
os.writeByte (char (n));
|
||||||
|
os.write (s, n);
|
||||||
|
}
|
||||||
|
else if (n <= 65535)
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xda));
|
||||||
|
os.writeShortBigEndian (short (n));
|
||||||
|
os.write (s, n);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xdb));
|
||||||
|
os.writeIntBigEndian (int (n));
|
||||||
|
os.write (s, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (obj.isObject() && obj.getDynamicObject() != nullptr)
|
||||||
|
{
|
||||||
|
auto& dobj = *obj.getDynamicObject();
|
||||||
|
auto& names = dobj.getProperties();
|
||||||
|
|
||||||
|
int n = names.size();
|
||||||
|
|
||||||
|
if (n <= 15)
|
||||||
|
{
|
||||||
|
os.writeByte (char (0x80 | n));
|
||||||
|
}
|
||||||
|
else if (n <= 65535)
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xde));
|
||||||
|
os.writeShortBigEndian (short (n));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xdf));
|
||||||
|
os.writeIntBigEndian (n);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& itm : names)
|
||||||
|
{
|
||||||
|
toData (os, var (itm.name.toString()));
|
||||||
|
toData (os, itm.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (obj.isArray())
|
||||||
|
{
|
||||||
|
auto& arr = *obj.getArray();
|
||||||
|
|
||||||
|
int n = arr.size();
|
||||||
|
|
||||||
|
if (n <= 15)
|
||||||
|
{
|
||||||
|
os.writeByte (char (0x90 | n));
|
||||||
|
}
|
||||||
|
else if (n <= 65535)
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xdc));
|
||||||
|
os.writeShortBigEndian (short (n));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xdc));
|
||||||
|
os.writeIntBigEndian (n);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& a : arr)
|
||||||
|
toData (os, a);
|
||||||
|
}
|
||||||
|
else if (obj.isBinaryData())
|
||||||
|
{
|
||||||
|
if (auto bd = obj.getBinaryData())
|
||||||
|
{
|
||||||
|
void* s = bd->getData();
|
||||||
|
size_t n = bd->getSize();
|
||||||
|
|
||||||
|
if (n <= 255)
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xc4));
|
||||||
|
os.writeByte (char (n));
|
||||||
|
os.write (s, n);
|
||||||
|
}
|
||||||
|
else if (n <= 65535)
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xc5));
|
||||||
|
os.writeShortBigEndian (short (n));
|
||||||
|
os.write (s, n);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
os.writeByte (char (0xc6));
|
||||||
|
os.writeIntBigEndian (short (n));
|
||||||
|
os.write (s, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
jassertfalse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static var fromData (InputStream& is);
|
||||||
|
|
||||||
|
static var fromArray (InputStream& is, int n)
|
||||||
|
{
|
||||||
|
Array<var> res;
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
res.add (fromData (is));
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static var fromMap (InputStream& is, int n)
|
||||||
|
{
|
||||||
|
auto obj = new DynamicObject();
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
var k = fromData (is);
|
||||||
|
var v = fromData (is);
|
||||||
|
|
||||||
|
auto ident = k.toString();
|
||||||
|
if (ident.isNotEmpty())
|
||||||
|
obj->setProperty (ident, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return var (obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static var fromString (InputStream& is, int n)
|
||||||
|
{
|
||||||
|
MemoryBlock mb;
|
||||||
|
is.readIntoMemoryBlock (mb, n);
|
||||||
|
|
||||||
|
return String::fromUTF8 ((const char*)mb.getData(), int (mb.getSize()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static var fromData (InputStream& is)
|
||||||
|
{
|
||||||
|
uint8_t d = uint8_t (is.readByte());
|
||||||
|
|
||||||
|
if ((d & 0x80) == 0x00)
|
||||||
|
{
|
||||||
|
return (int) d;
|
||||||
|
}
|
||||||
|
else if ((d & 0xf0) == 0x80)
|
||||||
|
{
|
||||||
|
return fromMap (is, d & 0x0f);
|
||||||
|
}
|
||||||
|
else if ((d & 0xf0) == 0x90)
|
||||||
|
{
|
||||||
|
return fromArray (is, d & 0x0f);
|
||||||
|
}
|
||||||
|
else if ((d & 0xe0) == 0xa0)
|
||||||
|
{
|
||||||
|
return fromString (is, d & 0x1f);
|
||||||
|
}
|
||||||
|
else if (d == 0xc0)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
else if (d == 0xc1)
|
||||||
|
{
|
||||||
|
jassertfalse;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
else if (d == 0xc2)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (d == 0xc3)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (d == 0xc4)
|
||||||
|
{
|
||||||
|
uint8_t n = uint8_t (is.readByte());
|
||||||
|
MemoryBlock mb;
|
||||||
|
is.readIntoMemoryBlock (mb, n);
|
||||||
|
return mb;
|
||||||
|
}
|
||||||
|
else if (d == 0xc5)
|
||||||
|
{
|
||||||
|
uint16_t n = uint16_t (is.readShortBigEndian());
|
||||||
|
MemoryBlock mb;
|
||||||
|
is.readIntoMemoryBlock (mb, n);
|
||||||
|
return mb;
|
||||||
|
}
|
||||||
|
else if (d == 0xc6)
|
||||||
|
{
|
||||||
|
uint32_t n = uint32_t (is.readIntBigEndian());
|
||||||
|
MemoryBlock mb;
|
||||||
|
is.readIntoMemoryBlock (mb, n);
|
||||||
|
return mb;
|
||||||
|
}
|
||||||
|
else if (d == 0xc7)
|
||||||
|
{
|
||||||
|
uint8_t n = uint8_t (is.readByte());
|
||||||
|
MemoryBlock mb;
|
||||||
|
is.readIntoMemoryBlock (mb, n + 1);
|
||||||
|
return mb;
|
||||||
|
}
|
||||||
|
else if (d == 0xc8)
|
||||||
|
{
|
||||||
|
uint16_t n = uint16_t (is.readShortBigEndian());
|
||||||
|
MemoryBlock mb;
|
||||||
|
is.readIntoMemoryBlock (mb, n + 1);
|
||||||
|
return mb;
|
||||||
|
}
|
||||||
|
else if (d == 0xc9)
|
||||||
|
{
|
||||||
|
uint32_t n = uint32_t (is.readIntBigEndian());
|
||||||
|
MemoryBlock mb;
|
||||||
|
is.readIntoMemoryBlock (mb, n + 1);
|
||||||
|
return mb;
|
||||||
|
}
|
||||||
|
else if (d == 0xca)
|
||||||
|
{
|
||||||
|
return is.readFloatBigEndian();
|
||||||
|
}
|
||||||
|
else if (d == 0xcb)
|
||||||
|
{
|
||||||
|
return is.readDoubleBigEndian();
|
||||||
|
}
|
||||||
|
else if (d == 0xcc)
|
||||||
|
{
|
||||||
|
return int (uint8_t (is.readByte()));
|
||||||
|
}
|
||||||
|
else if (d == 0xcd)
|
||||||
|
{
|
||||||
|
return int (uint8_t (is.readShortBigEndian()));
|
||||||
|
}
|
||||||
|
else if (d == 0xce)
|
||||||
|
{
|
||||||
|
return int (uint8_t (is.readIntBigEndian()));
|
||||||
|
}
|
||||||
|
else if (d == 0xcf)
|
||||||
|
{
|
||||||
|
return int (uint8_t (is.readInt64BigEndian()));
|
||||||
|
}
|
||||||
|
else if (d == 0xd4)
|
||||||
|
{
|
||||||
|
MemoryBlock mb;
|
||||||
|
is.readIntoMemoryBlock (mb, 1 + 1);
|
||||||
|
return mb;
|
||||||
|
}
|
||||||
|
else if (d == 0xd5)
|
||||||
|
{
|
||||||
|
MemoryBlock mb;
|
||||||
|
is.readIntoMemoryBlock (mb, 1 + 2);
|
||||||
|
return mb;
|
||||||
|
}
|
||||||
|
else if (d == 0xd6)
|
||||||
|
{
|
||||||
|
MemoryBlock mb;
|
||||||
|
is.readIntoMemoryBlock (mb, 1 + 4);
|
||||||
|
return mb;
|
||||||
|
}
|
||||||
|
else if (d == 0xd7)
|
||||||
|
{
|
||||||
|
MemoryBlock mb;
|
||||||
|
is.readIntoMemoryBlock (mb, 1 + 8);
|
||||||
|
return mb;
|
||||||
|
}
|
||||||
|
else if (d == 0xd8)
|
||||||
|
{
|
||||||
|
MemoryBlock mb;
|
||||||
|
is.readIntoMemoryBlock (mb, 1 + 16);
|
||||||
|
return mb;
|
||||||
|
}
|
||||||
|
else if (d == 0xd9)
|
||||||
|
{
|
||||||
|
uint8_t n = uint8_t (is.readByte());
|
||||||
|
return fromString (is, n);
|
||||||
|
}
|
||||||
|
else if (d == 0xda)
|
||||||
|
{
|
||||||
|
uint16_t n = uint16_t (is.readShortBigEndian());
|
||||||
|
return fromString (is, n);
|
||||||
|
}
|
||||||
|
else if (d == 0xdb)
|
||||||
|
{
|
||||||
|
uint32_t n = uint32_t (is.readIntBigEndian());
|
||||||
|
return fromString (is, int (n));
|
||||||
|
}
|
||||||
|
else if (d == 0xdc)
|
||||||
|
{
|
||||||
|
uint16_t n = uint16_t (is.readShortBigEndian());
|
||||||
|
return fromArray (is, n);
|
||||||
|
}
|
||||||
|
else if (d == 0xdd)
|
||||||
|
{
|
||||||
|
uint32_t n = uint32_t (is.readIntBigEndian());
|
||||||
|
return fromArray (is, int (n));
|
||||||
|
}
|
||||||
|
else if (d == 0xde)
|
||||||
|
{
|
||||||
|
uint16_t n = uint16_t (is.readShortBigEndian());
|
||||||
|
return fromArray (is, n);
|
||||||
|
}
|
||||||
|
else if (d == 0xdf)
|
||||||
|
{
|
||||||
|
uint32_t n = uint32_t (is.readIntBigEndian());
|
||||||
|
return fromArray (is, int (n));
|
||||||
|
}
|
||||||
|
else if ((d & 0xe0) == 0xe0)
|
||||||
|
{
|
||||||
|
return -(d & 0x1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
jassertfalse;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
juce::MemoryBlock MessagePack::toMessagePack (const juce::var& obj)
|
||||||
|
{
|
||||||
|
MemoryBlock data;
|
||||||
|
|
||||||
|
{
|
||||||
|
MemoryOutputStream os (data, false);
|
||||||
|
toData (os, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
juce::var MessagePack::parse (const juce::MemoryBlock& data)
|
||||||
|
{
|
||||||
|
MemoryInputStream is (data, false);
|
||||||
|
return fromData (is);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if _MSC_VER
|
||||||
|
#pragma warning (pop)
|
||||||
|
#endif
|
27
Modules/gin/utilities/messagepack.h
Executable file
27
Modules/gin/utilities/messagepack.h
Executable file
|
@ -0,0 +1,27 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/** Converts to/from MessagePack and juce::var
|
||||||
|
|
||||||
|
Due to limitations of juce:var, there are no unsigned ints,
|
||||||
|
object keys can only be strings (non strings will be converted
|
||||||
|
to string if possible)
|
||||||
|
|
||||||
|
All custom types will be returned as juce::MemoryBlock with the
|
||||||
|
first byte as the type.
|
||||||
|
|
||||||
|
All strings are assumed to be utf8
|
||||||
|
*/
|
||||||
|
class MessagePack
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static juce::MemoryBlock toMessagePack (const juce::var& obj);
|
||||||
|
|
||||||
|
static juce::var parse (const juce::MemoryBlock& data);
|
||||||
|
};
|
253
Modules/gin/utilities/openstreetmaps.cpp
Executable file
253
Modules/gin/utilities/openstreetmaps.cpp
Executable file
|
@ -0,0 +1,253 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
OpenStreetMaps::OpenStreetMaps()
|
||||||
|
: tileSource (OpenStreetMap)
|
||||||
|
{
|
||||||
|
mapTileDir = File::getSpecialLocation (File::tempDirectory).getChildFile ("mapTiles");
|
||||||
|
mapTileDir.createDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenStreetMaps::~OpenStreetMaps()
|
||||||
|
{
|
||||||
|
requests.clear();
|
||||||
|
cancelledRequests.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
Image OpenStreetMaps::fetchTile (int zoom, int x, int y)
|
||||||
|
{
|
||||||
|
int mw = getMapWidthTiles (zoom);
|
||||||
|
x = x % mw;
|
||||||
|
y = y % mw;
|
||||||
|
|
||||||
|
String fname = String::formatted ("%d-%d-%d-%d.png", (int)tileSource, zoom, x, y);
|
||||||
|
|
||||||
|
File file = mapTileDir.getChildFile (fname);
|
||||||
|
|
||||||
|
if (cache.contains (fname))
|
||||||
|
{
|
||||||
|
return cache[fname];
|
||||||
|
}
|
||||||
|
else if (File (file).existsAsFile())
|
||||||
|
{
|
||||||
|
Image img;
|
||||||
|
img = ImageFileFormat::loadFrom (file);
|
||||||
|
|
||||||
|
cache.set (fname, img);
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto newReq = std::make_unique<TileReq> (zoom, x, y);
|
||||||
|
|
||||||
|
bool pending = false;
|
||||||
|
for (int i = 0; i < requests.size(); i++)
|
||||||
|
{
|
||||||
|
if (*requests[i] == *newReq)
|
||||||
|
{
|
||||||
|
pending = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! pending)
|
||||||
|
{
|
||||||
|
requests.add (newReq.release());
|
||||||
|
startRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
Image img (Image::ARGB, 256, 256, false);
|
||||||
|
img.clear ({0,0,256,256}, Colour (0xff808080));
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int OpenStreetMaps::getNumServers()
|
||||||
|
{
|
||||||
|
switch (tileSource)
|
||||||
|
{
|
||||||
|
case OpenStreetMap: return 3;
|
||||||
|
case OpenCycleMap: return 3;
|
||||||
|
case OpenCycleMapTransport: return 3;
|
||||||
|
case OpenCycleMapLandscape: return 3;
|
||||||
|
case StamenTerrain: return 1;
|
||||||
|
case MapQuestOSM: return 4;
|
||||||
|
case MapQuestOpenAerial: return 4;
|
||||||
|
case MapQuestOpenStreetMap: return 3;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int OpenStreetMaps::getServer()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < getNumServers(); i++)
|
||||||
|
{
|
||||||
|
if (! serversInUse.contains(i))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenStreetMaps::startRequest()
|
||||||
|
{
|
||||||
|
int server = getServer();
|
||||||
|
if (server == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < requests.size(); i++)
|
||||||
|
{
|
||||||
|
if (requests[i]->reply == nullptr)
|
||||||
|
{
|
||||||
|
char buffer[1024];
|
||||||
|
switch (tileSource)
|
||||||
|
{
|
||||||
|
case OpenStreetMap:
|
||||||
|
sprintf(buffer, "http://%c.tile.openstreetmap.org/%d/%d/%d.png", "abc"[server], requests[i]->zoom, requests[i]->x, requests[i]->y);
|
||||||
|
break;
|
||||||
|
case OpenCycleMap:
|
||||||
|
sprintf(buffer, "http://%c.tile.opencyclemap.org/cycle/%d/%d/%d.png", "abc"[server], requests[i]->zoom, requests[i]->x, requests[i]->y);
|
||||||
|
break;
|
||||||
|
case OpenCycleMapTransport:
|
||||||
|
sprintf(buffer, "http://%c.tile2.opencyclemap.org/transport/%d/%d/%d.png", "abc"[server], requests[i]->zoom, requests[i]->x, requests[i]->y);
|
||||||
|
break;
|
||||||
|
case OpenCycleMapLandscape:
|
||||||
|
sprintf(buffer, "http://%c.tile3.opencyclemap.org/landscape/%d/%d/%d.png", "abc"[server], requests[i]->zoom, requests[i]->x, requests[i]->y);
|
||||||
|
break;
|
||||||
|
case StamenTerrain:
|
||||||
|
sprintf(buffer, "http://tile.stamen.com/terrain/%d/%d/%d.png", requests[i]->zoom, requests[i]->x, requests[i]->y);
|
||||||
|
break;
|
||||||
|
case MapQuestOSM:
|
||||||
|
sprintf(buffer, "http://otile%c.mqcdn.com/tiles/1.0.0/map/%d/%d/%d.jpg", "1234"[server], requests[i]->zoom, requests[i]->x, requests[i]->y);
|
||||||
|
break;
|
||||||
|
case MapQuestOpenAerial:
|
||||||
|
sprintf(buffer, "http://otile%c.mqcdn.com/tiles/1.0.0/sat/%d/%d/%d.jpg", "1234"[server], requests[i]->zoom, requests[i]->x, requests[i]->y);
|
||||||
|
break;
|
||||||
|
case MapQuestOpenStreetMap:
|
||||||
|
sprintf(buffer, "http://%c.tile.openstreetmap.org/%d/%d/%d.png", "abc"[server], requests[i]->zoom, requests[i]->x, requests[i]->y);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
jassertfalse;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
requests[i]->server = server;
|
||||||
|
|
||||||
|
serversInUse.add (server);
|
||||||
|
|
||||||
|
URL url = URL (String (buffer));
|
||||||
|
|
||||||
|
requests[i]->reply = std::make_unique<AsyncDownload> (buffer, [this] (AsyncDownload* ad, juce::MemoryBlock m, bool ok)
|
||||||
|
{
|
||||||
|
finished (ad, m, ok);
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenStreetMaps::finished (AsyncDownload* reply, juce::MemoryBlock data, bool success)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < requests.size(); i++)
|
||||||
|
{
|
||||||
|
if (requests[i]->reply.get() == reply)
|
||||||
|
{
|
||||||
|
serversInUse.removeFirstMatchingValue (requests[i]->server);
|
||||||
|
requests[i]->server = -1;
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
String fname;
|
||||||
|
fname = String::formatted ("%d-%d-%d-%d.png", (int)tileSource, requests[i]->zoom, requests[i]->x, requests[i]->y);
|
||||||
|
|
||||||
|
Image img = ImageFileFormat::loadFrom (data.getData(), data.getSize());
|
||||||
|
if (img.isValid())
|
||||||
|
{
|
||||||
|
cache.set (fname, img);
|
||||||
|
|
||||||
|
File dest = mapTileDir.getChildFile (fname);
|
||||||
|
dest.replaceWithData (data.getData(), data.getSize());
|
||||||
|
|
||||||
|
listeners.call (&OpenStreetMaps::Listener::tileFetched, requests[i]->zoom, requests[i]->x, requests[i]->y);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
requests.remove (i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
int OpenStreetMaps::getMapWidthPixels (int zoom)
|
||||||
|
{
|
||||||
|
double numberOfTiles = std::pow (2.0, zoom);
|
||||||
|
return roundToInt (numberOfTiles * 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
int OpenStreetMaps::getMapWidthTiles (int zoom)
|
||||||
|
{
|
||||||
|
return roundToInt (std::pow (2.0, zoom));
|
||||||
|
}
|
||||||
|
|
||||||
|
juce::Point<double> OpenStreetMaps::coordinateToDisplay (juce::Point<double> coordinate, int zoom)
|
||||||
|
{
|
||||||
|
double numberOfTiles = std::pow (2.0, zoom);
|
||||||
|
|
||||||
|
// LonToX
|
||||||
|
double x = (coordinate.getX() + 180) * (numberOfTiles * tilesize) / 360.0;
|
||||||
|
// LatToY
|
||||||
|
double projection = std::log (std::tan (double_Pi / 4 + degreesToRadians (coordinate.getY()) / 2));
|
||||||
|
|
||||||
|
double y = (projection / double_Pi);
|
||||||
|
y = 1 - y;
|
||||||
|
y = y /2 * (numberOfTiles * tilesize);
|
||||||
|
|
||||||
|
return juce::Point<double> (x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
juce::Point<double> OpenStreetMaps::displayToCoordinate (juce::Point<double> point, int zoom)
|
||||||
|
{
|
||||||
|
// longitude
|
||||||
|
double longitude = (point.getX() * (360 / (std::pow (2.0, zoom) * 256))) - 180;
|
||||||
|
// latitude
|
||||||
|
double latitude = point.getY() * (2 / (std::pow (2.0, zoom) * 256));
|
||||||
|
|
||||||
|
latitude = 1 - latitude;
|
||||||
|
latitude = latitude * double_Pi;
|
||||||
|
latitude = radiansToDegrees (std::atan (std::sinh (latitude)));
|
||||||
|
|
||||||
|
juce::Point<double> coord = {longitude, latitude};
|
||||||
|
return coord;
|
||||||
|
}
|
||||||
|
|
||||||
|
juce::Point<double> OpenStreetMaps::tileForCoordinate (double lat, double lng, int zoom)
|
||||||
|
{
|
||||||
|
double zn = static_cast<double>(1 << zoom);
|
||||||
|
double tx = (lng + 180.0) / 360.0;
|
||||||
|
double ty = (1.0 - std::log (std::tan (lat * double_Pi / 180.0) + 1.0 / std::cos (lat * double_Pi / 180.0)) / double_Pi) / 2.0;
|
||||||
|
return {tx * zn, ty * zn};
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenStreetMaps::clearQueue()
|
||||||
|
{
|
||||||
|
while (requests.size() > 0)
|
||||||
|
cancelledRequests.add (requests.removeAndReturn (0));
|
||||||
|
|
||||||
|
serversInUse.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenStreetMaps::TileSource OpenStreetMaps::getTileSource()
|
||||||
|
{
|
||||||
|
return tileSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenStreetMaps::setTileSource(TileSource t)
|
||||||
|
{
|
||||||
|
tileSource = t;
|
||||||
|
}
|
100
Modules/gin/utilities/openstreetmaps.h
Executable file
100
Modules/gin/utilities/openstreetmaps.h
Executable file
|
@ -0,0 +1,100 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//==============================================================================*/
|
||||||
|
// Fetches map files from OSM servers
|
||||||
|
class OpenStreetMaps
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum TileSource
|
||||||
|
{
|
||||||
|
OpenStreetMap,
|
||||||
|
OpenCycleMap,
|
||||||
|
OpenCycleMapTransport,
|
||||||
|
OpenCycleMapLandscape,
|
||||||
|
StamenTerrain,
|
||||||
|
MapQuestOSM,
|
||||||
|
MapQuestOpenAerial,
|
||||||
|
MapQuestOpenStreetMap,
|
||||||
|
};
|
||||||
|
|
||||||
|
OpenStreetMaps();
|
||||||
|
~OpenStreetMaps();
|
||||||
|
|
||||||
|
/** Set this or map tiles will be stored in the temp directory */
|
||||||
|
void setMapTileDir (File f) { mapTileDir = f; }
|
||||||
|
|
||||||
|
Image fetchTile (int zoom, int x, int y);
|
||||||
|
|
||||||
|
int getMapWidthPixels (int zoom);
|
||||||
|
int getMapWidthTiles (int zoom);
|
||||||
|
|
||||||
|
juce::Point<double> coordinateToDisplay (juce::Point<double> coordinate, int zoom);
|
||||||
|
juce::Point<double> displayToCoordinate (const juce::Point<double> point, int zoom);
|
||||||
|
juce::Point<double> tileForCoordinate (double lat, double lng, int zoom);
|
||||||
|
|
||||||
|
void clearQueue();
|
||||||
|
|
||||||
|
TileSource getTileSource();
|
||||||
|
void setTileSource (TileSource t);
|
||||||
|
|
||||||
|
class Listener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~Listener() {}
|
||||||
|
|
||||||
|
virtual void tileFetched (int zoom, int x, int y) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void addListener (Listener* listener) { listeners.add (listener); }
|
||||||
|
void removeListener (Listener* listener) { listeners.remove (listener); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void finished (AsyncDownload* task, juce::MemoryBlock data, bool success);
|
||||||
|
|
||||||
|
void startRequest();
|
||||||
|
int getNumServers();
|
||||||
|
int getServer();
|
||||||
|
|
||||||
|
class TileReq
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TileReq (int z_, int x_, int y_)
|
||||||
|
{
|
||||||
|
zoom = z_;
|
||||||
|
x = x_;
|
||||||
|
y = y_;
|
||||||
|
server = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const TileReq& b)
|
||||||
|
{
|
||||||
|
return zoom == b.zoom && x == b.x && y == b.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<AsyncDownload> reply;
|
||||||
|
int zoom;
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int server;
|
||||||
|
};
|
||||||
|
|
||||||
|
OwnedArray<TileReq> requests;
|
||||||
|
OwnedArray<TileReq> cancelledRequests;
|
||||||
|
|
||||||
|
File mapTileDir;
|
||||||
|
HashMap<String, Image> cache;
|
||||||
|
TileSource tileSource;
|
||||||
|
Array<int> serversInUse;
|
||||||
|
ListenerList<Listener> listeners;
|
||||||
|
|
||||||
|
const int tilesize = 256;
|
||||||
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenStreetMaps)
|
||||||
|
};
|
110
Modules/gin/utilities/plist.cpp
Executable file
110
Modules/gin/utilities/plist.cpp
Executable file
|
@ -0,0 +1,110 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2019 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
var parseData (const XmlElement& e)
|
||||||
|
{
|
||||||
|
if (e.hasTagName ("string"))
|
||||||
|
{
|
||||||
|
return e.getAllSubText();
|
||||||
|
}
|
||||||
|
else if (e.hasTagName ("array"))
|
||||||
|
{
|
||||||
|
Array<var> res;
|
||||||
|
|
||||||
|
auto c = e.getFirstChildElement();
|
||||||
|
while (c != nullptr)
|
||||||
|
{
|
||||||
|
res.add (parseData (*c));
|
||||||
|
c = c->getNextElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
else if (e.hasTagName ("dict"))
|
||||||
|
{
|
||||||
|
auto obj = new DynamicObject();
|
||||||
|
for (int i = 0; i < e.getNumChildElements(); i += 2)
|
||||||
|
{
|
||||||
|
auto key = e.getChildElement (i + 0);
|
||||||
|
auto val = e.getChildElement (i + 1);
|
||||||
|
|
||||||
|
if (key != nullptr && val != nullptr)
|
||||||
|
obj->setProperty (key->getAllSubText(), parseData (*val));
|
||||||
|
}
|
||||||
|
|
||||||
|
return var (obj);
|
||||||
|
}
|
||||||
|
else if (e.hasTagName ("data"))
|
||||||
|
{
|
||||||
|
MemoryBlock mb;
|
||||||
|
|
||||||
|
{
|
||||||
|
MemoryOutputStream os (mb, true);
|
||||||
|
Base64::convertFromBase64 (os, e.getAllSubText());
|
||||||
|
}
|
||||||
|
|
||||||
|
return mb;
|
||||||
|
}
|
||||||
|
else if (e.hasTagName ("date"))
|
||||||
|
{
|
||||||
|
return e.getAllSubText();
|
||||||
|
}
|
||||||
|
else if (e.hasTagName ("real"))
|
||||||
|
{
|
||||||
|
return e.getAllSubText().getDoubleValue();
|
||||||
|
}
|
||||||
|
else if (e.hasTagName ("integer"))
|
||||||
|
{
|
||||||
|
return e.getAllSubText().getIntValue();
|
||||||
|
}
|
||||||
|
else if (e.hasTagName ("true"))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (e.hasTagName ("false"))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
jassertfalse;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsePlist (const File& f)
|
||||||
|
{
|
||||||
|
XmlDocument doc (f);
|
||||||
|
if (auto e = doc.getDocumentElement())
|
||||||
|
return parsePlist (*e);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsePlist (const String& s)
|
||||||
|
{
|
||||||
|
XmlDocument doc (s);
|
||||||
|
if (auto e = doc.getDocumentElement())
|
||||||
|
return parsePlist (*e);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsePlist (const XmlElement& e)
|
||||||
|
{
|
||||||
|
if (auto dict = e.getChildByName ("dict"))
|
||||||
|
{
|
||||||
|
auto obj = new DynamicObject();
|
||||||
|
for (int i = 0; i < dict->getNumChildElements(); i += 2)
|
||||||
|
{
|
||||||
|
auto key = dict->getChildElement (i + 0);
|
||||||
|
auto val = dict->getChildElement (i + 1);
|
||||||
|
|
||||||
|
if (key != nullptr && val != nullptr)
|
||||||
|
obj->setProperty (key->getAllSubText(), parseData (*val));
|
||||||
|
}
|
||||||
|
|
||||||
|
return var (obj);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
16
Modules/gin/utilities/plist.h
Executable file
16
Modules/gin/utilities/plist.h
Executable file
|
@ -0,0 +1,16 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
juce::var parsePlist (const juce::File& f);
|
||||||
|
|
||||||
|
juce::var parsePlist (const juce::String& s);
|
||||||
|
|
||||||
|
juce::var parsePlist (const juce::XmlElement& f);
|
||||||
|
|
131
Modules/gin/utilities/realtimeasyncupdater.cpp
Executable file
131
Modules/gin/utilities/realtimeasyncupdater.cpp
Executable file
|
@ -0,0 +1,131 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
class RealtimeAsyncUpdater::Impl : public Thread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Impl() : Thread ("RealtimeAsyncUpdater")
|
||||||
|
{
|
||||||
|
startThread();
|
||||||
|
next = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Impl() override
|
||||||
|
{
|
||||||
|
signalThreadShouldExit ();
|
||||||
|
event.signal();
|
||||||
|
stopThread (1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerUpdater (RealtimeAsyncUpdater* ras)
|
||||||
|
{
|
||||||
|
ScopedLock sl (lock);
|
||||||
|
updaters.add (ras);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unregisterUpdater (RealtimeAsyncUpdater* ras)
|
||||||
|
{
|
||||||
|
ScopedLock sl (lock);
|
||||||
|
updaters.removeFirstMatchingValue (ras);
|
||||||
|
}
|
||||||
|
|
||||||
|
void signal (RealtimeAsyncUpdater& ras)
|
||||||
|
{
|
||||||
|
static PerformanceCounter counter {"signal", 1000};
|
||||||
|
counter.start();
|
||||||
|
ras.order = ++next;
|
||||||
|
event.signal();
|
||||||
|
counter.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CriticalSection lock;
|
||||||
|
Array<RealtimeAsyncUpdater*> updaters;
|
||||||
|
WaitableEvent event;
|
||||||
|
Atomic<uint32_t> next;
|
||||||
|
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
event.wait (-1);
|
||||||
|
|
||||||
|
if (threadShouldExit())
|
||||||
|
break;
|
||||||
|
|
||||||
|
WeakReference<Impl> weakSelf = this;
|
||||||
|
MessageManager::getInstance()->callAsync ([this, weakSelf] {
|
||||||
|
if (weakSelf != nullptr)
|
||||||
|
fireCallbacks();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fireCallbacks()
|
||||||
|
{
|
||||||
|
Array<RealtimeAsyncUpdater*> dirtyUpdaters;
|
||||||
|
|
||||||
|
ScopedLock sl (lock);
|
||||||
|
for (auto au : updaters)
|
||||||
|
if (au->triggered.get())
|
||||||
|
dirtyUpdaters.add (au);
|
||||||
|
|
||||||
|
std::sort (dirtyUpdaters.begin(), dirtyUpdaters.end(),
|
||||||
|
[] (const RealtimeAsyncUpdater* a, const RealtimeAsyncUpdater* b) -> bool
|
||||||
|
{
|
||||||
|
return a->order.get() < b->order.get();
|
||||||
|
});
|
||||||
|
|
||||||
|
for (auto au : dirtyUpdaters)
|
||||||
|
{
|
||||||
|
au->triggered = false;
|
||||||
|
au->handleAsyncUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JUCE_DECLARE_WEAK_REFERENCEABLE (Impl)
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
RealtimeAsyncUpdater::RealtimeAsyncUpdater()
|
||||||
|
{
|
||||||
|
impl->registerUpdater (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
RealtimeAsyncUpdater::~RealtimeAsyncUpdater()
|
||||||
|
{
|
||||||
|
impl->unregisterUpdater (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RealtimeAsyncUpdater::triggerAsyncUpdate()
|
||||||
|
{
|
||||||
|
if (! triggered.get())
|
||||||
|
{
|
||||||
|
triggered = true;
|
||||||
|
impl->signal (*this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RealtimeAsyncUpdater::cancelPendingUpdate() noexcept
|
||||||
|
{
|
||||||
|
triggered = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RealtimeAsyncUpdater::handleUpdateNowIfNeeded()
|
||||||
|
{
|
||||||
|
if (triggered.get())
|
||||||
|
{
|
||||||
|
triggered = false;
|
||||||
|
handleAsyncUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RealtimeAsyncUpdater::isUpdatePending() const noexcept
|
||||||
|
{
|
||||||
|
return triggered.get();
|
||||||
|
}
|
36
Modules/gin/utilities/realtimeasyncupdater.h
Executable file
36
Modules/gin/utilities/realtimeasyncupdater.h
Executable file
|
@ -0,0 +1,36 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2020 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class RealtimeAsyncUpdater
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RealtimeAsyncUpdater();
|
||||||
|
|
||||||
|
virtual ~RealtimeAsyncUpdater();
|
||||||
|
|
||||||
|
void triggerAsyncUpdate();
|
||||||
|
|
||||||
|
void cancelPendingUpdate() noexcept;
|
||||||
|
|
||||||
|
void handleUpdateNowIfNeeded();
|
||||||
|
|
||||||
|
bool isUpdatePending() const noexcept;
|
||||||
|
|
||||||
|
virtual void handleAsyncUpdate() = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
//==============================================================================
|
||||||
|
class Impl;
|
||||||
|
SharedResourcePointer<Impl> impl;
|
||||||
|
|
||||||
|
Atomic<bool> triggered;
|
||||||
|
Atomic<uint32_t> order;
|
||||||
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RealtimeAsyncUpdater)
|
||||||
|
};
|
160
Modules/gin/utilities/sharedmemory.cpp
Executable file
160
Modules/gin/utilities/sharedmemory.cpp
Executable file
|
@ -0,0 +1,160 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#if JUCE_WINDOWS
|
||||||
|
typedef enum _SECTION_INFORMATION_CLASS
|
||||||
|
{
|
||||||
|
SectionBasicInformation,
|
||||||
|
SectionImageInformation
|
||||||
|
} SECTION_INFORMATION_CLASS;
|
||||||
|
|
||||||
|
typedef struct _SECTION_BASIC_INFORMATION {
|
||||||
|
PVOID base;
|
||||||
|
ULONG attributes;
|
||||||
|
LARGE_INTEGER size;
|
||||||
|
} SECTION_BASIC_INFORMATION;
|
||||||
|
|
||||||
|
typedef DWORD (WINAPI* NTQUERYSECTION) (HANDLE, SECTION_INFORMATION_CLASS, PVOID, ULONG, PULONG);
|
||||||
|
|
||||||
|
class SharedMemory::Impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Impl (String name, int sz) : size (sz)
|
||||||
|
{
|
||||||
|
String shareName = "Local\\" + File::createLegalFileName (name);
|
||||||
|
|
||||||
|
fileMapping = OpenFileMappingW (FILE_MAP_ALL_ACCESS, FALSE, shareName.toWideCharPointer());
|
||||||
|
if (fileMapping == nullptr)
|
||||||
|
fileMapping = CreateFileMappingW (INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, sz, shareName.toWideCharPointer());
|
||||||
|
|
||||||
|
if (HMODULE dll = LoadLibrary ("ntdll.dll"))
|
||||||
|
{
|
||||||
|
NTQUERYSECTION ntQuerySection = (NTQUERYSECTION) GetProcAddress (dll, "NtQuerySection");
|
||||||
|
if (ntQuerySection != nullptr)
|
||||||
|
{
|
||||||
|
SECTION_BASIC_INFORMATION SectionInfo = { 0 };
|
||||||
|
ntQuerySection (fileMapping, SectionBasicInformation, &SectionInfo, sizeof (SectionInfo), 0);
|
||||||
|
|
||||||
|
if (SectionInfo.size.QuadPart > 0)
|
||||||
|
size = int (SectionInfo.size.QuadPart);
|
||||||
|
}
|
||||||
|
FreeLibrary (dll);
|
||||||
|
}
|
||||||
|
|
||||||
|
data = MapViewOfFile (fileMapping, FILE_MAP_ALL_ACCESS, 0, 0, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
~Impl()
|
||||||
|
{
|
||||||
|
if (data != nullptr)
|
||||||
|
UnmapViewOfFile (data);
|
||||||
|
|
||||||
|
if (fileMapping != nullptr)
|
||||||
|
CloseHandle (fileMapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove (const String& name)
|
||||||
|
{
|
||||||
|
ignoreUnused (name);
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE fileMapping = nullptr;
|
||||||
|
|
||||||
|
int size = 0;
|
||||||
|
void* data = nullptr;
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
class SharedMemory::Impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Impl (String name, int sz) : size (sz)
|
||||||
|
{
|
||||||
|
bool needsInit = false;
|
||||||
|
|
||||||
|
shareName = "/jshm" + File::createLegalFileName (name);
|
||||||
|
|
||||||
|
fd = shm_open (shareName.toRawUTF8(), O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
|
||||||
|
if (fd == -1)
|
||||||
|
fd = shm_open (shareName.toRawUTF8(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
|
||||||
|
else
|
||||||
|
needsInit = true;
|
||||||
|
|
||||||
|
if (fd == -1)
|
||||||
|
{
|
||||||
|
size = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsInit && ftruncate (fd, size) == -1)
|
||||||
|
{
|
||||||
|
size = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat statbuf;
|
||||||
|
if (fstat (fd, &statbuf) == -1)
|
||||||
|
{
|
||||||
|
size = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = int (statbuf.st_size);
|
||||||
|
|
||||||
|
data = mmap (nullptr, size_t (size), PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
|
jassert (data != nullptr);
|
||||||
|
|
||||||
|
if (data != nullptr && needsInit)
|
||||||
|
memset (data, 0, sizeof (data));
|
||||||
|
}
|
||||||
|
|
||||||
|
~Impl()
|
||||||
|
{
|
||||||
|
if (data != nullptr)
|
||||||
|
munmap (data, size_t (size));
|
||||||
|
|
||||||
|
if (fd != -1)
|
||||||
|
close (fd);
|
||||||
|
|
||||||
|
shm_unlink (shareName.toRawUTF8());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove (const String& name)
|
||||||
|
{
|
||||||
|
String shareName = "/jshm" + File::createLegalFileName (name);
|
||||||
|
shm_unlink (shareName.toRawUTF8());
|
||||||
|
}
|
||||||
|
|
||||||
|
String shareName;
|
||||||
|
int size;
|
||||||
|
void* data = nullptr;
|
||||||
|
int fd;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SharedMemory::SharedMemory (const String& name, int size)
|
||||||
|
{
|
||||||
|
impl = std::make_unique<Impl> (name, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedMemory::~SharedMemory()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void* SharedMemory::getData()
|
||||||
|
{
|
||||||
|
return impl->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SharedMemory::getSize()
|
||||||
|
{
|
||||||
|
return impl->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SharedMemory::remove (const String& name)
|
||||||
|
{
|
||||||
|
Impl::remove (name);
|
||||||
|
}
|
37
Modules/gin/utilities/sharedmemory.h
Executable file
37
Modules/gin/utilities/sharedmemory.h
Executable file
|
@ -0,0 +1,37 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/** Creates a block of shared memory. The first one to create the block sets the size
|
||||||
|
otherwise it is ignored. The block may be a different size than requested, especially
|
||||||
|
if it was already created.
|
||||||
|
|
||||||
|
On Unix based OSes, if a process using the block crashes, the block will leak. Clean
|
||||||
|
it up later with remove() and it will be deleted when the last process stops using it.
|
||||||
|
(No new processes will be able to attach)
|
||||||
|
|
||||||
|
On Windows the memory block will always disappear when the final process closes the
|
||||||
|
handle / crashes.
|
||||||
|
*/
|
||||||
|
class SharedMemory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SharedMemory (const String& name, int size);
|
||||||
|
~SharedMemory();
|
||||||
|
|
||||||
|
static void remove (const String& name);
|
||||||
|
|
||||||
|
void* getData();
|
||||||
|
int getSize();
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Impl;
|
||||||
|
std::unique_ptr<Impl> impl;
|
||||||
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SharedMemory)
|
||||||
|
};
|
78
Modules/gin/utilities/spline.cpp
Executable file
78
Modules/gin/utilities/spline.cpp
Executable file
|
@ -0,0 +1,78 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien, Devin Lane
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
/* "THE BEER-WARE LICENSE" (Revision 42): Devin Lane wrote this file. As long as you retain
|
||||||
|
* this notice you can do whatever you want with this stuff. If we meet some day, and you
|
||||||
|
* think this stuff is worth it, you can buy me a beer in return. */
|
||||||
|
|
||||||
|
|
||||||
|
Spline::Spline (const Array<juce::Point<double>>& points)
|
||||||
|
{
|
||||||
|
jassert (points.size() >= 3); // "Must have at least three points for interpolation"
|
||||||
|
points.size ();
|
||||||
|
int n = points.size() - 1;
|
||||||
|
|
||||||
|
Array<double> b, d, a, c, l, u, z, h;
|
||||||
|
|
||||||
|
a.resize (n);
|
||||||
|
b.resize (n);
|
||||||
|
c.resize (n + 1);
|
||||||
|
d.resize (n);
|
||||||
|
h.resize (n + 1);
|
||||||
|
l.resize (n + 1);
|
||||||
|
u.resize (n + 1);
|
||||||
|
z.resize (n + 1);
|
||||||
|
|
||||||
|
l.set (0, 1.0);
|
||||||
|
u.set (0, 0.0);
|
||||||
|
z.set (0, 0.0);
|
||||||
|
h.set (0, points[1].getX() - points[0].getX());
|
||||||
|
|
||||||
|
for (int i = 1; i < n; i++)
|
||||||
|
{
|
||||||
|
h.set (i, points[i+1].getX() - points[i].getX());
|
||||||
|
l.set (i, (2 * (points[i+1].getX() - points[i-1].getX())) - (h[i-1]) * u[i-1]);
|
||||||
|
u.set (i, l[i] == 0 ? 0 :h[i] / l[i]);
|
||||||
|
a.set (i, h[i] == 0 || h[i-1] == 0 ? 0 : (3.0 / (h[i])) * (points[i+1].getY() - points[i].getY()) - (3.0 / (h[i-1])) * (points[i].getY() - points[i-1].getY()));
|
||||||
|
z.set (i, l[i] == 0 ? 0 : (a[i] - (h[i-1]) * z[i-1]) / l[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
l.set (n, 1.0);
|
||||||
|
z.set (n, 0.0);
|
||||||
|
c.set (n, 0.0);
|
||||||
|
|
||||||
|
for (int j = n - 1; j >= 0; j--)
|
||||||
|
{
|
||||||
|
c.set (j, z[j] - u[j] * c[j+1]);
|
||||||
|
b.set (j, h[j] == 0 ? 0 : (points[j+1].getY() - points[j].getY()) / (h[j]) - ((h[j]) * (c[j+1] + 2.0 * c[j])) / 3.0);
|
||||||
|
d.set (j, h[j] == 0 ? 0 : (c[j+1] - c[j]) / (3.0 * h[j]));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
elements.add (Element (points[i].getX(), points[i].getY(), b[i], c[i], d[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
double Spline::operator[] (double x) const
|
||||||
|
{
|
||||||
|
return interpolate (x);
|
||||||
|
}
|
||||||
|
|
||||||
|
double Spline::interpolate (double x) const
|
||||||
|
{
|
||||||
|
if (elements.size() == 0)
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < elements.size(); i++)
|
||||||
|
{
|
||||||
|
if (! (elements[i] < x))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i != 0) i--;
|
||||||
|
|
||||||
|
return elements[i].eval (x);
|
||||||
|
}
|
47
Modules/gin/utilities/spline.h
Executable file
47
Modules/gin/utilities/spline.h
Executable file
|
@ -0,0 +1,47 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien, Devin Lane
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/** Cubic spline interpolation is a simple way to obtain a smooth curve from a set of
|
||||||
|
discrete points. It has both C1 (first derivative) and C2 (second derivative) continuity,
|
||||||
|
enabling it to produce a continuous piecewise function given a set of data points.
|
||||||
|
|
||||||
|
Add points in increasing x order */
|
||||||
|
class Spline
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Spline (const Array<juce::Point<double>>& points);
|
||||||
|
|
||||||
|
double operator[] (double x) const;
|
||||||
|
double interpolate (double x) const;
|
||||||
|
|
||||||
|
class Element
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Element (double x_ = 0) : x (x_) {}
|
||||||
|
|
||||||
|
Element (double x_, double a_, double b_, double c_, double d_)
|
||||||
|
: x (x_), a (a_), b (b_), c (c_), d (d_) {}
|
||||||
|
|
||||||
|
double eval (double xx) const
|
||||||
|
{
|
||||||
|
double xix (xx - x);
|
||||||
|
return a + b * xix + c * (xix * xix) + d * (xix * xix * xix);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator< (const Element& e) const { return x < e.x; }
|
||||||
|
bool operator< (const double xx) const { return x < xx; }
|
||||||
|
|
||||||
|
double x = 0, a = 0, b = 0, c = 0, d = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
Array<Element> elements;
|
||||||
|
|
||||||
|
JUCE_LEAK_DETECTOR (Spline)
|
||||||
|
};
|
110
Modules/gin/utilities/systemsemaphore.cpp
Executable file
110
Modules/gin/utilities/systemsemaphore.cpp
Executable file
|
@ -0,0 +1,110 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#ifdef JUCE_WINDOWS
|
||||||
|
class SystemSemaphore::Impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Impl (String name)
|
||||||
|
{
|
||||||
|
ignoreUnused (name);
|
||||||
|
jassertfalse;
|
||||||
|
// not implemented yet
|
||||||
|
}
|
||||||
|
|
||||||
|
~Impl()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lock()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unlock()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isValid()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
class SystemSemaphore::Impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Impl (String name)
|
||||||
|
{
|
||||||
|
String shareName = "/jsem" + File::createLegalFileName (name);
|
||||||
|
sem = sem_open (shareName.toRawUTF8(), O_CREAT, 0644, 1);
|
||||||
|
|
||||||
|
jassert (sem != SEM_FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
|
~Impl()
|
||||||
|
{
|
||||||
|
sem_close (sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lock()
|
||||||
|
{
|
||||||
|
if (! locked)
|
||||||
|
{
|
||||||
|
sem_wait (sem);
|
||||||
|
locked = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unlock()
|
||||||
|
{
|
||||||
|
if (locked)
|
||||||
|
{
|
||||||
|
sem_post (sem);
|
||||||
|
locked = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isValid()
|
||||||
|
{
|
||||||
|
return sem != SEM_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
sem_t* sem = SEM_FAILED;
|
||||||
|
bool locked = false;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SystemSemaphore::SystemSemaphore (const String& name)
|
||||||
|
{
|
||||||
|
impl = std::make_unique<Impl> (name);
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemSemaphore::~SystemSemaphore()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SystemSemaphore::lock()
|
||||||
|
{
|
||||||
|
return impl->lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SystemSemaphore::unlock()
|
||||||
|
{
|
||||||
|
return impl->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SystemSemaphore::isValid()
|
||||||
|
{
|
||||||
|
return impl->isValid();
|
||||||
|
}
|
29
Modules/gin/utilities/systemsemaphore.h
Executable file
29
Modules/gin/utilities/systemsemaphore.h
Executable file
|
@ -0,0 +1,29 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is not finished / implemented
|
||||||
|
*/
|
||||||
|
class SystemSemaphore
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SystemSemaphore (const String& name);
|
||||||
|
~SystemSemaphore();
|
||||||
|
|
||||||
|
bool lock();
|
||||||
|
bool unlock();
|
||||||
|
|
||||||
|
bool isValid();
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Impl;
|
||||||
|
std::unique_ptr<Impl> impl;
|
||||||
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SystemSemaphore)
|
||||||
|
};
|
43
Modules/gin/utilities/threading.cpp
Executable file
43
Modules/gin/utilities/threading.cpp
Executable file
|
@ -0,0 +1,43 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
class BackgroundCaller : private Thread,
|
||||||
|
private AsyncUpdater
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BackgroundCaller (std::function<void (void)> func)
|
||||||
|
: Thread ("BackgroundCaller"), function (func)
|
||||||
|
{
|
||||||
|
startThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
~BackgroundCaller() override
|
||||||
|
{
|
||||||
|
stopThread (1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
function();
|
||||||
|
triggerAsyncUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleAsyncUpdate() override
|
||||||
|
{
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::function<void (void)> function;
|
||||||
|
};
|
||||||
|
|
||||||
|
void callInBackground (std::function<void (void)> function)
|
||||||
|
{
|
||||||
|
new BackgroundCaller (function);
|
||||||
|
}
|
48
Modules/gin/utilities/threading.h
Executable file
48
Modules/gin/utilities/threading.h
Executable file
|
@ -0,0 +1,48 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Calls a function in background
|
||||||
|
void callInBackground (std::function<void (void)> function);
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Run a for loop split between each core.
|
||||||
|
// for (int i = 0; i < 10; i++) becomes multiThreadedFor<int> (0, 10, 1, threadPool, [&] (int i) {});
|
||||||
|
// Make sure each iteration of the loop is independant
|
||||||
|
template <typename T>
|
||||||
|
void multiThreadedFor (T start, T end, T interval, ThreadPool* threadPool, std::function<void (T idx)> callback)
|
||||||
|
{
|
||||||
|
if (threadPool == nullptr)
|
||||||
|
{
|
||||||
|
for (int i = start; i < end; i += interval)
|
||||||
|
callback (i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int num = threadPool->getNumThreads();
|
||||||
|
|
||||||
|
WaitableEvent wait;
|
||||||
|
Atomic<int> threadsRunning (num);
|
||||||
|
|
||||||
|
for (int i = 0; i < num; i++)
|
||||||
|
{
|
||||||
|
threadPool->addJob ([i, &callback, &wait, &threadsRunning, start, end, interval, num]
|
||||||
|
{
|
||||||
|
for (int j = start + interval * i; j < end; j += interval * num)
|
||||||
|
callback (j);
|
||||||
|
|
||||||
|
int stillRunning = --threadsRunning;
|
||||||
|
if (stillRunning == 0)
|
||||||
|
wait.signal();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
wait.wait();
|
||||||
|
}
|
||||||
|
}
|
52
Modules/gin/utilities/util.cpp
Executable file
52
Modules/gin/utilities/util.cpp
Executable file
|
@ -0,0 +1,52 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
Colour goldenRatioColor (int idx)
|
||||||
|
{
|
||||||
|
double h = std::fmod (idx * 0.618033988749895, 1);
|
||||||
|
return Colour (float (h), 0.8f, 0.95f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
int versionStringToInt (const String& versionString)
|
||||||
|
{
|
||||||
|
StringArray parts;
|
||||||
|
parts.addTokens (versionString, ".", "");
|
||||||
|
parts.trim();
|
||||||
|
parts.removeEmptyStrings();
|
||||||
|
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
for (auto part : parts)
|
||||||
|
res = (res << 8) + part.getIntValue();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
class DelayedLambdaHelper : public Timer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DelayedLambdaHelper (std::function<void ()>& cb, int ms)
|
||||||
|
: callback (cb)
|
||||||
|
{
|
||||||
|
startTimer (ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
void timerCallback() override
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::function<void ()> callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
void delayedLambda (std::function<void ()> callback, int delayMS)
|
||||||
|
{
|
||||||
|
new DelayedLambdaHelper (callback, delayMS);
|
||||||
|
}
|
267
Modules/gin/utilities/util.h
Executable file
267
Modules/gin/utilities/util.h
Executable file
|
@ -0,0 +1,267 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
inline bool compareAndReset (bool& flag)
|
||||||
|
{
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
flag = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float calculateRMS (const float* values, int n)
|
||||||
|
{
|
||||||
|
float rms = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
rms += values[i] * values[i];
|
||||||
|
|
||||||
|
return std::sqrt ((1.0f / n) * rms);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float calculateMedian (const float* values, int n)
|
||||||
|
{
|
||||||
|
Array<float> f;
|
||||||
|
f.insertArray (0, values, n);
|
||||||
|
f.sort();
|
||||||
|
|
||||||
|
if (f.size() % 2 == 0)
|
||||||
|
return (f[f.size() / 2] + f[f.size() / 2 - 1]) / 2.0f;
|
||||||
|
|
||||||
|
return f[f.size() / 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Fisher-Yates Shuffle
|
||||||
|
template <typename T>
|
||||||
|
void shuffleArray (Random& r, T array)
|
||||||
|
{
|
||||||
|
const int n = array.size();
|
||||||
|
for (int i = n - 1; i >= 1; i--)
|
||||||
|
{
|
||||||
|
int j = r.nextInt (i + 1);
|
||||||
|
array.swap (i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Based on reference implementation of Perlin Noise by Ken Perlin
|
||||||
|
// http://mrl.nyu.edu/~perlin/paper445.pdf
|
||||||
|
template <class T>
|
||||||
|
class PerlinNoise
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PerlinNoise()
|
||||||
|
{
|
||||||
|
p = { 151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,
|
||||||
|
8,99,37,240,21,10,23,190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,
|
||||||
|
35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,
|
||||||
|
134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,
|
||||||
|
55,46,245,40,244,102,143,54, 65,25,63,161,1,216,80,73,209,76,132,187,208, 89,
|
||||||
|
18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,
|
||||||
|
250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,
|
||||||
|
189,28,42,223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167,
|
||||||
|
43,172,9,129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,
|
||||||
|
97,228,251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,
|
||||||
|
107,49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
|
||||||
|
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 };
|
||||||
|
|
||||||
|
p.addArray (p);
|
||||||
|
}
|
||||||
|
|
||||||
|
PerlinNoise (unsigned int seed)
|
||||||
|
{
|
||||||
|
Random r (seed);
|
||||||
|
|
||||||
|
for (int i = 0; i <= 255; i++)
|
||||||
|
p.add (i);
|
||||||
|
|
||||||
|
shuffleArray (r, p);
|
||||||
|
|
||||||
|
p.addArray (p);
|
||||||
|
}
|
||||||
|
|
||||||
|
T noise (T x, T y = 0, T z = 0)
|
||||||
|
{
|
||||||
|
int X = (int) std::floor (x) & 255;
|
||||||
|
int Y = (int) std::floor (y) & 255;
|
||||||
|
int Z = (int) std::floor (z) & 255;
|
||||||
|
|
||||||
|
x -= std::floor (x);
|
||||||
|
y -= std::floor (y);
|
||||||
|
z -= std::floor (z);
|
||||||
|
|
||||||
|
T u = fade (x);
|
||||||
|
T v = fade (y);
|
||||||
|
T w = fade (z);
|
||||||
|
|
||||||
|
int A = p[X] + Y;
|
||||||
|
int AA = p[A] + Z;
|
||||||
|
int AB = p[A + 1] + Z;
|
||||||
|
int B = p[X + 1] + Y;
|
||||||
|
int BA = p[B] + Z;
|
||||||
|
int BB = p[B + 1] + Z;
|
||||||
|
|
||||||
|
// Add blended results from 8 corners of cube
|
||||||
|
T res = lerp (w,
|
||||||
|
lerp (v, lerp (u, grad(p[AA], x, y, z),
|
||||||
|
grad (p[BA], x-1, y, z)),
|
||||||
|
lerp (u, grad (p[AB], x, y-1, z),
|
||||||
|
grad (p[BB], x-1, y-1, z))),
|
||||||
|
lerp (v, lerp (u, grad (p[AA+1], x, y, z-1),
|
||||||
|
grad (p[BA+1], x-1, y, z-1)),
|
||||||
|
lerp (u, grad (p[AB+1], x, y-1, z-1),
|
||||||
|
grad (p[BB+1], x-1, y-1, z-1))));
|
||||||
|
|
||||||
|
return T ((res + 1.0) / 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T fade (T t)
|
||||||
|
{
|
||||||
|
return t * t * t * (t * (t * 6 - 15) + 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
T lerp (T t, T a, T b)
|
||||||
|
{
|
||||||
|
return a + t * (b - a);
|
||||||
|
}
|
||||||
|
|
||||||
|
T grad (int hash, T x, T y, T z)
|
||||||
|
{
|
||||||
|
int h = hash & 15;
|
||||||
|
T u = h < 8 ? x : y, v = h < 4 ? y : h == 12 || h == 14 ? x : z;
|
||||||
|
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<int> p;
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Keeps a rolling average of a series of numbers
|
||||||
|
class RollingAverage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RollingAverage (int numVals_)
|
||||||
|
: numVals (numVals_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
double average (double nextValue)
|
||||||
|
{
|
||||||
|
return (nextValue + numVals * currAvg) / (double)(numVals + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
double getAverage()
|
||||||
|
{
|
||||||
|
return currAvg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAverage (double avg)
|
||||||
|
{
|
||||||
|
currAvg = avg;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int numVals = 0;
|
||||||
|
double currAvg = 0.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Returns the next colour in a set where the hues differ by the golden ratio.
|
||||||
|
// Good for coming up with a random set of colours
|
||||||
|
Colour goldenRatioColor (int idx);
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Async Download. Doesn't have the main thread pause the URL::downloadToFile has
|
||||||
|
class AsyncDownload : private Thread,
|
||||||
|
private AsyncUpdater
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AsyncDownload (String url_, std::function<void (AsyncDownload*, juce::MemoryBlock, bool)> cb_, int timeoutMS_ = 0)
|
||||||
|
: Thread ("AsyncDownload"), url (url_), cb (cb_), timeoutMS (timeoutMS_)
|
||||||
|
{
|
||||||
|
startThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncDownload (URL url_, std::function<void (AsyncDownload*, juce::MemoryBlock, bool)> cb_, int timeoutMS_ = 0)
|
||||||
|
: Thread ("AsyncDownload"), url (url_), cb (cb_), timeoutMS (timeoutMS_)
|
||||||
|
{
|
||||||
|
startThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
~AsyncDownload() override
|
||||||
|
{
|
||||||
|
stopThread (100);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
ok = readEntireBinaryStream (data);
|
||||||
|
triggerAsyncUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readEntireBinaryStream (MemoryBlock& destData, bool usePostCommand = false)
|
||||||
|
{
|
||||||
|
const std::unique_ptr<InputStream> in (url.isLocalFile() ? url.getLocalFile().createInputStream() : url.createInputStream (usePostCommand, nullptr, nullptr, {}, timeoutMS));
|
||||||
|
|
||||||
|
if (in != nullptr)
|
||||||
|
{
|
||||||
|
in->readIntoMemoryBlock (destData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleAsyncUpdate() override
|
||||||
|
{
|
||||||
|
if (cb)
|
||||||
|
cb (this, data, ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
URL url;
|
||||||
|
std::function<void (AsyncDownload*, juce::MemoryBlock, bool)> cb;
|
||||||
|
int timeoutMS = 0;
|
||||||
|
bool ok = false;
|
||||||
|
juce::MemoryBlock data;
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Time Profiler -- get a quick idea how long something takes
|
||||||
|
class TimeProfiler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TimeProfiler (const String& name_) :
|
||||||
|
name (name_), start (Time::getMillisecondCounterHiRes()) {}
|
||||||
|
|
||||||
|
~TimeProfiler()
|
||||||
|
{
|
||||||
|
DBG (name + String::formatted (" %.2fs", (Time::getMillisecondCounterHiRes() - start) / 1000.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
String name;
|
||||||
|
double start;
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
template <typename T>
|
||||||
|
inline bool almostEqual (T a, T b, T precision = T (0.00001))
|
||||||
|
{
|
||||||
|
return std::abs (a - b) < precision;
|
||||||
|
}
|
||||||
|
|
||||||
|
int versionStringToInt (const String& versionString);
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
void delayedLambda (std::function<void ()> callback, int delayMS);
|
70
Modules/gin/utilities/valuetreeobject.cpp
Executable file
70
Modules/gin/utilities/valuetreeobject.cpp
Executable file
|
@ -0,0 +1,70 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
std::function<ValueTreeObject* (const Identifier&, const ValueTree&)> ValueTreeObject::factory;
|
||||||
|
|
||||||
|
ValueTreeObject::ValueTreeObject (const ValueTree& state_)
|
||||||
|
: state (state_)
|
||||||
|
{
|
||||||
|
for (auto c : state)
|
||||||
|
{
|
||||||
|
if (auto* newObj = factory (c.getType(), c))
|
||||||
|
{
|
||||||
|
newObj->parent = this;
|
||||||
|
children.add (newObj);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
jassertfalse; // type missing in factory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.addListener (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueTreeObject::valueTreePropertyChanged (ValueTree& p, const Identifier& i)
|
||||||
|
{
|
||||||
|
ignoreUnused (p, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueTreeObject::valueTreeChildAdded (ValueTree& p, ValueTree& c)
|
||||||
|
{
|
||||||
|
if (p == state)
|
||||||
|
{
|
||||||
|
if (auto* newObj = factory (c.getType(), c))
|
||||||
|
{
|
||||||
|
newObj->parent = this;
|
||||||
|
children.insert (p.indexOf (c), newObj);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
jassertfalse; // type missing in factory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueTreeObject::valueTreeChildRemoved (ValueTree& p, ValueTree& c, int i)
|
||||||
|
{
|
||||||
|
ignoreUnused (c);
|
||||||
|
|
||||||
|
if (p == state)
|
||||||
|
children.remove (i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueTreeObject::valueTreeChildOrderChanged (ValueTree& p, int oldIndex, int newIndex)
|
||||||
|
{
|
||||||
|
if (p == state)
|
||||||
|
children.move (oldIndex, newIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueTreeObject::valueTreeParentChanged (ValueTree&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueTreeObject::valueTreeRedirected (ValueTree&)
|
||||||
|
{
|
||||||
|
}
|
97
Modules/gin/utilities/valuetreeobject.h
Executable file
97
Modules/gin/utilities/valuetreeobject.h
Executable file
|
@ -0,0 +1,97 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/* Mirrors a ValueTree in Objects */
|
||||||
|
class ValueTreeObject : private ValueTree::Listener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ValueTreeObject (const ValueTree& state);
|
||||||
|
|
||||||
|
ValueTree& getState() { return state; }
|
||||||
|
|
||||||
|
static std::function<ValueTreeObject* (const Identifier&, const ValueTree&)> factory;
|
||||||
|
|
||||||
|
public:
|
||||||
|
const OwnedArray<ValueTreeObject>& getChildren() const { return children; }
|
||||||
|
|
||||||
|
template <class TargetClass>
|
||||||
|
TargetClass* findParentOfType() const
|
||||||
|
{
|
||||||
|
auto* p = parent;
|
||||||
|
while (p != nullptr)
|
||||||
|
{
|
||||||
|
if (auto* res = dynamic_cast<TargetClass*> (parent))
|
||||||
|
return res;
|
||||||
|
|
||||||
|
p = p->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TargetClass>
|
||||||
|
Array<TargetClass*> findChildrenOfClass() const
|
||||||
|
{
|
||||||
|
Array<TargetClass*> res;
|
||||||
|
|
||||||
|
for (auto* c : children)
|
||||||
|
if (auto* t = dynamic_cast<TargetClass*> (c))
|
||||||
|
res.add (t);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TargetClass>
|
||||||
|
int countChildrenOfClass() const
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (auto* c : children)
|
||||||
|
if (auto* t = dynamic_cast<TargetClass*> (c))
|
||||||
|
count++;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TargetClass>
|
||||||
|
TargetClass* findChildOfClass (int idx) const
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (auto* c : children)
|
||||||
|
{
|
||||||
|
if (auto* t = dynamic_cast<TargetClass*> (c))
|
||||||
|
{
|
||||||
|
if (count == idx)
|
||||||
|
return t;
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property) override;
|
||||||
|
void valueTreeChildAdded (ValueTree& parentTree, ValueTree& childWhichHasBeenAdded) override;
|
||||||
|
void valueTreeChildRemoved (ValueTree& parentTree, ValueTree& childWhichHasBeenRemoved, int indexFromWhichChildWasRemoved) override;
|
||||||
|
void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved, int oldIndex, int newIndex) override;
|
||||||
|
void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override;
|
||||||
|
void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ValueTree state;
|
||||||
|
ValueTreeObject* parent = nullptr;
|
||||||
|
|
||||||
|
OwnedArray<ValueTreeObject> children;
|
||||||
|
|
||||||
|
JUCE_LEAK_DETECTOR (ValueTreeObject)
|
||||||
|
};
|
93
Modules/gin/utilities/valuetreeutilities.cpp
Executable file
93
Modules/gin/utilities/valuetreeutilities.cpp
Executable file
|
@ -0,0 +1,93 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2019 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
static var toVar (const juce::ValueTree& v)
|
||||||
|
{
|
||||||
|
auto obj = new DynamicObject();
|
||||||
|
|
||||||
|
obj->setProperty ("_name", v.getType().toString());
|
||||||
|
|
||||||
|
Array<var> children;
|
||||||
|
|
||||||
|
for (auto c : v)
|
||||||
|
children.add (toVar (c));
|
||||||
|
|
||||||
|
if (children.size() > 0)
|
||||||
|
obj->setProperty ("_children", children);
|
||||||
|
|
||||||
|
for (int i = 0; i < v.getNumProperties(); i++)
|
||||||
|
{
|
||||||
|
auto name = v.getPropertyName (i).toString();
|
||||||
|
auto val = v.getProperty (name, {});
|
||||||
|
|
||||||
|
if (auto mb = val.getBinaryData())
|
||||||
|
{
|
||||||
|
obj->setProperty ("base64:" + name, mb->toBase64Encoding());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// These types can't be stored as JSON!
|
||||||
|
jassert (! val.isObject());
|
||||||
|
jassert (! val.isMethod());
|
||||||
|
jassert (! val.isArray());
|
||||||
|
|
||||||
|
obj->setProperty (name, val.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return var (obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
juce::String valueTreeToJSON (const juce::ValueTree& v)
|
||||||
|
{
|
||||||
|
auto obj = toVar (v);
|
||||||
|
return JSON::toString (obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
juce::ValueTree fromVar (const juce::var& obj)
|
||||||
|
{
|
||||||
|
if (auto dobj = obj.getDynamicObject())
|
||||||
|
{
|
||||||
|
ValueTree v (dobj->getProperty ("_name").toString());
|
||||||
|
|
||||||
|
auto c = dobj->getProperty ("_children");
|
||||||
|
if (c.isArray())
|
||||||
|
for (auto& child : *c.getArray())
|
||||||
|
v.addChild (fromVar (child), -1, nullptr);
|
||||||
|
|
||||||
|
auto properties = dobj->getProperties();
|
||||||
|
for (auto itr : properties)
|
||||||
|
{
|
||||||
|
auto name = itr.name.toString();
|
||||||
|
if (name == "_name" || name == "_children") continue;
|
||||||
|
|
||||||
|
if (name.startsWith ("base64:"))
|
||||||
|
{
|
||||||
|
MemoryBlock mb;
|
||||||
|
if (mb.fromBase64Encoding (itr.value.toString()))
|
||||||
|
v.setProperty (name.substring (7), var (mb), nullptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
v.setProperty (name, var (itr.value), nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
juce::ValueTree valueTreeFromJSON (const juce::String& jsonText)
|
||||||
|
{
|
||||||
|
var obj = JSON::parse (jsonText);
|
||||||
|
if (obj.isObject())
|
||||||
|
return fromVar (obj);
|
||||||
|
return {};
|
||||||
|
}
|
173
Modules/gin/utilities/valuetreeutilities.h
Executable file
173
Modules/gin/utilities/valuetreeutilities.h
Executable file
|
@ -0,0 +1,173 @@
|
||||||
|
/*==============================================================================
|
||||||
|
|
||||||
|
Copyright 2018 by Roland Rabien
|
||||||
|
For more information visit www.rabiensoftware.com
|
||||||
|
|
||||||
|
==============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
juce::String valueTreeToJSON (const juce::ValueTree& v);
|
||||||
|
|
||||||
|
juce::ValueTree valueTreeFromJSON (const juce::String& jsonText);
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
class LambdaValueTreeListener : public ValueTree::Listener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LambdaValueTreeListener (ValueTree& v_)
|
||||||
|
: vt (v_)
|
||||||
|
{
|
||||||
|
vt.addListener (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~LambdaValueTreeListener() override
|
||||||
|
{
|
||||||
|
vt.removeListener (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::function<void (ValueTree&, const Identifier&)> onValueTreePropertyChanged;
|
||||||
|
std::function<void (ValueTree&, ValueTree&)> onValueTreeChildAdded;
|
||||||
|
std::function<void (ValueTree&, ValueTree&, int)> onValueTreeChildRemoved;
|
||||||
|
std::function<void (ValueTree&, int, int)> onValueTreeChildOrderChanged;
|
||||||
|
std::function<void (ValueTree&)> onValueTreeParentChanged;
|
||||||
|
std::function<void (ValueTree&)> onValueTreeRedirected;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void valueTreePropertyChanged (ValueTree&v , const Identifier& i) override
|
||||||
|
{
|
||||||
|
if (onValueTreePropertyChanged)
|
||||||
|
onValueTreePropertyChanged (v, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void valueTreeChildAdded (ValueTree& p, ValueTree& c) override
|
||||||
|
{
|
||||||
|
if (onValueTreeChildAdded)
|
||||||
|
onValueTreeChildAdded (p, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void valueTreeChildRemoved (ValueTree& p, ValueTree& c, int n) override
|
||||||
|
{
|
||||||
|
if (onValueTreeChildRemoved)
|
||||||
|
onValueTreeChildRemoved (p, c, n);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void valueTreeChildOrderChanged (ValueTree& v, int a, int b) override
|
||||||
|
{
|
||||||
|
if (onValueTreeChildOrderChanged)
|
||||||
|
onValueTreeChildOrderChanged (v, a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void valueTreeParentChanged (ValueTree& v) override
|
||||||
|
{
|
||||||
|
if (onValueTreeParentChanged)
|
||||||
|
onValueTreeParentChanged (v);
|
||||||
|
}
|
||||||
|
|
||||||
|
void valueTreeRedirected (ValueTree& v) override
|
||||||
|
{
|
||||||
|
if (onValueTreeRedirected)
|
||||||
|
onValueTreeRedirected (v);
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueTree& vt;
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
class AsyncLambdaValueTreeListener : public ValueTree::Listener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AsyncLambdaValueTreeListener (ValueTree& v_)
|
||||||
|
: vt (v_)
|
||||||
|
{
|
||||||
|
vt.addListener (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~AsyncLambdaValueTreeListener() override
|
||||||
|
{
|
||||||
|
vt.removeListener (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::function<void (const ValueTree&, const Identifier&)> onValueTreePropertyChanged;
|
||||||
|
std::function<void (const ValueTree&, const ValueTree&)> onValueTreeChildAdded;
|
||||||
|
std::function<void (const ValueTree&, const ValueTree&, int)> onValueTreeChildRemoved;
|
||||||
|
std::function<void (const ValueTree&, int, int)> onValueTreeChildOrderChanged;
|
||||||
|
std::function<void (const ValueTree&)> onValueTreeParentChanged;
|
||||||
|
std::function<void (const ValueTree&)> onValueTreeRedirected;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void valueTreePropertyChanged (ValueTree& v, const Identifier& i) override
|
||||||
|
{
|
||||||
|
ValueTree vc = v;
|
||||||
|
Identifier ic = i;
|
||||||
|
WeakReference<AsyncLambdaValueTreeListener> weakThis (this);
|
||||||
|
MessageManager::getInstance()->callAsync ([this, weakThis, vc, ic]
|
||||||
|
{
|
||||||
|
if (weakThis != nullptr && onValueTreePropertyChanged)
|
||||||
|
onValueTreePropertyChanged (vc, ic);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void valueTreeChildAdded (ValueTree& p, ValueTree& c) override
|
||||||
|
{
|
||||||
|
ValueTree pc = p;
|
||||||
|
ValueTree cc = c;
|
||||||
|
WeakReference<AsyncLambdaValueTreeListener> weakThis (this);
|
||||||
|
MessageManager::getInstance()->callAsync ([this, weakThis, pc, cc]
|
||||||
|
{
|
||||||
|
if (weakThis != nullptr && onValueTreeChildAdded)
|
||||||
|
onValueTreeChildAdded (pc, cc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void valueTreeChildRemoved (ValueTree& p, ValueTree& c, int n) override
|
||||||
|
{
|
||||||
|
ValueTree pc = p;
|
||||||
|
ValueTree cc = c;
|
||||||
|
WeakReference<AsyncLambdaValueTreeListener> weakThis (this);
|
||||||
|
MessageManager::getInstance()->callAsync ([this, weakThis, pc, cc, n]
|
||||||
|
{
|
||||||
|
if (weakThis != nullptr && onValueTreeChildRemoved)
|
||||||
|
onValueTreeChildRemoved (pc, cc, n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void valueTreeChildOrderChanged (ValueTree& v, int a, int b) override
|
||||||
|
{
|
||||||
|
ValueTree vc = v;
|
||||||
|
WeakReference<AsyncLambdaValueTreeListener> weakThis (this);
|
||||||
|
MessageManager::getInstance()->callAsync ([this, weakThis, vc, a, b]
|
||||||
|
{
|
||||||
|
if (weakThis != nullptr && onValueTreeChildOrderChanged)
|
||||||
|
onValueTreeChildOrderChanged (vc, a, b);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void valueTreeParentChanged (ValueTree& v) override
|
||||||
|
{
|
||||||
|
ValueTree vc = v;
|
||||||
|
WeakReference<AsyncLambdaValueTreeListener> weakThis (this);
|
||||||
|
MessageManager::getInstance()->callAsync ([this, weakThis, vc]
|
||||||
|
{
|
||||||
|
if (weakThis != nullptr && onValueTreeParentChanged)
|
||||||
|
onValueTreeParentChanged (vc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void valueTreeRedirected (ValueTree& v) override
|
||||||
|
{
|
||||||
|
ValueTree vc = v;
|
||||||
|
WeakReference<AsyncLambdaValueTreeListener> weakThis (this);
|
||||||
|
MessageManager::getInstance()->callAsync ([this, weakThis, vc]
|
||||||
|
{
|
||||||
|
if (weakThis != nullptr && onValueTreeRedirected)
|
||||||
|
onValueTreeRedirected (vc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueTree& vt;
|
||||||
|
|
||||||
|
JUCE_DECLARE_WEAK_REFERENCEABLE (AsyncLambdaValueTreeListener)
|
||||||
|
};
|
Loading…
Reference in a new issue