Added LowPassFilter and support for smooth scatter rotation and zoom.
This commit is contained in:
Vendored
+1352
-1248
File diff suppressed because it is too large
Load Diff
Vendored
+114
-15
@@ -3265,8 +3265,8 @@
|
||||
|
||||
// Distance == 0.0 indicates an inside relation.
|
||||
static distanceToRect(p, r) {
|
||||
var cx = Math.max(Math.min(p.x, r.x + r.width), r.x);
|
||||
var cy = Math.max(Math.min(p.y, r.y + r.height), r.y);
|
||||
let cx = Math.max(Math.min(p.x, r.x + r.width), r.x);
|
||||
let cy = Math.max(Math.min(p.y, r.y + r.height), r.y);
|
||||
return Math.sqrt((p.x - cx) * (p.x - cx) + (p.y - cy) * (p.y - cy))
|
||||
}
|
||||
|
||||
@@ -3702,6 +3702,92 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class LowPassFilter {
|
||||
|
||||
constructor(smoothing = 0.5, bufferMaxSize=10) {
|
||||
this.smoothing = smoothing; // must be smaller than 1
|
||||
this.buffer = []; // FIFO queue
|
||||
this.bufferMaxSize = bufferMaxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup buffer with array of values
|
||||
*
|
||||
* @param {array} values
|
||||
* @returns {array}
|
||||
* @access public
|
||||
*/
|
||||
setup(values) {
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
this.__push(values[i]);
|
||||
}
|
||||
return this.buffer
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear buffer to prepare for new values.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
clear() {
|
||||
this.buffer = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new value to buffer (FIFO queue)
|
||||
*
|
||||
* @param {integer|float} value
|
||||
* @returns {integer|float}
|
||||
* @access private
|
||||
*/
|
||||
__push(value) {
|
||||
let removed = (this.buffer.length === this.bufferMaxSize)
|
||||
? this.buffer.shift()
|
||||
: 0;
|
||||
|
||||
this.buffer.push(value);
|
||||
return removed
|
||||
}
|
||||
|
||||
/**
|
||||
* Smooth value from stream
|
||||
*
|
||||
* @param {integer|float} nextValue
|
||||
* @returns {integer|float}
|
||||
* @access public
|
||||
*/
|
||||
next(nextValue) {
|
||||
|
||||
// push new value to the end, and remove oldest one
|
||||
let removed = this.__push(nextValue);
|
||||
// smooth value using all values from buffer
|
||||
let result = this.buffer.reduce((last, current) => {
|
||||
return this.smoothing * current + (1 - this.smoothing) * last
|
||||
}, removed);
|
||||
// replace smoothed value
|
||||
this.buffer[this.buffer.length - 1] = result;
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Smooth array of values
|
||||
*
|
||||
* @param {array} values
|
||||
* @returns {undefined}
|
||||
* @access public
|
||||
*/
|
||||
smoothArray(values) {
|
||||
let value = values[0];
|
||||
for (let i = 1; i < values.length; i++) {
|
||||
let currentValue = values[i];
|
||||
value += (currentValue - value) * this.smoothing;
|
||||
values[i] = Math.round(value);
|
||||
}
|
||||
return values
|
||||
}
|
||||
}
|
||||
|
||||
/* global apollo, subscriptions, gql */
|
||||
|
||||
/**
|
||||
@@ -6053,11 +6139,8 @@
|
||||
window.Capabilities = Capabilities;
|
||||
window.CapabilitiesTests = CapabilitiesTests;
|
||||
|
||||
/** Basic class for poppable elements that need to be closed as soon as one poppable is
|
||||
* shown.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
/**
|
||||
* A base class for scatter specific events.
|
||||
*
|
||||
@@ -6322,7 +6405,8 @@
|
||||
scaleAutoClose = false,
|
||||
scaleCloseThreshold = 0.10,
|
||||
scaleCloseBuffer = 0.05,
|
||||
maxRotation = Angle.degree2radian(5)
|
||||
maxRotation = Angle.degree2radian(5),
|
||||
useLowPassFilter = true
|
||||
} = {}) {
|
||||
if (rotationDegrees != null && rotation != null) {
|
||||
throw new Error('Use rotationDegrees or rotation but not both')
|
||||
@@ -6366,7 +6450,12 @@
|
||||
this.resizable = resizable;
|
||||
this.mouseZoomFactor = mouseZoomFactor;
|
||||
this.autoBringToFront = autoBringToFront;
|
||||
|
||||
this.useLowPassFilter = useLowPassFilter;
|
||||
if (useLowPassFilter) {
|
||||
this.rotateLPF = new LowPassFilter();
|
||||
this.zoomLPF = new LowPassFilter();
|
||||
this.zoomLPF.setup([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
|
||||
}
|
||||
this.dragging = false;
|
||||
this.onTransform = onTransform != null ? [onTransform] : null;
|
||||
this.onClose = onClose != null ? [onClose] : null;
|
||||
@@ -6390,6 +6479,11 @@
|
||||
this.bringToFront();
|
||||
this.killAnimation();
|
||||
this.observeVelocity();
|
||||
if (this.useLowPassFilter) {
|
||||
this.rotateLPF.clear();
|
||||
this.zoomLPF.clear();
|
||||
this.zoomLPF.setup([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -6403,14 +6497,19 @@
|
||||
let delta = interaction.delta();
|
||||
if (delta != null) {
|
||||
this.addVelocity(delta);
|
||||
let alpha = delta.rotate;
|
||||
let rotate = delta.rotate;
|
||||
let zoom = delta.zoom;
|
||||
if (this.maxRotation != null) {
|
||||
if (Math.abs(alpha) > this.maxRotation) {
|
||||
alpha = 0;
|
||||
if (Math.abs(rotate) > this.maxRotation) {
|
||||
rotate = 0;
|
||||
}
|
||||
}
|
||||
this.transform(delta, delta.zoom, alpha, delta.about);
|
||||
if (delta.zoom != 1) this.interactionAnchor = delta.about;
|
||||
if (this.useLowPassFilter) {
|
||||
rotate = this.rotateLPF.next(rotate);
|
||||
zoom = this.zoomLPF.next(zoom);
|
||||
}
|
||||
this.transform(delta, zoom, rotate, delta.about);
|
||||
if (zoom != 1) this.interactionAnchor = delta.about;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14870,7 +14969,7 @@
|
||||
* @extends Popup
|
||||
* @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/popupmenu.html|DocTest}
|
||||
*/
|
||||
class PopupMenu$1 extends Popup {
|
||||
class PopupMenu extends Popup {
|
||||
|
||||
/**
|
||||
* Creates an instance of a PopupMenu.
|
||||
@@ -15519,7 +15618,7 @@
|
||||
window.Stylus = Stylus;
|
||||
window.Switch = Switch;
|
||||
window.Popup = Popup;
|
||||
window.PopupMenu = PopupMenu$1;
|
||||
window.PopupMenu = PopupMenu;
|
||||
window.Modal = Modal;
|
||||
window.Volatile = Volatile;
|
||||
window.Message = Message;
|
||||
|
||||
Reference in New Issue
Block a user