From 2b429e93ccdf653a7f61b60db65260da09c269ac Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: May 01 2023 07:52:43 +0000 Subject: #assistants: TTrack difinition --- diff --git a/toonz/sources/include/tools/keyhistory.h b/toonz/sources/include/tools/keyhistory.h new file mode 100644 index 0000000..c49a662 --- /dev/null +++ b/toonz/sources/include/tools/keyhistory.h @@ -0,0 +1,256 @@ +#pragma once + +#ifndef KEYSTATE_INCLUDED +#define KEYSTATE_INCLUDED + +// TnzTools includes +#include + +// TnzCore includes +#include +#include + +// std includes +#include +#include +#include +#include + + +#undef DVAPI +#undef DVVAR +#ifdef TNZTOOLS_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + + +//=================================================================== + +// Base Classee Declarations + + +class DVAPI TKeyStateBase : public TSmartObject { + DECLARE_CLASS_CODE +}; + +class DVAPI TKeyHistoryBase : public TSmartObject { + DECLARE_CLASS_CODE +}; + +//=================================================================== + + +//***************************************************************************************** +// TKeyState definition +//***************************************************************************************** + +template +class TKeyStateT : public TKeyStateBase { +public: + typedef T Type; + typedef TSmartPointerT Pointer; + + struct Holder { + Pointer state; + TTimerTicks ticks; + double timeOffset; + + explicit Holder(const Pointer &state, TTimerTicks ticks = 0, double timeOffset = 0.0): + state(state), ticks(ticks), timeOffset(timeOffset) { } + + Pointer find(const Type &value) const + { return state ? state->find(value) : Pointer(); } + bool isEmpty() const + { return !state || state->isEmpty(); } + bool isPressed(const Type &value) const + { return find(value); } + double howLongPressed(const Type &value) + { return howLongPressed(find(value), ticks, timeOffset); } + + static double howLongPressed(const Pointer &state, long ticks, double timeOffset) { + return state ? std::max(TToolTimer::step, (ticks - state.ticks)*TToolTimer::step + timeOffset) : 0.0; + } + }; + + static const Type none; + static const Pointer empty; + + const Pointer previous; + const TTimerTicks ticks; + const Type value; + +private: + TKeyStateT(const Pointer &previous, TTimerTicks ticks, const Type &value): + previous(previous), ticks(ticks), value(value) { } + + Pointer makeChainWithout(const Pointer &state) { + return state == this || !previous ? previous + : Pointer(new TKeyStateT(previous->makeChainWithout(state), ticks, value)); + } + +public: + TKeyStateT(): ticks(0), value(none) { } + + Pointer find(const Type &value) { + return value == none ? Pointer() + : value == this->value ? this + : previous ? previous->find(value) + : Pointer(); + } + + Pointer change(bool press, const Type &value, TTimerTicks ticks) { + if (value == none) + return Pointer(this); + if (ticks <= this->ticks) + ticks = this->ticks + 1; + + Pointer p = find(value); + if (press) { + if (p) return Pointer(this); + return Pointer(new TKeyStateT((isEmpty() ? Pointer() : Pointer(this)), ticks, value)); + } + + if (!p) return Pointer(this); + Pointer chain = makeChainWithout(p); + return chain ? chain : Pointer(new TKeyStateT()); + } + + bool isEmpty() + { return value == none && (!previous || previous->isEmpty()); } + bool isPressed(const Type &value) + { return find(value); } + + Pointer pressed(const Type &value, long ticks) + { return change(true, value, ticks); } + Pointer released(const Type &value, long ticks) + { return change(false, value, ticks); } +}; + + +template +const typename TKeyStateT::Type TKeyStateT::none = typename TKeyStateT::Type(); + +template +const typename TKeyStateT::Pointer TKeyStateT::empty = new TKeyStateT(); + + +//***************************************************************************************** +// TKeyHistory definition +//***************************************************************************************** + +template +class TKeyHistoryT : public TKeyHistoryBase { +public: + typedef T Type; + typedef TSmartPointerT Pointer; + typedef TKeyStateT State; + typedef typename TKeyStateT::Pointer StatePointer; + typedef typename TKeyStateT::Holder StateHolder; + + class Holder { + private: + Pointer history_; + TTimerTicks ticks_; + double timeOffset_; + TTimerTicks heldTicks; + + public: + Holder(): + ticks_(), timeOffset_(), heldTicks() { } + Holder(const Pointer &history, TTimerTicks ticks, double timeOffset = 0.0): + ticks_(), timeOffset_(), heldTicks() + { set(history, ticks, timeOffset); } + Holder(const Holder &other): + ticks_(), timeOffset_(), heldTicks() + { set(other); } + ~Holder() + { reset(); } + + Holder& operator= (const Holder &other) + { set(other); return *this; } + + void set(const Pointer &history, TTimerTicks ticks, double timeOffset = 0.0) { + if (history_) history_->releaseTicks(heldTicks); + history_ = history; + ticks_ = ticks; + timeOffset_ = timeOffset; + heldTicks = (history_ ? history_->holdTicks(ticks_) : 0); + } + void set(const Holder &other) + { set(other.history(), other.ticks(), other.timeOffset()); } + void reset() + { set(Pointer(), 0); } + + Pointer history() const + { return history_; } + TTimerTicks ticks() const + { return ticks_; } + double timeOffset() const + { return timeOffset_; } + + Holder offset(double timeOffset) const { + return fabs(timeOffset) < TToolTimer::epsilon ? *this + : Holder(history, ticks, this->timeOffset + timeOffset); + } + + StateHolder get(double time) const { + TTimerTicks dticks = (TTimerTicks)ceil(TToolTimer::frequency*(time + timeOffset)); + StatePointer state = history_->get(ticks + dticks); + return StateHolder(state, ticks, timeOffset + time); + } + }; + +private: + std::map states; + std::multiset locks; + + void autoRemove() { + TTimerTicks ticks = locks.empty() + ? states.rbegin()->first + : *locks.begin(); + while(true) { + typename std::map::iterator i = states.begin(); + ++i; + if (i == states.end() || (!i->second->isEmpty() && i->first >= ticks)) break; + states.erase(i); + } + } + + TTimerTicks holdTicks(TTimerTicks ticks) + { return *locks.insert(std::max(ticks, states.begin()->first)); } + void releaseTicks(TTimerTicks heldTicks) + { locks.erase(heldTicks); autoRemove(); } + + StatePointer get(TTimerTicks ticks) { + typename std::map::iterator i = states.upper_bound(ticks); + return i == states.begin() ? i->second : (--i)->second; + } + +public: + TKeyHistoryT() + { states.insert(StatePointer(new State())); } + + StatePointer current() const + { return states.back(); } + + StatePointer change(bool press, Type value, TTimerTicks ticks) { + StatePointer state = current()->change(press, value, ticks); + if (state != current() && state.ticks > states.rbegin()->first) + states.insert(state.ticks, state); + autoRemove(); + return current(); + } + + StatePointer pressed(Type value, TTimerTicks ticks) + { return change(true, value, ticks); } + + StatePointer released(Type value, TTimerTicks ticks) + { return change(false, value, ticks); } +}; + + +#endif diff --git a/toonz/sources/include/tools/tooltimer.h b/toonz/sources/include/tools/tooltimer.h new file mode 100644 index 0000000..ec73471 --- /dev/null +++ b/toonz/sources/include/tools/tooltimer.h @@ -0,0 +1,47 @@ +#pragma once + +#ifndef TOOLTIMER_INCLUDED +#define TOOLTIMER_INCLUDED + +// TnzCore includes +#include + +// Qt includes +#include + +#undef DVAPI +#undef DVVAR +#ifdef TNZTOOLS_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + + +//=================================================================== + +//***************************************************************************************** +// TToolTimer definition +//***************************************************************************************** + +typedef qint64 TTimerTicks; + +class DVAPI TToolTimer { +private: + static TToolTimer instance; + QElapsedTimer timer; + TToolTimer(); + +public: + static const TTimerTicks frequency; + static const double step; + static const double epsilon; + + static inline TTimerTicks ticks() + { return instance.timer.nsecsElapsed(); } +}; + + +#endif diff --git a/toonz/sources/include/tools/track.h b/toonz/sources/include/tools/track.h new file mode 100644 index 0000000..dbb38ef --- /dev/null +++ b/toonz/sources/include/tools/track.h @@ -0,0 +1,290 @@ +#pragma once + +#ifndef TRACK_INCLUDED +#define TRACK_INCLUDED + +// TnzTools includes +#include +#include +#include +#include + +// Qt headers +#include + +// std includes +#include +#include + + +#undef DVAPI +#undef DVVAR +#ifdef TNZTOOLS_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + + +//=================================================================== + +// Forward Declarations + +class TTrack; +class TTrackHandler; +class TTrackModifier; +typedef TSmartPointerT TTrackP; +typedef TSmartPointerT TTrackHandlerP; +typedef TSmartPointerT TTrackModifierP; + +//=================================================================== + + +//***************************************************************************************** +// TTrackPoint definition +//***************************************************************************************** + +struct TTrackPoint { +public: + TPointD position; + double pressure; + TPointD tilt; + + double originalIndex; + double time; + double length; + + bool final; + + explicit TTrackPoint( + const TPointD &position = TPointD(), + double pressure = 0.5, + const TPointD &tilt = TPointD(), + double originalIndex = 0.0, + double time = 0.0, + double length = 0.0, + bool final = false + ): + position(position), + pressure(pressure), + tilt(tilt), + originalIndex(originalIndex), + time(time), + length(length), + final(final) + { } +}; + + +//***************************************************************************************** +// TTrackHandler definition +//***************************************************************************************** + +class DVAPI TTrackHandler : public TSmartObject { + DECLARE_CLASS_CODE +public: + TSmartObject &owner; + TTrack &original; + std::vector tracks; + TTrackHandler(TSmartObject &owner, TTrack &original): + owner(owner), original(original) { } +}; + + +//***************************************************************************************** +// TTrackModifier definition +//***************************************************************************************** + +class DVAPI TTrackModifier : public TSmartObject { + DECLARE_CLASS_CODE +public: + TTrackHandler &handler; + TTrack &original; + const double timeOffset; + + explicit TTrackModifier(TTrackHandler &handler, double timeOffset = 0.0): + handler(handler), original(handler.original), timeOffset(timeOffset) { } + virtual TTrackPoint calcPoint(double originalIndex); +}; + + +//***************************************************************************************** +// TTrack definition +//***************************************************************************************** + +class DVAPI TTrack : public TSmartObject { + DECLARE_CLASS_CODE + +public: + typedef long long Id; + typedef long long TouchId; + typedef qint64 DeviceId; + + static const double epsilon; + +private: + static Id lastId; + +public: + const Id id; + const DeviceId deviceId; + const TouchId touchId; + const TKeyHistoryT::Holder keyHistory; + const TKeyHistoryT::Holder buttonHistory; + const TTrackModifierP modifier; + + TTrackHandlerP handler; + int wayPointsRemoved; + int wayPointsAdded; + +private: + std::vector points_; + const TTrackPoint none; + +public: + + explicit TTrack( + DeviceId deviceId = 0, + TouchId touchId = 0, + const TKeyHistoryT::Holder &keyHistory = TKeyHistoryT::Holder(), + const TKeyHistoryT::Holder &buttonHistory = TKeyHistoryT::Holder() + ); + + explicit TTrack(const TTrackModifierP &modifier); + + inline TTrack* original() const + { return modifier ? &modifier->original : NULL; } + inline double timeOffset() const + { return modifier ? modifier->timeOffset : 0.0; } + inline TTimerTicks ticks() const + { return keyHistory.ticks(); } + inline bool changed() const + { return wayPointsAdded != 0 || wayPointsRemoved != 0; } + + const TTrack* root() const; + TTrack* root(); + int level() const; + + inline int clampIndex(int index) const + { return std::min(std::max(index, 0), size() - 1); } + inline int floorIndexNoClamp(double index) const + { return (int)floor(index + epsilon); } + inline int floorIndex(double index) const + { return clampIndex(floorIndexNoClamp(index)); } + inline int ceilIndexNoClamp(double index) const + { return (int)ceil(index - epsilon); } + inline int ceilIndex(double index) const + { return clampIndex(ceilIndexNoClamp(index)); } + + int floorIndex(double index, double &outFrac) const; + + inline const TTrackPoint& floorPoint(double index, double &outFrac) const + { return point(floorIndex(index, outFrac)); } + inline const TTrackPoint& floorPoint(double index) const + { return point(floorIndex(index)); } + inline const TTrackPoint& ceilPoint(double index) const + { return point(ceilIndex(index)); } + + inline const TTrackPoint& point(int index) const + { return empty() ? none : points_[clampIndex(index)]; } + + inline int size() const + { return (int)points_.size(); } + inline bool empty() const + { return points_.empty(); } + inline const TTrackPoint& front() const + { return point(0); } + inline const TTrackPoint& back() const + { return point(size() - 1); } + inline bool finished() const + { return !points_.empty() && back().final; } + inline const TTrackPoint& operator[] (int index) const + { return point(index); } + inline const std::vector& points() const + { return points_; } + + void push_back(const TTrackPoint &point); + void pop_back(int count = 1); + + inline void truncate(int count) + { pop_back(size() - count); } + + +private: + template + double binarySearch(double value) const { + // points_[a].value <= value < points_[b].value + if (points_.empty()) return 0.0; + int a = 0; + double aa = points_[a].*Field; + if (value - aa <= 0.5*epsilon) return (double)a; + int b = (int)points_.size() - 1; + double bb = points_[b].*Field; + if (bb - value <= 0.5*epsilon) return (double)b; + while(true) { + int c = (a + b)/2; + if (a == c) break; + double cc = points_[c].*Field; + if (cc - value > 0.5*epsilon) + { b = c; bb = cc; } else { a = c; aa = cc; } + } + return bb - aa >= 0.5*epsilon ? (double)a + (value - aa)/(bb - aa) : (double)a; + } + +public: + inline double indexByOriginalIndex(double originalIndex) const + { return binarySearch<&TTrackPoint::originalIndex>(originalIndex); } + inline double indexByTime(double time) const + { return binarySearch<&TTrackPoint::time>(time); } + inline double indexByLength(double length) const + { return binarySearch<&TTrackPoint::length>(length); } + + inline double originalIndexByIndex(double index) const { + double frac; + const TTrackPoint &p0 = floorPoint(index, frac); + const TTrackPoint &p1 = ceilPoint(index); + return interpolationLinear(p0.originalIndex, p1.originalIndex, frac); + } + inline double timeByIndex(double index) const { + double frac; + const TTrackPoint &p0 = floorPoint(index, frac); + const TTrackPoint &p1 = ceilPoint(index); + return interpolationLinear(p0.time, p1.time, frac); + } + inline double lengthByIndex(double index) const { + double frac; + const TTrackPoint &p0 = floorPoint(index, frac); + const TTrackPoint &p1 = ceilPoint(index); + return interpolationLinear(p0.length, p1.length, frac); + } + + TTrackPoint calcPoint(double index) const; + TPointD calcTangent(double index, double distance = 0.1) const; + + inline TTrackPoint interpolateLinear(double index) const { + double frac; + const TTrackPoint &p0 = floorPoint(index, frac); + const TTrackPoint &p1 = ceilPoint(index); + return interpolationLinear(p0, p1, frac); + } + + template + static inline T interpolationLinear(const T &p0, const T &p1, double l) + { return p0*(1.0 - l) + p1*l; } + + static inline TTrackPoint interpolationLinear(const TTrackPoint &p0, const TTrackPoint &p1, double l) { + if (l <= epsilon) return p0; + if (l >= 1.0 - epsilon) return p1; + return TTrackPoint( + interpolationLinear(p0.position , p1.position , l), + interpolationLinear(p0.pressure , p1.pressure , l), + interpolationLinear(p0.tilt , p1.tilt , l), + interpolationLinear(p0.originalIndex , p1.originalIndex , l), + interpolationLinear(p0.time , p1.time , l), + interpolationLinear(p0.length , p1.length , l) ); + } +}; + +#endif diff --git a/toonz/sources/tnztools/CMakeLists.txt b/toonz/sources/tnztools/CMakeLists.txt index d015add..a56b0e5 100644 --- a/toonz/sources/tnztools/CMakeLists.txt +++ b/toonz/sources/tnztools/CMakeLists.txt @@ -43,6 +43,9 @@ set(HEADERS ../include/tools/tool.h ../include/tools/toolcommandids.h ../include/tools/toolutils.h + ../include/tools/tooltimer.h + ../include/tools/keyhistory.h + ../include/tools/track.h ) set(SOURCES @@ -105,7 +108,10 @@ set(SOURCES mypainttoonzbrush.cpp shifttracetool.cpp toonzrasterbrushtool.cpp - toonzvectorbrushtool.cpp + toonzvectorbrushtool.cpp + tooltimer.cpp + keyhistory.cpp + track.cpp ) set(RESOURCES tnztools.qrc) diff --git a/toonz/sources/tnztools/keyhistory.cpp b/toonz/sources/tnztools/keyhistory.cpp new file mode 100644 index 0000000..121ac56 --- /dev/null +++ b/toonz/sources/tnztools/keyhistory.cpp @@ -0,0 +1,12 @@ + + +#include + + +//***************************************************************************************** +// TKeyStateBase static members +//***************************************************************************************** + +DEFINE_CLASS_CODE(TKeyStateBase, 125) +DEFINE_CLASS_CODE(TKeyHistoryBase, 126) + diff --git a/toonz/sources/tnztools/tooltimer.cpp b/toonz/sources/tnztools/tooltimer.cpp new file mode 100644 index 0000000..45bd778 --- /dev/null +++ b/toonz/sources/tnztools/tooltimer.cpp @@ -0,0 +1,21 @@ + + +#include + + +//***************************************************************************************** +// TToolTimer static members +//***************************************************************************************** + +const TTimerTicks TToolTimer::frequency = 1000000000; +const double TToolTimer::step = 1e-9; +const double TToolTimer::epsilon = 1e-10; +TToolTimer TToolTimer::instance; + + +//***************************************************************************************** +// TToolTimer implementation +//***************************************************************************************** + +TToolTimer::TToolTimer() + { timer.start(); } diff --git a/toonz/sources/tnztools/track.cpp b/toonz/sources/tnztools/track.cpp new file mode 100644 index 0000000..c949044 --- /dev/null +++ b/toonz/sources/tnztools/track.cpp @@ -0,0 +1,123 @@ + + +#include + + +//***************************************************************************************** +// Class definitions +//***************************************************************************************** + +const double TTrack::epsilon = 1e-9; +TTrack::Id TTrack::lastId = 0; + +DEFINE_CLASS_CODE(TTrackHandler, 130) +DEFINE_CLASS_CODE(TTrackModifier, 131) +DEFINE_CLASS_CODE(TTrack, 132) + + +//***************************************************************************************** +// TTrackModifier implemantation +//***************************************************************************************** + +TTrackPoint +TTrackModifier::calcPoint(double originalIndex) { + TTrackPoint p = original.calcPoint(originalIndex); + p.originalIndex = originalIndex; + return p; +} + + +//***************************************************************************************** +// TTrack implemantation +//***************************************************************************************** + +TTrack::TTrack( + DeviceId deviceId, + TouchId touchId, + const TKeyHistoryT::Holder &keyHistory, + const TKeyHistoryT::Holder &buttonHistory +): + id(++lastId), + deviceId(deviceId), + touchId(touchId), + keyHistory(keyHistory), + buttonHistory(buttonHistory), + wayPointsRemoved(), + wayPointsAdded() + { } + +TTrack::TTrack(const TTrackModifierP &modifier): + id(++lastId), + deviceId(modifier->original.deviceId), + touchId(modifier->original.touchId), + keyHistory(modifier->original.keyHistory), + buttonHistory(modifier->original.buttonHistory), + wayPointsRemoved(), + wayPointsAdded() + { } + +const TTrack* +TTrack::root() const + { return original() ? original()->root() : this; } + +TTrack* +TTrack::root() + { return original() ? original()->root() : this; } + +int +TTrack::level() const + { return original() ? original()->level() + 1 : 0; } + +int +TTrack::floorIndex(double index, double &outFrac) const { + int i = (int)floor(index + epsilon); + if (i > size() - 1) + { outFrac = 0.0; return size() - 1; } + if (i < 0) + { outFrac = 0.0; return 0; } + outFrac = std::max(0.0, index - (double)i); + return i; +} + +void +TTrack::push_back(const TTrackPoint &point) { + points_.push_back(point); + if (size() == 1) return; + + const TTrackPoint &prev = *(points_.end() - 2); + TTrackPoint &p = points_.back(); + + if (p.originalIndex < prev.originalIndex) + p.originalIndex = prev.originalIndex; + if (p.time < prev.time) + p.time = prev.time; + + TPointD d = p.position - prev.position; + p.length = prev.length + sqrt(d.x*d.x + d.y*d.y); +} + +void +TTrack::pop_back(int count) { + if (count > (int)size()) count = size(); + if (count <= 0) return; + points_.erase(points_.end() - count, points_.end()); +} + + +TTrackPoint +TTrack::calcPoint(double index) const { + return modifier + ? modifier->calcPoint( originalIndexByIndex(index) ) + : interpolateLinear(index); +} + +TPointD +TTrack::calcTangent(double index, double distance) const { + double minDistance = 10.0*epsilon; + if (distance < minDistance) distance = minDistance; + TTrackPoint p = calcPoint(index); + TTrackPoint pp = calcPoint(indexByLength(p.length - distance)); + TPointD dp = p.position - pp.position; + double lenSqr = dp.x*dp.x + dp.y*dp.y; + return lenSqr > epsilon*epsilon ? dp*sqrt(1.0/lenSqr) : TPointD(); +}