UNPKG

agentscript

Version:

AgentScript Model in Model/View architecture

169 lines (140 loc) 5.3 kB
import * as util from '../src/utils.js' import dat from '../vendor/dat.gui.js' /** @class */ class GUI { /** * @param {Object} template A set of name/object pairs, one per UI element * * @example * const gui = new GUI ({ * opacity: { // slider * val: [canvas.opacity, [0, 1, 0.1]], * cmd: val => canvas.setOpacity(val), * }, * download: { // button * cmd: () => util.downloadBlob(data, 'data.json', false), * }, * ... * }) */ constructor(template) { this.template = template this.controllers = {} this.values = {} // the key/val's from each template this.gui = new dat.GUI() const guis = [this.gui] // this.folders['default'] = this.baseGui let newFolder = obj => !obj.val && !obj.cmd const parseGuis = obj => { util.forLoop(obj, (obj, key) => { if (newFolder(obj)) { console.log('new follder', key) this.gui = this.gui.addFolder(key) guis.push(this.gui) parseGuis(obj) guis.pop() this.gui = guis.at(-1) } else { this.controllers[key] = this.addUI(obj, key) } }) } parseGuis(template) console.log('controllers, values', this.controllers, this.values, guis) } type(obj) { const { val, cmd } = obj const valType = util.typeOf(val) const cmdType = util.typeOf(cmd) if (this.isDatColor(val)) return 'color' if (valType === 'undefined') return 'button' // if (val === 'listen') return 'monitor' if (valType === 'boolean') return 'toggle' if (valType === 'string') return 'input' if (valType === 'array' && val.length === 2) { if (util.typeOf(val[0]) === 'number') return 'slider' if (util.typeOf(val[0]) === 'string') return 'chooser' if (util.typeOf(val[0]) === 'object') return 'monitor' if (util.typeOf(val[0]) === 'array') return 'monitor' } throw Error('GUI type error, val: ' + val + ' cmd: ' + cmd) } /** * * @param {Object} obj A gui object with two optional objects: 'val' and 'cmd' * @param {string} key The name of the gui * @returns A dat.gui control object */ addUI(obj, key) { let { val, cmd } = obj const type = this.type(obj) let control, extent if (type === 'monitor') cmd = () => val[0][val[1]] if (['slider', 'chooser'].includes(type)) [val, extent] = val if (type === 'button') val = cmd console.log('addUI:', type, key, val, cmd) this.values[key] = val switch (type) { case 'slider': const [min, max, step = 1] = extent control = this.gui.add(this.values, key, min, max).step(step) break case 'chooser': control = this.gui.add(this.values, key, extent) break case 'color': control = this.gui.addColor(this.values, key) break case 'button': case 'toggle': case 'input': control = this.gui.add(this.values, key) break case 'monitor': control = this.gui.add(val[0], val[1]) break default: throw Error(`Controller.addUI: bad type: ${type}`) } // initialize: set model etc initial values to this value if (!['monitor', 'button', 'toggle'].includes(type)) cmd(val) // if (cmd && val && type !== 'monitor') cmd(val) // if (val === 'listen') this.setListener(key, cmd) if (cmd) { // if (val === 'listen') control.listen().onChange(cmd) if (type === 'monitor') control.listen() //.onChange(cmd) else control.onChange(cmd) } return control } updateGui(name, value) { console.log('updateGui name, value', name, value) } isDatColor(val) { if (util.typeOf(val) === 'string') { if (val[0] === '#') return val.length === 7 || val.length === 4 if (val.startsWith('rgb(') || val.startsWith('rgba(')) return val.endsWith(')') if (val.startsWith('hsl(') || val.startsWith('hsla(')) return val.endsWith(')') if (val.startsWith('hsv(') || val.startsWith('hsva(')) return val.endsWith(')') } if (util.typeOf(val) === 'object') return val.h != null && val.s != null && val.v != null // if (util.typeOf(val) === 'array') { // if (val.length === 3 || val.length === 4) // return val.every(i => util.typeOf(i) === 'number') // } return false } // setListener(key, cmd) { // this.values[key] = cmd() // } // update() { // util.forLoop(this.template, (obj, key) => { // if (obj.val === 'listen') this.setListener(key, obj.cmd) // }) // } } export default GUI