Some jsdoc for util

Also allows for downloadCanvas to open image in new tab. This may be
useful for debugging anything that has a canvas (such as overmasking).

Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com>
This commit is contained in:
Victor Seiji Hariki 2022-11-28 12:11:19 -03:00
parent 6ec27df919
commit 8d0b44e36b
2 changed files with 64 additions and 7 deletions

7
js/jsconfig.json Normal file
View file

@ -0,0 +1,7 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6"
},
"include": ["**/*.js"]
}

View file

@ -27,7 +27,10 @@ Observer.prototype = {
}; };
/** /**
* Generates unique id * Generates a simple UID in the format xxxx-xxxx-...-xxxx, with x being [0-9a-f]
*
* @param {number} size Number of quartets of characters to generate
* @returns {string} The new UID
*/ */
const guid = (size = 3) => { const guid = (size = 3) => {
const s4 = () => { const s4 = () => {
@ -43,17 +46,25 @@ const guid = (size = 3) => {
}; };
/** /**
* Default option set * Assigns defaults to an option object passed to the function.
*
* @param {{[key: string]: any}} options Original options object
* @param {{[key: string]: any}} defaults Default values to assign
*/ */
function defaultOpt(options, defaults) { function defaultOpt(options, defaults) {
Object.keys(defaults).forEach((key) => { Object.keys(defaults).forEach((key) => {
if (options[key] === undefined) options[key] = defaults[key]; if (options[key] === undefined) options[key] = defaults[key];
}); });
} }
/** Custom error for attempt to set read-only objects */
class ProxyReadOnlySetError extends Error {}
/** /**
* Make object read-only * Makes a given object read-only; throws a ProxyReadOnlySetError exception if modification is attempted
*
* @param {any} obj Object to be proxied
* @param {string} name Name for logging purposes
* @returns {any} Proxied object, intercepting write attempts
*/ */
function makeReadOnly(obj, name = "read-only object") { function makeReadOnly(obj, name = "read-only object") {
return new Proxy(obj, { return new Proxy(obj, {
@ -65,7 +76,15 @@ function makeReadOnly(obj, name = "read-only object") {
}); });
} }
// Makes an object so you can't rewrite already written values /** Custom error for attempt to set write-once objects a second time */
class ProxyWriteOnceSetError extends Error {}
/**
* Makes a given object write-once; Attempts to overwrite an existing prop in the object will throw a ProxyWriteOnceSetError exception
*
* @param {any} obj Object to be proxied
* @param {string} name Name for logging purposes
* @returns {any} Proxied object, intercepting write attempts
*/
function makeWriteOnce(obj, name = "write-once object") { function makeWriteOnce(obj, name = "write-once object") {
return new Proxy(obj, { return new Proxy(obj, {
set: (obj, prop, value) => { set: (obj, prop, value) => {
@ -79,7 +98,12 @@ function makeWriteOnce(obj, name = "write-once object") {
} }
/** /**
* Bounding box Calculation * Snaps a single value to an infinite grid
*
* @param {number} i Original value to be snapped
* @param {boolean} scaled If grid will change alignment for odd scaleFactor values (default: true)
* @param {number} gridSize Size of the grid
* @returns an offset, in which [i + offset = (a location snapped to the grid)]
*/ */
function snap(i, scaled = true, gridSize = 64) { function snap(i, scaled = true, gridSize = 64) {
// very cheap test proof of concept but it works surprisingly well // very cheap test proof of concept but it works surprisingly well
@ -100,6 +124,16 @@ function snap(i, scaled = true, gridSize = 64) {
return -snapOffset; return -snapOffset;
} }
/**
* Gets a bounding box centered on a given set of coordinates. Supports grid snapping
*
* @param {number} cx x-coordinate of the center of the box
* @param {number} cy y-coordinate of the center of the box
* @param {number} w the width of the box
* @param {height} h the height of the box
* @param {number | null} gridSnap The size of the grid to snap to
* @returns {BoundingBox} A bounding box object centered at (cx, cy)
*/
function getBoundingBox(cx, cy, w, h, gridSnap = null) { function getBoundingBox(cx, cy, w, h, gridSnap = null) {
const offset = {x: 0, y: 0}; const offset = {x: 0, y: 0};
const box = {x: 0, y: 0}; const box = {x: 0, y: 0};
@ -122,6 +156,12 @@ function getBoundingBox(cx, cy, w, h, gridSnap = null) {
/** /**
* Triggers Canvas Download * Triggers Canvas Download
*/ */
/**
* Crops a given canvas to content, returning a new canvas object with the content in it.
*
* @param {HTMLCanvasElement} sourceCanvas Canvas to get a content crop from
* @returns {HTMLCanvasElement} A new canvas with the cropped part of the image
*/
function cropCanvas(sourceCanvas) { function cropCanvas(sourceCanvas) {
var w = sourceCanvas.width; var w = sourceCanvas.width;
var h = sourceCanvas.height; var h = sourceCanvas.height;
@ -169,6 +209,15 @@ function cropCanvas(sourceCanvas) {
return cutCanvas; return cutCanvas;
} }
/**
* Downloads the content of a canvas to the disk, or opens it
*
* @param {{cropToContent: boolean, canvas: HTMLCanvasElement, filename: string}} options A options array with the following:\
* cropToContent: If we wish to crop to content first (default: true)
* canvas: The source canvas (default: imgCanvas)
* filename: The filename to save as (default: '[ISO date] [Hours] [Minutes] [Seconds] openOutpaint image.png').\
* If null, opens image in new tab.
*/
function downloadCanvas(options = {}) { function downloadCanvas(options = {}) {
defaultOpt(options, { defaultOpt(options, {
cropToContent: true, cropToContent: true,
@ -182,7 +231,8 @@ function downloadCanvas(options = {}) {
}); });
var link = document.createElement("a"); var link = document.createElement("a");
link.download = options.filename; link.target = "_blank";
if (options.filename) link.download = options.filename;
var croppedCanvas = options.cropToContent var croppedCanvas = options.cropToContent
? cropCanvas(options.canvas) ? cropCanvas(options.canvas)