@aurigma/design-atoms
Version:
Design Atoms is a part of Customer's Canvas SDK which allows for manipulating individual design elements through your code.
350 lines • 17.8 kB
JavaScript
import { PointF, getTriangleAngle, normalizeAngle, EqualsOfFloatNumbers } from "@aurigma/design-atoms-model/Math";
import { ArgumentException } from "@aurigma/design-atoms-model/Exception";
import { SelectionState, SelectionHandler } from "./SelectionHandler";
import { EventObject } from "@aurigma/design-atoms-model/EventObject";
import { GroupItemHandler } from "../../ItemHandlers/GroupItemHandler";
export class SelectionRectangleHandler {
constructor(_selectionModifier = null) {
this._selectionModifier = _selectionModifier;
this._cumulativeDelta = new PointF(0, 0);
this._resizeIndex = null;
this._resizeOrigin = null;
this._allowNegativeResize = null;
this._itemHandlerResizeStartRects = null;
this._state = SelectionState.Idle;
this.startMovingByPoint = (startPoint, startRectangle) => {
this._checkStartArguments("startMovingByPoint");
this._setStartParams(startRectangle, startPoint);
this._setState(SelectionState.Drag);
};
this.updateMovingByPoint = (args) => {
var _a, _b, _c, _d, _e;
this._checkUpdateArguments("updateMove", SelectionState.Drag, args.finished);
const { point, finished } = args;
const lastPoint = (_a = this._lastPoint) !== null && _a !== void 0 ? _a : this._startPoint;
let delta = new PointF(point.x - this._startPoint.x, point.y - this._startPoint.y);
if (((_b = this._selectionModifier) === null || _b === void 0 ? void 0 : _b.beforeMovePerformed) != null) {
const momentDelta = new PointF(point.x - lastPoint.x, point.y - lastPoint.y);
let args = {
delta: delta,
momentDelta: momentDelta,
startPoint: this._startPoint,
startRectangle: this._startRectangle
};
delta = (_c = this._selectionModifier) === null || _c === void 0 ? void 0 : _c.beforeMovePerformed(args);
}
const targetMomentDelta = new PointF(this._startPoint.x + delta.x - lastPoint.x, this._startPoint.y + delta.y - lastPoint.y);
if (!((_d = args.allowMoveHorizontal) !== null && _d !== void 0 ? _d : true))
targetMomentDelta.x = 0;
if (!((_e = args.allowMoveVertical) !== null && _e !== void 0 ? _e : true))
targetMomentDelta.y = 0;
this._lastPoint = new PointF(lastPoint.x + targetMomentDelta.x, lastPoint.y + targetMomentDelta.y);
var r = this._rectangle.clone();
r.centerX = this._rectangle.centerX + targetMomentDelta.x;
r.centerY = this._rectangle.centerY + targetMomentDelta.y;
this._updateRectangle(r, finished);
this._rectangleCenterChanged.notify({ delta: targetMomentDelta.clone(), finished: finished });
};
this.startResizingByPoint = (startPoint, startRectangle, resizeIndex, allowNegativeResize, itemHandlers) => {
this._checkStartArguments("startResizingByPoint");
if (resizeIndex == null || allowNegativeResize == null) {
this._reset();
throw new ArgumentException("SelectionRectangleHandler.startResize: resizeIndex and allowNegativeResize cannot be null!");
}
this._resizeIndex = resizeIndex;
this._allowNegativeResize = allowNegativeResize;
this._setStartParams(startRectangle, startPoint);
this._setState(SelectionState.Resize);
let origin = new PointF(this._startRectangle.center.x - (SelectionHandler.cw[this._resizeIndex] * this._startRectangle.width / 2), this._startRectangle.center.y - (SelectionHandler.ch[this._resizeIndex] * this._startRectangle.height / 2));
this._resizeOrigin = origin.rotateAt(this._startRectangle.angle, this._startRectangle.center);
this._itemHandlerResizeStartRects = this._getItemHandlersRects(itemHandlers);
};
this._setState = (state) => {
if (this._state === state)
return;
this._state = state;
this._stateChanged.notify(state);
};
this._reset = () => {
this._lastPoint = null;
this._startPoint = null;
this._startRectangle = null;
this._resizeIndex = null;
this._resizeOrigin = null;
this._cumulativeDelta.x = 0;
this._cumulativeDelta.y = 0;
this._setState(SelectionState.Idle);
};
this._rectangleChanged = new EventObject();
this._rectangleAngleChanged = new EventObject();
this._rectangleResized = new EventObject();
this._rectangleCenterChanged = new EventObject();
this._stateChanged = new EventObject();
this._checkStartArguments = (methodName) => {
try {
if (this._state !== SelectionState.Idle)
throw new ArgumentException(`SelectionRectangleHandler.${methodName}: cannot start operation because ${SelectionState[this._state]} operation is in progress!`);
}
catch (ex) {
this._reset();
throw ex;
}
};
this._checkUpdateArguments = (methodName, state, finished) => {
try {
if (this._rectangle == null)
throw new ArgumentException(`SelectionRectangleHandler.${methodName}: rectangle must be defined`);
if (this._startRectangle == null || this._state !== state)
throw new ArgumentException(`SelectionRectangleHandler.${methodName}: ${SelectionState[this._state]} operation haven't started`);
if (finished && this._state === SelectionState.Idle)
throw new ArgumentException(`SelectionRectangleHandler.${methodName}: ${SelectionState[SelectionState.Idle]} cannot be finished`);
}
catch (ex) {
this._reset();
throw ex;
}
};
}
get rectangle() {
return this._rectangle;
}
startRotatingByPoint(startPoint, startRectangle) {
this._checkStartArguments("startRotatingByPoint");
this._setStartParams(startRectangle, startPoint);
this._setState(SelectionState.Rotate);
}
updateRotatingByPoint(params) {
var _a;
this._checkUpdateArguments("updateRotation", SelectionState.Rotate, params.finished);
const { point, tolerance, rotationStep, finished } = params;
let p1 = point.clone(), p2 = this._startPoint.clone(), r = this._rectangle.clone(), p3 = new PointF(r.centerX, r.centerY), angle = getTriangleAngle(p1, p2, p3);
if (EqualsOfFloatNumbers(angle, 0))
return;
angle = normalizeAngle(this._startRectangle.angle + angle);
var newAngle = angle;
var mod = angle % rotationStep;
if (mod < tolerance) {
newAngle = rotationStep * Math.floor(angle / rotationStep);
}
else if (mod > rotationStep - tolerance) {
newAngle = rotationStep * Math.floor(angle / rotationStep + 1);
}
r.angle = normalizeAngle(newAngle);
if (((_a = this._selectionModifier) === null || _a === void 0 ? void 0 : _a.beforeRotatePerformed) != null) {
let data = {
currentRect: r,
previousRectangle: this._rectangle
};
r = this._selectionModifier.beforeRotatePerformed(data);
}
const args = {
angle: r.angle - this._rectangle.angle,
origin: r.center,
finished: finished
};
this._updateRectangle(r, finished);
this._rectangleAngleChanged.notify(args);
}
_getItemHandlersRects(itemHandlers) {
let result = [];
const getHandlerRect = (handler) => {
result.push({ itemType: handler.item.type, startRectangle: handler.rectangle });
};
itemHandlers.forEach(handler => {
if (handler instanceof GroupItemHandler)
handler.getNestedItemHandlers(true).forEach(getHandlerRect);
else
getHandlerRect(handler);
});
return result;
}
updateResizingByPoint(args, itemHandlers = null) {
var _a;
this._checkUpdateArguments("updateResize", SelectionState.Resize, args.finished);
let { point, arbitraryResize, finished } = args;
if (point == null) {
point = this._lastPoint;
}
this._lastPoint = point;
const startRect = this._startRectangle;
var t, r = this._rectangle.clone();
// new width or height, depends on resize action
var newDem = new PointF(point.x - startRect.centerX, point.y - startRect.centerY);
newDem.rotate(-r.angle);
// old width or height, depends on resize action
var oldDem = new PointF(this._startPoint.x - startRect.centerX, this._startPoint.y - startRect.centerY);
oldDem.rotate(-r.angle);
// added width or height, depends on resize action
var addedDem = newDem.translate(-oldDem.x, -oldDem.y);
//word interpretation of gripses (movable anchors)
var gripsDict = { left: 6, top: 5 };
// 1 or -1 (for choosing plus or minus action)
var signMultiplierX = 1;
var signMultiplierY = 1;
if (!arbitraryResize) {
var dx = addedDem.x * SelectionHandler.cw[this._resizeIndex] / startRect.width;
var dy = addedDem.y * SelectionHandler.ch[this._resizeIndex] / startRect.height;
var d = dx < dy ? dx : dy;
addedDem.x = startRect.width * d;
addedDem.y = startRect.height * d;
r.width = startRect.width + addedDem.x;
r.height = startRect.height + addedDem.y;
}
else {
r.width = startRect.width + addedDem.x * SelectionHandler.cw[this._resizeIndex];
r.height = startRect.height + addedDem.y * SelectionHandler.ch[this._resizeIndex];
if (this._resizeIndex === gripsDict["left"] ||
this._resizeIndex === gripsDict["top"]) {
signMultiplierX = -1;
signMultiplierY = -1;
}
if (this._resizeIndex === 1) {
signMultiplierX = -1;
signMultiplierY = -1;
}
if (this._resizeIndex === 2) {
signMultiplierX = 1;
signMultiplierY = -1;
}
if (this._resizeIndex === 4) {
signMultiplierX = -1;
signMultiplierY = 1;
}
}
if (this._allowNegativeResize || r.width >= 0 && r.height >= 0) {
t = new PointF(addedDem.x / 2 * SelectionHandler.cw[this._resizeIndex], addedDem.y / 2 * SelectionHandler.ch[this._resizeIndex]);
t.rotate(r.angle);
r.centerX = startRect.centerX + (t.x * signMultiplierX);
r.centerY = startRect.centerY + (t.y * signMultiplierY);
if (arbitraryResize) {
var oldCenter = t = new PointF(startRect.centerX, startRect.centerY);
oldCenter.rotate(-r.angle);
oldCenter.x =
oldCenter.x +
addedDem.x /
2 *
SelectionHandler.cw[this._resizeIndex] *
SelectionHandler.cw[this._resizeIndex]; //signMultiplierX;
oldCenter.y =
oldCenter.y +
addedDem.y /
2 *
SelectionHandler.ch[this._resizeIndex] *
SelectionHandler.ch[this._resizeIndex]; //signMultiplierY;
oldCenter.rotate(r.angle);
r.centerX = oldCenter.x;
r.centerY = oldCenter.y;
}
}
else {
var newCenter = new PointF((-SelectionHandler.cw[this._resizeIndex] * this._rectangle.width) / 2, (-SelectionHandler.ch[this._resizeIndex] * this._rectangle.height) / 2);
newCenter.rotate(this._rectangle.angle);
newCenter.translate(this._rectangle.centerX, this._rectangle.centerY);
r.center = newCenter;
if (!arbitraryResize) {
// should keep proportions
var widthToHeightRate = startRect.width / startRect.height;
if (widthToHeightRate >= 1) {
r.width = Math.max(r.width, 1 * widthToHeightRate);
r.height = Math.max(r.height, 1);
}
else {
r.width = Math.max(r.width, 1);
r.height = Math.max(r.height, 1 / widthToHeightRate);
}
}
else {
r.width = Math.max(r.width, 1);
r.height = Math.max(r.height, 1);
}
var diff = new PointF((SelectionHandler.cw[this._resizeIndex] * r.width) / 2, (SelectionHandler.ch[this._resizeIndex] * r.height) / 2);
diff.rotate(r.angle);
r.centerX += diff.x;
r.centerY += diff.y;
}
if (((_a = this._selectionModifier) === null || _a === void 0 ? void 0 : _a.beforeResizePerformed) != null) {
let data = {
arbitraryResize: args.arbitraryResize,
currentRect: r,
previousRectangle: this._rectangle,
resizeIndex: args.resizeIndex,
startRectangle: this._startRectangle,
startItemHandlersRects: this._itemHandlerResizeStartRects,
rectangleItemHandlers: itemHandlers
};
r = this._selectionModifier.beforeResizePerformed(data);
}
if (EqualsOfFloatNumbers(r.width, 0))
r.width = 1;
if (EqualsOfFloatNumbers(r.height, 0))
r.height = 1;
var scaleX = r.width / this._rectangle.width;
var scaleY = r.height / this._rectangle.height;
const eventArgs = {
finished: finished,
origin: this._resizeOrigin,
scaleX: scaleX,
scaleY: scaleY,
angle: r.angle
};
this._updateRectangle(r, finished);
this._rectangleResized.notify(eventArgs);
}
startMove(startRectangle) {
this._checkStartArguments("startMove");
this._setStartParams(startRectangle, null);
this._setState(SelectionState.Drag);
}
move(params) {
var _a, _b, _c, _d;
this._checkUpdateArguments("move", SelectionState.Drag, params.finished);
this._cumulativeDelta.x += params.delta.x;
this._cumulativeDelta.y += params.delta.y;
if (((_a = this._selectionModifier) === null || _a === void 0 ? void 0 : _a.beforeMovePerformed) != null) {
let args = {
delta: this._cumulativeDelta,
momentDelta: params.delta,
startPoint: this._startPoint,
startRectangle: this._startRectangle
};
this._cumulativeDelta = (_b = this._selectionModifier) === null || _b === void 0 ? void 0 : _b.beforeMovePerformed(args);
}
if (!((_c = params.allowMoveHorizontal) !== null && _c !== void 0 ? _c : true))
this._cumulativeDelta.x = 0;
if (!((_d = params.allowMoveVertical) !== null && _d !== void 0 ? _d : true))
this._cumulativeDelta.y = 0;
var r = this._rectangle.clone();
if (!params.finished) {
r.centerX = this._startRectangle.centerX + this._cumulativeDelta.x;
r.centerY = this._startRectangle.centerY + this._cumulativeDelta.y;
}
var correctedDelta = new PointF((r.centerX - this._rectangle.centerX), (r.centerY - this._rectangle.centerY));
this._updateRectangle(r, params.finished);
this._rectangleCenterChanged.notify({ delta: correctedDelta, finished: params.finished });
}
_updateRectangle(rectangle, finished) {
this._rectangle = rectangle;
const args = { rectangle: rectangle, finished: finished };
this._rectangleChanged.notify(args);
if (finished) {
this._reset();
}
}
addRectangleChanged(handler) { this._rectangleChanged.add(handler); }
removeRectangleChanged(handler) { this._rectangleChanged.remove(handler); }
addRectangleAngleChanged(handler) { this._rectangleAngleChanged.add(handler); }
removeRectangleAngleChanged(handler) { this._rectangleAngleChanged.remove(handler); }
addRectangleResized(handler) { this._rectangleResized.add(handler); }
removeRectangleResized(handler) { this._rectangleResized.remove(handler); }
addRectangleMoved(handler) { this._rectangleCenterChanged.add(handler); }
removeRectangleMoved(handler) { this._rectangleCenterChanged.remove(handler); }
addStateChanged(handler) { this._stateChanged.add(handler); }
removeStateChanged(handler) { this._stateChanged.remove(handler); }
_setStartParams(startRectangle, startPoint) {
this._rectangle = startRectangle;
this._startRectangle = startRectangle;
if (startPoint != null)
this._startPoint = startPoint;
}
}
//# sourceMappingURL=SelectionRectangleHandler.js.map