allow shortcut deletion via callback
Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com> Former-commit-id: 0a46ffda9945ad95665634c21c9732e7d9d1497e
This commit is contained in:
parent
2fdcdc7f0c
commit
42144ea577
10 changed files with 458 additions and 267 deletions
|
@ -295,6 +295,7 @@ people, person, humans, human, divers, diver, glitch, error, text, watermark, ba
|
|||
<!-- Load Tools -->
|
||||
<script src="js/ui/tool/dream.js" type="text/javascript"></script>
|
||||
<script src="js/ui/tool/maskbrush.js" type="text/javascript"></script>
|
||||
<script src="js/ui/tool/select.js" type="text/javascript"></script>
|
||||
|
||||
<script src="js/ui/toolbar.js" type="text/javascript"></script>
|
||||
</body>
|
||||
|
|
14
js/input.js
14
js/input.js
|
@ -201,6 +201,8 @@ window.onmouseup = (evn) => {
|
|||
target: evn.target,
|
||||
initialTarget: mouse.coords[name].dragging[key].target,
|
||||
buttonId: evn.button,
|
||||
ix: mouse.coords[name].dragging[key].x,
|
||||
iy: mouse.coords[name].dragging[key].y,
|
||||
x: mouse.coords[name].pos.x,
|
||||
y: mouse.coords[name].pos.y,
|
||||
evn,
|
||||
|
@ -213,6 +215,8 @@ window.onmouseup = (evn) => {
|
|||
target: evn.target,
|
||||
initialTarget: mouse.coords[name].dragging[key].target,
|
||||
buttonId: evn.button,
|
||||
ix: mouse.coords[name].dragging[key].x,
|
||||
iy: mouse.coords[name].dragging[key].y,
|
||||
x: mouse.coords[name].pos.x,
|
||||
y: mouse.coords[name].pos.y,
|
||||
evn,
|
||||
|
@ -264,6 +268,8 @@ window.onmousemove = (evn) => {
|
|||
mouse.listen[name][key].ondragstart.emit({
|
||||
target: evn.target,
|
||||
buttonId: evn.button,
|
||||
ix: mouse.coords[name].dragging[key].x,
|
||||
iy: mouse.coords[name].dragging[key].y,
|
||||
x: mouse.coords[name].pos.x,
|
||||
y: mouse.coords[name].pos.y,
|
||||
evn,
|
||||
|
@ -283,6 +289,8 @@ window.onmousemove = (evn) => {
|
|||
target: evn.target,
|
||||
initialTarget: mouse.coords[name].dragging[key].target,
|
||||
button: index,
|
||||
ix: mouse.coords[name].dragging[key].x,
|
||||
iy: mouse.coords[name].dragging[key].y,
|
||||
px: mouse.coords[name].prev.x,
|
||||
py: mouse.coords[name].prev.y,
|
||||
x: mouse.coords[name].pos.x,
|
||||
|
@ -297,6 +305,8 @@ window.onmousemove = (evn) => {
|
|||
target: evn.target,
|
||||
initialTarget: mouse.coords[name].dragging[key].target,
|
||||
button: index,
|
||||
ix: mouse.coords[name].dragging[key].x,
|
||||
iy: mouse.coords[name].dragging[key].y,
|
||||
px: mouse.coords[name].prev.x,
|
||||
py: mouse.coords[name].prev.y,
|
||||
x: mouse.coords[name].pos.x,
|
||||
|
@ -384,7 +394,9 @@ const keyboard = {
|
|||
},
|
||||
deleteShortcut(id) {
|
||||
this.shortcuts.keys().forEach((key) => {
|
||||
this.shortcuts[key] = this.shortcuts[key].filter((v) => v.id !== id);
|
||||
this.shortcuts[key] = this.shortcuts[key].filter(
|
||||
(v) => v.id !== id && v.callback !== id
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -107,6 +107,7 @@ function createSlider(name, wrapper, options = {}) {
|
|||
textEl.value = `${name}: ${value}`;
|
||||
});
|
||||
textEl.addEventListener("focus", () => {
|
||||
overEl.style.pointerEvents = "none";
|
||||
textEl.value = value;
|
||||
});
|
||||
|
||||
|
@ -125,7 +126,6 @@ function createSlider(name, wrapper, options = {}) {
|
|||
|
||||
mouse.listen.window.left.onclick.on((evn) => {
|
||||
if (evn.target === overEl) {
|
||||
overEl.style.pointerEvents = "none";
|
||||
textEl.select();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -212,3 +212,179 @@ const dream_img2img_callback = (evn, state) => {
|
|||
dream(bb.x, bb.y, request, {method: "img2img", stopMarching, bb});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers Tools
|
||||
*/
|
||||
const dreamTool = () =>
|
||||
toolbar.registerTool(
|
||||
"res/icons/image-plus.svg",
|
||||
"Dream",
|
||||
(state, opt) => {
|
||||
// Draw new cursor immediately
|
||||
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
||||
state.mousemovecb({
|
||||
...mouse.coords.canvas.pos,
|
||||
target: {id: "overlayCanvas"},
|
||||
});
|
||||
|
||||
// Start Listeners
|
||||
mouse.listen.canvas.onmousemove.on(state.mousemovecb);
|
||||
mouse.listen.canvas.left.onclick.on(state.dreamcb);
|
||||
mouse.listen.canvas.right.onclick.on(state.erasecb);
|
||||
},
|
||||
(state, opt) => {
|
||||
// Clear Listeners
|
||||
mouse.listen.canvas.onmousemove.clear(state.mousemovecb);
|
||||
mouse.listen.canvas.left.onclick.clear(state.dreamcb);
|
||||
mouse.listen.canvas.right.onclick.clear(state.erasecb);
|
||||
},
|
||||
{
|
||||
init: (state) => {
|
||||
state.snapToGrid = true;
|
||||
state.overMaskPx = 0;
|
||||
state.mousemovecb = (evn) => _reticle_draw(evn, state.snapToGrid);
|
||||
state.dreamcb = (evn) => {
|
||||
dream_generate_callback(evn, state);
|
||||
};
|
||||
state.erasecb = (evn) => dream_erase_callback(evn, state);
|
||||
},
|
||||
populateContextMenu: (menu, state) => {
|
||||
if (!state.ctxmenu) {
|
||||
state.ctxmenu = {};
|
||||
state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox(
|
||||
state,
|
||||
"snapToGrid",
|
||||
"Snap To Grid"
|
||||
).label;
|
||||
state.ctxmenu.overMaskPxLabel = _toolbar_input.slider(
|
||||
state,
|
||||
"overMaskPx",
|
||||
"Overmask px",
|
||||
0,
|
||||
128,
|
||||
1
|
||||
).slider;
|
||||
}
|
||||
|
||||
menu.appendChild(state.ctxmenu.snapToGridLabel);
|
||||
menu.appendChild(document.createElement("br"));
|
||||
menu.appendChild(state.ctxmenu.overMaskPxLabel);
|
||||
},
|
||||
shortcut: "D",
|
||||
}
|
||||
);
|
||||
|
||||
const img2imgTool = () =>
|
||||
toolbar.registerTool(
|
||||
"res/icons/image.svg",
|
||||
"Img2Img",
|
||||
(state, opt) => {
|
||||
// Draw new cursor immediately
|
||||
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
||||
state.mousemovecb({
|
||||
...mouse.coords.canvas.pos,
|
||||
target: {id: "overlayCanvas"},
|
||||
});
|
||||
|
||||
// Start Listeners
|
||||
mouse.listen.canvas.onmousemove.on(state.mousemovecb);
|
||||
mouse.listen.canvas.left.onclick.on(state.dreamcb);
|
||||
mouse.listen.canvas.right.onclick.on(state.erasecb);
|
||||
},
|
||||
(state, opt) => {
|
||||
// Clear Listeners
|
||||
mouse.listen.canvas.onmousemove.clear(state.mousemovecb);
|
||||
mouse.listen.canvas.left.onclick.clear(state.dreamcb);
|
||||
mouse.listen.canvas.right.onclick.clear(state.erasecb);
|
||||
},
|
||||
{
|
||||
init: (state) => {
|
||||
state.snapToGrid = true;
|
||||
state.denoisingStrength = 0.7;
|
||||
|
||||
state.borderMaskSize = 64;
|
||||
|
||||
state.mousemovecb = (evn) => {
|
||||
_reticle_draw(evn, state.snapToGrid);
|
||||
const bb = getBoundingBox(
|
||||
evn.x,
|
||||
evn.y,
|
||||
basePixelCount * scaleFactor,
|
||||
basePixelCount * scaleFactor,
|
||||
state.snapToGrid && basePixelCount
|
||||
);
|
||||
|
||||
// For displaying border mask
|
||||
const auxCanvas = document.createElement("canvas");
|
||||
auxCanvas.width = bb.w;
|
||||
auxCanvas.height = bb.h;
|
||||
const auxCtx = auxCanvas.getContext("2d");
|
||||
|
||||
if (state.borderMaskSize > 0) {
|
||||
auxCtx.fillStyle = "#FF6A6A50";
|
||||
auxCtx.fillRect(0, 0, state.borderMaskSize, bb.h);
|
||||
auxCtx.fillRect(0, 0, bb.w, state.borderMaskSize);
|
||||
auxCtx.fillRect(
|
||||
bb.w - state.borderMaskSize,
|
||||
0,
|
||||
state.borderMaskSize,
|
||||
bb.h
|
||||
);
|
||||
auxCtx.fillRect(
|
||||
0,
|
||||
bb.h - state.borderMaskSize,
|
||||
bb.w,
|
||||
state.borderMaskSize
|
||||
);
|
||||
}
|
||||
|
||||
const tmp = ovCtx.globalAlpha;
|
||||
ovCtx.globalAlpha = 0.4;
|
||||
ovCtx.drawImage(auxCanvas, bb.x, bb.y);
|
||||
ovCtx.globalAlpha = tmp;
|
||||
};
|
||||
state.dreamcb = (evn) => {
|
||||
dream_img2img_callback(evn, state);
|
||||
};
|
||||
state.erasecb = (evn) => dream_erase_callback(evn, state);
|
||||
},
|
||||
populateContextMenu: (menu, state) => {
|
||||
if (!state.ctxmenu) {
|
||||
state.ctxmenu = {};
|
||||
// Snap To Grid Checkbox
|
||||
state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox(
|
||||
state,
|
||||
"snapToGrid",
|
||||
"Snap To Grid"
|
||||
).label;
|
||||
|
||||
// Denoising Strength Slider
|
||||
state.ctxmenu.denoisingStrengthSlider = _toolbar_input.slider(
|
||||
state,
|
||||
"denoisingStrength",
|
||||
"Denoising Strength",
|
||||
0,
|
||||
1,
|
||||
0.05
|
||||
).slider;
|
||||
|
||||
// Border Mask Size Slider
|
||||
state.ctxmenu.borderMaskSlider = _toolbar_input.slider(
|
||||
state,
|
||||
"borderMaskSize",
|
||||
"Border Mask Size",
|
||||
0,
|
||||
128,
|
||||
1
|
||||
).slider;
|
||||
}
|
||||
|
||||
menu.appendChild(state.ctxmenu.snapToGridLabel);
|
||||
menu.appendChild(document.createElement("br"));
|
||||
menu.appendChild(state.ctxmenu.denoisingStrengthSlider);
|
||||
menu.appendChild(state.ctxmenu.borderMaskSlider);
|
||||
},
|
||||
shortcut: "I",
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const mask_brush_draw_callback = (evn, state) => {
|
||||
const _mask_brush_draw_callback = (evn, state) => {
|
||||
if (evn.initialTarget.id === "overlayCanvas") {
|
||||
maskPaintCtx.globalCompositeOperation = "source-over";
|
||||
maskPaintCtx.strokeStyle = "#FF6A6A";
|
||||
|
@ -12,7 +12,7 @@ const mask_brush_draw_callback = (evn, state) => {
|
|||
}
|
||||
};
|
||||
|
||||
const mask_brush_erase_callback = (evn, state) => {
|
||||
const _mask_brush_erase_callback = (evn, state) => {
|
||||
if (evn.initialTarget.id === "overlayCanvas") {
|
||||
maskPaintCtx.globalCompositeOperation = "destination-out";
|
||||
maskPaintCtx.strokeStyle = "#FFFFFFFF";
|
||||
|
@ -25,3 +25,85 @@ const mask_brush_erase_callback = (evn, state) => {
|
|||
maskPaintCtx.stroke();
|
||||
}
|
||||
};
|
||||
|
||||
const maskBrushTool = () =>
|
||||
toolbar.registerTool(
|
||||
"res/icons/paintbrush.svg",
|
||||
"Mask Brush",
|
||||
(state, opt) => {
|
||||
// Draw new cursor immediately
|
||||
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
||||
state.movecb({...mouse.coords.canvas.pos, target: {id: "overlayCanvas"}});
|
||||
|
||||
// Start Listeners
|
||||
mouse.listen.canvas.onmousemove.on(state.movecb);
|
||||
mouse.listen.canvas.onwheel.on(state.wheelcb);
|
||||
mouse.listen.canvas.left.onpaint.on(state.drawcb);
|
||||
mouse.listen.canvas.right.onpaint.on(state.erasecb);
|
||||
},
|
||||
(state, opt) => {
|
||||
// Clear Listeners
|
||||
mouse.listen.canvas.onmousemove.clear(state.movecb);
|
||||
mouse.listen.canvas.onwheel.on(state.wheelcb);
|
||||
mouse.listen.canvas.left.onpaint.clear(state.drawcb);
|
||||
mouse.listen.canvas.right.onpaint.clear(state.erasecb);
|
||||
},
|
||||
{
|
||||
init: (state) => {
|
||||
state.config = {
|
||||
brushScrollSpeed: 1 / 4,
|
||||
minBrushSize: 10,
|
||||
maxBrushSize: 500,
|
||||
};
|
||||
|
||||
state.brushSize = 64;
|
||||
state.setBrushSize = (size) => {
|
||||
state.brushSize = size;
|
||||
state.ctxmenu.brushSizeRange.value = size;
|
||||
state.ctxmenu.brushSizeText.value = size;
|
||||
};
|
||||
|
||||
state.movecb = (evn) => {
|
||||
if (evn.target.id === "overlayCanvas") {
|
||||
// draw big translucent red blob cursor
|
||||
ovCtx.beginPath();
|
||||
ovCtx.arc(evn.x, evn.y, state.brushSize / 2, 0, 2 * Math.PI, true); // for some reason 4x on an arc is === to 8x on a line???
|
||||
ovCtx.fillStyle = "#FF6A6A50";
|
||||
ovCtx.fill();
|
||||
}
|
||||
};
|
||||
|
||||
state.wheelcb = (evn) => {
|
||||
if (evn.target.id === "overlayCanvas") {
|
||||
state.brushSize = state.setBrushSize(
|
||||
state.brushSize -
|
||||
Math.floor(state.config.brushScrollSpeed * evn.delta)
|
||||
);
|
||||
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
||||
state.movecb(evn);
|
||||
}
|
||||
};
|
||||
|
||||
state.drawcb = (evn) => _mask_brush_draw_callback(evn, state);
|
||||
state.erasecb = (evn) => _mask_brush_erase_callback(evn, state);
|
||||
},
|
||||
populateContextMenu: (menu, state) => {
|
||||
if (!state.ctxmenu) {
|
||||
state.ctxmenu = {};
|
||||
const brushSizeSlider = _toolbar_input.slider(
|
||||
state,
|
||||
"brushSize",
|
||||
"Brush Size",
|
||||
state.config.minBrushSize,
|
||||
state.config.maxBrushSize,
|
||||
1
|
||||
);
|
||||
state.ctxmenu.brushSizeSlider = brushSizeSlider.slider;
|
||||
state.setBrushSize = brushSizeSlider.setValue;
|
||||
}
|
||||
|
||||
menu.appendChild(state.ctxmenu.brushSizeSlider);
|
||||
},
|
||||
shortcut: "M",
|
||||
}
|
||||
);
|
||||
|
|
0
js/ui/tool/populate.js
Normal file
0
js/ui/tool/populate.js
Normal file
153
js/ui/tool/select.js
Normal file
153
js/ui/tool/select.js
Normal file
|
@ -0,0 +1,153 @@
|
|||
const selectTransformTool = () =>
|
||||
toolbar.registerTool(
|
||||
"res/icons/box-select.svg",
|
||||
"Select Image",
|
||||
(state, opt) => {
|
||||
// Draw new cursor immediately
|
||||
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
||||
state.movecb({...mouse.coords.canvas.pos, target: {id: "overlayCanvas"}});
|
||||
|
||||
mouse.listen.canvas.onmousemove.on(state.movecb);
|
||||
mouse.listen.canvas.left.ondragstart.on(state.dragstartcb);
|
||||
mouse.listen.canvas.left.ondragend.on(state.dragendcb);
|
||||
|
||||
mouse.listen.canvas.right.onclick.on(state.cancelcb);
|
||||
},
|
||||
(state, opt) => {
|
||||
mouse.listen.canvas.onmousemove.clear(state.movecb);
|
||||
mouse.listen.canvas.left.ondragstart.clear(state.dragstartcb);
|
||||
mouse.listen.canvas.left.ondragend.clear(state.dragendcb);
|
||||
|
||||
mouse.listen.canvas.right.onclick.clear(state.cancelcb);
|
||||
},
|
||||
{
|
||||
init: (state) => {
|
||||
state.snapToGrid = true;
|
||||
state.dragging = null;
|
||||
|
||||
const selectionBB = (x1, y1, x2, y2) => {
|
||||
return {
|
||||
x: Math.min(x1, x2),
|
||||
y: Math.min(y1, y2),
|
||||
w: Math.abs(x1 - x2),
|
||||
h: Math.abs(y1 - y2),
|
||||
};
|
||||
};
|
||||
|
||||
state.movecb = (evn) => {
|
||||
if (evn.target.id === "overlayCanvas") {
|
||||
let x = evn.x;
|
||||
let y = evn.y;
|
||||
if (state.snapToGrid) {
|
||||
x += snap(evn.x, true, 64);
|
||||
y += snap(evn.y, true, 64);
|
||||
}
|
||||
|
||||
// Draw dragging box
|
||||
if (state.dragging) {
|
||||
ovCtx.setLineDash([2, 2]);
|
||||
ovCtx.lineWidth = 1;
|
||||
ovCtx.strokeStyle = "#FFF";
|
||||
|
||||
const ix = state.dragging.ix;
|
||||
const iy = state.dragging.iy;
|
||||
|
||||
const bb = selectionBB(ix, iy, x, y);
|
||||
|
||||
ovCtx.strokeRect(bb.x, bb.y, bb.w, bb.h);
|
||||
ovCtx.setLineDash([]);
|
||||
}
|
||||
|
||||
// Draw selection box
|
||||
if (state.selected) {
|
||||
ovCtx.lineWidth = 1;
|
||||
ovCtx.strokeStyle = "#FFF";
|
||||
|
||||
ovCtx.setLineDash([4, 2]);
|
||||
ovCtx.strokeRect(
|
||||
state.selected.x,
|
||||
state.selected.y,
|
||||
state.selected.w,
|
||||
state.selected.h
|
||||
);
|
||||
ovCtx.setLineDash([]);
|
||||
}
|
||||
|
||||
// Draw cuttent cursor location
|
||||
ovCtx.lineWidth = 3;
|
||||
ovCtx.strokeStyle = "#FFF";
|
||||
|
||||
ovCtx.beginPath();
|
||||
ovCtx.moveTo(x, y + 10);
|
||||
ovCtx.lineTo(x, y - 10);
|
||||
ovCtx.moveTo(x + 10, y);
|
||||
ovCtx.lineTo(x - 10, y);
|
||||
ovCtx.stroke();
|
||||
}
|
||||
};
|
||||
|
||||
state.dragstartcb = (evn) => {
|
||||
if (evn.target.id === "overlayCanvas") {
|
||||
let ix = evn.ix;
|
||||
let iy = evn.iy;
|
||||
if (state.snapToGrid) {
|
||||
ix += snap(evn.ix, true, 64);
|
||||
iy += snap(evn.iy, true, 64);
|
||||
}
|
||||
|
||||
state.dragging = {ix, iy};
|
||||
}
|
||||
};
|
||||
|
||||
state.dragendcb = (evn) => {
|
||||
if (evn.target.id === "overlayCanvas" && state.dragging) {
|
||||
let x = evn.x;
|
||||
let y = evn.y;
|
||||
if (state.snapToGrid) {
|
||||
x += snap(evn.x, true, 64);
|
||||
y += snap(evn.y, true, 64);
|
||||
}
|
||||
|
||||
state.selected = selectionBB(
|
||||
state.dragging.ix,
|
||||
state.dragging.iy,
|
||||
x,
|
||||
y
|
||||
);
|
||||
state.dragging = null;
|
||||
}
|
||||
};
|
||||
|
||||
state.cancelcb = (evn) => {
|
||||
if (evn.target.id === "overlayCanvas") {
|
||||
if (state.dragging) state.dragging = null;
|
||||
else state.selected = null;
|
||||
|
||||
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
||||
state.movecb(evn);
|
||||
}
|
||||
};
|
||||
|
||||
// Keyboard callbacks
|
||||
state.keydowncb = (evn) => {
|
||||
console.debug(evn);
|
||||
};
|
||||
|
||||
state.keyclickcb = (evn) => {
|
||||
console.debug(evn);
|
||||
};
|
||||
},
|
||||
populateContextMenu: (menu, state) => {
|
||||
if (!state.ctxmenu) {
|
||||
state.ctxmenu = {};
|
||||
// Snap To Grid Checkbox
|
||||
state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox(
|
||||
state,
|
||||
"snapToGrid",
|
||||
"Snap To Grid"
|
||||
).label;
|
||||
}
|
||||
menu.appendChild(state.ctxmenu.snapToGridLabel);
|
||||
},
|
||||
}
|
||||
);
|
273
js/ui/toolbar.js
273
js/ui/toolbar.js
|
@ -134,15 +134,7 @@ const _toolbar_input = {
|
|||
return {checkbox, label};
|
||||
},
|
||||
|
||||
slider: (
|
||||
state,
|
||||
dataKey,
|
||||
text,
|
||||
min = 0,
|
||||
max = 1,
|
||||
step = 0.1,
|
||||
defaultValue = 0.3
|
||||
) => {
|
||||
slider: (state, dataKey, text, min = 0, max = 1, step = 0.1) => {
|
||||
const slider = document.createElement("div");
|
||||
|
||||
const value = createSlider(text, slider, {
|
||||
|
@ -152,7 +144,7 @@ const _toolbar_input = {
|
|||
valuecb: (v) => {
|
||||
state[dataKey] = v;
|
||||
},
|
||||
defaultValue,
|
||||
defaultValue: state[dataKey],
|
||||
});
|
||||
|
||||
return {
|
||||
|
@ -179,6 +171,7 @@ const _reticle_draw = (evn, snapToGrid = true) => {
|
|||
);
|
||||
|
||||
// draw targeting square reticle thingy cursor
|
||||
ovCtx.lineWidth = 1;
|
||||
ovCtx.strokeStyle = "#FFF";
|
||||
ovCtx.strokeRect(bb.x, bb.y, bb.w, bb.h); //origin is middle of the frame
|
||||
}
|
||||
|
@ -189,179 +182,8 @@ const tools = {};
|
|||
/**
|
||||
* Dream tool
|
||||
*/
|
||||
tools.dream = toolbar.registerTool(
|
||||
"res/icons/image-plus.svg",
|
||||
"Dream",
|
||||
(state, opt) => {
|
||||
// Draw new cursor immediately
|
||||
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
||||
state.mousemovecb({
|
||||
...mouse.coords.canvas.pos,
|
||||
target: {id: "overlayCanvas"},
|
||||
});
|
||||
|
||||
// Start Listeners
|
||||
mouse.listen.canvas.onmousemove.on(state.mousemovecb);
|
||||
mouse.listen.canvas.left.onclick.on(state.dreamcb);
|
||||
mouse.listen.canvas.right.onclick.on(state.erasecb);
|
||||
},
|
||||
(state, opt) => {
|
||||
// Clear Listeners
|
||||
mouse.listen.canvas.onmousemove.clear(state.mousemovecb);
|
||||
mouse.listen.canvas.left.onclick.clear(state.dreamcb);
|
||||
mouse.listen.canvas.right.onclick.clear(state.erasecb);
|
||||
},
|
||||
{
|
||||
init: (state) => {
|
||||
state.snapToGrid = true;
|
||||
state.overMaskPx = 0;
|
||||
state.mousemovecb = (evn) => _reticle_draw(evn, state.snapToGrid);
|
||||
state.dreamcb = (evn) => {
|
||||
dream_generate_callback(evn, state);
|
||||
};
|
||||
state.erasecb = (evn) => dream_erase_callback(evn, state);
|
||||
},
|
||||
populateContextMenu: (menu, state) => {
|
||||
if (!state.ctxmenu) {
|
||||
state.ctxmenu = {};
|
||||
state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox(
|
||||
state,
|
||||
"snapToGrid",
|
||||
"Snap To Grid"
|
||||
).label;
|
||||
state.ctxmenu.overMaskPxLabel = _toolbar_input.slider(
|
||||
state,
|
||||
"overMaskPx",
|
||||
"Overmask px",
|
||||
0,
|
||||
128,
|
||||
1,
|
||||
64
|
||||
).slider;
|
||||
}
|
||||
|
||||
menu.appendChild(state.ctxmenu.snapToGridLabel);
|
||||
menu.appendChild(document.createElement("br"));
|
||||
menu.appendChild(state.ctxmenu.overMaskPxLabel);
|
||||
},
|
||||
shortcut: "D",
|
||||
}
|
||||
);
|
||||
|
||||
tools.img2img = toolbar.registerTool(
|
||||
"res/icons/image.svg",
|
||||
"Img2Img",
|
||||
(state, opt) => {
|
||||
// Draw new cursor immediately
|
||||
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
||||
state.mousemovecb({
|
||||
...mouse.coords.canvas.pos,
|
||||
target: {id: "overlayCanvas"},
|
||||
});
|
||||
|
||||
// Start Listeners
|
||||
mouse.listen.canvas.onmousemove.on(state.mousemovecb);
|
||||
mouse.listen.canvas.left.onclick.on(state.dreamcb);
|
||||
mouse.listen.canvas.right.onclick.on(state.erasecb);
|
||||
},
|
||||
(state, opt) => {
|
||||
// Clear Listeners
|
||||
mouse.listen.canvas.onmousemove.clear(state.mousemovecb);
|
||||
mouse.listen.canvas.left.onclick.clear(state.dreamcb);
|
||||
mouse.listen.canvas.right.onclick.clear(state.erasecb);
|
||||
},
|
||||
{
|
||||
init: (state) => {
|
||||
state.snapToGrid = true;
|
||||
state.denoisingStrength = 0.7;
|
||||
|
||||
state.borderMaskSize = 64;
|
||||
|
||||
state.mousemovecb = (evn) => {
|
||||
_reticle_draw(evn, state.snapToGrid);
|
||||
const bb = getBoundingBox(
|
||||
evn.x,
|
||||
evn.y,
|
||||
basePixelCount * scaleFactor,
|
||||
basePixelCount * scaleFactor,
|
||||
state.snapToGrid && basePixelCount
|
||||
);
|
||||
|
||||
// For displaying border mask
|
||||
const auxCanvas = document.createElement("canvas");
|
||||
auxCanvas.width = bb.w;
|
||||
auxCanvas.height = bb.h;
|
||||
const auxCtx = auxCanvas.getContext("2d");
|
||||
|
||||
if (state.borderMaskSize > 0) {
|
||||
auxCtx.fillStyle = "#FF6A6A50";
|
||||
auxCtx.fillRect(0, 0, state.borderMaskSize, bb.h);
|
||||
auxCtx.fillRect(0, 0, bb.w, state.borderMaskSize);
|
||||
auxCtx.fillRect(
|
||||
bb.w - state.borderMaskSize,
|
||||
0,
|
||||
state.borderMaskSize,
|
||||
bb.h
|
||||
);
|
||||
auxCtx.fillRect(
|
||||
0,
|
||||
bb.h - state.borderMaskSize,
|
||||
bb.w,
|
||||
state.borderMaskSize
|
||||
);
|
||||
}
|
||||
|
||||
const tmp = ovCtx.globalAlpha;
|
||||
ovCtx.globalAlpha = 0.4;
|
||||
ovCtx.drawImage(auxCanvas, bb.x, bb.y);
|
||||
ovCtx.globalAlpha = tmp;
|
||||
};
|
||||
state.dreamcb = (evn) => {
|
||||
dream_img2img_callback(evn, state);
|
||||
};
|
||||
state.erasecb = (evn) => dream_erase_callback(evn, state);
|
||||
},
|
||||
populateContextMenu: (menu, state) => {
|
||||
if (!state.ctxmenu) {
|
||||
state.ctxmenu = {};
|
||||
// Snap To Grid Checkbox
|
||||
state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox(
|
||||
state,
|
||||
"snapToGrid",
|
||||
"Snap To Grid"
|
||||
).label;
|
||||
|
||||
// Denoising Strength Slider
|
||||
state.ctxmenu.denoisingStrengthSlider = _toolbar_input.slider(
|
||||
state,
|
||||
"denoisingStrength",
|
||||
"Denoising Strength",
|
||||
0,
|
||||
1,
|
||||
0.05,
|
||||
0.7
|
||||
).slider;
|
||||
|
||||
// Border Mask Size Slider
|
||||
state.ctxmenu.borderMaskSlider = _toolbar_input.slider(
|
||||
state,
|
||||
"borderMaskSize",
|
||||
"Border Mask Size",
|
||||
0,
|
||||
128,
|
||||
1,
|
||||
64
|
||||
).slider;
|
||||
}
|
||||
|
||||
menu.appendChild(state.ctxmenu.snapToGridLabel);
|
||||
menu.appendChild(document.createElement("br"));
|
||||
menu.appendChild(state.ctxmenu.denoisingStrengthSlider);
|
||||
menu.appendChild(state.ctxmenu.borderMaskSlider);
|
||||
},
|
||||
shortcut: "I",
|
||||
}
|
||||
);
|
||||
tools.dream = dreamTool();
|
||||
tools.img2img = img2imgTool();
|
||||
|
||||
/**
|
||||
* Mask Editing tools
|
||||
|
@ -371,86 +193,13 @@ toolbar.addSeparator();
|
|||
/**
|
||||
* Mask Brush tool
|
||||
*/
|
||||
tools.maskbrush = toolbar.registerTool(
|
||||
"res/icons/paintbrush.svg",
|
||||
"Mask Brush",
|
||||
(state, opt) => {
|
||||
// Draw new cursor immediately
|
||||
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
||||
state.movecb({...mouse.coords.canvas.pos, target: {id: "overlayCanvas"}});
|
||||
tools.maskbrush = maskBrushTool();
|
||||
|
||||
// Start Listeners
|
||||
mouse.listen.canvas.onmousemove.on(state.movecb);
|
||||
mouse.listen.canvas.onwheel.on(state.wheelcb);
|
||||
mouse.listen.canvas.left.onpaint.on(state.drawcb);
|
||||
mouse.listen.canvas.right.onpaint.on(state.erasecb);
|
||||
},
|
||||
(state, opt) => {
|
||||
// Clear Listeners
|
||||
mouse.listen.canvas.onmousemove.clear(state.movecb);
|
||||
mouse.listen.canvas.onwheel.on(state.wheelcb);
|
||||
mouse.listen.canvas.left.onpaint.clear(state.drawcb);
|
||||
mouse.listen.canvas.right.onpaint.clear(state.erasecb);
|
||||
},
|
||||
{
|
||||
init: (state) => {
|
||||
state.config = {
|
||||
brushScrollSpeed: 1 / 4,
|
||||
minBrushSize: 10,
|
||||
maxBrushSize: 500,
|
||||
};
|
||||
/**
|
||||
* Image Editing tools
|
||||
*/
|
||||
toolbar.addSeparator();
|
||||
|
||||
state.brushSize = 64;
|
||||
state.setBrushSize = (size) => {
|
||||
state.brushSize = size;
|
||||
state.ctxmenu.brushSizeRange.value = size;
|
||||
state.ctxmenu.brushSizeText.value = size;
|
||||
};
|
||||
|
||||
state.movecb = (evn) => {
|
||||
if (evn.target.id === "overlayCanvas") {
|
||||
// draw big translucent red blob cursor
|
||||
ovCtx.beginPath();
|
||||
ovCtx.arc(evn.x, evn.y, state.brushSize / 2, 0, 2 * Math.PI, true); // for some reason 4x on an arc is === to 8x on a line???
|
||||
ovCtx.fillStyle = "#FF6A6A50";
|
||||
ovCtx.fill();
|
||||
}
|
||||
};
|
||||
|
||||
state.wheelcb = (evn) => {
|
||||
if (evn.target.id === "overlayCanvas") {
|
||||
state.brushSize = state.setBrushSize(
|
||||
state.brushSize -
|
||||
Math.floor(state.config.brushScrollSpeed * evn.delta)
|
||||
);
|
||||
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
||||
state.movecb(evn);
|
||||
}
|
||||
};
|
||||
|
||||
state.drawcb = (evn) => mask_brush_draw_callback(evn, state);
|
||||
state.erasecb = (evn) => mask_brush_erase_callback(evn, state);
|
||||
},
|
||||
populateContextMenu: (menu, state) => {
|
||||
if (!state.ctxmenu) {
|
||||
state.ctxmenu = {};
|
||||
const brushSizeSlider = _toolbar_input.slider(
|
||||
state,
|
||||
"brushSize",
|
||||
"Brush Size",
|
||||
state.config.minBrushSize,
|
||||
state.config.maxBrushSize,
|
||||
1,
|
||||
64
|
||||
);
|
||||
state.ctxmenu.brushSizeSlider = brushSizeSlider.slider;
|
||||
state.setBrushSize = brushSizeSlider.setValue;
|
||||
}
|
||||
|
||||
menu.appendChild(state.ctxmenu.brushSizeSlider);
|
||||
},
|
||||
shortcut: "M",
|
||||
}
|
||||
);
|
||||
tools.selecttransform = selectTransformTool();
|
||||
|
||||
toolbar.tools[0].enable();
|
||||
|
|
|
@ -64,7 +64,10 @@ function snap(i, scaled = true, gridSize = 64) {
|
|||
scaleOffset = gridSize / 2;
|
||||
}
|
||||
}
|
||||
var snapOffset = (i % gridSize) - scaleOffset;
|
||||
const modulus = i % gridSize;
|
||||
var snapOffset = modulus - scaleOffset;
|
||||
if (modulus > gridSize / 2) snapOffset = modulus - gridSize;
|
||||
|
||||
if (snapOffset == 0) {
|
||||
return snapOffset;
|
||||
}
|
||||
|
|
15
res/icons/box-select.svg
Normal file
15
res/icons/box-select.svg
Normal file
|
@ -0,0 +1,15 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M5 3a2 2 0 0 0-2 2"></path>
|
||||
<path d="M19 3a2 2 0 0 1 2 2"></path>
|
||||
<path d="M21 19a2 2 0 0 1-2 2"></path>
|
||||
<path d="M5 21a2 2 0 0 1-2-2"></path>
|
||||
<path d="M9 3h1"></path>
|
||||
<path d="M9 21h1"></path>
|
||||
<path d="M14 3h1"></path>
|
||||
<path d="M14 21h1"></path>
|
||||
<path d="M3 9v1"></path>
|
||||
<path d="M21 9v1"></path>
|
||||
<path d="M3 14v1"></path>
|
||||
<path d="M21 14v1"></path>
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 573 B |
Loading…
Reference in a new issue