Global Metrics
path: .metrics.nexits.average
old: 0.0
new: 0.5714285714285714
path: .metrics.nexits.sum
old: 0.0
new: 4.0
path: .metrics.nargs.sum
old: 0.0
new: 3.0
path: .metrics.nargs.average
old: 0.0
new: 0.42857142857142855
path: .metrics.cyclomatic.average
old: 1.0
new: 1.2666666666666666
path: .metrics.cyclomatic.sum
old: 6.0
new: 19.0
path: .metrics.loc.cloc
old: 18.0
new: 85.0
path: .metrics.loc.blank
old: 8.0
new: 30.0
path: .metrics.loc.sloc
old: 49.0
new: 191.0
path: .metrics.loc.ploc
old: 23.0
new: 76.0
path: .metrics.loc.lloc
old: 0.0
new: 10.0
path: .metrics.mi.mi_original
old: 77.60984893600903
new: 41.67134000188257
path: .metrics.mi.mi_sei
old: 77.22441753244352
new: 29.30680521115906
path: .metrics.mi.mi_visual_studio
old: 45.38587657076551
new: 24.369204679463493
path: .metrics.nom.total
old: 2.0
new: 7.0
path: .metrics.nom.functions
old: 2.0
new: 7.0
path: .metrics.halstead.estimated_program_length
old: 81.83229392178727
new: 506.11891378608817
path: .metrics.halstead.N1
old: 38.0
new: 190.0
path: .metrics.halstead.volume
old: 262.36659345130676
new: 2138.071865768946
path: .metrics.halstead.length
old: 58.0
new: 331.0
path: .metrics.halstead.vocabulary
old: 23.0
new: 88.0
path: .metrics.halstead.purity_ratio
old: 1.41090161934116
new: 1.5290601624957347
path: .metrics.halstead.level
old: 0.15555555555555556
new: 0.0592407175636212
path: .metrics.halstead.effort
old: 1686.6423864726864
new: 36091.2553679448
path: .metrics.halstead.N2
old: 20.0
new: 141.0
path: .metrics.halstead.n1
old: 9.0
new: 17.0
path: .metrics.halstead.n2
old: 14.0
new: 71.0
path: .metrics.halstead.bugs
old: 0.04723103023209937
new: 0.3640380149770425
path: .metrics.halstead.time
old: 93.70235480403814
new: 2005.0697426636
path: .metrics.halstead.difficulty
old: 6.428571428571429
new: 16.880281690140844
Spaces Data
Minimal test - lines (18, 188)
path: .spaces[0].spaces[0].metrics.nom.functions
old: 2.0
new: 7.0
path: .spaces[0].spaces[0].metrics.nom.total
old: 2.0
new: 7.0
path: .spaces[0].spaces[0].metrics.nexits.sum
old: 0.0
new: 4.0
path: .spaces[0].spaces[0].metrics.nexits.average
old: 0.0
new: 0.5714285714285714
path: .spaces[0].spaces[0].metrics.halstead.volume
old: 232.7928234072743
new: 2065.5127757564037
path: .spaces[0].spaces[0].metrics.halstead.difficulty
old: 6.375
new: 17.386363636363637
path: .spaces[0].spaces[0].metrics.halstead.N2
old: 17.0
new: 135.0
path: .spaces[0].spaces[0].metrics.halstead.purity_ratio
old: 1.3499787739931073
new: 1.4457311116633138
path: .spaces[0].spaces[0].metrics.halstead.level
old: 0.1568627450980392
new: 0.05751633986928104
path: .spaces[0].spaces[0].metrics.halstead.n1
old: 9.0
new: 17.0
path: .spaces[0].spaces[0].metrics.halstead.vocabulary
old: 21.0
new: 83.0
path: .spaces[0].spaces[0].metrics.halstead.N1
old: 36.0
new: 189.0
path: .spaces[0].spaces[0].metrics.halstead.n2
old: 12.0
new: 66.0
path: .spaces[0].spaces[0].metrics.halstead.estimated_program_length
old: 71.54887502163469
new: 468.4168801789137
path: .spaces[0].spaces[0].metrics.halstead.effort
old: 1484.0542492213738
new: 35911.75621485565
path: .spaces[0].spaces[0].metrics.halstead.time
old: 82.44745829007633
new: 1995.0975674919807
path: .spaces[0].spaces[0].metrics.halstead.length
old: 53.0
new: 324.0
path: .spaces[0].spaces[0].metrics.halstead.bugs
old: 0.04336891892558609
new: 0.3628299882729645
path: .spaces[0].spaces[0].metrics.cyclomatic.sum
old: 4.0
new: 17.0
path: .spaces[0].spaces[0].metrics.cyclomatic.average
old: 1.0
new: 1.3076923076923077
path: .spaces[0].spaces[0].metrics.loc.blank
old: 4.0
new: 28.0
path: .spaces[0].spaces[0].metrics.loc.sloc
old: 25.0
new: 171.0
path: .spaces[0].spaces[0].metrics.loc.lloc
old: 0.0
new: 10.0
path: .spaces[0].spaces[0].metrics.loc.ploc
old: 17.0
new: 66.0
path: .spaces[0].spaces[0].metrics.loc.cloc
old: 4.0
new: 77.0
path: .spaces[0].spaces[0].metrics.nargs.average
old: 0.0
new: 0.42857142857142855
path: .spaces[0].spaces[0].metrics.nargs.sum
old: 0.0
new: 3.0
path: .spaces[0].spaces[0].metrics.mi.mi_sei
old: 83.00106133241832
new: 32.76618560127405
path: .spaces[0].spaces[0].metrics.mi.mi_visual_studio
old: 52.3938230520746
new: 25.791084599764005
path: .spaces[0].spaces[0].metrics.mi.mi_original
old: 89.59343741904756
new: 44.10275466559645
Code
namespace widget {
/**
* De-jitters touch motions by resampling (interpolating or extrapolating) touch
* positions for the vsync timestamp.
*
* Touch resampling improves the touch panning experience on devices where touch
* positions are sampled at a rate that's not an integer multiple of the display
* refresh rate, for example 100Hz touch sampling on a 60Hz display: Without
* resampling, we would alternate between taking one touch sample or two touch
* samples into account each frame, creating a jittery motion ("small step, big
* step, small step, big step").
* Intended for use on Android, where both touch events and vsync notifications
* arrive on the same thread, the Java UI thread.
* This class is not thread safe.
*
* TouchResampler operates in the following way:
*
* Original events are fed into ProcessEvent().
* Outgoing events (potentially resampled for resampling) are added to a queue
* and can be consumed by calling ConsumeOutgoingEvents(). Touch events which
* are not touch move events are forwarded instantly and not resampled. Only
* touch move events are resampled. Whenever a touch move event is received, it
* gets delayed until NotifyFrame() is called, at which point it is resampled
* into a resampled version for the given frame timestamp, and added to the
* outgoing queue. If no touch move event is received between two consecutive
* frames, this is treated as a stop in the touch motion. If the last outgoing
* event was an resampled touch move event, we return back to the non-resampled
* state by emitting a copy of the last original touch move event, which has
* unmodified position data. Touch events which are not touch move events also
* force a return to the non-resampled state before they are moved to the
* outgoing queue.
*/
class TouchResampler final {
public:
// Feed a touch event into the interpolater. Returns an ID that can be used to
// match outgoing events to this incoming event, to track data associated with
// this event.
uint64_t ProcessEvent(MultiTouchInput&& aInput);
// Emit events, potentially resampled, for this timestamp. The events are put
// into the outgoing queue. May not emit any events if there's no update.
void NotifyFrame(const TimeStamp& aTimeStamp);
// Returns true between the start and the end of a touch gesture. During this
// time, the caller should keep itself registered with the system frame
// callback mechanism, so that NotifyFrame() can be called on every frame.
// (Otherwise, if we only registered the callback after receiving a touch move
// event, the frame callback might be delayed by a full frame.)
bool InTouchingState() const { return mCurrentTouches.HasTouch(); }
struct OutgoingEvent {
// The event, potentially modified from the original for resampling.
MultiTouchInput mEvent;
// Some(eventId) if this event is a modified version of an original event,
// Nothing() if this is an extra event.
Maybe mEventId;
};
// Returns the outgoing events that were produced since the last call.
// No event IDs will be skipped. Returns at least one outgoing event for each
// incoming event (possibly after a delay), and potential extra events with
// no originating event ID.
// Outgoing events should be consumed after every call to ProcessEvent() and
// after every call to NotifyFrame().
std::queue ConsumeOutgoingEvents() {
return std::move(mOutgoingEvents);
}
private:
// Add the event to the outgoing queue.
void EmitEvent(MultiTouchInput&& aInput, uint64_t aEventId) {
mLastEmittedEventTime = aInput.mTimeStamp;
mOutgoingEvents.push(OutgoingEvent{std::move(aInput), Some(aEventId)});
}
// Emit an event that does not correspond to an incoming event.
void EmitExtraEvent(MultiTouchInput&& aInput) {
mLastEmittedEventTime = aInput.mTimeStamp;
mOutgoingEvents.push(OutgoingEvent{std::move(aInput), Nothing()});
}
// Move any touch move events that we deferred for resampling to the outgoing
// queue unmodified, leaving mDeferredTouchMoveEvents empty.
void FlushDeferredTouchMoveEventsUnresampled();
// Must only be called if mInResampledState is true and
// mDeferredTouchMoveEvents is empty. Emits mOriginalOfResampledTouchMove,
// with a potentially adjusted timestamp for correct ordering.
void ReturnToNonResampledState();
// Takes historical touch data from mRemainingTouchData and prepends it to the
// data in aInput.
void PrependLeftoverHistoricalData(MultiTouchInput* aInput);
struct DataPoint {
TimeStamp mTimeStamp;
ScreenIntPoint mPosition;
};
struct TouchInfo {
void Update(const SingleTouchData& aTouch, const TimeStamp& aEventTime);
ScreenIntPoint ResampleAtTime(const ScreenIntPoint& aLastObservedPosition,
const TimeStamp& aTimeStamp);
int32_t mIdentifier = 0;
Maybe mBaseDataPoint;
Maybe mLatestDataPoint;
};
struct CurrentTouches {
void UpdateFromEvent(const MultiTouchInput& aInput);
bool HasTouch() const { return !mTouches.IsEmpty(); }
TimeStamp LatestDataPointTime() { return mLatestDataPointTime; }
ScreenIntPoint ResampleTouchPositionAtTime(
int32_t aIdentifier, const ScreenIntPoint& aLastObservedPosition,
const TimeStamp& aTimeStamp);
void ClearDataPoints() {
for (auto& touch : mTouches) {
touch.mBaseDataPoint = Nothing();
touch.mLatestDataPoint = Nothing();
}
}
private:
nsTArray::iterator TouchByIdentifier(int32_t aIdentifier);
nsTArray mTouches;
TimeStamp mLatestDataPointTime;
};
// The current touch positions with historical data points. This data only
// contains original non-resampled positions from the incoming touch events.
CurrentTouches mCurrentTouches;
// Incoming touch move events are stored here until NotifyFrame is called.
std::queue> mDeferredTouchMoveEvents;
// Stores any touch samples that were not included in the last emitted touch
// move event because they were in the future compared to the emitted event's
// timestamp. These data points should be prepended to the historical data of
// the next emitted touch move evnt.
// Can only be non-empty if mInResampledState is true.
std::unordered_map>
mRemainingTouchData;
// If we're in an resampled state, because the last outgoing event was a
// resampled touch move event, then this contains a copy of the unresampled,
// original touch move event.
// Some() iff mInResampledState is true.
Maybe mOriginalOfResampledTouchMove;
// The stream of outgoing events that can be consumed by our caller.
std::queue mOutgoingEvents;
// The timestamp of the event that was emitted most recently, or the null
// timestamp if no event has been emitted yet.
TimeStamp mLastEmittedEventTime;
uint64_t mNextEventId = 0;
// True if the last outgoing event was a touch move event with an resampled
// position. We only want to stay in this state as long as a continuous stream
// of touch move events is coming in.
bool mInResampledState = false;
};
} // namespace widget
Minimal test - lines (51, 186)
path: .spaces[0].spaces[0].spaces[0].metrics.nargs.sum
old: 0.0
new: 3.0
path: .spaces[0].spaces[0].spaces[0].metrics.nargs.average
old: 0.0
new: 0.42857142857142855
path: .spaces[0].spaces[0].spaces[0].metrics.cyclomatic.average
old: 1.0
new: 1.3333333333333333
path: .spaces[0].spaces[0].spaces[0].metrics.cyclomatic.sum
old: 2.0
new: 16.0
path: .spaces[0].spaces[0].spaces[0].metrics.loc.ploc
old: 11.0
new: 64.0
path: .spaces[0].spaces[0].spaces[0].metrics.loc.lloc
old: 0.0
new: 10.0
path: .spaces[0].spaces[0].spaces[0].metrics.loc.cloc
old: 2.0
new: 46.0
path: .spaces[0].spaces[0].spaces[0].metrics.loc.blank
old: 2.0
new: 26.0
path: .spaces[0].spaces[0].spaces[0].metrics.loc.sloc
old: 15.0
new: 136.0
path: .spaces[0].spaces[0].spaces[0].metrics.nexits.average
old: 0.0
new: 0.5714285714285714
path: .spaces[0].spaces[0].spaces[0].metrics.nexits.sum
old: 0.0
new: 4.0
path: .spaces[0].spaces[0].spaces[0].metrics.nom.total
old: 1.0
new: 7.0
path: .spaces[0].spaces[0].spaces[0].metrics.nom.functions
old: 1.0
new: 7.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.length
old: 35.0
new: 321.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.estimated_program_length
old: 57.05865002596162
new: 460.9407761481053
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.level
old: 0.18181818181818185
new: 0.057067603160667245
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.volume
old: 145.94737505048093
new: 2040.7741934824048
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.n2
old: 9.0
new: 65.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.vocabulary
old: 18.0
new: 82.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.effort
old: 802.7105627776451
new: 35760.64317502245
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.N2
old: 11.0
new: 134.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.purity_ratio
old: 1.6302471435989034
new: 1.43595257367011
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.difficulty
old: 5.5
new: 17.523076923076925
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.bugs
old: 0.028790645175253947
new: 0.36181143850239467
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.n1
old: 9.0
new: 17.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.N1
old: 24.0
new: 187.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.time
old: 44.59503126542473
new: 1986.7023986123584
path: .spaces[0].spaces[0].spaces[0].metrics.mi.mi_sei
old: 96.66371880954054
new: 34.526398748934426
path: .spaces[0].spaces[0].spaces[0].metrics.mi.mi_visual_studio
old: 58.92205085403852
new: 28.131784409076637
path: .spaces[0].spaces[0].spaces[0].metrics.mi.mi_original
old: 100.75670696040586
new: 48.10535133952105
Code
class TouchResampler final {
public:
// Feed a touch event into the interpolater. Returns an ID that can be used to
// match outgoing events to this incoming event, to track data associated with
// this event.
uint64_t ProcessEvent(MultiTouchInput&& aInput);
// Emit events, potentially resampled, for this timestamp. The events are put
// into the outgoing queue. May not emit any events if there's no update.
void NotifyFrame(const TimeStamp& aTimeStamp);
// Returns true between the start and the end of a touch gesture. During this
// time, the caller should keep itself registered with the system frame
// callback mechanism, so that NotifyFrame() can be called on every frame.
// (Otherwise, if we only registered the callback after receiving a touch move
// event, the frame callback might be delayed by a full frame.)
bool InTouchingState() const { return mCurrentTouches.HasTouch(); }
struct OutgoingEvent {
// The event, potentially modified from the original for resampling.
MultiTouchInput mEvent;
// Some(eventId) if this event is a modified version of an original event,
// Nothing() if this is an extra event.
Maybe mEventId;
};
// Returns the outgoing events that were produced since the last call.
// No event IDs will be skipped. Returns at least one outgoing event for each
// incoming event (possibly after a delay), and potential extra events with
// no originating event ID.
// Outgoing events should be consumed after every call to ProcessEvent() and
// after every call to NotifyFrame().
std::queue ConsumeOutgoingEvents() {
return std::move(mOutgoingEvents);
}
private:
// Add the event to the outgoing queue.
void EmitEvent(MultiTouchInput&& aInput, uint64_t aEventId) {
mLastEmittedEventTime = aInput.mTimeStamp;
mOutgoingEvents.push(OutgoingEvent{std::move(aInput), Some(aEventId)});
}
// Emit an event that does not correspond to an incoming event.
void EmitExtraEvent(MultiTouchInput&& aInput) {
mLastEmittedEventTime = aInput.mTimeStamp;
mOutgoingEvents.push(OutgoingEvent{std::move(aInput), Nothing()});
}
// Move any touch move events that we deferred for resampling to the outgoing
// queue unmodified, leaving mDeferredTouchMoveEvents empty.
void FlushDeferredTouchMoveEventsUnresampled();
// Must only be called if mInResampledState is true and
// mDeferredTouchMoveEvents is empty. Emits mOriginalOfResampledTouchMove,
// with a potentially adjusted timestamp for correct ordering.
void ReturnToNonResampledState();
// Takes historical touch data from mRemainingTouchData and prepends it to the
// data in aInput.
void PrependLeftoverHistoricalData(MultiTouchInput* aInput);
struct DataPoint {
TimeStamp mTimeStamp;
ScreenIntPoint mPosition;
};
struct TouchInfo {
void Update(const SingleTouchData& aTouch, const TimeStamp& aEventTime);
ScreenIntPoint ResampleAtTime(const ScreenIntPoint& aLastObservedPosition,
const TimeStamp& aTimeStamp);
int32_t mIdentifier = 0;
Maybe mBaseDataPoint;
Maybe mLatestDataPoint;
};
struct CurrentTouches {
void UpdateFromEvent(const MultiTouchInput& aInput);
bool HasTouch() const { return !mTouches.IsEmpty(); }
TimeStamp LatestDataPointTime() { return mLatestDataPointTime; }
ScreenIntPoint ResampleTouchPositionAtTime(
int32_t aIdentifier, const ScreenIntPoint& aLastObservedPosition,
const TimeStamp& aTimeStamp);
void ClearDataPoints() {
for (auto& touch : mTouches) {
touch.mBaseDataPoint = Nothing();
touch.mLatestDataPoint = Nothing();
}
}
private:
nsTArray::iterator TouchByIdentifier(int32_t aIdentifier);
nsTArray mTouches;
TimeStamp mLatestDataPointTime;
};
// The current touch positions with historical data points. This data only
// contains original non-resampled positions from the incoming touch events.
CurrentTouches mCurrentTouches;
// Incoming touch move events are stored here until NotifyFrame is called.
std::queue> mDeferredTouchMoveEvents;
// Stores any touch samples that were not included in the last emitted touch
// move event because they were in the future compared to the emitted event's
// timestamp. These data points should be prepended to the historical data of
// the next emitted touch move evnt.
// Can only be non-empty if mInResampledState is true.
std::unordered_map>
mRemainingTouchData;
// If we're in an resampled state, because the last outgoing event was a
// resampled touch move event, then this contains a copy of the unresampled,
// original touch move event.
// Some() iff mInResampledState is true.
Maybe mOriginalOfResampledTouchMove;
// The stream of outgoing events that can be consumed by our caller.
std::queue mOutgoingEvents;
// The timestamp of the event that was emitted most recently, or the null
// timestamp if no event has been emitted yet.
TimeStamp mLastEmittedEventTime;
uint64_t mNextEventId = 0;
// True if the last outgoing event was a touch move event with an resampled
// position. We only want to stay in this state as long as a continuous stream
// of touch move events is coming in.
bool mInResampledState = false;
};
Minimal test - lines (67, 67)
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.loc.lloc
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.mi.mi_sei
old: 155.17000000000002
new: 144.8408406278255
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.mi.mi_visual_studio
old: 93.54204911302038
new: 89.35513233151332
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.mi.mi_original
old: 159.95690398326485
new: 152.79727628688778
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.halstead.estimated_program_length
old: 4.754887502163468
new: 20.264662506490403
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.halstead.level
old: 0.6666666666666666
new: 0.3333333333333333
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.halstead.time
old: 0.6666666666666666
new: 5.2832083357371875
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.halstead.volume
old: 8.0
new: 31.69925001442312
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.halstead.length
old: 4.0
new: 10.0
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.halstead.vocabulary
old: 4.0
new: 9.0
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.halstead.N1
old: 3.0
new: 7.0
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.halstead.n2
old: 1.0
new: 3.0
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.halstead.difficulty
old: 1.5
new: 3.0
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.halstead.bugs
old: 0.0017471609294725976
new: 0.006944786620971931
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.halstead.n1
old: 3.0
new: 6.0
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.halstead.purity_ratio
old: 1.188721875540867
new: 2.0264662506490403
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.halstead.N2
old: 1.0
new: 3.0
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.halstead.effort
old: 12.0
new: 95.09775004326936
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.nexits.sum
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[0].spaces[0].metrics.nexits.average
old: 0.0
new: 1.0
Code
bool InTouchingState() const { return mCurrentTouches.HasTouch(); }
Minimal test - lines (17, 189)
path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 4.0
path: .spaces[0].metrics.nexits.average
old: 0.0
new: 0.5714285714285714
path: .spaces[0].metrics.mi.mi_sei
old: 80.57671602890764
new: 32.21473506783513
path: .spaces[0].metrics.mi.mi_original
old: 84.9991387284759
new: 43.63830607963422
path: .spaces[0].metrics.mi.mi_visual_studio
old: 49.707098671623335
new: 25.519477239552177
path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 1.2857142857142858
path: .spaces[0].metrics.cyclomatic.sum
old: 5.0
new: 18.0
path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 10.0
path: .spaces[0].metrics.loc.sloc
old: 32.0
new: 173.0
path: .spaces[0].metrics.loc.blank
old: 6.0
new: 27.0
path: .spaces[0].metrics.loc.ploc
old: 19.0
new: 68.0
path: .spaces[0].metrics.loc.cloc
old: 7.0
new: 78.0
path: .spaces[0].metrics.halstead.time
old: 86.44436676127685
new: 1997.4984864665944
path: .spaces[0].metrics.halstead.vocabulary
old: 22.0
new: 84.0
path: .spaces[0].metrics.halstead.bugs
old: 0.044759464313189494
new: 0.363121018576748
path: .spaces[0].metrics.halstead.estimated_program_length
old: 76.63504134881501
new: 475.9148440619265
path: .spaces[0].metrics.halstead.length
old: 56.0
new: 326.0
path: .spaces[0].metrics.halstead.level
old: 0.16049382716049382
new: 0.05795847750865052
path: .spaces[0].metrics.halstead.purity_ratio
old: 1.3684828812288394
new: 1.4598614848525353
path: .spaces[0].metrics.halstead.N1
old: 38.0
new: 190.0
path: .spaces[0].metrics.halstead.volume
old: 249.72817064368863
new: 2083.895479825876
path: .spaces[0].metrics.halstead.N2
old: 18.0
new: 136.0
path: .spaces[0].metrics.halstead.n2
old: 13.0
new: 67.0
path: .spaces[0].metrics.halstead.effort
old: 1555.9986017029833
new: 35954.972756398696
path: .spaces[0].metrics.halstead.n1
old: 9.0
new: 17.0
path: .spaces[0].metrics.halstead.difficulty
old: 6.230769230769231
new: 17.253731343283583
path: .spaces[0].metrics.nargs.sum
old: 0.0
new: 3.0
path: .spaces[0].metrics.nargs.average
old: 0.0
new: 0.42857142857142855
path: .spaces[0].metrics.nom.functions
old: 2.0
new: 7.0
path: .spaces[0].metrics.nom.total
old: 2.0
new: 7.0
Code
namespace mozilla {
namespace widget {
/**
* De-jitters touch motions by resampling (interpolating or extrapolating) touch
* positions for the vsync timestamp.
*
* Touch resampling improves the touch panning experience on devices where touch
* positions are sampled at a rate that's not an integer multiple of the display
* refresh rate, for example 100Hz touch sampling on a 60Hz display: Without
* resampling, we would alternate between taking one touch sample or two touch
* samples into account each frame, creating a jittery motion ("small step, big
* step, small step, big step").
* Intended for use on Android, where both touch events and vsync notifications
* arrive on the same thread, the Java UI thread.
* This class is not thread safe.
*
* TouchResampler operates in the following way:
*
* Original events are fed into ProcessEvent().
* Outgoing events (potentially resampled for resampling) are added to a queue
* and can be consumed by calling ConsumeOutgoingEvents(). Touch events which
* are not touch move events are forwarded instantly and not resampled. Only
* touch move events are resampled. Whenever a touch move event is received, it
* gets delayed until NotifyFrame() is called, at which point it is resampled
* into a resampled version for the given frame timestamp, and added to the
* outgoing queue. If no touch move event is received between two consecutive
* frames, this is treated as a stop in the touch motion. If the last outgoing
* event was an resampled touch move event, we return back to the non-resampled
* state by emitting a copy of the last original touch move event, which has
* unmodified position data. Touch events which are not touch move events also
* force a return to the non-resampled state before they are moved to the
* outgoing queue.
*/
class TouchResampler final {
public:
// Feed a touch event into the interpolater. Returns an ID that can be used to
// match outgoing events to this incoming event, to track data associated with
// this event.
uint64_t ProcessEvent(MultiTouchInput&& aInput);
// Emit events, potentially resampled, for this timestamp. The events are put
// into the outgoing queue. May not emit any events if there's no update.
void NotifyFrame(const TimeStamp& aTimeStamp);
// Returns true between the start and the end of a touch gesture. During this
// time, the caller should keep itself registered with the system frame
// callback mechanism, so that NotifyFrame() can be called on every frame.
// (Otherwise, if we only registered the callback after receiving a touch move
// event, the frame callback might be delayed by a full frame.)
bool InTouchingState() const { return mCurrentTouches.HasTouch(); }
struct OutgoingEvent {
// The event, potentially modified from the original for resampling.
MultiTouchInput mEvent;
// Some(eventId) if this event is a modified version of an original event,
// Nothing() if this is an extra event.
Maybe mEventId;
};
// Returns the outgoing events that were produced since the last call.
// No event IDs will be skipped. Returns at least one outgoing event for each
// incoming event (possibly after a delay), and potential extra events with
// no originating event ID.
// Outgoing events should be consumed after every call to ProcessEvent() and
// after every call to NotifyFrame().
std::queue ConsumeOutgoingEvents() {
return std::move(mOutgoingEvents);
}
private:
// Add the event to the outgoing queue.
void EmitEvent(MultiTouchInput&& aInput, uint64_t aEventId) {
mLastEmittedEventTime = aInput.mTimeStamp;
mOutgoingEvents.push(OutgoingEvent{std::move(aInput), Some(aEventId)});
}
// Emit an event that does not correspond to an incoming event.
void EmitExtraEvent(MultiTouchInput&& aInput) {
mLastEmittedEventTime = aInput.mTimeStamp;
mOutgoingEvents.push(OutgoingEvent{std::move(aInput), Nothing()});
}
// Move any touch move events that we deferred for resampling to the outgoing
// queue unmodified, leaving mDeferredTouchMoveEvents empty.
void FlushDeferredTouchMoveEventsUnresampled();
// Must only be called if mInResampledState is true and
// mDeferredTouchMoveEvents is empty. Emits mOriginalOfResampledTouchMove,
// with a potentially adjusted timestamp for correct ordering.
void ReturnToNonResampledState();
// Takes historical touch data from mRemainingTouchData and prepends it to the
// data in aInput.
void PrependLeftoverHistoricalData(MultiTouchInput* aInput);
struct DataPoint {
TimeStamp mTimeStamp;
ScreenIntPoint mPosition;
};
struct TouchInfo {
void Update(const SingleTouchData& aTouch, const TimeStamp& aEventTime);
ScreenIntPoint ResampleAtTime(const ScreenIntPoint& aLastObservedPosition,
const TimeStamp& aTimeStamp);
int32_t mIdentifier = 0;
Maybe mBaseDataPoint;
Maybe mLatestDataPoint;
};
struct CurrentTouches {
void UpdateFromEvent(const MultiTouchInput& aInput);
bool HasTouch() const { return !mTouches.IsEmpty(); }
TimeStamp LatestDataPointTime() { return mLatestDataPointTime; }
ScreenIntPoint ResampleTouchPositionAtTime(
int32_t aIdentifier, const ScreenIntPoint& aLastObservedPosition,
const TimeStamp& aTimeStamp);
void ClearDataPoints() {
for (auto& touch : mTouches) {
touch.mBaseDataPoint = Nothing();
touch.mLatestDataPoint = Nothing();
}
}
private:
nsTArray::iterator TouchByIdentifier(int32_t aIdentifier);
nsTArray mTouches;
TimeStamp mLatestDataPointTime;
};
// The current touch positions with historical data points. This data only
// contains original non-resampled positions from the incoming touch events.
CurrentTouches mCurrentTouches;
// Incoming touch move events are stored here until NotifyFrame is called.
std::queue> mDeferredTouchMoveEvents;
// Stores any touch samples that were not included in the last emitted touch
// move event because they were in the future compared to the emitted event's
// timestamp. These data points should be prepended to the historical data of
// the next emitted touch move evnt.
// Can only be non-empty if mInResampledState is true.
std::unordered_map>
mRemainingTouchData;
// If we're in an resampled state, because the last outgoing event was a
// resampled touch move event, then this contains a copy of the unresampled,
// original touch move event.
// Some() iff mInResampledState is true.
Maybe mOriginalOfResampledTouchMove;
// The stream of outgoing events that can be consumed by our caller.
std::queue mOutgoingEvents;
// The timestamp of the event that was emitted most recently, or the null
// timestamp if no event has been emitted yet.
TimeStamp mLastEmittedEventTime;
uint64_t mNextEventId = 0;
// True if the last outgoing event was a touch move event with an resampled
// position. We only want to stay in this state as long as a continuous stream
// of touch move events is coming in.
bool mInResampledState = false;
};
} // namespace widget
} // namespace mozilla