From 9df1d77b4f24adf5736e35eb9a1bfb2bd717973c Mon Sep 17 00:00:00 2001 From: Ruben van de Ven Date: Mon, 11 Nov 2019 18:25:04 +0100 Subject: [PATCH] Faster interface and timing factor --- hugvey/story.py | 26 +++++++++---- www/css/styles.css | 19 ++++----- www/js/hugvey_console.js | 84 +++++++++++++++++++++++++++++----------- www/scss/styles.scss | 24 ++++++------ 4 files changed, 101 insertions(+), 52 deletions(-) diff --git a/hugvey/story.py b/hugvey/story.py index 820def5..2561e17 100644 --- a/hugvey/story.py +++ b/hugvey/story.py @@ -372,7 +372,7 @@ class Condition(object): # else: # story.logger.debug('Only if no reply has no text yet!') - hasMetTimeout = now - story.lastMsgFinishTime >= float(self.vars['seconds']) + hasMetTimeout = now - story.lastMsgFinishTime >= story.applyTimeFactor(self.vars['seconds']) if not hasMetTimeout: return False @@ -518,7 +518,7 @@ class Condition(object): ) return True - + def _hasMetReplyContains(self, story) -> bool: """ Check the reply for specific characteristics: @@ -575,7 +575,7 @@ class Condition(object): if replyDuration > float(delay['minReplyDuration']): timeSinceReply = r.getTimeSinceLastUtterance() story.logger.log(LOG_BS, f"check delay duration is now {replyDuration}, already waiting for {timeSinceReply}, have to wait {delay['waitTime']}") - if timeSinceReply > float(delay['waitTime']): + if timeSinceReply > story.applyTimeFactor(delay['waitTime']): # if variables are captured, only set them the moment the condition matches if capturedVariables is not None: for captureGroup in capturedVariables: @@ -585,7 +585,7 @@ class Condition(object): capturedVariables, timeSinceReply ) - self.usedContainsDuration = float(delay['waitTime']) + self.usedContainsDuration = story.applyTimeFactor(delay['waitTime']) return True break # don't check other delays # wait for delay to match @@ -897,7 +897,7 @@ class Diversion(object): # ignore the direction argument, and only check if the current message has a valid default return - waitTime = 1.8 if 'waitTime' not in self.params else float(self.params['waitTime']) + waitTime = story.applyTimeFactor(1.8 if 'waitTime' not in self.params else float(self.params['waitTime'])) timeSince = story.currentReply.getTimeSinceLastUtterance() if timeSince < waitTime: story.logger.log(LOG_BS, f"Waiting for replyContains: {timeSince} (needs {waitTime})") @@ -984,7 +984,7 @@ class Diversion(object): # TODO: how to handle this now we sometimes use different timings. # Perhaps set isFinished when matching condition. - if story.currentReply is None or story.currentReply.getTimeSinceLastUtterance() < 1.8: + if story.currentReply is None or story.currentReply.getTimeSinceLastUtterance() < story.applyTimeFactor(1.8): return if story.currentMessage.didRepeat: @@ -1025,12 +1025,12 @@ class Diversion(object): return now = story.timer.getElapsed() - if now - story.lastMsgFinishTime < float(self.params['minTimeAfterMessage']): + if now - story.lastMsgFinishTime < story.applyTimeFactor(self.params['minTimeAfterMessage']): # not less than x sec after it return - interval = float(self.params['interval']) + interval = story.applyTimeFactor(self.params['interval']) if not self.params['fromLastMessage']: # (1) last spoken at all @@ -1115,6 +1115,7 @@ class Configuration(object): id = 'configuration' volume = 1 # Volume multiplier for 'play' command nothing_text = "nothing" # When variable is not set, but used in sentence, replace it with this word. + time_factor = 1 light0_intensity = 0 light0_fade = 30. # fade duration in seconds light1_intensity = 150 @@ -1953,6 +1954,15 @@ class Story(object): return self.strands[msg.id] return self.calculateFinishesForMsg(msg.id, checked=[]) + + def applyTimeFactor(self, time) -> float: + """ + Apply the particularities of the configuration.time_factor + """ + time = float(time) + if time < 1: + return time + return time * self.configuration.time_factor def getDefaultDirectionForMsg(self, msg): """ diff --git a/www/css/styles.css b/www/css/styles.css index 9d225e8..85e6a7a 100644 --- a/www/css/styles.css +++ b/www/css/styles.css @@ -43,6 +43,16 @@ body.dark { .btn:hover, input[type="submit"]:hover { background: #666; } +.btn.loading:after { + display: inline-block; + content: ''; + width: 15px; + height: 15px; + background-color: white; + border-radius: 100%; + -webkit-animation: sk-scaleout 1.0s infinite ease-in-out; + animation: sk-scaleout 1.0s infinite ease-in-out; } + input[type="number"] { width: 80px; text-align: right; } @@ -91,15 +101,6 @@ img.icon { color: #ccc; list-style: none; padding: 0; } - #status > div#overview #languages .loading:after { - display: inline-block; - content: ''; - width: 15px; - height: 15px; - background-color: white; - border-radius: 100%; - -webkit-animation: sk-scaleout 1.0s infinite ease-in-out; - animation: sk-scaleout 1.0s infinite ease-in-out; } #status .counts dd, #status .counts dt { display: inline-block; width: 30px; diff --git a/www/js/hugvey_console.js b/www/js/hugvey_console.js index c8ba5d2..a83702f 100644 --- a/www/js/hugvey_console.js +++ b/www/js/hugvey_console.js @@ -324,7 +324,16 @@ class Graph { this.linkG = this.container.append( "g" ) .attr( "id", "links" ); - document.getElementById( 'btn-save' ).addEventListener( 'click', function( e ) { graph.saveJson(); } ); + document.getElementById( 'btn-save' ).addEventListener( 'click', function( e ) { + let el = e.target; + el.classList.add('loading'); + // give the ui a fraction to actually apply the 'loading' class + setTimeout(function(){ + graph.saveJson(); + el.classList.remove('loading'); + }, 100); + + } ); document.getElementById( 'btn-addMsg' ).addEventListener( 'click', function( e ) { graph.createMsg(); } ); document.getElementById( 'btn-diversions' ).addEventListener( 'click', function( e ) { graph.showDiversions(); } ); document.getElementById( 'btn-audio' ).addEventListener( 'click', function( e ) { graph.showAudioFiles(); } ); @@ -1025,6 +1034,20 @@ class Graph { 'value': this.configuration.hasOwnProperty('nothing_text') ? this.configuration.nothing_text : "nothing" }) ), + crel( + 'label', + "Timing factor: (< 1 is faster, >1 is slower)", + crel('input', { + 'type': 'number', + 'on': { + 'change': function(e){ + panopticon.graph.configuration['time_factor'] = parseFloat(e.target.value) + } + }, + 'value': this.configuration.hasOwnProperty('time_factor') ? this.configuration.time_factor : 1, + 'step': 0.01 + }) + ), crel('hr'), crel('h2', 'Light fade setting #0'), crel( @@ -1989,10 +2012,14 @@ class Graph { if(this.getDirectionsFrom( source ).length < 1 && this.getDirectionsFrom( target ).length < 1 && this.getDirectionsTo( target ).length < 1) { skipDistances = true; let distance = this.distances[source['@id']]; - let d = [distance[0] + 1, distance[1]]; - // create a distance based on source's position - // this saves us from running the slow calculateDistancesFromStart - this.distances[target['@id']] = d; + if(distance == null) { + skipDistances = false; + } else { + let d = [distance[0] + 1, distance[1]]; + // create a distance based on source's position + // this saves us from running the slow calculateDistancesFromStart + this.distances[target['@id']] = d; + } } else { skipDistances = false; } @@ -2009,7 +2036,7 @@ class Graph { createMsg() { this.addMsg(); - this.build(); +// this.build(); // already happens in addMsg() } createConnectedMsg(sourceMsg) { @@ -2150,10 +2177,16 @@ class Graph { } loadData( data, language_code ) { + console.time('load'); this.language_code = language_code; this.data = data; + console.time('load:update'); this.updateFromData(); + console.timeEnd('load:update'); + console.time('load:build'); this.build( true ); + console.timeEnd('load:build'); + console.timeEnd('load'); } updateFromData(skipDistances) { @@ -2181,7 +2214,9 @@ class Graph { } // save state; - this.saveState(); +// console.time('update:save') +// this.saveState(); +// console.timeEnd('update:save') } updateHugveyStatus(hv) { @@ -2205,19 +2240,20 @@ class Graph { } } - saveState() { - window.localStorage.setItem( "lastState", this.getJsonString() ); - } - - hasSavedState() { - return window.localStorage.getItem( "lastState" ) !== null; - } - - loadFromState() { - this.loadData( JSON.parse( window.localStorage.getItem( "lastState" ) ) ); - } +// saveState() { +// window.localStorage.setItem( "lastState", this.getJsonString() ); +// } +// +// hasSavedState() { +// return window.localStorage.getItem( "lastState" ) !== null; +// } +// +// loadFromState() { +// this.loadData( JSON.parse( window.localStorage.getItem( "lastState" ) ) ); +// } build( isInit ) { + console.trace(); this.simulation = d3.forceSimulation( this.messages ) .force( "link", d3.forceLink( this.directions ).id( d => d['@id'] ).strength(0) ) // .force( "charge", d3.forceManyBody().strength( 100 ) ) @@ -2399,9 +2435,11 @@ class Graph { // this.simulation.alpha(1); // this.simulation.restart(); + console.time('build:simulate') for ( let i = 0, n = Math.ceil( Math.log( this.simulation.alphaMin() ) / Math.log( 1 - this.simulation.alphaDecay() ) ); i < n; ++i ) { this.simulation.tick(); } + console.timeEnd('build:simulate') return this.svg.node(); } @@ -2461,7 +2499,7 @@ class Graph { } i++; - console.log('set for id', childMsgId, goingDown, depth, yPos); +// console.log('set for id', childMsgId, goingDown, depth, yPos); distances[childMsgId] = [depth, yPos]; } @@ -2498,26 +2536,26 @@ class Graph { let yPos = 0; console.time('step1'); for(let startMsg of starts) { - console.time('start: '+startMsg['@id']); +// console.time('start: '+startMsg['@id']); if(distances[startMsg['@id']] === null) { distances[startMsg['@id']] = [0, yPos]; } yPos = traverseMsg(startMsg['@id'], 1 , true, yPos); yPos += 1; - console.timeEnd('start: '+startMsg['@id']); +// console.timeEnd('start: '+startMsg['@id']); } console.timeEnd('step1'); console.time('step2'); // now we have the formal tree, lets try to polish the rest: for(let msgId in distances) { - console.time('polish: '+ msgId); +// console.time('polish: '+ msgId); if(distances[msgId] === null) { continue; } // let's see if there are parent nodes that are not in the distances array // traverse up and see whether we encounter anything new traverseMsg(msgId, distances[msgId][0] -1, false, distances[msgId][1]) - console.timeEnd('polish: '+ msgId); +// console.timeEnd('polish: '+ msgId); } console.timeEnd('step2'); diff --git a/www/scss/styles.scss b/www/scss/styles.scss index 9798cf0..dafabcf 100644 --- a/www/scss/styles.scss +++ b/www/scss/styles.scss @@ -59,6 +59,18 @@ body.dark{ } } +.btn.loading:after { + display:inline-block; + content: ''; + width: 15px; + height: 15px; + background-color: white; + + border-radius: 100%; + -webkit-animation: sk-scaleout 1.0s infinite ease-in-out; + animation: sk-scaleout 1.0s infinite ease-in-out; + } + input[type="number"] { width: 80px; text-align:right; @@ -121,18 +133,6 @@ img.icon{ #languages{ color:#ccc; list-style: none;padding:0; - - .loading:after { - display:inline-block; - content: ''; - width: 15px; - height: 15px; - background-color: white; - - border-radius: 100%; - -webkit-animation: sk-scaleout 1.0s infinite ease-in-out; - animation: sk-scaleout 1.0s infinite ease-in-out; - } } }