df5dc32eee
Allows for selections and dreams to be sent to the resource manager, allows resource deselection when clicking on the currently selected item, put some extra comments and allow saving of a selected canvas area Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com> Former-commit-id: 963312115d4827de1c8d4fac88a97dd64db7fa15
168 lines
3.7 KiB
JavaScript
168 lines
3.7 KiB
JavaScript
/**
|
|
* Implementation of a simple Oberver Pattern for custom event handling
|
|
*/
|
|
function Observer() {
|
|
this.handlers = new Set();
|
|
}
|
|
|
|
Observer.prototype = {
|
|
// Adds handler for this message
|
|
on(callback) {
|
|
this.handlers.add(callback);
|
|
return callback;
|
|
},
|
|
clear(callback) {
|
|
return this.handlers.delete(callback);
|
|
},
|
|
emit(msg) {
|
|
this.handlers.forEach(async (handler) => {
|
|
try {
|
|
await handler(msg);
|
|
} catch (e) {
|
|
console.warn("Observer failed to run handler");
|
|
console.warn(e);
|
|
}
|
|
});
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Generates unique id
|
|
*/
|
|
const guid = (size = 3) => {
|
|
const s4 = () => {
|
|
return Math.floor((1 + Math.random()) * 0x10000)
|
|
.toString(16)
|
|
.substring(1);
|
|
};
|
|
// returns id of format 'aaaaaaaa'-'aaaa'-'aaaa'-'aaaa'-'aaaaaaaaaaaa'
|
|
let id = "";
|
|
for (var i = 0; i < size - 1; i++) id += s4() + "-";
|
|
id += s4();
|
|
return id;
|
|
};
|
|
|
|
/**
|
|
* Default option set
|
|
*/
|
|
|
|
function defaultOpt(options, defaults) {
|
|
Object.keys(defaults).forEach((key) => {
|
|
if (options[key] === undefined) options[key] = defaults[key];
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Bounding box Calculation
|
|
*/
|
|
function snap(i, scaled = true, gridSize = 64) {
|
|
// very cheap test proof of concept but it works surprisingly well
|
|
var scaleOffset = 0;
|
|
if (scaled) {
|
|
if (scaleFactor % 2 != 0) {
|
|
// odd number, snaps to center of cell, oops
|
|
scaleOffset = gridSize / 2;
|
|
}
|
|
}
|
|
const modulus = i % gridSize;
|
|
var snapOffset = modulus - scaleOffset;
|
|
if (modulus > gridSize / 2) snapOffset = modulus - gridSize;
|
|
|
|
if (snapOffset == 0) {
|
|
return snapOffset;
|
|
}
|
|
return -snapOffset;
|
|
}
|
|
|
|
function getBoundingBox(cx, cy, w, h, gridSnap = null) {
|
|
const offset = {x: 0, y: 0};
|
|
const box = {x: 0, y: 0};
|
|
|
|
if (gridSnap) {
|
|
offset.x = snap(cx, true, gridSnap);
|
|
offset.y = snap(cy, true, gridSnap);
|
|
}
|
|
box.x = offset.x + cx;
|
|
box.y = offset.y + cy;
|
|
|
|
return {
|
|
x: Math.floor(box.x - w / 2),
|
|
y: Math.floor(box.y - h / 2),
|
|
w,
|
|
h,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Triggers Canvas Download
|
|
*/
|
|
function cropCanvas(sourceCanvas) {
|
|
var w = sourceCanvas.width;
|
|
var h = sourceCanvas.height;
|
|
var pix = {x: [], y: []};
|
|
var imageData = sourceCanvas.getContext("2d").getImageData(0, 0, w, h);
|
|
var x, y, index;
|
|
|
|
for (y = 0; y < h; y++) {
|
|
for (x = 0; x < w; x++) {
|
|
// lol i need to learn what this part does
|
|
index = (y * w + x) * 4; // OHHH OK this is setting the imagedata.data uint8clampeddataarray index for the specified x/y coords
|
|
//this part i get, this is checking that 4th RGBA byte for opacity
|
|
if (imageData.data[index + 3] > 0) {
|
|
pix.x.push(x);
|
|
pix.y.push(y);
|
|
}
|
|
}
|
|
}
|
|
// ...need to learn what this part does too :badpokerface:
|
|
// is this just determining the boundaries of non-transparent pixel data?
|
|
pix.x.sort(function (a, b) {
|
|
return a - b;
|
|
});
|
|
pix.y.sort(function (a, b) {
|
|
return a - b;
|
|
});
|
|
var n = pix.x.length - 1;
|
|
w = pix.x[n] - pix.x[0] + 1;
|
|
h = pix.y[n] - pix.y[0] + 1;
|
|
// yup sure looks like it
|
|
|
|
try {
|
|
var cut = sourceCanvas
|
|
.getContext("2d")
|
|
.getImageData(pix.x[0], pix.y[0], w, h);
|
|
var cutCanvas = document.createElement("canvas");
|
|
cutCanvas.width = w;
|
|
cutCanvas.height = h;
|
|
cutCanvas.getContext("2d").putImageData(cut, 0, 0);
|
|
} catch (ex) {
|
|
// probably empty image
|
|
//TODO confirm edge cases?
|
|
cutCanvas = null;
|
|
}
|
|
return cutCanvas;
|
|
}
|
|
|
|
function downloadCanvas(options) {
|
|
defaultOpt(options, {
|
|
cropToContent: true,
|
|
canvas: imgCanvas,
|
|
filename:
|
|
new Date()
|
|
.toISOString()
|
|
.slice(0, 19)
|
|
.replace("T", " ")
|
|
.replace(":", " ") + " openOutpaint image.png",
|
|
});
|
|
|
|
var link = document.createElement("a");
|
|
link.download = options.filename;
|
|
|
|
var croppedCanvas = options.cropToContent
|
|
? cropCanvas(options.canvas)
|
|
: options.canvas;
|
|
if (croppedCanvas != null) {
|
|
link.href = croppedCanvas.toDataURL("image/png");
|
|
link.click();
|
|
}
|
|
}
|