diff --git a/example.html b/example.html index 2c3d7c3f..cb320abc 100644 --- a/example.html +++ b/example.html @@ -1,32 +1,36 @@ + + - + + \ No newline at end of file diff --git a/src/dat/controllers/PlotterController.js b/src/dat/controllers/PlotterController.js new file mode 100644 index 00000000..3d73de79 --- /dev/null +++ b/src/dat/controllers/PlotterController.js @@ -0,0 +1,69 @@ +/** + * 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 Plotter from '../utils/plotter'; + +/** + * @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 and liClass properties + */ +class PlotterController extends Controller { + constructor(object, property, params) { + super(object, property); + + params = params || {}; + + /** The graph will be these many units high */ + this.max = params.max || 3; + + /** The containing li will have this class added */ + this.liClass = params.liClass || 'plotter'; + + /** Refresh rate. Value of 0 disables auto-refresh */ + this.period = params.period || 0; + + /** 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 || '#fff', params.bgColor || '#000', params.type || 'line'); + 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 ((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() * 2 - this.lastUpdate - this.period; + } + + this.prevValue = value; + + return super.updateDisplay(); + } + +} + +export default PlotterController; \ No newline at end of file diff --git a/src/dat/gui/GUI.js b/src/dat/gui/GUI.js index 972c3073..4c0ccbf9 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'; @@ -125,12 +126,12 @@ const GUI = function(pars) { * @example * [ * { - * propertyName: Controller, - * anotherPropertyName: Controller - * }, + * propertyName: Controller, + * anotherPropertyName: Controller + * }, * { - * propertyName: Controller - * } + * propertyName: Controller + * } * ] */ this.__rememberedObjectIndecesToControllers = []; @@ -168,7 +169,7 @@ const GUI = function(pars) { if (params.autoPlace && common.isUndefined(params.scrollable)) { params.scrollable = true; } -// params.scrollable = common.isUndefined(params.parent) && params.scrollable === true; + // params.scrollable = common.isUndefined(params.parent) && params.scrollable === true; // Not part of params because I don't want people passing this in via // constructor. Should be a 'remembered' value. @@ -1131,17 +1132,25 @@ function recallSavedValue(gui, controller) { } function add(gui, object, property, params) { - if (object[property] === undefined) { - throw new Error(`Object "${object}" has no property "${property}"`); - } - let controller; - if (params.color) { - controller = new ColorController(object, property); + // add( new SomeCustomerController(a,b,c), params) + if (object instanceof Controller) { + controller = object; + params = property || { }; } else { - const factoryArgs = [object, property].concat(params.factoryArgs); - controller = ControllerFactory.apply(gui, factoryArgs); + + if (object[property] === undefined) { + throw new Error(`Object "${object}" has no property "${property}"`); + } + + if (params.color) { + controller = new ColorController(object, property); + } else { + const factoryArgs = [object, property].concat(params.factoryArgs); + controller = ControllerFactory.apply(gui, factoryArgs); + } + } if (params.before instanceof Controller) { @@ -1165,6 +1174,10 @@ function add(gui, object, property, params) { dom.addClass(li, GUI.CLASS_CONTROLLER_ROW); if (controller instanceof ColorController) { dom.addClass(li, 'color'); + } else if (params.liClass) { + dom.addClass(li, params.liClass); + } else if (controller.liClass) { + dom.addClass(li, controller.liClass); } else { dom.addClass(li, typeof controller.getValue()); } diff --git a/src/dat/gui/_structure.scss b/src/dat/gui/_structure.scss index 3193f8e0..8d750a4a 100644 --- a/src/dat/gui/_structure.scss +++ b/src/dat/gui/_structure.scss @@ -70,12 +70,15 @@ $button-height: 20px; cursor: pointer; text-align: center; background-color: #000; + &.close-top { position: relative; } + &.close-bottom { position: absolute; } + &:hover { background-color: #111; } @@ -89,13 +92,14 @@ $button-height: 20px; float: right; margin-right: 15px; - overflow-y:visible; + overflow-y: visible; &.has-save > ul { &.close-top { margin-top: 0; } + &.close-bottom { margin-top: $row-height; } @@ -112,6 +116,7 @@ $button-height: 20px; &.close-top { position: relative; } + &.close-bottom { position: fixed; } @@ -195,6 +200,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%; @@ -290,6 +301,7 @@ $button-height: 20px; background-color: #333; padding: 8px; margin-top: 10px; + code { font-size: 10px; } @@ -297,4 +309,4 @@ $button-height: 20px; #dat-gui-save-locally { display: none; -} +} \ No newline at end of file diff --git a/src/dat/gui/style.scss b/src/dat/gui/style.scss index 89705cd4..0fa8112a 100644 --- a/src/dat/gui/style.scss +++ b/src/dat/gui/style.scss @@ -63,10 +63,12 @@ $input-color: lighten($background-color, 8.5%); width: 5px; background: $background-color; } + &::-webkit-scrollbar-corner { height: 0; display: none; } + &::-webkit-scrollbar-thumb { border-radius: 5px; background: lighten($background-color, 30%); @@ -138,12 +140,17 @@ $input-color: lighten($background-color, 8.5%); border-left: 3px solid; } + &.plotter { + height: 58px; + } + &.function { border-left: 3px solid $function-color; } &.number { border-left: 3px solid $number-color; + input[type=text] { color: $number-color; } @@ -151,6 +158,7 @@ $input-color: lighten($background-color, 8.5%); &.string { border-left: 3px solid $string-color; + input[type=text] { color: $string-color; } @@ -170,9 +178,11 @@ $input-color: lighten($background-color, 8.5%); background: $input-color; outline: none; + &:hover { background: lighten($input-color, $hover-lighten); } + &:focus { background: lighten($input-color, $active-lighten); color: #fff; @@ -192,6 +202,7 @@ $input-color: lighten($background-color, 8.5%); .slider:hover { background: lighten($input-color, $hover-lighten); + .slider-fg { background: lighten($number-color, $hover-lighten); } @@ -199,4 +210,4 @@ $input-color: lighten($background-color, 8.5%); } -} +} \ No newline at end of file diff --git a/src/dat/index.js b/src/dat/index.js index 87b1a1ec..756cc326 100644 --- a/src/dat/index.js +++ b/src/dat/index.js @@ -24,6 +24,7 @@ import NumberControllerBox from './controllers/NumberControllerBox'; import NumberControllerSlider from './controllers/NumberControllerSlider'; import FunctionController from './controllers/FunctionController'; import ColorController from './controllers/ColorController'; +import PlotterController from './controllers/PlotterController'; import domImport from './dom/dom'; import GUIImport from './gui/GUI'; @@ -43,7 +44,8 @@ export const controllers = { NumberControllerBox: NumberControllerBox, NumberControllerSlider: NumberControllerSlider, FunctionController: FunctionController, - ColorController: ColorController + ColorController: ColorController, + PlotterController: PlotterController }; export const dom = { dom: domImport }; diff --git a/src/dat/utils/plotter.js b/src/dat/utils/plotter.js new file mode 100644 index 00000000..b9cb58e3 --- /dev/null +++ b/src/dat/utils/plotter.js @@ -0,0 +1,112 @@ +/** + * 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/ + * Original code from stats.js r17: https://github.com/mrdoob/stats.js + * Modified by MacroMan + * 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, 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; \ No newline at end of file