|
|
2b429e |
#pragma once
|
|
|
2b429e |
|
|
|
2b429e |
#ifndef TRACK_INCLUDED
|
|
|
2b429e |
#define TRACK_INCLUDED
|
|
|
2b429e |
|
|
|
2b429e |
// TnzTools includes
|
|
|
efa14d |
#include <tools inputstate.h=""></tools>
|
|
|
efa14d |
|
|
|
efa14d |
// TnzCore includes
|
|
|
2b429e |
#include <tcommon.h></tcommon.h>
|
|
|
2b429e |
#include <tgeometry.h></tgeometry.h>
|
|
|
2b429e |
|
|
|
2b429e |
// Qt headers
|
|
|
2b429e |
#include <qt></qt>
|
|
|
2b429e |
|
|
|
2b429e |
// std includes
|
|
|
2b429e |
#include <vector></vector>
|
|
|
2b429e |
#include <algorithm></algorithm>
|
|
|
2b429e |
|
|
|
2b429e |
|
|
|
2b429e |
#undef DVAPI
|
|
|
2b429e |
#undef DVVAR
|
|
|
2b429e |
#ifdef TNZTOOLS_EXPORTS
|
|
|
2b429e |
#define DVAPI DV_EXPORT_API
|
|
|
2b429e |
#define DVVAR DV_EXPORT_VAR
|
|
|
2b429e |
#else
|
|
|
2b429e |
#define DVAPI DV_IMPORT_API
|
|
|
2b429e |
#define DVVAR DV_IMPORT_VAR
|
|
|
2b429e |
#endif
|
|
|
2b429e |
|
|
|
2b429e |
|
|
|
2b429e |
//===================================================================
|
|
|
2b429e |
|
|
|
2b429e |
// Forward Declarations
|
|
|
2b429e |
|
|
|
2b429e |
class TTrack;
|
|
|
49945e |
class TTrackPoint;
|
|
|
d8eddc |
class TTrackTangent;
|
|
|
2b429e |
class TTrackHandler;
|
|
|
7a5892 |
class TTrackToolHandler;
|
|
|
2b429e |
class TTrackModifier;
|
|
|
49945e |
|
|
|
2b429e |
typedef TSmartPointerT<ttrack> TTrackP;</ttrack>
|
|
|
2b429e |
typedef TSmartPointerT<ttrackhandler> TTrackHandlerP;</ttrackhandler>
|
|
|
7a5892 |
typedef TSmartPointerT<ttracktoolhandler> TTrackToolHandlerP;</ttracktoolhandler>
|
|
|
2b429e |
typedef TSmartPointerT<ttrackmodifier> TTrackModifierP;</ttrackmodifier>
|
|
|
49945e |
|
|
|
49945e |
typedef std::vector<ttrackpoint> TTrackPointList;</ttrackpoint>
|
|
|
d8eddc |
typedef std::vector<ttracktangent> TTrackTangentList;</ttracktangent>
|
|
|
efa14d |
typedef std::vector<ttrackp> TTrackList;</ttrackp>
|
|
|
2b429e |
|
|
|
2b429e |
//===================================================================
|
|
|
2b429e |
|
|
|
2b429e |
|
|
|
2b429e |
//*****************************************************************************************
|
|
|
7a5892 |
// TTrackToolHandler definition
|
|
|
7a5892 |
//*****************************************************************************************
|
|
|
7a5892 |
|
|
|
7a5892 |
class DVAPI TTrackToolHandler : public TSmartObject { };
|
|
|
7a5892 |
|
|
|
7a5892 |
|
|
|
7a5892 |
//*****************************************************************************************
|
|
|
16421e |
// export template implementations for win32
|
|
|
16421e |
//*****************************************************************************************
|
|
|
16421e |
|
|
|
16421e |
#ifdef _WIN32
|
|
|
16421e |
template class DVAPI TSmartPointerT<ttrack>;</ttrack>
|
|
|
16421e |
template class DVAPI TSmartPointerT<ttrackhandler>;</ttrackhandler>
|
|
|
16421e |
template class DVAPI TSmartPointerT<ttracktoolhandler>;</ttracktoolhandler>
|
|
|
16421e |
template class DVAPI TSmartPointerT<ttrackmodifier>;</ttrackmodifier>
|
|
|
16421e |
#endif
|
|
|
16421e |
|
|
|
16421e |
|
|
|
16421e |
//*****************************************************************************************
|
|
|
2b429e |
// TTrackPoint definition
|
|
|
2b429e |
//*****************************************************************************************
|
|
|
2b429e |
|
|
|
6be163 |
class DVAPI TTrackPoint {
|
|
|
2b429e |
public:
|
|
|
2b429e |
TPointD position;
|
|
|
2b429e |
double pressure;
|
|
|
2b429e |
TPointD tilt;
|
|
|
2b429e |
|
|
|
2b429e |
double originalIndex;
|
|
|
2b429e |
double time;
|
|
|
2b429e |
double length;
|
|
|
2b429e |
|
|
|
2b429e |
bool final;
|
|
|
2b429e |
|
|
|
2b429e |
explicit TTrackPoint(
|
|
|
2b429e |
const TPointD &position = TPointD(),
|
|
|
2b429e |
double pressure = 0.5,
|
|
|
2b429e |
const TPointD &tilt = TPointD(),
|
|
|
2b429e |
double originalIndex = 0.0,
|
|
|
2b429e |
double time = 0.0,
|
|
|
2b429e |
double length = 0.0,
|
|
|
2b429e |
bool final = false
|
|
|
2b429e |
):
|
|
|
2b429e |
position(position),
|
|
|
2b429e |
pressure(pressure),
|
|
|
2b429e |
tilt(tilt),
|
|
|
2b429e |
originalIndex(originalIndex),
|
|
|
2b429e |
time(time),
|
|
|
2b429e |
length(length),
|
|
|
2b429e |
final(final)
|
|
|
2b429e |
{ }
|
|
|
2b429e |
};
|
|
|
2b429e |
|
|
|
2b429e |
|
|
|
2b429e |
//*****************************************************************************************
|
|
|
d8eddc |
// TTrackTangent definition
|
|
|
d8eddc |
//*****************************************************************************************
|
|
|
d8eddc |
|
|
|
6be163 |
class DVAPI TTrackTangent {
|
|
|
d8eddc |
public:
|
|
|
d8eddc |
TPointD position;
|
|
|
d8eddc |
double pressure;
|
|
|
d8eddc |
TPointD tilt;
|
|
|
d8eddc |
|
|
|
d8eddc |
inline explicit TTrackTangent(
|
|
|
d8eddc |
const TPointD &position = TPointD(),
|
|
|
d8eddc |
double pressure = 0.0,
|
|
|
d8eddc |
const TPointD &tilt = TPointD()
|
|
|
d8eddc |
):
|
|
|
d8eddc |
position(position),
|
|
|
d8eddc |
pressure(pressure),
|
|
|
d8eddc |
tilt(tilt)
|
|
|
d8eddc |
{ }
|
|
|
d8eddc |
};
|
|
|
d8eddc |
|
|
|
d8eddc |
|
|
|
d8eddc |
//*****************************************************************************************
|
|
|
2b429e |
// TTrackHandler definition
|
|
|
2b429e |
//*****************************************************************************************
|
|
|
2b429e |
|
|
|
2b429e |
class DVAPI TTrackHandler : public TSmartObject {
|
|
|
2b429e |
public:
|
|
|
c3c215 |
const TTrack &original;
|
|
|
2b429e |
std::vector<ttrackp> tracks;</ttrackp>
|
|
|
c3c215 |
TTrackHandler(const TTrack &original):
|
|
|
efa14d |
original(original) { }
|
|
|
2b429e |
};
|
|
|
2b429e |
|
|
|
2b429e |
|
|
|
2b429e |
//*****************************************************************************************
|
|
|
2b429e |
// TTrackModifier definition
|
|
|
2b429e |
//*****************************************************************************************
|
|
|
2b429e |
|
|
|
2b429e |
class DVAPI TTrackModifier : public TSmartObject {
|
|
|
2b429e |
public:
|
|
|
2b429e |
TTrackHandler &handler;
|
|
|
c3c215 |
const TTrack &original;
|
|
|
2b429e |
const double timeOffset;
|
|
|
2b429e |
|
|
|
2b429e |
explicit TTrackModifier(TTrackHandler &handler, double timeOffset = 0.0):
|
|
|
2b429e |
handler(handler), original(handler.original), timeOffset(timeOffset) { }
|
|
|
2b429e |
virtual TTrackPoint calcPoint(double originalIndex);
|
|
|
2b429e |
};
|
|
|
2b429e |
|
|
|
2b429e |
|
|
|
2b429e |
//*****************************************************************************************
|
|
|
2b429e |
// TTrack definition
|
|
|
2b429e |
//*****************************************************************************************
|
|
|
2b429e |
|
|
|
2b429e |
class DVAPI TTrack : public TSmartObject {
|
|
|
2b429e |
public:
|
|
|
2b429e |
typedef long long Id;
|
|
|
2b429e |
|
|
|
2b429e |
private:
|
|
|
49945e |
static Id m_lastId;
|
|
|
2b429e |
|
|
|
2b429e |
public:
|
|
|
2b429e |
const Id id;
|
|
|
efa14d |
const TInputState::DeviceId deviceId;
|
|
|
efa14d |
const TInputState::TouchId touchId;
|
|
|
efa14d |
const TInputState::KeyHistory::Holder keyHistory;
|
|
|
efa14d |
const TInputState::ButtonHistory::Holder buttonHistory;
|
|
|
49945e |
const bool hasPressure;
|
|
|
49945e |
const bool hasTilt;
|
|
|
2b429e |
const TTrackModifierP modifier;
|
|
|
2b429e |
|
|
|
c3c215 |
mutable TTrackHandlerP handler;
|
|
|
7a5892 |
mutable TTrackToolHandlerP toolHandler;
|
|
|
c3c215 |
mutable int pointsRemoved;
|
|
|
c3c215 |
mutable int pointsAdded;
|
|
|
d0b5c0 |
mutable int fixedPointsAdded;
|
|
|
2b429e |
|
|
|
2b429e |
private:
|
|
|
49945e |
TTrackPointList m_points;
|
|
|
49945e |
const TTrackPoint m_none;
|
|
|
d0b5c0 |
int m_pointsFixed;
|
|
|
2b429e |
|
|
|
2b429e |
public:
|
|
|
2b429e |
|
|
|
2b429e |
explicit TTrack(
|
|
|
efa14d |
TInputState::DeviceId deviceId = TInputState::DeviceId(),
|
|
|
efa14d |
TInputState::TouchId touchId = TInputState::TouchId(),
|
|
|
efa14d |
const TInputState::KeyHistory::Holder &keyHistory = TInputState::KeyHistory::Holder(),
|
|
|
49945e |
const TInputState::ButtonHistory::Holder &buttonHistory = TInputState::ButtonHistory::Holder(),
|
|
|
49945e |
bool hasPressure = false,
|
|
|
49945e |
bool hasTilt = false
|
|
|
2b429e |
);
|
|
|
2b429e |
|
|
|
2b429e |
explicit TTrack(const TTrackModifierP &modifier);
|
|
|
2b429e |
|
|
|
c3c215 |
inline const TTrack* original() const
|
|
|
2b429e |
{ return modifier ? &modifier->original : NULL; }
|
|
|
2b429e |
inline double timeOffset() const
|
|
|
2b429e |
{ return modifier ? modifier->timeOffset : 0.0; }
|
|
|
2b429e |
inline TTimerTicks ticks() const
|
|
|
2b429e |
{ return keyHistory.ticks(); }
|
|
|
2b429e |
inline bool changed() const
|
|
|
d0b5c0 |
{ return pointsRemoved || pointsAdded || fixedPointsAdded; }
|
|
|
2b429e |
|
|
|
2b429e |
const TTrack* root() const;
|
|
|
2b429e |
int level() const;
|
|
|
2b429e |
|
|
|
2b429e |
inline int clampIndex(int index) const
|
|
|
2b429e |
{ return std::min(std::max(index, 0), size() - 1); }
|
|
|
2b429e |
inline int floorIndexNoClamp(double index) const
|
|
|
9f0c16 |
{ return (int)floor(index + TConsts::epsilon); }
|
|
|
2b429e |
inline int floorIndex(double index) const
|
|
|
2b429e |
{ return clampIndex(floorIndexNoClamp(index)); }
|
|
|
2b429e |
inline int ceilIndexNoClamp(double index) const
|
|
|
9f0c16 |
{ return (int)ceil(index - TConsts::epsilon); }
|
|
|
2b429e |
inline int ceilIndex(double index) const
|
|
|
2b429e |
{ return clampIndex(ceilIndexNoClamp(index)); }
|
|
|
2b429e |
|
|
|
7e9eb1 |
int floorIndex(double index, double *outFrac) const;
|
|
|
2b429e |
|
|
|
7e9eb1 |
inline const TTrackPoint& floorPoint(double index, double *outFrac = NULL) const
|
|
|
2b429e |
{ return point(floorIndex(index, outFrac)); }
|
|
|
2b429e |
inline const TTrackPoint& ceilPoint(double index) const
|
|
|
2b429e |
{ return point(ceilIndex(index)); }
|
|
|
2b429e |
|
|
|
2b429e |
inline const TTrackPoint& point(int index) const
|
|
|
49945e |
{ return empty() ? m_none : m_points[clampIndex(index)]; }
|
|
|
2b429e |
|
|
|
2b429e |
inline int size() const
|
|
|
49945e |
{ return (int)m_points.size(); }
|
|
|
d0b5c0 |
inline int fixedSize() const
|
|
|
d0b5c0 |
{ return m_pointsFixed; }
|
|
|
d0b5c0 |
inline int previewSize() const
|
|
|
d0b5c0 |
{ return size() - fixedSize(); }
|
|
|
2b429e |
inline bool empty() const
|
|
|
49945e |
{ return m_points.empty(); }
|
|
|
2b429e |
inline const TTrackPoint& front() const
|
|
|
2b429e |
{ return point(0); }
|
|
|
2b429e |
inline const TTrackPoint& back() const
|
|
|
2b429e |
{ return point(size() - 1); }
|
|
|
2b429e |
inline bool finished() const
|
|
|
49945e |
{ return !m_points.empty() && back().final; }
|
|
|
d0b5c0 |
inline bool fixedFinished() const
|
|
|
d0b5c0 |
{ return finished() && !previewSize(); }
|
|
|
2b429e |
inline const TTrackPoint& operator[] (int index) const
|
|
|
2b429e |
{ return point(index); }
|
|
|
49945e |
inline const TTrackPointList& points() const
|
|
|
49945e |
{ return m_points; }
|
|
|
2b429e |
|
|
|
00337d |
inline void resetRemoved() const
|
|
|
00337d |
{ pointsRemoved = 0; }
|
|
|
00337d |
inline void resetAdded() const
|
|
|
00337d |
{ pointsAdded = 0; }
|
|
|
d0b5c0 |
inline void resetFixedAdded() const
|
|
|
d0b5c0 |
{ fixedPointsAdded = 0; }
|
|
|
00337d |
inline void resetChanges() const
|
|
|
d0b5c0 |
{ resetRemoved(); resetAdded(); resetFixedAdded(); }
|
|
|
00337d |
|
|
|
d0b5c0 |
void push_back(const TTrackPoint &point, bool fixed);
|
|
|
2b429e |
void pop_back(int count = 1);
|
|
|
d0b5c0 |
void fix_points(int count = 1);
|
|
|
2b429e |
|
|
|
2b429e |
inline void truncate(int count)
|
|
|
2b429e |
{ pop_back(size() - count); }
|
|
|
d0b5c0 |
inline void fix_to(int count)
|
|
|
d0b5c0 |
{ fix_points(count - fixedSize()); }
|
|
|
d0b5c0 |
inline void fix_all()
|
|
|
d0b5c0 |
{ fix_to(size()); }
|
|
|
2b429e |
|
|
|
49945e |
inline const TTrackPoint& current() const
|
|
|
49945e |
{ return point(size() - pointsAdded); }
|
|
|
7a5892 |
inline const TTrackPoint& previous() const
|
|
|
7a5892 |
{ return point(size() - pointsAdded - 1); }
|
|
|
7a5892 |
inline const TTrackPoint& next() const
|
|
|
7a5892 |
{ return point(size() - pointsAdded + 1); }
|
|
|
7a5892 |
|
|
|
49945e |
inline TInputState::KeyState::Holder getKeyState(double time) const
|
|
|
49945e |
{ return keyHistory.get(time); }
|
|
|
d8eddc |
inline TInputState::KeyState::Holder getKeyState(const TTrackPoint &point) const
|
|
|
d8eddc |
{ return getKeyState(timeOffset() + point.time); }
|
|
|
49945e |
inline TInputState::KeyState::Holder getCurrentKeyState() const
|
|
|
9f0c16 |
{ return getKeyState(timeOffset() + current().time); }
|
|
|
49945e |
inline TInputState::ButtonState::Holder getButtonState(double time) const
|
|
|
49945e |
{ return buttonHistory.get(time); }
|
|
|
d8eddc |
inline TInputState::ButtonState::Holder getButtonState(const TTrackPoint &point) const
|
|
|
d8eddc |
{ return getButtonState(timeOffset() + point.time); }
|
|
|
49945e |
inline TInputState::ButtonState::Holder getCurrentButtonState() const
|
|
|
9f0c16 |
{ return getButtonState(timeOffset() + current().time); }
|
|
|
2b429e |
|
|
|
2b429e |
private:
|
|
|
2b429e |
template<double ttrackpoint::*field=""></double>
|
|
|
2b429e |
double binarySearch(double value) const {
|
|
|
2b429e |
// points_[a].value <= value < points_[b].value
|
|
|
49945e |
if (m_points.empty()) return 0.0;
|
|
|
2b429e |
int a = 0;
|
|
|
49945e |
double aa = m_points[a].*Field;
|
|
|
9f0c16 |
if (value - aa <= 0.5*TConsts::epsilon) return (double)a;
|
|
|
49945e |
int b = (int)m_points.size() - 1;
|
|
|
49945e |
double bb = m_points[b].*Field;
|
|
|
9f0c16 |
if (bb - value <= 0.5*TConsts::epsilon) return (double)b;
|
|
|
2b429e |
while(true) {
|
|
|
2b429e |
int c = (a + b)/2;
|
|
|
2b429e |
if (a == c) break;
|
|
|
49945e |
double cc = m_points[c].*Field;
|
|
|
9f0c16 |
if (cc - value > 0.5*TConsts::epsilon)
|
|
|
2b429e |
{ b = c; bb = cc; } else { a = c; aa = cc; }
|
|
|
2b429e |
}
|
|
|
9f0c16 |
return bb - aa >= 0.5*TConsts::epsilon ? (double)a + (value - aa)/(bb - aa) : (double)a;
|
|
|
2b429e |
}
|
|
|
2b429e |
|
|
|
2b429e |
public:
|
|
|
2b429e |
inline double indexByOriginalIndex(double originalIndex) const
|
|
|
2b429e |
{ return binarySearch<&TTrackPoint::originalIndex>(originalIndex); }
|
|
|
2b429e |
inline double indexByTime(double time) const
|
|
|
2b429e |
{ return binarySearch<&TTrackPoint::time>(time); }
|
|
|
2b429e |
inline double indexByLength(double length) const
|
|
|
2b429e |
{ return binarySearch<&TTrackPoint::length>(length); }
|
|
|
2b429e |
|
|
|
2b429e |
inline double originalIndexByIndex(double index) const {
|
|
|
2b429e |
double frac;
|
|
|
7e9eb1 |
const TTrackPoint &p0 = floorPoint(index, &frac);
|
|
|
2b429e |
const TTrackPoint &p1 = ceilPoint(index);
|
|
|
2b429e |
return interpolationLinear(p0.originalIndex, p1.originalIndex, frac);
|
|
|
2b429e |
}
|
|
|
2b429e |
inline double timeByIndex(double index) const {
|
|
|
2b429e |
double frac;
|
|
|
7e9eb1 |
const TTrackPoint &p0 = floorPoint(index, &frac);
|
|
|
2b429e |
const TTrackPoint &p1 = ceilPoint(index);
|
|
|
2b429e |
return interpolationLinear(p0.time, p1.time, frac);
|
|
|
2b429e |
}
|
|
|
2b429e |
inline double lengthByIndex(double index) const {
|
|
|
2b429e |
double frac;
|
|
|
7e9eb1 |
const TTrackPoint &p0 = floorPoint(index, &frac);
|
|
|
2b429e |
const TTrackPoint &p1 = ceilPoint(index);
|
|
|
2b429e |
return interpolationLinear(p0.length, p1.length, frac);
|
|
|
2b429e |
}
|
|
|
2b429e |
|
|
|
2b429e |
TTrackPoint calcPoint(double index) const;
|
|
|
2b429e |
TPointD calcTangent(double index, double distance = 0.1) const;
|
|
|
49945e |
double rootIndexByIndex(double index) const;
|
|
|
49945e |
TTrackPoint calcRootPoint(double index) const;
|
|
|
2b429e |
|
|
|
2b429e |
inline TTrackPoint interpolateLinear(double index) const {
|
|
|
2b429e |
double frac;
|
|
|
7e9eb1 |
const TTrackPoint &p0 = floorPoint(index, &frac);
|
|
|
2b429e |
const TTrackPoint &p1 = ceilPoint(index);
|
|
|
2b429e |
return interpolationLinear(p0, p1, frac);
|
|
|
2b429e |
}
|
|
|
2b429e |
|
|
|
2b429e |
template<typename t=""></typename>
|
|
|
2b429e |
static inline T interpolationLinear(const T &p0, const T &p1, double l)
|
|
|
2b429e |
{ return p0*(1.0 - l) + p1*l; }
|
|
|
2b429e |
|
|
|
d8eddc |
template<typename t=""></typename>
|
|
|
d8eddc |
static T interpolationSpline(const T &p0, const T &p1, const T &t0, const T &t1, double l) {
|
|
|
d8eddc |
double ll = l*l;
|
|
|
d8eddc |
double lll = ll*l;
|
|
|
d8eddc |
return p0*( 2.0*lll - 3.0*ll + 1.0)
|
|
|
d8eddc |
+ p1*(-2.0*lll + 3.0*ll )
|
|
|
d8eddc |
+ t0*( lll - 2.0*ll + l )
|
|
|
d8eddc |
+ t1*( lll - 1.0*ll );
|
|
|
d8eddc |
}
|
|
|
d8eddc |
|
|
|
2b429e |
static inline TTrackPoint interpolationLinear(const TTrackPoint &p0, const TTrackPoint &p1, double l) {
|
|
|
9f0c16 |
if (l <= TConsts::epsilon) return p0;
|
|
|
9f0c16 |
if (l >= 1.0 - TConsts::epsilon) return p1;
|
|
|
2b429e |
return TTrackPoint(
|
|
|
2b429e |
interpolationLinear(p0.position , p1.position , l),
|
|
|
2b429e |
interpolationLinear(p0.pressure , p1.pressure , l),
|
|
|
2b429e |
interpolationLinear(p0.tilt , p1.tilt , l),
|
|
|
2b429e |
interpolationLinear(p0.originalIndex , p1.originalIndex , l),
|
|
|
2b429e |
interpolationLinear(p0.time , p1.time , l),
|
|
|
7dd550 |
interpolationLinear(p0.length , p1.length , l),
|
|
|
7dd550 |
p0.final && p1.final );
|
|
|
2b429e |
}
|
|
|
d8eddc |
|
|
|
d8eddc |
static inline TTrackPoint interpolationSpline(
|
|
|
d8eddc |
const TTrackPoint &p0,
|
|
|
d8eddc |
const TTrackPoint &p1,
|
|
|
d8eddc |
const TTrackTangent &t0,
|
|
|
d8eddc |
const TTrackTangent &t1,
|
|
|
d8eddc |
double l )
|
|
|
d8eddc |
{
|
|
|
9f0c16 |
if (l <= TConsts::epsilon) return p0;
|
|
|
9f0c16 |
if (l >= 1.0 - TConsts::epsilon) return p1;
|
|
|
d8eddc |
return TTrackPoint(
|
|
|
d8eddc |
interpolationSpline(p0.position , p1.position , t0.position , t1.position , l),
|
|
|
9f0c16 |
interpolationLinear(p0.pressure , p1.pressure , l),
|
|
|
38dda8 |
//interpolationSpline(p0.pressure , p1.pressure , t0.pressure , t1.pressure , l),
|
|
|
9f0c16 |
interpolationLinear(p0.tilt , p1.tilt , l),
|
|
|
38dda8 |
//interpolationSpline(p0.tilt , p1.tilt , t0.tilt , t1.tilt , l),
|
|
|
d8eddc |
interpolationLinear(p0.originalIndex , p1.originalIndex , l),
|
|
|
d8eddc |
interpolationLinear(p0.time , p1.time , l),
|
|
|
7dd550 |
interpolationLinear(p0.length , p1.length , l),
|
|
|
7dd550 |
p0.final && p1.final );
|
|
|
d8eddc |
}
|
|
|
2b429e |
};
|
|
|
2b429e |
|
|
|
16421e |
|
|
|
2b429e |
#endif
|