From cacea2a7454a1efd67a4414502d1a6d858e92f35 Mon Sep 17 00:00:00 2001 From: David Wakelin Date: Fri, 12 Apr 2019 17:17:00 +0100 Subject: [PATCH 1/3] Plotter WIP --- src/dat/controllers/PlotterController.js | 39 +++++++++++ src/dat/gui/GUI.js | 15 ++++ src/dat/gui/_structure.scss | 2 +- src/dat/utils/plotter.js | 87 ++++++++++++++++++++++++ 4 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 src/dat/controllers/PlotterController.js create mode 100644 src/dat/utils/plotter.js diff --git a/src/dat/controllers/PlotterController.js b/src/dat/controllers/PlotterController.js new file mode 100644 index 00000000..42dcc501 --- /dev/null +++ b/src/dat/controllers/PlotterController.js @@ -0,0 +1,39 @@ +/** + * dat-gui JavaScript Controller Library + * http://code.google.com/p/dat-gui + * + * Copyright 2011 Data Arts Team, Google Creative Lab + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import Controller from './Controller'; +import dom from '../dom/dom'; +import plotter from '../utils/plotter' + +/** + * @class Provides a GUI interface to fire a specified method, a property of an object. + * + * @extends dat.controllers.Controller + * + * @param {Object} object The object to be manipulated + * @param {string} property The name of the property to be manipulated + */ +class PlotterController extends Controller { + constructor(object, property, params) { + super(object, property); + + const _this = this; + + this.__panel = new plotter('#0ff', '#002'); + this.domElement.appendChild(this.__panel.dom); + } + + +} + +export default PlotterController; diff --git a/src/dat/gui/GUI.js b/src/dat/gui/GUI.js index 972c3073..87cee09d 100644 --- a/src/dat/gui/GUI.js +++ b/src/dat/gui/GUI.js @@ -20,6 +20,7 @@ import FunctionController from '../controllers/FunctionController'; import NumberControllerBox from '../controllers/NumberControllerBox'; import NumberControllerSlider from '../controllers/NumberControllerSlider'; import ColorController from '../controllers/ColorController'; +import PlotterController from '../controllers/PlotterController'; import requestAnimationFrame from '../utils/requestAnimationFrame'; import CenteredDiv from '../dom/CenteredDiv'; import dom from '../dom/dom'; @@ -556,6 +557,17 @@ common.extend( ); }, + addPlotter: function(object, property) { + return add( + this, + object, + property, + { + plotter: true + } + ); + }, + /** * Removes the given controller from the GUI. * @param {Controller} controller @@ -1139,6 +1151,9 @@ function add(gui, object, property, params) { if (params.color) { controller = new ColorController(object, property); + } else if (params.plotter) { + console.log(params.factoryArgs); + controller = new PlotterController(object, property); } else { const factoryArgs = [object, property].concat(params.factoryArgs); controller = ControllerFactory.apply(gui, factoryArgs); diff --git a/src/dat/gui/_structure.scss b/src/dat/gui/_structure.scss index 3193f8e0..23e4c6a9 100644 --- a/src/dat/gui/_structure.scss +++ b/src/dat/gui/_structure.scss @@ -180,7 +180,7 @@ $button-height: 20px; } /** Controller placement */ - .c input[type=text] { + .c input[type=text], .c canvas { border: 0; margin-top: 4px; padding: 3px; diff --git a/src/dat/utils/plotter.js b/src/dat/utils/plotter.js new file mode 100644 index 00000000..a6eac052 --- /dev/null +++ b/src/dat/utils/plotter.js @@ -0,0 +1,87 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * Code from stats.js r17: https://github.com/mrdoob/stats.js + * Licence from that project: + + The MIT License + +Copyright (c) 2009-2016 stats.js authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + */ + +const plotter = function ( fg, bg ) { + + var min = Infinity, max = 0, round = Math.round; + var PR = round( window.devicePixelRatio || 1 ); + + var WIDTH = 160 * PR, HEIGHT = 48 * PR, + TEXT_X = 3 * PR, TEXT_Y = 2 * PR, + GRAPH_X = 3 * PR, GRAPH_Y = 3 * PR, + GRAPH_WIDTH = 154 * PR, GRAPH_HEIGHT = 30 * PR; + + var canvas = document.createElement( 'canvas' ); + canvas.width = WIDTH; + canvas.height = HEIGHT; + //canvas.style.cssText = 'width:80px;height:48px'; + + var context = canvas.getContext( '2d' ); + context.font = 'bold ' + ( 9 * PR ) + 'px Helvetica,Arial,sans-serif'; + context.textBaseline = 'top'; + + context.fillStyle = bg; + context.fillRect( 0, 0, WIDTH, HEIGHT ); + + context.fillStyle = fg; + context.fillRect( GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT ); + + context.fillStyle = bg; + context.globalAlpha = 0.9; + context.fillRect( GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT ); + + return { + + dom: canvas, + + update: function ( value, maxValue ) { + + min = Math.min( min, value ); + max = Math.max( max, value ); + + context.fillStyle = bg; + context.globalAlpha = 1; + context.fillRect( 0, 0, WIDTH, GRAPH_Y ); + context.fillStyle = fg; + context.fillText( round( value ) + ' ' + name + ' (' + round( min ) + '-' + round( max ) + ')', TEXT_X, TEXT_Y ); + + context.drawImage( canvas, GRAPH_X + PR, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT, GRAPH_X, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT ); + + context.fillRect( GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, GRAPH_HEIGHT ); + + context.fillStyle = bg; + context.globalAlpha = 0.9; + context.fillRect( GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, round( ( 1 - ( value / maxValue ) ) * GRAPH_HEIGHT ) ); + + } + + }; + +}; + +export default plotter; From 1b3bd75c1c8b0035cb0a34f0e038ebd52c0bd6a6 Mon Sep 17 00:00:00 2001 From: David Wakelin Date: Mon, 15 Apr 2019 17:02:04 +0100 Subject: [PATCH 2/3] Partial commit --- src/dat/controllers/PlotterController.js | 29 ++++++++++++++---- src/dat/gui/GUI.js | 29 +++++++++++++++--- src/dat/gui/_structure.scss | 6 ++++ src/dat/gui/style.scss | 4 +++ src/dat/utils/plotter.js | 39 ++++++++++++++++++++---- 5 files changed, 91 insertions(+), 16 deletions(-) diff --git a/src/dat/controllers/PlotterController.js b/src/dat/controllers/PlotterController.js index 42dcc501..680e6b7c 100644 --- a/src/dat/controllers/PlotterController.js +++ b/src/dat/controllers/PlotterController.js @@ -12,27 +12,44 @@ */ import Controller from './Controller'; -import dom from '../dom/dom'; -import plotter from '../utils/plotter' +import Plotter from '../utils/plotter'; /** - * @class Provides a GUI interface to fire a specified method, a property of an object. + * @class Provides a canvas that graphically displays the value of the object property at the specified interval * * @extends dat.controllers.Controller * * @param {Object} object The object to be manipulated * @param {string} property The name of the property to be manipulated + * @param {Object} params Contains the max and period properties */ class PlotterController extends Controller { constructor(object, property, params) { super(object, property); + this.max = params.max; + this.period = params.period; - const _this = this; - - this.__panel = new plotter('#0ff', '#002'); + // TODO: Allow graph type to passed in via params: line or bar + // TODO: Allow fg and bg colours to be passed in via params + this.__panel = new Plotter('#fff', '#000'); this.domElement.appendChild(this.__panel.dom); + this.prevValue = this.getValue(); + this.lastUpdate = Date.now(); } + updateDisplay() { + const value = this.getValue(); + if (this.period < 1 && value !== this.prevValue) { + this.__panel.update(value, this.max); + } else if (Math.floor((Date.now() - this.lastUpdate) / this.period) * this.period) { + this.__panel.update(value, this.max); + this.lastUpdate = Date.now(); + } + + this.prevValue = value; + + return super.updateDisplay(); + } } diff --git a/src/dat/gui/GUI.js b/src/dat/gui/GUI.js index 87cee09d..c729dd7d 100644 --- a/src/dat/gui/GUI.js +++ b/src/dat/gui/GUI.js @@ -557,13 +557,32 @@ common.extend( ); }, - addPlotter: function(object, property) { + /** + * Adds a new plotter controller to the GUI. + * + * @param object + * @param property + * @param max The maximum value that the plotter will display (default 10) + * @param period The update interval in ms or use 0 to only update on value change (default 500) + * @returns {Controller} The controller that was added to the GUI. + * @instance + * + * @example + * var obj = { + * value: 5 + * }; + * gui.addPlotter(obj, 'value', 10, 100); + * gui.addPlotter(obj, 'value', 10, 0); + */ + addPlotter: function(object, property, max, period) { return add( this, object, property, { - plotter: true + plotter: true, + max: max || 10, + period: (typeof period === 'number') ? period : 500 } ); }, @@ -1152,8 +1171,8 @@ function add(gui, object, property, params) { if (params.color) { controller = new ColorController(object, property); } else if (params.plotter) { - console.log(params.factoryArgs); - controller = new PlotterController(object, property); + controller = new PlotterController(object, property, params); + gui.listen(controller); } else { const factoryArgs = [object, property].concat(params.factoryArgs); controller = ControllerFactory.apply(gui, factoryArgs); @@ -1180,6 +1199,8 @@ function add(gui, object, property, params) { dom.addClass(li, GUI.CLASS_CONTROLLER_ROW); if (controller instanceof ColorController) { dom.addClass(li, 'color'); + } else if (controller instanceof PlotterController) { + dom.addClass(li, 'plotter'); } else { dom.addClass(li, typeof controller.getValue()); } diff --git a/src/dat/gui/_structure.scss b/src/dat/gui/_structure.scss index 23e4c6a9..ba430e78 100644 --- a/src/dat/gui/_structure.scss +++ b/src/dat/gui/_structure.scss @@ -195,6 +195,12 @@ $button-height: 20px; margin-left: 0; } + /** Reduce margin and set height for plotter */ + .c canvas { + padding: 0; + height: 53px; + } + .slider { float: left; width: 66%; diff --git a/src/dat/gui/style.scss b/src/dat/gui/style.scss index 89705cd4..3d5f0248 100644 --- a/src/dat/gui/style.scss +++ b/src/dat/gui/style.scss @@ -138,6 +138,10 @@ $input-color: lighten($background-color, 8.5%); border-left: 3px solid; } + &.plotter { + height: 58px; + } + &.function { border-left: 3px solid $function-color; } diff --git a/src/dat/utils/plotter.js b/src/dat/utils/plotter.js index a6eac052..96ca3df1 100644 --- a/src/dat/utils/plotter.js +++ b/src/dat/utils/plotter.js @@ -1,6 +1,20 @@ +/** + * dat-gui JavaScript Controller Library + * http://code.google.com/p/dat-gui + * + * Copyright 2011 Data Arts Team, Google Creative Lab + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + /** * @author mrdoob / http://mrdoob.com/ * Code from stats.js r17: https://github.com/mrdoob/stats.js + * Modified by MacroMan * Licence from that project: The MIT License @@ -31,10 +45,10 @@ const plotter = function ( fg, bg ) { var min = Infinity, max = 0, round = Math.round; var PR = round( window.devicePixelRatio || 1 ); - var WIDTH = 160 * PR, HEIGHT = 48 * PR, + var WIDTH = 160 * PR, HEIGHT = 60 * PR, TEXT_X = 3 * PR, TEXT_Y = 2 * PR, GRAPH_X = 3 * PR, GRAPH_Y = 3 * PR, - GRAPH_WIDTH = 154 * PR, GRAPH_HEIGHT = 30 * PR; + GRAPH_WIDTH = 154 * PR, GRAPH_HEIGHT = 54 * PR; var canvas = document.createElement( 'canvas' ); canvas.width = WIDTH; @@ -64,20 +78,33 @@ const plotter = function ( fg, bg ) { min = Math.min( min, value ); max = Math.max( max, value ); - context.fillStyle = bg; context.globalAlpha = 1; - context.fillRect( 0, 0, WIDTH, GRAPH_Y ); context.fillStyle = fg; - context.fillText( round( value ) + ' ' + name + ' (' + round( min ) + '-' + round( max ) + ')', TEXT_X, TEXT_Y ); + // Move graph over 1px context.drawImage( canvas, GRAPH_X + PR, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT, GRAPH_X, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT ); + // Draw fg color context.fillRect( GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, GRAPH_HEIGHT ); context.fillStyle = bg; context.globalAlpha = 0.9; - context.fillRect( GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, round( ( 1 - ( value / maxValue ) ) * GRAPH_HEIGHT ) ); + // Blank out above the value + context.fillRect( + GRAPH_X + GRAPH_WIDTH - PR, + GRAPH_Y, + PR, + round( ( 1 - ( value / maxValue ) ) * GRAPH_HEIGHT ) + ); + + // Blank out below the value + context.fillRect( + GRAPH_X + GRAPH_WIDTH - PR, + round( ( 1 - ( value / maxValue ) ) * GRAPH_HEIGHT ) + PR + 3, + PR, + round( ( value / maxValue ) * GRAPH_HEIGHT ) + ); } }; From 50c38cf9e5401482b67659256cd9e18bc5124be0 Mon Sep 17 00:00:00 2001 From: David Wakelin Date: Mon, 15 Apr 2019 21:18:48 +0100 Subject: [PATCH 3/3] Added value plotter --- src/dat/controllers/PlotterController.js | 20 +++- src/dat/gui/GUI.js | 10 +- src/dat/utils/plotter.js | 140 +++++++++++------------ 3 files changed, 91 insertions(+), 79 deletions(-) diff --git a/src/dat/controllers/PlotterController.js b/src/dat/controllers/PlotterController.js index 680e6b7c..4735b16b 100644 --- a/src/dat/controllers/PlotterController.js +++ b/src/dat/controllers/PlotterController.js @@ -26,24 +26,32 @@ import Plotter from '../utils/plotter'; class PlotterController extends Controller { constructor(object, property, params) { super(object, property); + + /** The graph will be these many units high */ this.max = params.max; + + /** Refresh rate. Value of 0 disables auto-refresh */ this.period = params.period; - // TODO: Allow graph type to passed in via params: line or bar - // TODO: Allow fg and bg colours to be passed in via params - this.__panel = new Plotter('#fff', '#000'); - this.domElement.appendChild(this.__panel.dom); + /** Stores the current value for comparison during animation frame */ this.prevValue = this.getValue(); + + /** Allows acurate timing for the period to be checked during animation frame */ this.lastUpdate = Date.now(); + + this.__panel = new Plotter(params.fgColor, params.bgColor, params.type); + this.domElement.appendChild(this.__panel.dom); } updateDisplay() { const value = this.getValue(); if (this.period < 1 && value !== this.prevValue) { + /* Update only on value change when auto-refresh is off */ this.__panel.update(value, this.max); - } else if (Math.floor((Date.now() - this.lastUpdate) / this.period) * this.period) { + } else if ((Date.now() - this.lastUpdate) > this.period) { + /* Update if elapsed time since last update is greater than the period */ this.__panel.update(value, this.max); - this.lastUpdate = Date.now(); + this.lastUpdate = Date.now() * 2 - this.lastUpdate - this.period; } this.prevValue = value; diff --git a/src/dat/gui/GUI.js b/src/dat/gui/GUI.js index c729dd7d..2e1a2ff8 100644 --- a/src/dat/gui/GUI.js +++ b/src/dat/gui/GUI.js @@ -564,6 +564,9 @@ common.extend( * @param property * @param max The maximum value that the plotter will display (default 10) * @param period The update interval in ms or use 0 to only update on value change (default 500) + * @param type Type of graph to render - line or bar (default line) + * @param fgColor Foreground color of the graph in hex (default #fff) + * @param bgColor Background color of the graph in hex (default #000) * @returns {Controller} The controller that was added to the GUI. * @instance * @@ -574,7 +577,7 @@ common.extend( * gui.addPlotter(obj, 'value', 10, 100); * gui.addPlotter(obj, 'value', 10, 0); */ - addPlotter: function(object, property, max, period) { + addPlotter: function(object, property, max, period, type, fgColor, bgColor) { return add( this, object, @@ -582,7 +585,10 @@ common.extend( { plotter: true, max: max || 10, - period: (typeof period === 'number') ? period : 500 + period: (typeof period === 'number') ? period : 500, + type: type || 'line', + fgColor: fgColor || '#fff', + bgColor: bgColor || '#000' } ); }, diff --git a/src/dat/utils/plotter.js b/src/dat/utils/plotter.js index 96ca3df1..f2a09e71 100644 --- a/src/dat/utils/plotter.js +++ b/src/dat/utils/plotter.js @@ -13,7 +13,7 @@ /** * @author mrdoob / http://mrdoob.com/ - * Code from stats.js r17: https://github.com/mrdoob/stats.js + * Original code from stats.js r17: https://github.com/mrdoob/stats.js * Modified by MacroMan * Licence from that project: @@ -39,76 +39,74 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -const plotter = function ( fg, bg ) { - - var min = Infinity, max = 0, round = Math.round; - var PR = round( window.devicePixelRatio || 1 ); - - var WIDTH = 160 * PR, HEIGHT = 60 * PR, - TEXT_X = 3 * PR, TEXT_Y = 2 * PR, - GRAPH_X = 3 * PR, GRAPH_Y = 3 * PR, - GRAPH_WIDTH = 154 * PR, GRAPH_HEIGHT = 54 * PR; - - var canvas = document.createElement( 'canvas' ); - canvas.width = WIDTH; - canvas.height = HEIGHT; - //canvas.style.cssText = 'width:80px;height:48px'; - - var context = canvas.getContext( '2d' ); - context.font = 'bold ' + ( 9 * PR ) + 'px Helvetica,Arial,sans-serif'; - context.textBaseline = 'top'; - - context.fillStyle = bg; - context.fillRect( 0, 0, WIDTH, HEIGHT ); - - context.fillStyle = fg; - context.fillRect( GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT ); - - context.fillStyle = bg; - context.globalAlpha = 0.9; - context.fillRect( GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT ); - - return { - - dom: canvas, - - update: function ( value, maxValue ) { - - min = Math.min( min, value ); - max = Math.max( max, value ); - - context.globalAlpha = 1; - context.fillStyle = fg; - - // Move graph over 1px - context.drawImage( canvas, GRAPH_X + PR, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT, GRAPH_X, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT ); - - // Draw fg color - context.fillRect( GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, GRAPH_HEIGHT ); - - context.fillStyle = bg; - context.globalAlpha = 0.9; - - // Blank out above the value - context.fillRect( - GRAPH_X + GRAPH_WIDTH - PR, - GRAPH_Y, - PR, - round( ( 1 - ( value / maxValue ) ) * GRAPH_HEIGHT ) - ); - - // Blank out below the value - context.fillRect( - GRAPH_X + GRAPH_WIDTH - PR, - round( ( 1 - ( value / maxValue ) ) * GRAPH_HEIGHT ) + PR + 3, - PR, - round( ( value / maxValue ) * GRAPH_HEIGHT ) - ); - } - - }; - +const plotter = function(fg, bg, type) { + let min = Infinity; + let max = 0; + const round = Math.round; + const PR = round(window.devicePixelRatio || 1); + + const WIDTH = 160 * PR; + const HEIGHT = 60 * PR; + const GRAPH_X = 3 * PR; + const GRAPH_Y = 3 * PR; + const GRAPH_WIDTH = 154 * PR; + const GRAPH_HEIGHT = 54 * PR; + + const canvas = document.createElement('canvas'); + canvas.width = WIDTH; + canvas.height = HEIGHT; + + const context = canvas.getContext('2d'); + context.font = 'bold ' + (9 * PR) + 'px Helvetica,Arial,sans-serif'; + context.textBaseline = 'top'; + + context.fillStyle = bg; + context.fillRect(0, 0, WIDTH, HEIGHT); + + context.fillStyle = fg; + context.fillRect(GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT); + + context.fillStyle = bg; + context.globalAlpha = 0.9; + context.fillRect(GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT); + + return { + dom: canvas, + update: function (value, maxValue) { + min = Math.min(min, value); + max = Math.max(max, value); + + context.globalAlpha = 1; + context.fillStyle = fg; + + // Move graph over 1px + context.drawImage(canvas, GRAPH_X + PR, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT, GRAPH_X, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT); + + // Draw fg color + context.fillRect(GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, GRAPH_HEIGHT); + + context.fillStyle = bg; + context.globalAlpha = 0.9; + + // Blank out above the value + context.fillRect( + GRAPH_X + GRAPH_WIDTH - PR, + GRAPH_Y, + PR, + round((1 - (value / maxValue)) * GRAPH_HEIGHT) + ); + + // Blank out below the value if line + if (type === 'line') { + context.fillRect( + GRAPH_X + GRAPH_WIDTH - PR, + round((1 - (value / maxValue)) * GRAPH_HEIGHT) + PR + 3, + PR, + round((value / maxValue) * GRAPH_HEIGHT) + ); + } + } + }; }; export default plotter;