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