plotboilerplate
Version:
A simple javascript plotting boilerplate for 2d stuff.
435 lines (414 loc) • 15.5 kB
JavaScript
/**
* A utility class used by PlotBoilerplate: creategui.
* The PlotBoilerplate will try to use this for the initialization of the input GUI.
*
* Requires the label() polyfill for dat.gui.GUI.
*
* @author Ikaros Kappler
* @date 2020-03-30
* @modified 2020-04-03 Added empty default global object 'utils'. Added createGUI as an optional child.
* @modified 2023-09-29 Added try-catch for color attributes; invalid values might break construction of whole gui.
* @modified 2024-06-25 Replacing dat.gui by equivalent lil-gui calls. Collapsing all lil-gui folders (are open by default).
* @modified 2024-08-25 Added `CSSBackdropEffects` to the GUI (if present).
* @version 1.2.0
**/
var utils = (globalThis.utils = globalThis.utils || {});
/**
* Need to access the dat.gui folders of plotboilerplate? You will find them here.
*
* Following keys are currently available:
* - editor_settings
* - editor_settings.export (if enableSVGExport=true)
* - draw_settings
*/
globalThis.utils.guiFolders = globalThis.utils.guiFolders || {};
/**
* Creates a control GUI (a dat.gui instance) for this
* plot boilerplate instance.
*
* Requires the label() polyfill for dat.gui.GUI.
*
* @method createGUI
* @memberof utils
* @param {PlotBoilerplate} pb
* @param {DatGuiProps|LilGuiProps} props
* @return {dat.gui.GUI|lil-gui.GUI|null}
**/
globalThis.utils.createGUI = (function () {
var _tryGetFilterGETParams = function () {
// Rule is:
// * GET PARAMS first
// * pre-defnined params second
// * default values third
var globalPredefinedValues =
typeof globalThis["cssBackdropfilterValues"] === "object" ? globalThis["cssBackdropfilterValues"] : {};
var preDefinedValues = Object.assign(Object.assign({}, CSSBackdropEffects.DEFAULT_FILTER_VALUES), globalPredefinedValues);
if (typeof gup === "undefined") {
return preDefinedValues; // CSSBackdropEffects.DEFAULT_FILTER_VALUES;
}
var GUP = gup();
var params = new Params(GUP);
var initialFilterValues = {
isBackdropFiltersEnabled: false,
// This is just for the global effect color
effectFilterColor: params.getString("filter:effectFilterColor", preDefinedValues.effectFilterColor), // "#204a87",
isEffectsColorEnabled: params.hasParam("filter:effectFilterColor"),
// These are real filter values
opacity: params.getNumber("filter:opacity", preDefinedValues.opacity),
isOpacityEnabled: params.hasParam("filter:opacity") || preDefinedValues.isOpacityEnabled,
invert: params.getNumber("filter:invert", preDefinedValues.invert),
isInvertEnabled: params.hasParam("filter:invert") || preDefinedValues.isInvertEnabled,
sepia: params.getNumber("filter:sepia", preDefinedValues.sepia),
isSepiaEnabled: params.hasParam("filter:sepia") || preDefinedValues.isSepiaEnabled,
blur: params.getNumber("filter:blur", preDefinedValues.blur), // px
isBlurEnabled: params.hasParam("filter:blur") || preDefinedValues.isBlurEnabled,
brightness: params.getNumber("filter:brightness", preDefinedValues.brightness),
isBrightnessEnabled: params.hasParam("filter:brightness") || preDefinedValues.isBrightnessEnabled,
contrast: params.getNumber("filter:contrast", preDefinedValues.contrast),
isContrastEnabled: params.hasParam("filter:contrast") || preDefinedValues.isContrastEnabled,
dropShadow: params.getNumber("filter:dropShadow", preDefinedValues.dropShadow), // px
dropShadowColor: "#00ffff", // HOW TO DISABLE THIS PROPERLY?
isDropShadowEnabled: params.hasParam("filter:dropShadow") || preDefinedValues.isDropShadowEnabled,
grayscale: params.getNumber("filter:grayscale", preDefinedValues.grayscale),
isGrayscaleEnabled: params.hasParam("filter:grayscale") || preDefinedValues.isGrayscaleEnabled,
hueRotate: params.getNumber("filter:hueRotate", preDefinedValues.hueRotate), // deg
isHueRotateEnabled: params.hasParam("filter:hueRotate") || preDefinedValues.isHueRotateEnabled,
saturate: params.getNumber("filter:saturate", preDefinedValues.saturate),
isSaturateEnabled: params.hasParam("filter:saturate") || preDefinedValues.isSaturateEnabled
};
initialFilterValues.isBackdropFiltersEnabled =
initialFilterValues.isOpacityEnabled ||
initialFilterValues.isEffectsColorEnabled ||
initialFilterValues.isInvertEnabled ||
initialFilterValues.isSepiaEnabled ||
initialFilterValues.isBlurEnabled ||
initialFilterValues.isBrightnessEnabled ||
initialFilterValues.isContrastEnabled ||
initialFilterValues.isDropShadowEnabled ||
initialFilterValues.isGrayscaleEnabled ||
initialFilterValues.isHueRotateEnabled ||
initialFilterValues.isSaturateEnabled;
return initialFilterValues;
};
var _tryGetGUIInstance = function (props) {
if (
globalThis.hasOwnProperty("lil") &&
typeof globalThis["lil"] == "object" &&
globalThis["lil"].hasOwnProperty("GUI") &&
typeof globalThis["lil"]["GUI"] == "function"
) {
// console.log("Creating lil");
return new lil.GUI(props);
} else if (
globalThis.hasOwnProperty("dat") &&
typeof globalThis["dat"] == "object" &&
globalThis["dat"].hasOwnProperty("gui") &&
typeof globalThis["dat"]["gui"] == "object"
) {
// console.log("Creating dat.gui");
return new dat.gui.GUI(props);
} else {
console.warn("Warning: cannot create GUI. Nor dat.gui not lil-gui seem present.");
return null;
}
};
var _tryDetectMobileDevice = function () {
return globalThis.hasOwnProperty("isMobileDevice") && typeof globalThis["isMobileDevice"] == "function" && isMobileDevice();
};
var _tryGetGUISizeToggler = function (gui, dummyConfig) {
if (!gui) {
return null;
}
if (globalThis.hasOwnProperty("guiSizeToggler") && typeof globalThis["guiSizeToggler"] == "function") {
return guiSizeToggler(gui, dummyConfig, { transformOrigin: "top right" });
} else {
console.warn("Warning: cannot create GUI's double size checkbox. guiSizeToggler is not present.");
return null;
}
};
var createGuiFun = function (pb, props) {
var dummy = {
resetOffset: function () {
var viewport = pb.viewport();
pb.setOffset({ x: (viewport.width / 2.0) * pb.draw.scale.x, y: (viewport.height / 2.0) * pb.draw.scale.y });
pb.redraw();
},
resetScale: function () {
pb.setZoom(1.0, 1.0, new Vertex(0, 0));
pb.redraw();
},
guiDoubleSize: false
};
// Try to initialise dat.gui (old demos) or lil-gui (new demos). They have the ~same~ signature.
var gui = _tryGetGUIInstance(props);
var mobileDevice = _tryDetectMobileDevice();
var guiSize = _tryGetGUISizeToggler(gui, dummy);
if (mobileDevice && guiSize) {
dummy.guiDoubleSize = true;
guiSize.update();
}
var _self = pb;
if (guiSize) {
gui
.add(dummy, "guiDoubleSize")
.name("doubleGUISize")
.onChange(function () {
guiSize.update();
});
}
var fold0 = gui.addFolder("Editor settings");
fold0.close(); // important only for lil-gui
var fold00 = fold0.addFolder("Canvas size");
fold00.close(); // important only for lil-gui
fold00
.add(pb.config, "fullSize")
.onChange(function () {
_self.resizeCanvas();
})
.title("Toggles the fullpage mode.")
.listen();
fold00
.add(pb.config, "fitToParent")
.onChange(function () {
_self.resizeCanvas();
})
.title("Toggles the fit-to-parent mode to fit to parent container (overrides fullsize).")
.listen();
fold00
.add(pb.config, "defaultCanvasWidth")
.min(1)
.step(10)
.onChange(function () {
_self.resizeCanvas();
})
.title("Specifies the fallback width.");
fold00
.add(pb.config, "defaultCanvasHeight")
.min(1)
.step(10)
.onChange(function () {
_self.resizeCanvas();
})
.title("Specifies the fallback height.");
fold00
.add(pb.config, "canvasWidthFactor")
.min(0.1)
.step(0.1)
.max(10)
.onChange(function () {
_self.resizeCanvas();
})
.title("Specifies a factor for the current width.")
.listen();
fold00
.add(pb.config, "canvasHeightFactor")
.min(0.1)
.step(0.1)
.max(10)
.onChange(function () {
_self.resizeCanvas();
})
.title("Specifies a factor for the current height.")
.listen();
fold00
.add(pb.config, "cssScaleX")
.min(0.01)
.step(0.01)
.max(1.0)
.onChange(function () {
if (_self.config.cssUniformScale) _self.config.cssScaleY = _self.config.cssScaleX;
_self.updateCSSscale();
})
.title("Specifies the visual x scale (CSS).")
.listen();
fold00
.add(pb.config, "cssScaleY")
.min(0.01)
.step(0.01)
.max(1.0)
.onChange(function () {
if (_self.config.cssUniformScale) _self.config.cssScaleX = _self.config.cssScaleY;
_self.updateCSSscale();
})
.title("Specifies the visual y scale (CSS).")
.listen();
fold00
.add(pb.config, "cssUniformScale")
.onChange(function () {
if (_self.config.cssUniformScale) _self.config.cssScaleY = _self.config.cssScaleX;
_self.updateCSSscale();
})
.title("CSS uniform scale (x-scale equlsa y-scale).");
fold00.add(pb.config, "setToRetina").name("Set to highres fullsize").title("Set canvas to high-res retina resoultion (x2).");
var fold01 = fold0.addFolder("Draw settings");
fold01.close(); // important only for lil-gui
fold01
.add(pb.drawConfig, "drawBezierHandlePoints")
.onChange(function () {
_self.redraw();
})
.title("Draw Bézier handle points.");
fold01
.add(pb.drawConfig, "drawBezierHandleLines")
.onChange(function () {
_self.redraw();
})
.title("Draw Bézier handle lines.");
fold01
.add(pb.drawConfig, "drawHandlePoints")
.onChange(function () {
_self.redraw();
})
.title("Draw handle points (overrides all other settings).");
fold01
.add(pb.drawConfig, "drawHandleLines")
.onChange(function () {
_self.redraw();
})
.title("Draw handle lines in general (overrides all other settings).");
fold01
.add(pb.drawConfig, "drawVertices")
.onChange(function () {
_self.redraw();
})
.title("Draw vertices in general.");
const fold0100 = fold01.addFolder("Colors and Lines");
fold0100.close(); // important only for lil-gui
const _addDrawConfigElement = function (fold, basePath, conf) {
for (var i in conf) {
if (typeof conf[i] == "object") {
if (conf[i].hasOwnProperty("color")) {
try {
fold
.addColor(conf[i], "color")
.onChange(function () {
_self.redraw();
})
.name(basePath + i + ".color")
.title(basePath + i + ".color")
.listen();
} catch (e) {
console.error("[createGUI] Invalid color value, gui library cannot recognize.", conf[i]["color"]);
}
}
// console.log("basePath", basePath, i, conf[i], conf[i].hasOwnProperty("lineWidth"));
if (conf[i].hasOwnProperty("lineWidth")) {
try {
// console.log("adding ", basePath, i);
fold
.add(conf[i], "lineWidth")
.min(1)
.max(10)
.step(1)
.onChange(function () {
_self.redraw();
})
.name(basePath + i + ".lineWidth")
.title(basePath + i + ".lineWidth")
.listen();
// console.log("adding 2", basePath, i);
} catch (e) {
console.error("[createGUI] Invalid lineWidth value, gui library cannot recognize.", conf[i]["lineWidth"]);
}
}
for (var e in conf[i]) {
if (conf[i].hasOwnProperty(e) && typeof conf[i][e] == "object") {
// console.log(e);
_addDrawConfigElement(fold, (basePath != "" ? basePath + "." : "") + i + "." + e, conf[i]);
}
}
}
}
};
_addDrawConfigElement(fold0100, "", pb.drawConfig);
fold0
.add(pb.config, "scaleX")
.title("Scale x.")
.min(0.01)
.max(10.0)
.step(0.01)
.onChange(function () {
_self.draw.scale.x = _self.fill.scale.x = _self.config.scaleX;
_self.redraw();
})
.listen();
fold0
.add(pb.config, "scaleY")
.title("Scale y.")
.min(0.01)
.max(10.0)
.step(0.01)
.onChange(function () {
_self.draw.scale.y = _self.fill.scale.y = _self.config.scaleY;
_self.redraw();
})
.listen();
fold0
.add(pb.config, "offsetX")
.title("Offset x.")
.step(10.0)
.onChange(function () {
_self.draw.offset.x = _self.fill.offset.x = _self.config.offsetX;
_self.redraw();
})
.listen();
fold0
.add(pb.config, "offsetY")
.title("Offset y.")
.step(10.0)
.onChange(function () {
_self.draw.offset.y = _self.fill.offset.y = _self.config.offsetY;
_self.redraw();
})
.listen();
fold0
.add(pb.config, "drawOrigin")
.title("Draw the origin (0,0).")
.onChange(function () {
_self.redraw();
})
.listen();
fold0
.add(pb.config, "rasterGrid")
.title("Draw a fine raster instead a full grid.")
.onChange(function () {
_self.redraw();
})
.listen();
fold0
.add(pb.config, "drawRaster")
.title("If set to false no raster or grid will be drawn at all.")
.onChange(function () {
_self.redraw();
})
.listen();
fold0.add(pb.config, "redrawOnResize").title("Automatically redraw the data if window or canvas is resized.").listen();
fold0
.addColor(pb.config, "backgroundColor")
.onChange(function () {
_self.redraw();
})
.title("Choose a background color.");
fold0.add(dummy, "resetOffset").title("Reset the draw offset to (0,0).");
fold0.add(dummy, "resetScale").title("Reset the draw scale to (1.,1.).");
if (pb.config.enableSVGExport) {
var foldExport = gui.addFolder("Export");
foldExport.close(); // important only for lil-gui
foldExport.add(pb.config, "saveFile").name("SVG Image").title("Save as SVG.");
globalThis.utils.guiFolders["editor_settings.export"] = foldExport;
}
if (typeof CSSBackdropEffects !== "undefined") {
try {
var backdropEffects = new CSSBackdropEffects(pb, gui, _tryGetFilterGETParams());
// CSSBackdropEffects.DEFAULT_FILTER_VALUES);
globalThis.utils["cssBackdropEffects"] = backdropEffects;
} catch (e) {
console.warn("[creategui] Failed to create backdrop effect input for gui.", e);
}
}
globalThis.utils.guiFolders["editor_settings"] = fold0;
globalThis.utils.guiFolders["draw_settings"] = fold01;
return gui;
};
return createGuiFun;
})(); // END creategui