UNPKG

@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
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