You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
591 lines
18 KiB
591 lines
18 KiB
2 years ago
|
//----------------------------------------------------------------------------
|
||
|
// Anti-Grain Geometry - Version 2.4
|
||
|
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
|
||
|
//
|
||
|
// Permission to copy, use, modify, sell and distribute this software
|
||
|
// is granted provided this copyright notice appears in all copies.
|
||
|
// This software is provided "as is" without express or implied
|
||
|
// warranty, and with no claim as to its suitability for any purpose.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Contact: mcseem@antigrain.com
|
||
|
// mcseemagg@yahoo.com
|
||
|
// http://www.antigrain.com
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
#ifndef AGG_BASICS_INCLUDED
|
||
|
#define AGG_BASICS_INCLUDED
|
||
|
|
||
|
#include <cmath>
|
||
|
#include "agg_config.h"
|
||
|
|
||
|
#include <eez/core/alloc.h>
|
||
|
|
||
|
//---------------------------------------------------------AGG_CUSTOM_ALLOCATOR
|
||
|
#ifdef AGG_CUSTOM_ALLOCATOR
|
||
|
#include "agg_allocator.h"
|
||
|
#else
|
||
|
namespace agg
|
||
|
{
|
||
|
// The policy of all AGG containers and memory allocation strategy
|
||
|
// in general is that no allocated data requires explicit construction.
|
||
|
// It means that the allocator can be really simple; you can even
|
||
|
// replace new/delete to malloc/free. The constructors and destructors
|
||
|
// won't be called in this case, however everything will remain working.
|
||
|
// The second argument of deallocate() is the size of the allocated
|
||
|
// block. You can use this information if you wish.
|
||
|
//------------------------------------------------------------pod_allocator
|
||
|
template<class T> struct pod_allocator
|
||
|
{
|
||
|
static T* allocate(unsigned num) {
|
||
|
//return new T [num];
|
||
|
return (T*) eez::alloc(num * sizeof(T), 0xf1522a42);
|
||
|
}
|
||
|
static void deallocate(T* ptr, unsigned) {
|
||
|
//delete [] ptr;
|
||
|
eez::free(ptr);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Single object allocator. It's also can be replaced with your custom
|
||
|
// allocator. The difference is that it can only allocate a single
|
||
|
// object and the constructor and destructor must be called.
|
||
|
// In AGG there is no need to allocate an array of objects with
|
||
|
// calling their constructors (only single ones). So that, if you
|
||
|
// replace these new/delete to malloc/free make sure that the in-place
|
||
|
// new is called and take care of calling the destructor too.
|
||
|
//------------------------------------------------------------obj_allocator
|
||
|
template<class T> struct obj_allocator
|
||
|
{
|
||
|
static T* allocate() {
|
||
|
// return new T;
|
||
|
auto ptr = eez::alloc(sizeof(T), 0x338f85b9);
|
||
|
return new (ptr) T;
|
||
|
}
|
||
|
static void deallocate(T* ptr) {
|
||
|
//delete ptr;
|
||
|
ptr->~T();
|
||
|
eez::free(ptr);
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
//-------------------------------------------------------- Default basic types
|
||
|
//
|
||
|
// If the compiler has different capacity of the basic types you can redefine
|
||
|
// them via the compiler command line or by generating agg_config.h that is
|
||
|
// empty by default.
|
||
|
//
|
||
|
#ifndef AGG_INT8
|
||
|
#define AGG_INT8 signed char
|
||
|
#endif
|
||
|
|
||
|
#ifndef AGG_INT8U
|
||
|
#define AGG_INT8U unsigned char
|
||
|
#endif
|
||
|
|
||
|
#ifndef AGG_INT16
|
||
|
#define AGG_INT16 short
|
||
|
#endif
|
||
|
|
||
|
#ifndef AGG_INT16U
|
||
|
#define AGG_INT16U unsigned short
|
||
|
#endif
|
||
|
|
||
|
#ifndef AGG_INT32
|
||
|
#define AGG_INT32 int
|
||
|
#endif
|
||
|
|
||
|
#ifndef AGG_INT32U
|
||
|
#define AGG_INT32U unsigned
|
||
|
#endif
|
||
|
|
||
|
#ifndef AGG_INT64
|
||
|
#if defined(_MSC_VER) || defined(__BORLANDC__)
|
||
|
#define AGG_INT64 signed __int64
|
||
|
#else
|
||
|
#define AGG_INT64 signed long long
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
#ifndef AGG_INT64U
|
||
|
#if defined(_MSC_VER) || defined(__BORLANDC__)
|
||
|
#define AGG_INT64U unsigned __int64
|
||
|
#else
|
||
|
#define AGG_INT64U unsigned long long
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
//------------------------------------------------ Some fixes for MS Visual C++
|
||
|
#if defined(_MSC_VER)
|
||
|
#pragma warning(disable:4786) // Identifier was truncated...
|
||
|
#endif
|
||
|
|
||
|
#if defined(_MSC_VER)
|
||
|
#define AGG_INLINE __forceinline
|
||
|
#else
|
||
|
#define AGG_INLINE inline
|
||
|
#endif
|
||
|
|
||
|
namespace agg
|
||
|
{
|
||
|
//-------------------------------------------------------------------------
|
||
|
typedef AGG_INT8 int8; //----int8
|
||
|
typedef AGG_INT8U int8u; //----int8u
|
||
|
typedef AGG_INT16 int16; //----int16
|
||
|
typedef AGG_INT16U int16u; //----int16u
|
||
|
typedef AGG_INT32 int32; //----int32
|
||
|
typedef AGG_INT32U int32u; //----int32u
|
||
|
typedef AGG_INT64 int64; //----int64
|
||
|
typedef AGG_INT64U int64u; //----int64u
|
||
|
|
||
|
#if defined(AGG_FISTP)
|
||
|
#pragma warning(push)
|
||
|
#pragma warning(disable : 4035) //Disable warning "no return value"
|
||
|
AGG_INLINE int iround(double v) //-------iround
|
||
|
{
|
||
|
int t;
|
||
|
__asm fld qword ptr [v]
|
||
|
__asm fistp dword ptr [t]
|
||
|
__asm mov eax, dword ptr [t]
|
||
|
}
|
||
|
AGG_INLINE unsigned uround(double v) //-------uround
|
||
|
{
|
||
|
unsigned t;
|
||
|
__asm fld qword ptr [v]
|
||
|
__asm fistp dword ptr [t]
|
||
|
__asm mov eax, dword ptr [t]
|
||
|
}
|
||
|
#pragma warning(pop)
|
||
|
AGG_INLINE int ifloor(double v)
|
||
|
{
|
||
|
return int(floor(v));
|
||
|
}
|
||
|
AGG_INLINE unsigned ufloor(double v) //-------ufloor
|
||
|
{
|
||
|
return unsigned(floor(v));
|
||
|
}
|
||
|
AGG_INLINE int iceil(double v)
|
||
|
{
|
||
|
return int(ceil(v));
|
||
|
}
|
||
|
AGG_INLINE unsigned uceil(double v) //--------uceil
|
||
|
{
|
||
|
return unsigned(ceil(v));
|
||
|
}
|
||
|
#elif defined(AGG_QIFIST)
|
||
|
AGG_INLINE int iround(double v)
|
||
|
{
|
||
|
return int(v);
|
||
|
}
|
||
|
AGG_INLINE int uround(double v)
|
||
|
{
|
||
|
return unsigned(v);
|
||
|
}
|
||
|
AGG_INLINE int ifloor(double v)
|
||
|
{
|
||
|
return int(std::floor(v));
|
||
|
}
|
||
|
AGG_INLINE unsigned ufloor(double v)
|
||
|
{
|
||
|
return unsigned(std::floor(v));
|
||
|
}
|
||
|
AGG_INLINE int iceil(double v)
|
||
|
{
|
||
|
return int(std::ceil(v));
|
||
|
}
|
||
|
AGG_INLINE unsigned uceil(double v)
|
||
|
{
|
||
|
return unsigned(std::ceil(v));
|
||
|
}
|
||
|
#else
|
||
|
AGG_INLINE int iround(double v)
|
||
|
{
|
||
|
return int((v < 0.0) ? v - 0.5 : v + 0.5);
|
||
|
}
|
||
|
AGG_INLINE int uround(double v)
|
||
|
{
|
||
|
return unsigned(v + 0.5);
|
||
|
}
|
||
|
AGG_INLINE int ifloor(double v)
|
||
|
{
|
||
|
int i = int(v);
|
||
|
return i - (i > v);
|
||
|
}
|
||
|
AGG_INLINE unsigned ufloor(double v)
|
||
|
{
|
||
|
return unsigned(v);
|
||
|
}
|
||
|
AGG_INLINE int iceil(double v)
|
||
|
{
|
||
|
return int(std::ceil(v));
|
||
|
}
|
||
|
AGG_INLINE unsigned uceil(double v)
|
||
|
{
|
||
|
return unsigned(std::ceil(v));
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//---------------------------------------------------------------saturation
|
||
|
template<int Limit> struct saturation
|
||
|
{
|
||
|
AGG_INLINE static int iround(double v)
|
||
|
{
|
||
|
if(v < double(-Limit)) return -Limit;
|
||
|
if(v > double( Limit)) return Limit;
|
||
|
return agg::iround(v);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//------------------------------------------------------------------mul_one
|
||
|
template<unsigned Shift> struct mul_one
|
||
|
{
|
||
|
AGG_INLINE static unsigned mul(unsigned a, unsigned b)
|
||
|
{
|
||
|
unsigned q = a * b + (1 << (Shift-1));
|
||
|
return (q + (q >> Shift)) >> Shift;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
typedef unsigned char cover_type; //----cover_type
|
||
|
enum cover_scale_e
|
||
|
{
|
||
|
cover_shift = 8, //----cover_shift
|
||
|
cover_size = 1 << cover_shift, //----cover_size
|
||
|
cover_mask = cover_size - 1, //----cover_mask
|
||
|
cover_none = 0, //----cover_none
|
||
|
cover_full = cover_mask //----cover_full
|
||
|
};
|
||
|
|
||
|
//----------------------------------------------------poly_subpixel_scale_e
|
||
|
// These constants determine the subpixel accuracy, to be more precise,
|
||
|
// the number of bits of the fractional part of the coordinates.
|
||
|
// The possible coordinate capacity in bits can be calculated by formula:
|
||
|
// sizeof(int) * 8 - poly_subpixel_shift, i.e, for 32-bit integers and
|
||
|
// 8-bits fractional part the capacity is 24 bits.
|
||
|
enum poly_subpixel_scale_e
|
||
|
{
|
||
|
poly_subpixel_shift = 8, //----poly_subpixel_shift
|
||
|
poly_subpixel_scale = 1<<poly_subpixel_shift, //----poly_subpixel_scale
|
||
|
poly_subpixel_mask = poly_subpixel_scale-1 //----poly_subpixel_mask
|
||
|
};
|
||
|
|
||
|
//----------------------------------------------------------filling_rule_e
|
||
|
enum filling_rule_e
|
||
|
{
|
||
|
fill_non_zero,
|
||
|
fill_even_odd
|
||
|
};
|
||
|
|
||
|
//-----------------------------------------------------------------------pi
|
||
|
const double pi = 3.14159265358979323846;
|
||
|
|
||
|
//------------------------------------------------------------------deg2rad
|
||
|
inline double deg2rad(double deg)
|
||
|
{
|
||
|
return deg * pi / 180.0;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------rad2deg
|
||
|
inline double rad2deg(double rad)
|
||
|
{
|
||
|
return rad * 180.0 / pi;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------rect_base
|
||
|
template<class T> struct rect_base
|
||
|
{
|
||
|
typedef T value_type;
|
||
|
typedef rect_base<T> self_type;
|
||
|
T x1, y1, x2, y2;
|
||
|
|
||
|
rect_base() {}
|
||
|
rect_base(T x1_, T y1_, T x2_, T y2_) :
|
||
|
x1(x1_), y1(y1_), x2(x2_), y2(y2_) {}
|
||
|
|
||
|
void init(T x1_, T y1_, T x2_, T y2_)
|
||
|
{
|
||
|
x1 = x1_; y1 = y1_; x2 = x2_; y2 = y2_;
|
||
|
}
|
||
|
|
||
|
const self_type& normalize()
|
||
|
{
|
||
|
T t;
|
||
|
if(x1 > x2) { t = x1; x1 = x2; x2 = t; }
|
||
|
if(y1 > y2) { t = y1; y1 = y2; y2 = t; }
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
bool clip(const self_type& r)
|
||
|
{
|
||
|
if(x2 > r.x2) x2 = r.x2;
|
||
|
if(y2 > r.y2) y2 = r.y2;
|
||
|
if(x1 < r.x1) x1 = r.x1;
|
||
|
if(y1 < r.y1) y1 = r.y1;
|
||
|
return x1 <= x2 && y1 <= y2;
|
||
|
}
|
||
|
|
||
|
bool is_valid() const
|
||
|
{
|
||
|
return x1 <= x2 && y1 <= y2;
|
||
|
}
|
||
|
|
||
|
bool hit_test(T x, T y) const
|
||
|
{
|
||
|
return (x >= x1 && x <= x2 && y >= y1 && y <= y2);
|
||
|
}
|
||
|
|
||
|
bool overlaps(const self_type& r) const
|
||
|
{
|
||
|
return !(r.x1 > x2 || r.x2 < x1
|
||
|
|| r.y1 > y2 || r.y2 < y1);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//-----------------------------------------------------intersect_rectangles
|
||
|
template<class Rect>
|
||
|
inline Rect intersect_rectangles(const Rect& r1, const Rect& r2)
|
||
|
{
|
||
|
Rect r = r1;
|
||
|
|
||
|
// First process x2,y2 because the other order
|
||
|
// results in Internal Compiler Error under
|
||
|
// Microsoft Visual C++ .NET 2003 69462-335-0000007-18038 in
|
||
|
// case of "Maximize Speed" optimization option.
|
||
|
//-----------------
|
||
|
if(r.x2 > r2.x2) r.x2 = r2.x2;
|
||
|
if(r.y2 > r2.y2) r.y2 = r2.y2;
|
||
|
if(r.x1 < r2.x1) r.x1 = r2.x1;
|
||
|
if(r.y1 < r2.y1) r.y1 = r2.y1;
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
|
||
|
//---------------------------------------------------------unite_rectangles
|
||
|
template<class Rect>
|
||
|
inline Rect unite_rectangles(const Rect& r1, const Rect& r2)
|
||
|
{
|
||
|
Rect r = r1;
|
||
|
if(r.x2 < r2.x2) r.x2 = r2.x2;
|
||
|
if(r.y2 < r2.y2) r.y2 = r2.y2;
|
||
|
if(r.x1 > r2.x1) r.x1 = r2.x1;
|
||
|
if(r.y1 > r2.y1) r.y1 = r2.y1;
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
typedef rect_base<int> rect_i; //----rect_i
|
||
|
typedef rect_base<float> rect_f; //----rect_f
|
||
|
typedef rect_base<double> rect_d; //----rect_d
|
||
|
|
||
|
//---------------------------------------------------------path_commands_e
|
||
|
enum path_commands_e
|
||
|
{
|
||
|
path_cmd_stop = 0, //----path_cmd_stop
|
||
|
path_cmd_move_to = 1, //----path_cmd_move_to
|
||
|
path_cmd_line_to = 2, //----path_cmd_line_to
|
||
|
path_cmd_curve3 = 3, //----path_cmd_curve3
|
||
|
path_cmd_curve4 = 4, //----path_cmd_curve4
|
||
|
path_cmd_curveN = 5, //----path_cmd_curveN
|
||
|
path_cmd_catrom = 6, //----path_cmd_catrom
|
||
|
path_cmd_ubspline = 7, //----path_cmd_ubspline
|
||
|
path_cmd_end_poly = 0x0F, //----path_cmd_end_poly
|
||
|
path_cmd_mask = 0x0F //----path_cmd_mask
|
||
|
};
|
||
|
|
||
|
//------------------------------------------------------------path_flags_e
|
||
|
enum path_flags_e
|
||
|
{
|
||
|
path_flags_none = 0, //----path_flags_none
|
||
|
path_flags_ccw = 0x10, //----path_flags_ccw
|
||
|
path_flags_cw = 0x20, //----path_flags_cw
|
||
|
path_flags_close = 0x40, //----path_flags_close
|
||
|
path_flags_mask = 0xF0 //----path_flags_mask
|
||
|
};
|
||
|
|
||
|
//---------------------------------------------------------------is_vertex
|
||
|
inline bool is_vertex(unsigned c)
|
||
|
{
|
||
|
return c >= path_cmd_move_to && c < path_cmd_end_poly;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------is_drawing
|
||
|
inline bool is_drawing(unsigned c)
|
||
|
{
|
||
|
return c >= path_cmd_line_to && c < path_cmd_end_poly;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------is_stop
|
||
|
inline bool is_stop(unsigned c)
|
||
|
{
|
||
|
return c == path_cmd_stop;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------is_move_to
|
||
|
inline bool is_move_to(unsigned c)
|
||
|
{
|
||
|
return c == path_cmd_move_to;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------is_line_to
|
||
|
inline bool is_line_to(unsigned c)
|
||
|
{
|
||
|
return c == path_cmd_line_to;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------is_curve
|
||
|
inline bool is_curve(unsigned c)
|
||
|
{
|
||
|
return c == path_cmd_curve3 || c == path_cmd_curve4;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------is_curve3
|
||
|
inline bool is_curve3(unsigned c)
|
||
|
{
|
||
|
return c == path_cmd_curve3;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------is_curve4
|
||
|
inline bool is_curve4(unsigned c)
|
||
|
{
|
||
|
return c == path_cmd_curve4;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------is_end_poly
|
||
|
inline bool is_end_poly(unsigned c)
|
||
|
{
|
||
|
return (c & path_cmd_mask) == path_cmd_end_poly;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------is_close
|
||
|
inline bool is_close(unsigned c)
|
||
|
{
|
||
|
return (c & ~(path_flags_cw | path_flags_ccw)) ==
|
||
|
(path_cmd_end_poly | path_flags_close);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------is_next_poly
|
||
|
inline bool is_next_poly(unsigned c)
|
||
|
{
|
||
|
return is_stop(c) || is_move_to(c) || is_end_poly(c);
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------is_cw
|
||
|
inline bool is_cw(unsigned c)
|
||
|
{
|
||
|
return (c & path_flags_cw) != 0;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------is_ccw
|
||
|
inline bool is_ccw(unsigned c)
|
||
|
{
|
||
|
return (c & path_flags_ccw) != 0;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------is_oriented
|
||
|
inline bool is_oriented(unsigned c)
|
||
|
{
|
||
|
return (c & (path_flags_cw | path_flags_ccw)) != 0;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------is_closed
|
||
|
inline bool is_closed(unsigned c)
|
||
|
{
|
||
|
return (c & path_flags_close) != 0;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------get_close_flag
|
||
|
inline unsigned get_close_flag(unsigned c)
|
||
|
{
|
||
|
return c & path_flags_close;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------clear_orientation
|
||
|
inline unsigned clear_orientation(unsigned c)
|
||
|
{
|
||
|
return c & ~(path_flags_cw | path_flags_ccw);
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------get_orientation
|
||
|
inline unsigned get_orientation(unsigned c)
|
||
|
{
|
||
|
return c & (path_flags_cw | path_flags_ccw);
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------set_orientation
|
||
|
inline unsigned set_orientation(unsigned c, unsigned o)
|
||
|
{
|
||
|
return clear_orientation(c) | o;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------point_base
|
||
|
template<class T> struct point_base
|
||
|
{
|
||
|
typedef T value_type;
|
||
|
T x,y;
|
||
|
point_base() {}
|
||
|
point_base(T x_, T y_) : x(x_), y(y_) {}
|
||
|
};
|
||
|
typedef point_base<int> point_i; //-----point_i
|
||
|
typedef point_base<float> point_f; //-----point_f
|
||
|
typedef point_base<double> point_d; //-----point_d
|
||
|
|
||
|
//-------------------------------------------------------------vertex_base
|
||
|
template<class T> struct vertex_base
|
||
|
{
|
||
|
typedef T value_type;
|
||
|
T x,y;
|
||
|
unsigned cmd;
|
||
|
vertex_base() {}
|
||
|
vertex_base(T x_, T y_, unsigned cmd_) : x(x_), y(y_), cmd(cmd_) {}
|
||
|
};
|
||
|
typedef vertex_base<int> vertex_i; //-----vertex_i
|
||
|
typedef vertex_base<float> vertex_f; //-----vertex_f
|
||
|
typedef vertex_base<double> vertex_d; //-----vertex_d
|
||
|
|
||
|
//----------------------------------------------------------------row_info
|
||
|
template<class T> struct row_info
|
||
|
{
|
||
|
int x1, x2;
|
||
|
T* ptr;
|
||
|
row_info() {}
|
||
|
row_info(int x1_, int x2_, T* ptr_) : x1(x1_), x2(x2_), ptr(ptr_) {}
|
||
|
};
|
||
|
|
||
|
//----------------------------------------------------------const_row_info
|
||
|
template<class T> struct const_row_info
|
||
|
{
|
||
|
int x1, x2;
|
||
|
const T* ptr;
|
||
|
const_row_info() {}
|
||
|
const_row_info(int x1_, int x2_, const T* ptr_) :
|
||
|
x1(x1_), x2(x2_), ptr(ptr_) {}
|
||
|
};
|
||
|
|
||
|
//------------------------------------------------------------is_equal_eps
|
||
|
template<class T> inline bool is_equal_eps(T v1, T v2, T epsilon)
|
||
|
{
|
||
|
bool neg1 = v1 < 0.0;
|
||
|
bool neg2 = v2 < 0.0;
|
||
|
|
||
|
if (neg1 != neg2)
|
||
|
return std::fabs(v1) < epsilon && std::fabs(v2) < epsilon;
|
||
|
|
||
|
int int1, int2;
|
||
|
std::frexp(v1, &int1);
|
||
|
std::frexp(v2, &int2);
|
||
|
int min12 = int1 < int2 ? int1 : int2;
|
||
|
|
||
|
v1 = std::ldexp(v1, -min12);
|
||
|
v2 = std::ldexp(v2, -min12);
|
||
|
|
||
|
return std::fabs(v1 - v2) < epsilon;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#endif
|
||
|
|