2
0
Fork 0

Remove GIN module

This commit is contained in:
George Reales 2020-07-21 09:33:44 +02:00
parent e6c61b404c
commit 1f5fb7b33b
90 changed files with 0 additions and 26503 deletions

View file

@ -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"

View file

@ -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.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -1,2 +0,0 @@
# gin
Shared generic tools, utilities and UI classes

View file

@ -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 );
}

View file

@ -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));
}

View file

@ -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;
};

View file

@ -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);
}

View file

@ -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;
};

View file

@ -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();
}

View file

@ -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;
};

View file

@ -1,8 +0,0 @@
/*==============================================================================
Copyright 2018 by Roland Rabien
For more information visit www.rabiensoftware.com
==============================================================================*/

View file

@ -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

View file

@ -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)
};

View file

@ -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;
}
}

View file

@ -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

View file

@ -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"
}

View file

@ -1,9 +0,0 @@
/*==============================================================================
Copyright 2018 by Roland Rabien
For more information visit www.rabiensoftware.com
==============================================================================*/
#include "gin.cpp"

View file

@ -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;
}

View file

@ -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;
};

View file

@ -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;
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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 );

View file

@ -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;
};

View file

@ -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);
});
}
}
}
}

View file

@ -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)
};

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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;
}
}

View file

@ -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)
};

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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);

View file

@ -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());
}

View file

@ -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)
};

View file

@ -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;
}
}

View file

@ -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();
}

View file

@ -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)
};

View file

@ -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;
}
}
}

View file

@ -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)
};

View file

@ -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

View file

@ -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);
};

View file

@ -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;
}

View file

@ -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)
};

View file

@ -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 {};
}

View file

@ -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);

View file

@ -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();
}

View file

@ -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)
};

View file

@ -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);
}

View file

@ -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)
};

View file

@ -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);
}

View file

@ -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)
};

View file

@ -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();
}

View file

@ -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)
};

View file

@ -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);
}

View file

@ -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();
}
}

View file

@ -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);
}

View file

@ -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);

View file

@ -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&)
{
}

View file

@ -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)
};

View file

@ -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 {};
}

View file

@ -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)
};