back to original transform tool state
But way more flexible implementation Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com>
This commit is contained in:
parent
d7e20f87dc
commit
35a62ae9fe
6 changed files with 499 additions and 87 deletions
10
index.html
10
index.html
|
@ -341,17 +341,17 @@
|
|||
<script src="js/global.js?v=5ee46bd" type="text/javascript"></script>
|
||||
|
||||
<!-- Base Libs -->
|
||||
<script src="js/lib/util.js?v=39564fb" type="text/javascript"></script>
|
||||
<script src="js/lib/util.js?v=49a78a6" type="text/javascript"></script>
|
||||
<script src="js/lib/events.js?v=2ab7933" type="text/javascript"></script>
|
||||
<script src="js/lib/input.js?v=09298ac" type="text/javascript"></script>
|
||||
<script src="js/lib/layers.js?v=a1f8aea" type="text/javascript"></script>
|
||||
<script src="js/lib/commands.js?v=bf23c83" type="text/javascript"></script>
|
||||
|
||||
<script src="js/lib/toolbar.js?v=ca3fccf" type="text/javascript"></script>
|
||||
<script src="js/lib/toolbar.js?v=b3ffbff" type="text/javascript"></script>
|
||||
<script src="js/lib/ui.js?v=76ede2b" type="text/javascript"></script>
|
||||
|
||||
<script
|
||||
src="js/initalize/layers.populate.js?v=938a1b2"
|
||||
src="js/initalize/layers.populate.js?v=fcca23e"
|
||||
type="text/javascript"></script>
|
||||
|
||||
<!-- Configuration -->
|
||||
|
@ -370,7 +370,7 @@
|
|||
|
||||
<!-- Load Tools -->
|
||||
<script
|
||||
src="js/ui/tool/generic.js?v=d63d268"
|
||||
src="js/ui/tool/generic.js?v=9f1f84e"
|
||||
type="text/javascript"></script>
|
||||
|
||||
<script src="js/ui/tool/dream.js?v=76bc565" type="text/javascript"></script>
|
||||
|
@ -381,7 +381,7 @@
|
|||
src="js/ui/tool/colorbrush.js?v=3f8c01a"
|
||||
type="text/javascript"></script>
|
||||
<script
|
||||
src="js/ui/tool/select.js?v=44e8ec4"
|
||||
src="js/ui/tool/select.js?v=080237c"
|
||||
type="text/javascript"></script>
|
||||
<script src="js/ui/tool/stamp.js?v=3c07ac8" type="text/javascript"></script>
|
||||
<script
|
||||
|
|
|
@ -322,7 +322,8 @@ mouse.listen.camera.onwheel.on((evn) => {
|
|||
|
||||
//viewport.transform(imageCollection.element);
|
||||
|
||||
toolbar.currentTool.redraw();
|
||||
toolbar._current_tool.state.redrawui &&
|
||||
toolbar._current_tool.state.redrawui();
|
||||
});
|
||||
|
||||
const cameraPaintStart = (evn) => {
|
||||
|
@ -348,6 +349,9 @@ const cameraPaint = (evn) => {
|
|||
}
|
||||
|
||||
viewport.transform(imageCollection.element);
|
||||
toolbar._current_tool.state.redrawui &&
|
||||
toolbar._current_tool.state.redrawui();
|
||||
|
||||
if (global.debug) {
|
||||
debugCtx.clearRect(0, 0, debugCanvas.width, debugCanvas.height);
|
||||
debugCtx.fillStyle = "#F0F";
|
||||
|
|
|
@ -90,14 +90,20 @@ const toolbar = {
|
|||
name: toolname,
|
||||
enabled: false,
|
||||
_element: null,
|
||||
state: {},
|
||||
state: {
|
||||
redrawui: () => tool.redraw(),
|
||||
},
|
||||
options,
|
||||
/**
|
||||
* If the tool has a redraw() function in its state, then run it
|
||||
*/
|
||||
redraw: () => {
|
||||
tool.state.redrawui && tool.state.redrawui();
|
||||
tool.state.redraw && tool.state.redraw();
|
||||
},
|
||||
redrawui: () => {
|
||||
tool.state.redrawui && tool.state.redrawui();
|
||||
},
|
||||
enable: (opt = null) => {
|
||||
if (toolbar._locked) return;
|
||||
|
||||
|
|
|
@ -37,6 +37,16 @@ class BoundingBox {
|
|||
return {x: this.x, y: this.y};
|
||||
}
|
||||
|
||||
/** @type {Point} */
|
||||
get tr() {
|
||||
return {x: this.x + this.w, y: this.y};
|
||||
}
|
||||
|
||||
/** @type {Point} */
|
||||
get bl() {
|
||||
return {x: this.x, y: this.y + this.h};
|
||||
}
|
||||
|
||||
/** @type {Point} */
|
||||
get br() {
|
||||
return {x: this.x + this.w, y: this.y + this.h};
|
||||
|
|
|
@ -315,12 +315,40 @@ const _tool = {
|
|||
/** @type {HTMLCanvasElement} */
|
||||
canvas;
|
||||
|
||||
_dirty = false;
|
||||
_position = {x: 0, y: 0};
|
||||
/**
|
||||
* @type {Point}
|
||||
*/
|
||||
position = {x: 0, y: 0};
|
||||
scale = {x: 1, y: 1};
|
||||
rotation = 0;
|
||||
get position() {
|
||||
return this._position;
|
||||
}
|
||||
set position(v) {
|
||||
this._dirty = true;
|
||||
this._position = v;
|
||||
}
|
||||
|
||||
_scale = {x: 1, y: 1};
|
||||
/**
|
||||
* @type {Point}
|
||||
*/
|
||||
get scale() {
|
||||
return this._scale;
|
||||
}
|
||||
set scale(v) {
|
||||
if (v.x === 0 || v.y === 0) return;
|
||||
this._dirty = true;
|
||||
this._scale = v;
|
||||
}
|
||||
|
||||
_rotation = 0;
|
||||
get rotation() {
|
||||
return this._rotation;
|
||||
}
|
||||
set rotation(v) {
|
||||
this._dirty = true;
|
||||
this._rotation = v;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLCanvasElement} canvas Selected image canvas
|
||||
|
@ -331,14 +359,28 @@ const _tool = {
|
|||
this.position = position;
|
||||
}
|
||||
|
||||
get matrix() {
|
||||
/** @type {DOMMatrix} */
|
||||
_rtmatrix = null;
|
||||
get rtmatrix() {
|
||||
if (!this._rtmatrix || this._dirty) {
|
||||
const m = new DOMMatrix();
|
||||
|
||||
m.scaleSelf(this.scale.x, this.scale.y);
|
||||
m.rotateSelf((this.rotation * 180) / Math.PI);
|
||||
m.translateSelf(this.position.x, this.position.y);
|
||||
m.rotateSelf((this.rotation * 180) / Math.PI);
|
||||
|
||||
return m;
|
||||
this._rtmatrix = m;
|
||||
}
|
||||
|
||||
return this._rtmatrix;
|
||||
}
|
||||
|
||||
/** @type {DOMMatrix} */
|
||||
_matrix = null;
|
||||
get matrix() {
|
||||
if (!this._matrix || this._dirty) {
|
||||
this._matrix = this.rtmatrix.scaleSelf(this.scale.x, this.scale.y);
|
||||
}
|
||||
return this._matrix;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -356,44 +398,201 @@ const _tool = {
|
|||
);
|
||||
}
|
||||
|
||||
hoveringHandle(x, y) {
|
||||
const localbb = new BoundingBox({
|
||||
x: -this.canvas.width / 2,
|
||||
y: -this.canvas.height / 2,
|
||||
w: this.canvas.width,
|
||||
h: this.canvas.height,
|
||||
});
|
||||
|
||||
const localc = this.matrix.inverse().transformPoint({x, y});
|
||||
const ontl =
|
||||
Math.max(
|
||||
Math.abs(localc.x - localbb.tl.x),
|
||||
Math.abs(localc.y - localbb.tl.y)
|
||||
) <
|
||||
config.handleDetectSize / 2;
|
||||
const ontr =
|
||||
Math.max(
|
||||
Math.abs(localc.x - localbb.tr.x),
|
||||
Math.abs(localc.y - localbb.tr.y)
|
||||
) <
|
||||
config.handleDetectSize / 2;
|
||||
const onbl =
|
||||
Math.max(
|
||||
Math.abs(localc.x - localbb.bl.x),
|
||||
Math.abs(localc.y - localbb.bl.y)
|
||||
) <
|
||||
config.handleDetectSize / 2;
|
||||
const onbr =
|
||||
Math.max(
|
||||
Math.abs(localc.x - localbb.br.x),
|
||||
Math.abs(localc.y - localbb.br.y)
|
||||
) <
|
||||
config.handleDetectSize / 2;
|
||||
|
||||
return {onHandle: ontl || ontr || onbl || onbr, ontl, ontr, onbl, onbr};
|
||||
}
|
||||
|
||||
hoveringBox(x, y) {
|
||||
const localbb = new BoundingBox({
|
||||
x: -this.canvas.width / 2,
|
||||
y: -this.canvas.height / 2,
|
||||
w: this.canvas.width,
|
||||
h: this.canvas.height,
|
||||
});
|
||||
|
||||
const localc = this.matrix.inverse().transformPoint({x, y});
|
||||
|
||||
return (
|
||||
!this.hoveringHandle(x, y).onHandle &&
|
||||
localbb.contains(localc.x, localc.y)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the marquee selector box
|
||||
*
|
||||
* @param {CanvasRenderingContext2D} context A context for rendering the box to
|
||||
* @param {Point} cursor Cursor position
|
||||
* @param {DOMMatrix} transform A transformation matrix to transform the position by
|
||||
*/
|
||||
drawBox(context, transform = new DOMMatrix()) {
|
||||
context.save();
|
||||
|
||||
drawBox(context, cursor, transform = new DOMMatrix()) {
|
||||
const m = transform.multiply(this.matrix);
|
||||
|
||||
context.setTransform(m);
|
||||
|
||||
// Draw the box itself
|
||||
context.save();
|
||||
|
||||
const localbb = new BoundingBox({
|
||||
x: -this.canvas.width / 2,
|
||||
y: -this.canvas.height / 2,
|
||||
w: this.canvas.width,
|
||||
h: this.canvas.height,
|
||||
});
|
||||
|
||||
// Line Style
|
||||
context.strokeStyle = "#FFF";
|
||||
context.lineWidth = 2;
|
||||
context.setLineDash([4, 2]);
|
||||
|
||||
const tl = m.transformPoint(localbb.tl);
|
||||
const tr = m.transformPoint(localbb.tr);
|
||||
const bl = m.transformPoint(localbb.bl);
|
||||
const br = m.transformPoint(localbb.br);
|
||||
|
||||
const bbc = m.transformPoint({x: 0, y: 0});
|
||||
|
||||
context.beginPath();
|
||||
context.strokeRect(
|
||||
context.arc(bbc.x, bbc.y, 5, 0, Math.PI * 2);
|
||||
context.stroke();
|
||||
|
||||
context.setLineDash([4, 2]);
|
||||
|
||||
// Draw main rectangle
|
||||
context.beginPath();
|
||||
context.moveTo(tl.x, tl.y);
|
||||
context.lineTo(tr.x, tr.y);
|
||||
context.lineTo(br.x, br.y);
|
||||
context.lineTo(bl.x, bl.y);
|
||||
context.lineTo(tl.x, tl.y);
|
||||
context.stroke();
|
||||
|
||||
// Draw handles
|
||||
const drawHandle = (pt, hover) => {
|
||||
let hsz = config.handleDrawSize / 2;
|
||||
if (hover) hsz *= config.handleDrawHoverScale;
|
||||
|
||||
const hm = new DOMMatrix().rotateSelf(this.rotation);
|
||||
|
||||
const htl = hm.transformPoint({x: -hsz, y: -hsz});
|
||||
const htr = hm.transformPoint({x: hsz, y: -hsz});
|
||||
const hbr = hm.transformPoint({x: hsz, y: hsz});
|
||||
const hbl = hm.transformPoint({x: -hsz, y: hsz});
|
||||
|
||||
context.beginPath();
|
||||
context.moveTo(htl.x + pt.x, htl.y + pt.y);
|
||||
context.lineTo(htr.x + pt.x, htr.y + pt.y);
|
||||
context.lineTo(hbr.x + pt.x, hbr.y + pt.y);
|
||||
context.lineTo(hbl.x + pt.x, hbl.y + pt.y);
|
||||
context.lineTo(htl.x + pt.x, htl.y + pt.y);
|
||||
context.stroke();
|
||||
};
|
||||
|
||||
context.strokeStyle = "#FFF";
|
||||
context.lineWidth = 2;
|
||||
context.setLineDash([]);
|
||||
|
||||
const {ontl, ontr, onbl, onbr} = this.hoveringHandle(cursor.x, cursor.y);
|
||||
|
||||
drawHandle(tl, ontl);
|
||||
drawHandle(tr, ontr);
|
||||
drawHandle(bl, onbl);
|
||||
drawHandle(br, onbr);
|
||||
|
||||
context.restore();
|
||||
|
||||
return () => {
|
||||
const border = config.handleDrawSize * config.handleDrawHoverScale;
|
||||
|
||||
const minx = Math.min(tl.x, tr.x, bl.x, br.x) - border;
|
||||
const maxx = Math.max(tl.x, tr.x, bl.x, br.x) + border;
|
||||
const miny = Math.min(tl.y, tr.y, bl.y, br.y) - border;
|
||||
const maxy = Math.max(tl.y, tr.y, bl.y, br.y) + border;
|
||||
|
||||
context.clearRect(minx, miny, maxx - minx, maxy - miny);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the selected image
|
||||
*
|
||||
* @param {CanvasRenderingContext2D} context A context for rendering the image to
|
||||
* @param {CanvasRenderingContext2D} peekctx A context for rendering the layer peeking to
|
||||
* @param {object} options
|
||||
* @param {DOMMatrix} options.transform A transformation matrix to transform the position by
|
||||
* @param {number} options.opacity Opacity of the peek display
|
||||
*/
|
||||
drawImage(context, peekctx, options = {}) {
|
||||
defaultOpt(options, {
|
||||
transform: new DOMMatrix(),
|
||||
opacity: 0.4,
|
||||
});
|
||||
|
||||
context.save();
|
||||
peekctx.save();
|
||||
|
||||
const m = options.transform.multiply(this.matrix);
|
||||
|
||||
// Draw image
|
||||
context.setTransform(m);
|
||||
context.drawImage(
|
||||
this.canvas,
|
||||
-this.canvas.width / 2,
|
||||
-this.canvas.height / 2,
|
||||
this.canvas.width,
|
||||
this.canvas.height
|
||||
);
|
||||
context.stroke();
|
||||
|
||||
context.restore();
|
||||
// Draw peek
|
||||
peekctx.filter = `opacity(${options.opacity * 100}%)`;
|
||||
peekctx.setTransform(m);
|
||||
peekctx.drawImage(
|
||||
this.canvas,
|
||||
-this.canvas.width / 2,
|
||||
-this.canvas.height / 2,
|
||||
this.canvas.width,
|
||||
this.canvas.height
|
||||
);
|
||||
|
||||
peekctx.restore();
|
||||
context.restore();
|
||||
|
||||
return () => {
|
||||
context.save();
|
||||
// Here we only save transform for performance
|
||||
const pt = context.getTransform();
|
||||
const ppt = context.getTransform();
|
||||
|
||||
context.setTransform(m);
|
||||
peekctx.setTransform(m);
|
||||
|
||||
context.clearRect(
|
||||
-this.canvas.width / 2 - 10,
|
||||
|
@ -402,46 +601,16 @@ const _tool = {
|
|||
this.canvas.height + 20
|
||||
);
|
||||
|
||||
context.restore();
|
||||
peekctx.clearRect(
|
||||
-this.canvas.width / 2 - 10,
|
||||
-this.canvas.height / 2 - 10,
|
||||
this.canvas.width + 20,
|
||||
this.canvas.height + 20
|
||||
);
|
||||
|
||||
context.setTransform(pt);
|
||||
peekctx.setTransform(ppt);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the selected images
|
||||
*
|
||||
* @param {CanvasRenderingContext2D} context A context for rendering the box to
|
||||
* @param {DOMMatrix} transform A transformation matrix to transform the position by
|
||||
*/
|
||||
drawImage(context, transform = new DOMMatrix()) {
|
||||
context.save();
|
||||
context.setTransform(transform);
|
||||
|
||||
context.scale(this.scale, this.scale);
|
||||
context.rotate((this.rotation * 180) / Math.PI);
|
||||
context.translate(this.position.x, this.position.y);
|
||||
|
||||
context.restore();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Marquee Selection with an image
|
||||
*/
|
||||
_marquee_selection(state) {
|
||||
return {
|
||||
// Location of the origin of the selection
|
||||
position: {x: 0, y: 0},
|
||||
// Scale of the selection
|
||||
scale: 1,
|
||||
// Angle of the selection (radians)
|
||||
rotation: 0,
|
||||
|
||||
/**
|
||||
* Draws the selection
|
||||
*/
|
||||
draw() {},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
name;
|
||||
|
|
|
@ -34,6 +34,12 @@ const selectTransformTool = () =>
|
|||
keyboard.onShortcut({ctrl: true, key: "KeyX"}, state.ctrlxcb);
|
||||
|
||||
state.selected = null;
|
||||
|
||||
// Register Layer
|
||||
state.originalDisplayLayer = imageCollection.registerLayer(null, {
|
||||
after: uil.layer,
|
||||
category: "select-display",
|
||||
});
|
||||
},
|
||||
(state, opt) => {
|
||||
// Clear all those listeners and shortcuts we set up
|
||||
|
@ -61,6 +67,10 @@ const selectTransformTool = () =>
|
|||
|
||||
// Clears overlay
|
||||
imageCollection.inputElement.style.cursor = "auto";
|
||||
|
||||
// Delete Layer
|
||||
imageCollection.deleteLayer(state.originalDisplayLayer);
|
||||
state.originalDisplayLayer = null;
|
||||
},
|
||||
{
|
||||
init: (state) => {
|
||||
|
@ -104,15 +114,14 @@ const selectTransformTool = () =>
|
|||
// Clears selection and make things right
|
||||
state.reset = (erase = false) => {
|
||||
if (state.selected && !erase)
|
||||
state.originalLayer.ctx.drawImage(
|
||||
state.original.image,
|
||||
state.original.layer.ctx.drawImage(
|
||||
state.selected.canvas,
|
||||
state.original.x,
|
||||
state.original.y
|
||||
);
|
||||
|
||||
if (state.originalDisplayLayer) {
|
||||
imageCollection.deleteLayer(state.originalDisplayLayer);
|
||||
state.originalDisplayLayer = null;
|
||||
state.originalDisplayLayer.clear();
|
||||
}
|
||||
|
||||
if (state.dragging) state.dragging = null;
|
||||
|
@ -120,6 +129,7 @@ const selectTransformTool = () =>
|
|||
|
||||
state.rotation = 0;
|
||||
state.original = null;
|
||||
state.moving = false;
|
||||
|
||||
state.redraw();
|
||||
};
|
||||
|
@ -127,23 +137,70 @@ const selectTransformTool = () =>
|
|||
// Selection Handlers
|
||||
const selection = _tool._draggable_selection(state);
|
||||
|
||||
// Mouse Move Handler
|
||||
/** @type {{selected: Point, offset: Point} | null} */
|
||||
let moving = null;
|
||||
/** @type {{handle: Point} | null} */
|
||||
let scaling = null;
|
||||
|
||||
// UI Erasers
|
||||
let eraseSelectedBox = () => null;
|
||||
let eraseSelectedImage = () => null;
|
||||
let eraseCursor = () => null;
|
||||
let eraseSelection = () => null;
|
||||
|
||||
// Redraw UI
|
||||
state.redrawui = () => {
|
||||
// Get cursor positions
|
||||
const {x, y, sx, sy} = _tool._process_cursor(
|
||||
state.lastMouseMove,
|
||||
state.snapToGrid
|
||||
);
|
||||
|
||||
eraseSelectedBox();
|
||||
|
||||
if (state.selected) {
|
||||
eraseSelectedBox = state.selected.drawBox(
|
||||
uiCtx,
|
||||
{x, y},
|
||||
viewport.c2v
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Mouse Move Handler
|
||||
state.movecb = (evn) => {
|
||||
state.lastMouseMove = evn;
|
||||
|
||||
// Get cursor positions
|
||||
const {x, y, sx, sy} = _tool._process_cursor(evn, state.snapToGrid);
|
||||
|
||||
// Erase Cursor
|
||||
eraseSelectedBox();
|
||||
eraseSelectedImage();
|
||||
eraseSelection();
|
||||
eraseCursor();
|
||||
imageCollection.inputElement.style.cursor = "default";
|
||||
|
||||
// Draw Box and Selected Image
|
||||
if (state.selected) {
|
||||
eraseSelectedBox = state.selected.drawBox(uiCtx, viewport.c2v);
|
||||
eraseSelectedBox = state.selected.drawBox(
|
||||
uiCtx,
|
||||
{x, y},
|
||||
viewport.c2v
|
||||
);
|
||||
|
||||
if (
|
||||
state.selected.hoveringBox(x, y) ||
|
||||
state.selected.hoveringHandle(x, y).onHandle
|
||||
) {
|
||||
imageCollection.inputElement.style.cursor = "pointer";
|
||||
}
|
||||
|
||||
eraseSelectedImage = state.selected.drawImage(
|
||||
state.originalDisplayLayer.ctx,
|
||||
ovCtx,
|
||||
{opacity: state.selectionPeekOpacity / 100}
|
||||
);
|
||||
}
|
||||
|
||||
// Draw Selection
|
||||
|
@ -171,42 +228,208 @@ const selectTransformTool = () =>
|
|||
|
||||
// Draw cursor
|
||||
eraseCursor = _tool._cursor_draw(sx, sy);
|
||||
|
||||
// Pointer
|
||||
if (state.selected && state.selected.contains(x, y)) {
|
||||
imageCollection.inputElement.style.cursor = "pointer";
|
||||
}
|
||||
};
|
||||
|
||||
// Handles left mouse clicks
|
||||
state.clickcb = (evn) => {};
|
||||
state.clickcb = (evn) => {
|
||||
if (
|
||||
state.selected &&
|
||||
!(
|
||||
state.selected.rotation === 0 &&
|
||||
state.selected.scale.x === 1 &&
|
||||
state.selected.scale.y === 1 &&
|
||||
state.selected.position.x === state.original.sx &&
|
||||
state.selected.position.y === state.original.sy &&
|
||||
state.original.layer === uil.layer
|
||||
)
|
||||
) {
|
||||
// Put original image back
|
||||
state.original.layer.ctx.drawImage(
|
||||
state.selected.canvas,
|
||||
state.original.x,
|
||||
state.original.y
|
||||
);
|
||||
|
||||
// Erase Original Selection Area
|
||||
commands.runCommand("eraseImage", "Transform Tool Erase", {
|
||||
ctx: state.original.layer.ctx,
|
||||
x: state.original.x,
|
||||
y: state.original.y,
|
||||
w: state.selected.canvas.width,
|
||||
h: state.selected.canvas.height,
|
||||
});
|
||||
|
||||
// Draw Image
|
||||
const {canvas, bb} = cropCanvas(state.originalDisplayLayer.canvas, {
|
||||
border: 10,
|
||||
});
|
||||
commands.runCommand("drawImage", "Transform Tool Apply", {
|
||||
image: canvas,
|
||||
...bb,
|
||||
});
|
||||
}
|
||||
state.reset(true);
|
||||
};
|
||||
|
||||
// Handles left mouse drag start events
|
||||
state.dragstartcb = (evn) => {
|
||||
const {
|
||||
x: ix,
|
||||
y: iy,
|
||||
sx: six,
|
||||
sy: siy,
|
||||
} = _tool._process_cursor({x: evn.ix, y: evn.iy}, state.snapToGrid);
|
||||
const {x, y, sx, sy} = _tool._process_cursor(evn, state.snapToGrid);
|
||||
|
||||
if (state.selected) {
|
||||
const hoveringBox = state.selected.hoveringBox(ix, iy);
|
||||
const hoveringHandle = state.selected.hoveringHandle(ix, iy);
|
||||
|
||||
const localc = state.selected.matrix
|
||||
.inverse()
|
||||
.transformPoint({x: ix, y: iy});
|
||||
|
||||
if (hoveringBox) {
|
||||
// Start dragging
|
||||
moving = {
|
||||
selected: state.selected.position,
|
||||
offset: {
|
||||
x: six - state.selected.position.x,
|
||||
y: siy - state.selected.position.y,
|
||||
},
|
||||
};
|
||||
return;
|
||||
} else if (hoveringHandle.onHandle) {
|
||||
// Start scaling
|
||||
let handle = {x: 0, y: 0};
|
||||
|
||||
const lbb = new BoundingBox({
|
||||
x: -state.selected.canvas.width / 2,
|
||||
y: -state.selected.canvas.height / 2,
|
||||
w: state.selected.canvas.width,
|
||||
h: state.selected.canvas.height,
|
||||
});
|
||||
|
||||
if (hoveringHandle.ontl) {
|
||||
handle = lbb.tl;
|
||||
} else if (hoveringHandle.ontr) {
|
||||
handle = lbb.tr;
|
||||
} else if (hoveringHandle.onbl) {
|
||||
handle = lbb.bl;
|
||||
} else {
|
||||
handle = lbb.br;
|
||||
}
|
||||
|
||||
scaling = {
|
||||
handle,
|
||||
};
|
||||
return;
|
||||
}
|
||||
}
|
||||
selection.dragstartcb(evn);
|
||||
};
|
||||
|
||||
// Handles left mouse drag events
|
||||
state.dragcb = (evn) => {
|
||||
selection.dragcb(evn);
|
||||
const {x, y, sx, sy} = _tool._process_cursor(evn, state.snapToGrid);
|
||||
|
||||
if (state.selected) {
|
||||
if (moving) {
|
||||
state.selected.position = {
|
||||
x: sx - moving.offset.x,
|
||||
y: sy - moving.offset.y,
|
||||
};
|
||||
}
|
||||
|
||||
if (scaling) {
|
||||
/** @type {DOMMatrix} */
|
||||
const m = state.selected.rtmatrix.invertSelf();
|
||||
const lscursor = m.transformPoint({x: sx, y: sy});
|
||||
|
||||
const xs = lscursor.x / scaling.handle.x;
|
||||
const xy = lscursor.y / scaling.handle.y;
|
||||
|
||||
if (!state.keepAspectRatio) state.selected.scale = {x: xs, y: xy};
|
||||
else {
|
||||
const scale = Math.max(xs, xy);
|
||||
state.selected.scale = {x: scale, y: scale};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selection.exists) selection.dragcb(evn);
|
||||
};
|
||||
|
||||
// Handles left mouse drag end events
|
||||
state.dragendcb = (evn) => {
|
||||
selection.dragendcb(evn);
|
||||
const {x, y, sx, sy} = _tool._process_cursor(evn, state.snapToGrid);
|
||||
|
||||
if (selection.exists) {
|
||||
if (selection.bb.w !== 0 && selection.bb.h !== 0) {
|
||||
// TODO: CHANGE THIS
|
||||
state.selected = new _tool.MarqueeSelection(
|
||||
uil.getVisible(selection.bb),
|
||||
selection.bb.center
|
||||
selection.dragendcb(evn);
|
||||
|
||||
const bb = selection.bb;
|
||||
|
||||
state.reset();
|
||||
|
||||
if (selection.exists && bb.w !== 0 && bb.h !== 0) {
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = bb.w;
|
||||
canvas.height = bb.h;
|
||||
canvas
|
||||
.getContext("2d")
|
||||
.drawImage(
|
||||
uil.canvas,
|
||||
bb.x,
|
||||
bb.y,
|
||||
bb.w,
|
||||
bb.h,
|
||||
0,
|
||||
0,
|
||||
bb.w,
|
||||
bb.h
|
||||
);
|
||||
|
||||
uil.ctx.clearRect(bb.x, bb.y, bb.w, bb.h);
|
||||
|
||||
state.original = {
|
||||
...bb,
|
||||
sx: selection.bb.center.x,
|
||||
sy: selection.bb.center.y,
|
||||
layer: uil.layer,
|
||||
};
|
||||
state.selected = new _tool.MarqueeSelection(canvas, bb.center);
|
||||
}
|
||||
|
||||
selection.deselect();
|
||||
}
|
||||
|
||||
if (state.selected) {
|
||||
if (moving) {
|
||||
state.selected.position = {
|
||||
x: sx - moving.offset.x,
|
||||
y: sy - moving.offset.y,
|
||||
};
|
||||
}
|
||||
|
||||
if (scaling) {
|
||||
/** @type {DOMMatrix} */
|
||||
const m = state.selected.rtmatrix.invertSelf();
|
||||
const lscursor = m.transformPoint({x: sx, y: sy});
|
||||
|
||||
const xs = lscursor.x / scaling.handle.x;
|
||||
const xy = lscursor.y / scaling.handle.y;
|
||||
|
||||
if (!state.keepAspectRatio) state.selected.scale = {x: xs, y: xy};
|
||||
else {
|
||||
const scale = Math.max(xs, xy);
|
||||
state.selected.scale = {x: scale, y: scale};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
moving = null;
|
||||
scaling = null;
|
||||
|
||||
state.redraw();
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue