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>
|
<script src="js/global.js?v=5ee46bd" type="text/javascript"></script>
|
||||||
|
|
||||||
<!-- Base Libs -->
|
<!-- 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/events.js?v=2ab7933" type="text/javascript"></script>
|
||||||
<script src="js/lib/input.js?v=09298ac" 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/layers.js?v=a1f8aea" type="text/javascript"></script>
|
||||||
<script src="js/lib/commands.js?v=bf23c83" 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/lib/ui.js?v=76ede2b" type="text/javascript"></script>
|
||||||
|
|
||||||
<script
|
<script
|
||||||
src="js/initalize/layers.populate.js?v=938a1b2"
|
src="js/initalize/layers.populate.js?v=fcca23e"
|
||||||
type="text/javascript"></script>
|
type="text/javascript"></script>
|
||||||
|
|
||||||
<!-- Configuration -->
|
<!-- Configuration -->
|
||||||
|
@ -370,7 +370,7 @@
|
||||||
|
|
||||||
<!-- Load Tools -->
|
<!-- Load Tools -->
|
||||||
<script
|
<script
|
||||||
src="js/ui/tool/generic.js?v=d63d268"
|
src="js/ui/tool/generic.js?v=9f1f84e"
|
||||||
type="text/javascript"></script>
|
type="text/javascript"></script>
|
||||||
|
|
||||||
<script src="js/ui/tool/dream.js?v=76bc565" 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"
|
src="js/ui/tool/colorbrush.js?v=3f8c01a"
|
||||||
type="text/javascript"></script>
|
type="text/javascript"></script>
|
||||||
<script
|
<script
|
||||||
src="js/ui/tool/select.js?v=44e8ec4"
|
src="js/ui/tool/select.js?v=080237c"
|
||||||
type="text/javascript"></script>
|
type="text/javascript"></script>
|
||||||
<script src="js/ui/tool/stamp.js?v=3c07ac8" type="text/javascript"></script>
|
<script src="js/ui/tool/stamp.js?v=3c07ac8" type="text/javascript"></script>
|
||||||
<script
|
<script
|
||||||
|
|
|
@ -322,7 +322,8 @@ mouse.listen.camera.onwheel.on((evn) => {
|
||||||
|
|
||||||
//viewport.transform(imageCollection.element);
|
//viewport.transform(imageCollection.element);
|
||||||
|
|
||||||
toolbar.currentTool.redraw();
|
toolbar._current_tool.state.redrawui &&
|
||||||
|
toolbar._current_tool.state.redrawui();
|
||||||
});
|
});
|
||||||
|
|
||||||
const cameraPaintStart = (evn) => {
|
const cameraPaintStart = (evn) => {
|
||||||
|
@ -348,6 +349,9 @@ const cameraPaint = (evn) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
viewport.transform(imageCollection.element);
|
viewport.transform(imageCollection.element);
|
||||||
|
toolbar._current_tool.state.redrawui &&
|
||||||
|
toolbar._current_tool.state.redrawui();
|
||||||
|
|
||||||
if (global.debug) {
|
if (global.debug) {
|
||||||
debugCtx.clearRect(0, 0, debugCanvas.width, debugCanvas.height);
|
debugCtx.clearRect(0, 0, debugCanvas.width, debugCanvas.height);
|
||||||
debugCtx.fillStyle = "#F0F";
|
debugCtx.fillStyle = "#F0F";
|
||||||
|
|
|
@ -90,14 +90,20 @@ const toolbar = {
|
||||||
name: toolname,
|
name: toolname,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
_element: null,
|
_element: null,
|
||||||
state: {},
|
state: {
|
||||||
|
redrawui: () => tool.redraw(),
|
||||||
|
},
|
||||||
options,
|
options,
|
||||||
/**
|
/**
|
||||||
* If the tool has a redraw() function in its state, then run it
|
* If the tool has a redraw() function in its state, then run it
|
||||||
*/
|
*/
|
||||||
redraw: () => {
|
redraw: () => {
|
||||||
|
tool.state.redrawui && tool.state.redrawui();
|
||||||
tool.state.redraw && tool.state.redraw();
|
tool.state.redraw && tool.state.redraw();
|
||||||
},
|
},
|
||||||
|
redrawui: () => {
|
||||||
|
tool.state.redrawui && tool.state.redrawui();
|
||||||
|
},
|
||||||
enable: (opt = null) => {
|
enable: (opt = null) => {
|
||||||
if (toolbar._locked) return;
|
if (toolbar._locked) return;
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,16 @@ class BoundingBox {
|
||||||
return {x: this.x, y: this.y};
|
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} */
|
/** @type {Point} */
|
||||||
get br() {
|
get br() {
|
||||||
return {x: this.x + this.w, y: this.y + this.h};
|
return {x: this.x + this.w, y: this.y + this.h};
|
||||||
|
|
|
@ -315,12 +315,40 @@ const _tool = {
|
||||||
/** @type {HTMLCanvasElement} */
|
/** @type {HTMLCanvasElement} */
|
||||||
canvas;
|
canvas;
|
||||||
|
|
||||||
|
_dirty = false;
|
||||||
|
_position = {x: 0, y: 0};
|
||||||
/**
|
/**
|
||||||
* @type {Point}
|
* @type {Point}
|
||||||
*/
|
*/
|
||||||
position = {x: 0, y: 0};
|
get position() {
|
||||||
scale = {x: 1, y: 1};
|
return this._position;
|
||||||
rotation = 0;
|
}
|
||||||
|
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
|
* @param {HTMLCanvasElement} canvas Selected image canvas
|
||||||
|
@ -331,14 +359,28 @@ const _tool = {
|
||||||
this.position = position;
|
this.position = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @type {DOMMatrix} */
|
||||||
|
_rtmatrix = null;
|
||||||
|
get rtmatrix() {
|
||||||
|
if (!this._rtmatrix || this._dirty) {
|
||||||
|
const m = new DOMMatrix();
|
||||||
|
|
||||||
|
m.translateSelf(this.position.x, this.position.y);
|
||||||
|
m.rotateSelf((this.rotation * 180) / Math.PI);
|
||||||
|
|
||||||
|
this._rtmatrix = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._rtmatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {DOMMatrix} */
|
||||||
|
_matrix = null;
|
||||||
get matrix() {
|
get matrix() {
|
||||||
const m = new DOMMatrix();
|
if (!this._matrix || this._dirty) {
|
||||||
|
this._matrix = this.rtmatrix.scaleSelf(this.scale.x, this.scale.y);
|
||||||
m.scaleSelf(this.scale.x, this.scale.y);
|
}
|
||||||
m.rotateSelf((this.rotation * 180) / Math.PI);
|
return this._matrix;
|
||||||
m.translateSelf(this.position.x, this.position.y);
|
|
||||||
|
|
||||||
return m;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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
|
* Draws the marquee selector box
|
||||||
*
|
*
|
||||||
* @param {CanvasRenderingContext2D} context A context for rendering the box to
|
* @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
|
* @param {DOMMatrix} transform A transformation matrix to transform the position by
|
||||||
*/
|
*/
|
||||||
drawBox(context, transform = new DOMMatrix()) {
|
drawBox(context, cursor, transform = new DOMMatrix()) {
|
||||||
context.save();
|
|
||||||
|
|
||||||
const m = transform.multiply(this.matrix);
|
const m = transform.multiply(this.matrix);
|
||||||
|
|
||||||
context.setTransform(m);
|
|
||||||
|
|
||||||
// Draw the box itself
|
|
||||||
context.save();
|
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
|
// Line Style
|
||||||
context.strokeStyle = "#FFF";
|
context.strokeStyle = "#FFF";
|
||||||
context.lineWidth = 2;
|
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.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.width / 2,
|
||||||
-this.canvas.height / 2,
|
-this.canvas.height / 2,
|
||||||
this.canvas.width,
|
this.canvas.width,
|
||||||
this.canvas.height
|
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();
|
context.restore();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
context.save();
|
// Here we only save transform for performance
|
||||||
|
const pt = context.getTransform();
|
||||||
|
const ppt = context.getTransform();
|
||||||
|
|
||||||
context.setTransform(m);
|
context.setTransform(m);
|
||||||
|
peekctx.setTransform(m);
|
||||||
|
|
||||||
context.clearRect(
|
context.clearRect(
|
||||||
-this.canvas.width / 2 - 10,
|
-this.canvas.width / 2 - 10,
|
||||||
|
@ -402,46 +601,16 @@ const _tool = {
|
||||||
this.canvas.height + 20
|
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);
|
keyboard.onShortcut({ctrl: true, key: "KeyX"}, state.ctrlxcb);
|
||||||
|
|
||||||
state.selected = null;
|
state.selected = null;
|
||||||
|
|
||||||
|
// Register Layer
|
||||||
|
state.originalDisplayLayer = imageCollection.registerLayer(null, {
|
||||||
|
after: uil.layer,
|
||||||
|
category: "select-display",
|
||||||
|
});
|
||||||
},
|
},
|
||||||
(state, opt) => {
|
(state, opt) => {
|
||||||
// Clear all those listeners and shortcuts we set up
|
// Clear all those listeners and shortcuts we set up
|
||||||
|
@ -61,6 +67,10 @@ const selectTransformTool = () =>
|
||||||
|
|
||||||
// Clears overlay
|
// Clears overlay
|
||||||
imageCollection.inputElement.style.cursor = "auto";
|
imageCollection.inputElement.style.cursor = "auto";
|
||||||
|
|
||||||
|
// Delete Layer
|
||||||
|
imageCollection.deleteLayer(state.originalDisplayLayer);
|
||||||
|
state.originalDisplayLayer = null;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
init: (state) => {
|
init: (state) => {
|
||||||
|
@ -104,15 +114,14 @@ const selectTransformTool = () =>
|
||||||
// Clears selection and make things right
|
// Clears selection and make things right
|
||||||
state.reset = (erase = false) => {
|
state.reset = (erase = false) => {
|
||||||
if (state.selected && !erase)
|
if (state.selected && !erase)
|
||||||
state.originalLayer.ctx.drawImage(
|
state.original.layer.ctx.drawImage(
|
||||||
state.original.image,
|
state.selected.canvas,
|
||||||
state.original.x,
|
state.original.x,
|
||||||
state.original.y
|
state.original.y
|
||||||
);
|
);
|
||||||
|
|
||||||
if (state.originalDisplayLayer) {
|
if (state.originalDisplayLayer) {
|
||||||
imageCollection.deleteLayer(state.originalDisplayLayer);
|
state.originalDisplayLayer.clear();
|
||||||
state.originalDisplayLayer = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.dragging) state.dragging = null;
|
if (state.dragging) state.dragging = null;
|
||||||
|
@ -120,6 +129,7 @@ const selectTransformTool = () =>
|
||||||
|
|
||||||
state.rotation = 0;
|
state.rotation = 0;
|
||||||
state.original = null;
|
state.original = null;
|
||||||
|
state.moving = false;
|
||||||
|
|
||||||
state.redraw();
|
state.redraw();
|
||||||
};
|
};
|
||||||
|
@ -127,23 +137,70 @@ const selectTransformTool = () =>
|
||||||
// Selection Handlers
|
// Selection Handlers
|
||||||
const selection = _tool._draggable_selection(state);
|
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 eraseSelectedBox = () => null;
|
||||||
|
let eraseSelectedImage = () => null;
|
||||||
let eraseCursor = () => null;
|
let eraseCursor = () => null;
|
||||||
let eraseSelection = () => 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.movecb = (evn) => {
|
||||||
|
state.lastMouseMove = evn;
|
||||||
|
|
||||||
// Get cursor positions
|
// Get cursor positions
|
||||||
const {x, y, sx, sy} = _tool._process_cursor(evn, state.snapToGrid);
|
const {x, y, sx, sy} = _tool._process_cursor(evn, state.snapToGrid);
|
||||||
|
|
||||||
// Erase Cursor
|
// Erase Cursor
|
||||||
eraseSelectedBox();
|
eraseSelectedBox();
|
||||||
|
eraseSelectedImage();
|
||||||
eraseSelection();
|
eraseSelection();
|
||||||
eraseCursor();
|
eraseCursor();
|
||||||
imageCollection.inputElement.style.cursor = "default";
|
imageCollection.inputElement.style.cursor = "default";
|
||||||
|
|
||||||
// Draw Box and Selected Image
|
// Draw Box and Selected Image
|
||||||
if (state.selected) {
|
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
|
// Draw Selection
|
||||||
|
@ -171,42 +228,208 @@ const selectTransformTool = () =>
|
||||||
|
|
||||||
// Draw cursor
|
// Draw cursor
|
||||||
eraseCursor = _tool._cursor_draw(sx, sy);
|
eraseCursor = _tool._cursor_draw(sx, sy);
|
||||||
|
|
||||||
// Pointer
|
|
||||||
if (state.selected && state.selected.contains(x, y)) {
|
|
||||||
imageCollection.inputElement.style.cursor = "pointer";
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handles left mouse clicks
|
// 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
|
// Handles left mouse drag start events
|
||||||
state.dragstartcb = (evn) => {
|
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);
|
selection.dragstartcb(evn);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handles left mouse drag events
|
// Handles left mouse drag events
|
||||||
state.dragcb = (evn) => {
|
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
|
// Handles left mouse drag end events
|
||||||
state.dragendcb = (evn) => {
|
state.dragendcb = (evn) => {
|
||||||
selection.dragendcb(evn);
|
const {x, y, sx, sy} = _tool._process_cursor(evn, state.snapToGrid);
|
||||||
|
|
||||||
if (selection.exists) {
|
if (selection.exists) {
|
||||||
if (selection.bb.w !== 0 && selection.bb.h !== 0) {
|
selection.dragendcb(evn);
|
||||||
// TODO: CHANGE THIS
|
|
||||||
state.selected = new _tool.MarqueeSelection(
|
const bb = selection.bb;
|
||||||
uil.getVisible(selection.bb),
|
|
||||||
selection.bb.center
|
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();
|
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();
|
state.redraw();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue