aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/views/InkHandles.tsx32
-rw-r--r--src/client/views/InkStrokeProperties.ts6
-rw-r--r--src/fields/InkField.ts2
3 files changed, 32 insertions, 8 deletions
diff --git a/src/client/views/InkHandles.tsx b/src/client/views/InkHandles.tsx
index c2163c124..993e427b3 100644
--- a/src/client/views/InkHandles.tsx
+++ b/src/client/views/InkHandles.tsx
@@ -15,6 +15,8 @@ export interface InkControlProps {
@observer
export class InkHandles extends React.Component<InkControlProps> {
+ @observable private _brokenIndices: number[] = [];
+
/**
* Handles the movement of a selected handle point when the user clicks and drags.
* @param handleNum The index of the currently selected handle point.
@@ -24,13 +26,25 @@ export class InkHandles extends React.Component<InkControlProps> {
InkStrokeProperties.Instance.moveControl(0, 0, 1);
const controlUndo = UndoManager.StartBatch("DocDecs set radius");
const screenScale = this.props.ScreenToLocalTransform().Scale;
+ document.addEventListener("keydown", (e: KeyboardEvent) => this.onBreakTangent(e, handleNum), true);
setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => {
- InkStrokeProperties.Instance?.moveHandle(-delta[0] * screenScale, -delta[1] * screenScale, handleNum);
+ InkStrokeProperties.Instance?.moveHandle(-delta[0] * screenScale, -delta[1] * screenScale, handleNum, this._brokenIndices);
return false;
}, () => controlUndo?.end(), emptyFunction
);
}
}
+
+ @action
+ onBreakTangent = (e: KeyboardEvent, handleIndex: number) => {
+ if (["Alt"].includes(e.key)) {
+ const order = handleIndex % 4;
+ const oppositeHandleIndex = order === 1 ? handleIndex - 3 : handleIndex + 3;
+ if (!this._brokenIndices.includes(handleIndex) && !this._brokenIndices.includes(oppositeHandleIndex)) {
+ this._brokenIndices.push(handleIndex, oppositeHandleIndex);
+ }
+ }
+ }
render() {
const formatInstance = InkStrokeProperties.Instance;
@@ -39,15 +53,15 @@ export class InkHandles extends React.Component<InkControlProps> {
const handlePoints: HandlePoint[] = [];
const handleLines: HandleLine[] = [];
if (data.length >= 4) {
- // adding first and last (single) handle lines
- handleLines.push({ X1: data[0].X, Y1: data[0].Y, X2: data[1].X, Y2: data[1].Y, dot1: 0, dot2: 0 });
- handleLines.push({ X1: data[data.length - 2].X, Y1: data[data.length - 2].Y, X2: data[data.length - 1].X, Y2: data[data.length - 1].Y, dot1: data.length - 1, dot2: data.length - 1 });
for (let i = 0; i <= data.length - 4; i += 4) {
handlePoints.push({ X: data[i + 1].X, Y: data[i + 1].Y, I: i + 1, dot1: i, dot2: i === 0 ? i : i - 1 });
handlePoints.push({ X: data[i + 2].X, Y: data[i + 2].Y, I: i + 2, dot1: i + 3, dot2: i === data.length ? i + 3 : i + 4 });
}
+ // adding first and last (single) handle lines
+ handleLines.push({ X1: data[0].X, Y1: data[0].Y, X2: data[0].X, Y2: data[0].Y, X3: data[1].X, Y3: data[1].Y, dot1: 0, dot2: 0 });
+ handleLines.push({ X1: data[data.length - 2].X, Y1: data[data.length - 2].Y, X2: data[data.length - 1].X, Y2: data[data.length - 1].Y, X3: data[data.length - 1].X, Y3: data[data.length - 1].Y, dot1: data.length - 1, dot2: data.length - 1 });
for (let i = 2; i < data.length - 4; i += 4) {
- handleLines.push({ X1: data[i].X, Y1: data[i].Y, X2: data[i + 3].X, Y2: data[i + 3].Y, dot1: i + 1, dot2: i + 2 });
+ handleLines.push({ X1: data[i].X, Y1: data[i].Y, X2: data[i + 1].X, Y2: data[i + 1].Y, X3: data[i + 3].X, Y3: data[i + 3].Y, dot1: i + 1, dot2: i + 2 });
}
}
const [left, top, scaleX, scaleY, strokeWidth, dotsize] = this.props.format;
@@ -77,6 +91,14 @@ export class InkHandles extends React.Component<InkControlProps> {
stroke="#1F85DE"
strokeWidth={dotsize / 8}
display={(pts.dot1 === formatInstance._currentPoint || pts.dot2 === formatInstance._currentPoint) ? "inherit" : "none"} />
+ <line
+ x1={(pts.X2 - left - strokeWidth / 2) * scaleX + strokeWidth / 2}
+ y1={(pts.Y2 - top - strokeWidth / 2) * scaleY + strokeWidth / 2}
+ x2={(pts.X3 - left - strokeWidth / 2) * scaleX + strokeWidth / 2}
+ y2={(pts.Y3 - top - strokeWidth / 2) * scaleY + strokeWidth / 2}
+ stroke="#1F85DE"
+ strokeWidth={dotsize / 8}
+ display={(pts.dot1 === formatInstance._currentPoint || pts.dot2 === formatInstance._currentPoint) ? "inherit" : "none"} />
</svg>)}
</>
);
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index a5c028730..812e8ff6e 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -220,15 +220,15 @@ export class InkStrokeProperties {
*/
@undoBatch
@action
- moveHandle = (deltaX: number, deltaY: number, handleIndex: number) =>
+ moveHandle = (deltaX: number, deltaY: number, handleIndex: number, brokenIndices: number[]) =>
this.applyFunction((doc: Doc, ink: InkData, xScale: number, yScale: number) => {
const order = handleIndex % 4;
const oldHandlePoint = ink[handleIndex];
const newHandlePoint = { X: ink[handleIndex].X - deltaX / xScale, Y: ink[handleIndex].Y - deltaY / yScale };
ink[handleIndex] = newHandlePoint;
- // Rotating opposite handle (first and final control point only have one handle).
- if (handleIndex !== 1 && handleIndex !== ink.length - 2) {
+ // Rotate opposite handle if user hasn't held 'Alt' key or not first/final control (which have only 1 handle).
+ if (!brokenIndices.includes(handleIndex) && handleIndex !== 1 && handleIndex !== ink.length - 2) {
let oppositeHandlePoint = order === 1 ? ink[handleIndex - 3] : ink[handleIndex + 3];
const controlPoint = order === 1 ? ink[handleIndex - 1] : ink[handleIndex + 1];
const angle = this.angleChange(oldHandlePoint, newHandlePoint, controlPoint);
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index c158dac42..485376a34 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -42,6 +42,8 @@ export interface HandleLine {
Y1: number;
X2: number;
Y2: number;
+ X3: number;
+ Y3: number;
dot1: number;
dot2: number;
}