aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/InkStrokeProperties.ts
diff options
context:
space:
mode:
authorvkalev <50213748+vkalev@users.noreply.github.com>2021-06-30 12:52:52 -0500
committervkalev <50213748+vkalev@users.noreply.github.com>2021-06-30 12:52:52 -0500
commitb0efa4a390415072eaeb06c8719ea57d73e10466 (patch)
tree867e9bd5c1c05908e2e6ab39f0b7abf18b2e5272 /src/client/views/InkStrokeProperties.ts
parent441a3dab4ada425d28a55435be51339e3d28c892 (diff)
ink Bézier handle movement fixed + small visual changes
Diffstat (limited to 'src/client/views/InkStrokeProperties.ts')
-rw-r--r--src/client/views/InkStrokeProperties.ts119
1 files changed, 89 insertions, 30 deletions
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 533fdf006..5c5395984 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -2,12 +2,11 @@ import { action, computed, observable } from "mobx";
import { ColorState } from 'react-color';
import { Doc, Field, Opt } from "../../fields/Doc";
import { Document } from "../../fields/documentSchemas";
-import { InkField, InkData } from "../../fields/InkField";
+import { InkField, InkData, PointData } from "../../fields/InkField";
import { Cast, NumCast } from "../../fields/Types";
import { DocumentType } from "../documents/DocumentTypes";
import { SelectionManager } from "../util/SelectionManager";
import { undoBatch } from "../util/UndoManager";
-import { bool } from "sharp";
export class InkStrokeProperties {
static Instance: InkStrokeProperties | undefined;
@@ -15,15 +14,15 @@ export class InkStrokeProperties {
private _lastFill = "#D0021B";
private _lastLine = "#D0021B";
private _lastDash = "2";
- private _inkDocs: { x: number, y: number, width: number, height: number }[] = [];
- // Indicates whether the ink is locked.
@observable _lock = false;
- // Indicates whether the ink's format is being currently edited (displaying of control points).
@observable _controlBtn = false;
- // Stores the index of the current selected control point of the ink instance.
@observable _currPoint = -1;
+ constructor() {
+ InkStrokeProperties.Instance = this;
+ }
+
getField(key: string) {
return this.selectedInk?.reduce((p, i) =>
(p === undefined || (p && p === i.rootDoc[key])) && i.rootDoc[key] !== "0" ? Field.toString(i.rootDoc[key] as Field) : "", undefined as Opt<string>);
@@ -79,10 +78,6 @@ export class InkStrokeProperties {
});
}
- constructor() {
- InkStrokeProperties.Instance = this;
- }
-
/**
* Adds a new control point to the ink instance when editing its format.
* @param x The x-coordinate of the current new point.
@@ -213,36 +208,100 @@ export class InkStrokeProperties {
}
/**
- * Handles the movement / scaling of control points of an ink instance.
- * @param xDiff The movement of the control point's x-coordinate.
- * @param yDiff The movement of the control point's y-coordinate.
- * @param controlNum The index of the current control point selected.
- * @returns The changed x- and y-coordinates of the control points.
+ * Handles the movement/scaling of a control point.
*/
@undoBatch
@action
- control = (xDiff: number, yDiff: number, controlNum: number) =>
- this.applyFunction((doc: Doc, ink: InkData, ptsXscale: number, ptsYscale: number) => {
+ moveControl = (deltaX: number, deltaY: number, controlIndex: number) =>
+ this.applyFunction((doc: Doc, ink: InkData, xScale: number, yScale: number) => {
const newPoints: { X: number, Y: number }[] = [];
- const order = controlNum % 4;
+ const order = controlIndex % 4;
for (var i = 0; i < ink.length; i++) {
- newPoints.push(
- (controlNum === i ||
- (order === 0 && i === controlNum + 1) ||
- (order === 0 && controlNum !== 0 && i === controlNum - 2) ||
- (order === 0 && controlNum !== 0 && i === controlNum - 1) ||
- (order === 3 && i === controlNum - 1) ||
- (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 1) ||
- (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 2) ||
- ((ink[0].X === ink[ink.length - 1].X) && (ink[0].Y === ink[ink.length - 1].Y) && (i === 0 || i === ink.length - 1) && (controlNum === 0 || controlNum === ink.length - 1))
- ) ?
- { X: ink[i].X - xDiff / ptsXscale, Y: ink[i].Y - yDiff / ptsYscale } :
- { X: ink[i].X, Y: ink[i].Y });
+ const leftHandlePoint = order === 0 && i === controlIndex + 1;
+ const rightHandlePoint = order === 0 && controlIndex !== 0 && i === controlIndex - 2;
+ if (controlIndex === i ||
+ leftHandlePoint ||
+ rightHandlePoint ||
+ (order === 0 && controlIndex !== 0 && i === controlIndex - 1) ||
+ (order === 3 && i === controlIndex - 1) ||
+ (order === 3 && controlIndex !== ink.length - 1 && i === controlIndex + 1) ||
+ (order === 3 && controlIndex !== ink.length - 1 && i === controlIndex + 2) ||
+ ((ink[0].X === ink[ink.length - 1].X) && (ink[0].Y === ink[ink.length - 1].Y) && (i === 0 || i === ink.length - 1) && (controlIndex === 0 || controlIndex === ink.length - 1))) {
+ newPoints.push({ X: ink[i].X - deltaX / xScale, Y: ink[i].Y - deltaY / yScale });
+ } else {
+ newPoints.push({ X: ink[i].X, Y: ink[i].Y });
+ }
}
return newPoints;
});
/**
+ * Rotates the target point about the origin point for a given angle (radians).
+ */
+ @action
+ rotatePoint = (target: PointData, origin: PointData, angle: number) => {
+ target.X -= origin.X;
+ target.Y -= origin.Y;
+ const newX = Math.cos(angle) * target.X - Math.sin(angle) * target.Y;
+ const newY = Math.sin(angle) * target.X + Math.cos(angle) * target.Y;
+ target.X = newX + origin.X;
+ target.Y = newY + origin.Y;
+ return target
+ }
+
+ /**
+ * Finds the angle difference (in radians) between two vectors relative to an arbitrary origin.
+ */
+ angleChange = (a: PointData, b: PointData, origin: PointData) => {
+ // Finding vector representation of inputted points relative to new origin.
+ let vectorA = { X: a.X - origin.X, Y: a.Y - origin.Y };
+ let vectorB = { X: b.X - origin.X, Y: b.Y - origin.Y };
+ const crossProduct = vectorB.X * vectorA.Y - vectorB.Y * vectorA.X;
+ // Determining whether rotation is clockwise or counterclockwise.
+ const sign = crossProduct < 0 ? 1 : -1;
+ const magnitudeA = Math.sqrt(vectorA.X * vectorA.X + vectorA.Y * vectorA.Y);
+ const magnitudeB = Math.sqrt(vectorB.X * vectorB.X + vectorB.Y * vectorB.Y);
+ // Normalizing the vectors.
+ vectorA = { X: vectorA.X / magnitudeA, Y: vectorA.Y / magnitudeA };
+ vectorB = { X: vectorB.X / magnitudeB, Y: vectorB.Y / magnitudeB };
+ const dotProduct = vectorB.X * vectorA.X + vectorB.Y * vectorA.Y;
+ const theta = Math.acos(dotProduct);
+ return sign * theta;
+ }
+
+ /**
+ * Handles the movement/scaling of a handle point.
+ */
+ @undoBatch
+ @action
+ moveHandle = (deltaX: number, deltaY: number, handleIndex: number) =>
+ this.applyFunction((doc: Doc, ink: InkData, xScale: number, yScale: number) => {
+ const newPoints: { X: number, Y: number }[] = [];
+ const order = handleIndex % 4;
+ let newHandlePoint = { X: 0, Y: 0 };
+
+ for (var i = 0; i < ink.length; i++) {
+ if (handleIndex === i) {
+ newHandlePoint = { X: ink[i].X - deltaX / xScale, Y: ink[i].Y - deltaY / yScale };
+ newPoints.push({ X: newHandlePoint.X, Y: newHandlePoint.Y });
+ } else {
+ newPoints.push({ X: ink[i].X, Y: ink[i].Y });
+ }
+ }
+
+ if (handleIndex !== 1 && handleIndex !== ink.length - 2) {
+ const oldHandlePoint = ink[handleIndex];
+ 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);
+ oppositeHandlePoint = this.rotatePoint(oppositeHandlePoint, controlPoint, angle);
+ order === 1 ? newPoints[handleIndex - 3] = oppositeHandlePoint : newPoints[handleIndex + 3] = oppositeHandlePoint;
+ }
+
+ return newPoints;
+ });
+
+ /**
* Changes the color of the border of the ink instance.
* @param color The new hex value to change the border to.
* @returns true.