How to debounce a signal in QML? - qml

I have a few properties that have change handlers that request a repaint of a canvas. This works fairly well, but I would like to debounce the signal because a user can only see a limited number of refreshes and some of the data items can change quite frequently. This causes far more refreshes than the user can see and reduces how responsive the UI is.
I have looked at debouncing the signal in javascript (there are examples on the web for how to do that) and tie it in to QML, but I haven't figured out how to get QML and javascript to work together in that way yet.
I would love to see something like the following:
function rateLimitedRefresh(){
// magic to limit to 30 frames per second
canvas.requestPaint()
}
onValueChanged: {
rateLimitedRefresh();
}
With the requestPaint method only being called on the canvas a maximum of 30 times per second.
I used a modification of Mertanian's answer. This modification enforces the frame rate limit at a per frame level as opposed to a per second level.
property var limitStartTime: new Date()
function rateLimitedRefresh(){
// magic to limit to 30 frames per second
var now = new Date();
if (now - limitStartTime >= 32) {
limitStartTime = now
canvas.requestPaint()
}
}

How about something like this:
property var limitStartTime: new Date()
property int refreshesThisSecond: 0
function rateLimitedRefresh(){
// magic to limit to 30 frames per second
if ((new Date()) - limitStartTime >= 33) {
limitStartTime = new Date(limitStartTime.getTime() + 33)
canvas.requestPaint()
}
}
onValueChanged: {
rateLimitedRefresh();
}

Related

Synchronizing TWEEN, CCapture, and THREE.js rendering code to all be on the same clock

I'm using a library called CCapture to capture equally time-spaced frames. This code seems to be able to hook the clock and control it so that is slows down my rendering loop when it is capturing. My code uses clock.getDelta() to get the time and it uses this time to calculate the positions of moving transit vehicles. When CCapture controls the rendering rate, the vehicles are correctly positioned.
I recently started using the TWEEN library in my code.
function animate() {
renderer.setAnimationLoop( renderFrame );
}
function renderFrame() {
const delta = clock.getDelta()
timeSinceStart += delta;
repositionTransitVehicles(timeSinceStart);
renderer.render(scene, camera);
if (capturer) {
capturer.capture( renderer.domElement );
}
orbitControls.enabled = false;
TWEEN.update();
orbitControls.enabled = true;
orbitControls.update();
},
With the no-arguments version of TWEEN.update(), tweening works but always proceeds at real time, which is too fast when I'm using CCapture. If I use...
TWEEN.update(timeSinceStart)
...then tweening does not work at all.
Does anyone know the secret to making TWEEN.update() operate using the same clock as the rest of the model?
The timer that TWEEN uses is a millisecond timer, an thus the units of the time that need to be passed into TWEEN.update() need to be in units of milliseconds. The units of THREE.Clock() are seconds. So use...
TWEEN.update(timeSinceStart*1000)
Wherever you use TWEEN's .start() method to initiate your tweens, you will also need to pass in the argument timeSinceStart*1000 as well.

How to change the current time in the progress control?

Is it possible to manualy set the current time position on the timeline? I need this because I have several separated videos which represent one video and are playing in different players.
For example, the actual current time is marked as white dot, the desired current time that I want to show as red dot.
Can I do this somehow without creating completely new custom controlbar?
Middleware could be part of the solution, which allows you to intercept and modify what's sent to / received from the playback tech (video element).
var myMiddleware = function(player) {
return {
currentTime: function(ct) {
// When the video el says the currentTime is 60s, report 160s
return ct + 100;
},
setCurrentTime: function(time) {
// When the user / progress bar wants to seek to 220s, actually seek to 120s
return time - 100;
}
};
};
videojs.use('*', myMiddleware);

Titanium geolocation distanceFilter property is not working as expected

Need a help here for geolocation API - even after using Titanium.Geolocation.distanceFilter = 10; 10 in meter callback function is getting triggered randomly without moving here and there. Any idea what is wrong here ?
function Geolocate() {
var self = this;
// The callback function we should call when location is finally determined
this.locationReceivedCallback = function () {};
// Check if location event is already triggered or not
this.isLocationEventRegister = false;
// The function that unregisters the location event
this.unregisterLocationEvent = function () {
this.isLocationEventRegister = false;
Ti.Geolocation.removeEventListener('location', self.locationReceivedCallback);
};
}
Geolocate.prototype.init = function () {
// Setting up some preference values
Titanium.Geolocation.distanceFilter = 10; //The minimum change of position (in meters) before a 'location' event is fired.
if (deviceDetect.isIos()) {
Titanium.Geolocation.accuracy = Titanium.Geolocation.ACCURACY_BEST; // Using this value on Android enables legacy mode operation, which is not recommended.
} else {
Titanium.Geolocation.accuracy = Titanium.Geolocation.ACCURACY_LOW; //Using this value on Android enables simple mode operation.
}
};
Geolocate.prototype.getCurrentPosition = function (callback) {
this.isLocationEventRegister = true;
this.locationReceivedCallback = callback;
Titanium.Geolocation.addEventListener("location", this.locationReceivedCallback);
};
Geolocate.prototype.locationServicesAreAvailable = function () {
if (Titanium.Geolocation.locationServicesEnabled) {
return true;
} else {
return false;
}
};
Geolocate.prototype.cancelLocationRequest = function () {
this.unregisterLocationEvent();
};
module.exports = Geolocate;
Actual scenario is whenever I clicked on get location its gives me current location. then i tried to drag it map or image view for nearest place. Suddenly my view go current location. This is happening because of call back ? How to get rid off this ?
It's not a problem to do with your code, it is simply GPS in-accuracy.
GPS accuracy is (almost) never better than 10 meters, meaning it can be 10 meters off. When it recalculates your position it can be 10 meters down the line, so even if you're perfectly still with perfect GPS accuracy, you still might have differences of about 10 meters.
That said, you probably don't have best GPS accuracy when sitting behind a computer in a building, you probably have closer to 30-45 meters accuracy. Meaning every recalculation can easily be 10 meters differently.
Your solution would actually be rate-limit on top of using this property. The property will make sure it will not trigger multiple times per second, and then you can, in your own code, rate limit what you do with it.

Game loop on redux-saga

I'm playing with redux-saga to create a version of the snake game in react native, and I'm not sure how to go about the game loop implementation. So far I have a working version, but it feels clunky.. the snake moves at different speeds and the game is not very smooth.
Here is what I've got so far:
function *tickSaga() {
while (true) {
yield call(updateGameSaga)
const game = yield select(getGame)
if (game.crashed)
break
yield delay(1000)
}
}
The updateGameSaga basically gets the state from the store, process some logic (finds out if the snake next move will be a crash, move to an empty tile or move to a food tile and grow) and dispatches the corresponding action for it.
I'm slightly aware of things like requestAnimationFrame which I know I should be using, but I'm not sure how to make it work within the saga (maybe I don't need too do it in the saga, hence this question).
Any ideas on how to improve the smoothness of the game loop would be welcome.
UPDATE
I've included redux-saga-ticker, which internally uses setInterval (code here) to periodically send updates to the channel.
function *tickSaga() {
const channel = Ticker(1000); // game tick every 1000ms
while (true) {
yield take(channel);
yield call(updateGameSaga);
const game = yield select(getGame)
if (game.crashed)
break
}
}
It works better now and I like the channel aproach, but I still feel requestAnimationFrame is the way to go, although I'm not yet sure on how to implement it.
How about this.
let lastTime = 0;
function *tickSaga() {
while (true) {
var time = +new Date();
var delayTime = Math.max(0, 1000 - (time - lastTime));
lastTime = time + delayTime;
yield call(updateGameSaga)
const game = yield select(getGame)
if (game.crashed)
break
yield delay(delayTime)
}
}
If you need 60 fps, replace 1000 to 16 (1000/60).
For each tick you have a correction of the interval (time - lastTime) so the ticker interval should be uniform.

Youtube API event on a specified time

Is it possible through the YouTube API to fire an event when the video reaches a specified time? e.g. when the video reaches 20 sec. fire the event.
Thanks,
Mauro
not sure if you still need an answer to this (as I'm finding it 4 months later) but here's how I accomplished this with youtube's iframe embed api. It's ugly in that it requires a setInterval, but there really isn't any kind of "timeupdate" event in the YouTube API (at least not in the iframe API), so you have to kind of fake it by checking the video time every so often. It seems to run just fine.
Let's say you want to start up your player as shown here with YT.Player(), and you want to implement your own onProgress() function that is called whenever the video time changes:
In HTML:
<div id="myPlayer"></div>
In JS:
// first, load the YouTube Iframe API:
var tag = document.createElement('script');
tag.src = "//www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// some variables (global here, but they don't have to be)
var player;
var videoId = 'SomeYoutubeIdHere';
var videotime = 0;
var timeupdater = null;
// load your player when the API becomes ready
function onYoutubeIframeAPIReady() {
player = new YT.Player('myPlayer', {
width: '640',
height: '390',
videoId: videoId,
events: {
'onReady': onPlayerReady
}
});
}
// when the player is ready, start checking the current time every 100 ms.
function onPlayerReady() {
function updateTime() {
var oldTime = videotime;
if(player && player.getCurrentTime) {
videotime = player.getCurrentTime();
}
if(videotime !== oldTime) {
onProgress(videotime);
}
}
timeupdater = setInterval(updateTime, 100);
}
// when the time changes, this will be called.
function onProgress(currentTime) {
if(currentTime > 20) {
console.log("the video reached 20 seconds!");
}
}
It's a little sloppy, requiring a few global variables, but you could easily refactor it into a closure and/or make the interval stop and start itself on play/pause by also including the onStateChange event when you initialize the player, and writing an onPlayerStateChange function that checks for play and pause. You'd just need to seperate the updateTime() function from onPlayerReady, and strategically call timeupdater = setInterval(updateTime, 100); and clearInterval(timeupdater); in the right event handlers. See here for more on using events with YouTube.
I think you may have a look at popcorn.js.
It's an interesting mozilla project and it seems to solve your problem.
Not sure if anyone agrees here but I might prefer to use setTimeout() over setInterval() as it avoids using event listeners, eg:
function checkTime(){
setTimeout(function(){
videotime = getVideoTime();
if(videotime !== requiredTime) {
checkTime();//Recursive call.
}else{
//run stuff you want to at a particular time.
//do not call checkTime() again.
}
},100);
}
Its a basic pseudocode but hopefully conveys the idea.