Remove GIN module
This commit is contained in:
parent
e6c61b404c
commit
1f5fb7b33b
90 changed files with 0 additions and 26503 deletions
26
Modules/gin/3rdparty/avir/LICENSE
vendored
26
Modules/gin/3rdparty/avir/LICENSE
vendored
|
@ -1,26 +0,0 @@
|
||||||
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
330
Modules/gin/3rdparty/avir/README.md
vendored
|
@ -1,330 +0,0 @@
|
||||||
# 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
6198
Modules/gin/3rdparty/avir/avir.h
vendored
File diff suppressed because it is too large
Load diff
1012
Modules/gin/3rdparty/avir/avir_dil.h
vendored
1012
Modules/gin/3rdparty/avir/avir_dil.h
vendored
File diff suppressed because it is too large
Load diff
319
Modules/gin/3rdparty/avir/avir_float4_sse.h
vendored
319
Modules/gin/3rdparty/avir/avir_float4_sse.h
vendored
|
@ -1,319 +0,0 @@
|
||||||
//$ 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
359
Modules/gin/3rdparty/avir/avir_float8_avx.h
vendored
|
@ -1,359 +0,0 @@
|
||||||
//$ 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
397
Modules/gin/3rdparty/muParser/muParser.cpp
vendored
|
@ -1,397 +0,0 @@
|
||||||
/*
|
|
||||||
__________
|
|
||||||
_____ __ __\______ \_____ _______ ______ ____ _______
|
|
||||||
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
|
||||||
| 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
114
Modules/gin/3rdparty/muParser/muParser.h
vendored
|
@ -1,114 +0,0 @@
|
||||||
/*
|
|
||||||
__________
|
|
||||||
_____ __ __\______ \_____ _______ ______ ____ _______
|
|
||||||
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
|
||||||
| 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
1805
Modules/gin/3rdparty/muParser/muParserBase.cpp
vendored
File diff suppressed because it is too large
Load diff
324
Modules/gin/3rdparty/muParser/muParserBase.h
vendored
324
Modules/gin/3rdparty/muParser/muParserBase.h
vendored
|
@ -1,324 +0,0 @@
|
||||||
/*
|
|
||||||
__________
|
|
||||||
_____ __ __\______ \_____ _______ ______ ____ _______
|
|
||||||
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
|
||||||
| 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
604
Modules/gin/3rdparty/muParser/muParserBytecode.cpp
vendored
|
@ -1,604 +0,0 @@
|
||||||
/*
|
|
||||||
__________
|
|
||||||
_____ __ __\______ \_____ _______ ______ ____ _______
|
|
||||||
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
|
||||||
| 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
143
Modules/gin/3rdparty/muParser/muParserBytecode.h
vendored
|
@ -1,143 +0,0 @@
|
||||||
/*
|
|
||||||
__________
|
|
||||||
_____ __ __\______ \_____ _______ ______ ____ _______
|
|
||||||
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
|
||||||
| 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
497
Modules/gin/3rdparty/muParser/muParserCallback.cpp
vendored
|
@ -1,497 +0,0 @@
|
||||||
/*
|
|
||||||
__________
|
|
||||||
_____ __ __\______ \_____ _______ ______ ____ _______
|
|
||||||
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
|
||||||
| 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
119
Modules/gin/3rdparty/muParser/muParserCallback.h
vendored
|
@ -1,119 +0,0 @@
|
||||||
/*
|
|
||||||
__________
|
|
||||||
_____ __ __\______ \_____ _______ ______ ____ _______
|
|
||||||
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
|
||||||
| 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
372
Modules/gin/3rdparty/muParser/muParserDef.h
vendored
|
@ -1,372 +0,0 @@
|
||||||
/*
|
|
||||||
__________
|
|
||||||
_____ __ __\______ \_____ _______ ______ ____ _______
|
|
||||||
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
|
||||||
| 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
345
Modules/gin/3rdparty/muParser/muParserError.cpp
vendored
|
@ -1,345 +0,0 @@
|
||||||
/*
|
|
||||||
__________
|
|
||||||
_____ __ __\______ \_____ _______ ______ ____ _______
|
|
||||||
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
|
||||||
| 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
175
Modules/gin/3rdparty/muParser/muParserError.h
vendored
|
@ -1,175 +0,0 @@
|
||||||
/*
|
|
||||||
__________
|
|
||||||
_____ __ __\______ \_____ _______ ______ ____ _______
|
|
||||||
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
|
||||||
| 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
60
Modules/gin/3rdparty/muParser/muParserFixes.h
vendored
|
@ -1,60 +0,0 @@
|
||||||
/*
|
|
||||||
__________
|
|
||||||
_____ __ __\______ \_____ _______ ______ ____ _______
|
|
||||||
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
|
||||||
| 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
125
Modules/gin/3rdparty/muParser/muParserStack.h
vendored
|
@ -1,125 +0,0 @@
|
||||||
/*
|
|
||||||
__________
|
|
||||||
_____ __ __\______ \_____ _______ ______ ____ _______
|
|
||||||
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
|
||||||
| 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
|
|
|
@ -1,113 +0,0 @@
|
||||||
#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
406
Modules/gin/3rdparty/muParser/muParserToken.h
vendored
|
@ -1,406 +0,0 @@
|
||||||
/*
|
|
||||||
__________
|
|
||||||
_____ __ __\______ \_____ _______ ______ ____ _______
|
|
||||||
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
|
||||||
| 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
|
|
|
@ -1,982 +0,0 @@
|
||||||
/*
|
|
||||||
__________
|
|
||||||
_____ __ __\______ \_____ _______ ______ ____ _______
|
|
||||||
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
|
||||||
| 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
159
Modules/gin/3rdparty/muParser/muParserTokenReader.h
vendored
|
@ -1,159 +0,0 @@
|
||||||
/*
|
|
||||||
__________
|
|
||||||
_____ __ __\______ \_____ _______ ______ ____ _______
|
|
||||||
/ \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
|
|
||||||
| 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
|
|
|
@ -1,2 +0,0 @@
|
||||||
# gin
|
|
||||||
Shared generic tools, utilities and UI classes
|
|
|
@ -1,20 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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 );
|
|
||||||
}
|
|
|
@ -1,364 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
|
@ -1,193 +0,0 @@
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
#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;
|
|
||||||
};
|
|
|
@ -1,165 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
|
@ -1,8 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
Copyright 2018 by Roland Rabien
|
|
||||||
For more information visit www.rabiensoftware.com
|
|
||||||
|
|
||||||
==============================================================================*/
|
|
||||||
|
|
||||||
|
|
|
@ -1,154 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,617 +0,0 @@
|
||||||
/*
|
|
||||||
==============================================================================
|
|
||||||
|
|
||||||
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)
|
|
||||||
};
|
|
|
@ -1,112 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,134 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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
|
|
|
@ -1,106 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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"
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
Copyright 2018 by Roland Rabien
|
|
||||||
For more information visit www.rabiensoftware.com
|
|
||||||
|
|
||||||
==============================================================================*/
|
|
||||||
|
|
||||||
#include "gin.cpp"
|
|
||||||
|
|
|
@ -1,153 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
|
@ -1,811 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,117 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
|
|
@ -1,282 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
|
@ -1,717 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
Copyright 2019 by Roland Rabien
|
|
||||||
For more information visit www.rabiensoftware.com
|
|
||||||
|
|
||||||
==============================================================================*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
juce::Image rasterizeSVG (juce::String svgText, int w, int h);
|
|
|
@ -1,32 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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 );
|
|
|
@ -1,89 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
|
@ -1,308 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,162 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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)
|
|
||||||
};
|
|
|
@ -1,289 +0,0 @@
|
||||||
// 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;
|
|
||||||
}
|
|
|
@ -1,354 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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
|
|
|
@ -1,82 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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
|
|
|
@ -1,233 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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)
|
|
||||||
};
|
|
|
@ -1,329 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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
|
|
|
@ -1,93 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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
|
|
|
@ -1,43 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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)
|
|
||||||
};
|
|
|
@ -1,70 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,266 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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)
|
|
||||||
};
|
|
|
@ -1,68 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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)
|
|
||||||
};
|
|
|
@ -1,442 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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);
|
|
||||||
};
|
|
|
@ -1,253 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
|
@ -1,100 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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)
|
|
||||||
};
|
|
|
@ -1,110 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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 {};
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
|
@ -1,131 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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)
|
|
||||||
};
|
|
|
@ -1,160 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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)
|
|
||||||
};
|
|
|
@ -1,78 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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)
|
|
||||||
};
|
|
|
@ -1,110 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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)
|
|
||||||
};
|
|
|
@ -1,43 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,267 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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);
|
|
|
@ -1,70 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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&)
|
|
||||||
{
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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)
|
|
||||||
};
|
|
|
@ -1,93 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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 {};
|
|
||||||
}
|
|
|
@ -1,173 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
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